use super::{ panel::PanelView, stack_panel::StackPanel, ClosePanel, DockArea, PanelEvent, PanelStyle, ToggleZoom, }; use crate::{ button::{Button, ButtonVariants as _}, dock_area::{dock::DockPlacement, panel::Panel}, h_flex, popup_menu::{PopupMenu, PopupMenuExt}, tab::{tab_bar::TabBar, Tab}, theme::{scale::ColorScaleStep, ActiveTheme}, v_flex, AxisExt, IconName, Placement, Selectable, Sizable, }; use gpui::{ div, img, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength, DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement as _, IntoElement, ObjectFit, ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, StyledImage, WeakEntity, Window, }; use std::sync::Arc; #[derive(Clone, Copy)] struct TabState { closeable: bool, zoomable: bool, draggable: bool, droppable: bool, } #[derive(Clone)] pub(crate) struct DragPanel { pub(crate) panel: Arc, pub(crate) tab_panel: Entity, } impl DragPanel { pub(crate) fn new(panel: Arc, tab_panel: Entity) -> Self { Self { panel, tab_panel } } } impl Render for DragPanel { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { div() .id("drag-panel") .cursor_grab() .py_1() .px_2() .w_24() .flex() .items_center() .justify_center() .overflow_hidden() .whitespace_nowrap() .rounded(px(cx.theme().radius)) .text_xs() .shadow_lg() .bg(cx.theme().background) .text_color(cx.theme().accent.step(cx, ColorScaleStep::TWELVE)) .child(self.panel.title(cx)) } } pub struct TabPanel { focus_handle: FocusHandle, dock_area: WeakEntity, /// The stock_panel can be None, if is None, that means the panels can't be split or move stack_panel: Option>, pub(crate) panels: Vec>, pub(crate) active_ix: usize, /// If this is true, the Panel closeable will follow the active panel's closeable, /// otherwise this TabPanel will not able to close pub(crate) closeable: bool, tab_bar_scroll_handle: ScrollHandle, is_zoomed: bool, is_collapsed: bool, /// When drag move, will get the placement of the panel to be split will_split_placement: Option, } impl Panel for TabPanel { fn panel_id(&self) -> SharedString { "TabPanel".into() } fn title(&self, cx: &App) -> gpui::AnyElement { self.active_panel() .map(|panel| panel.title(cx)) .unwrap_or("Empty Tab".into_any_element()) } fn closable(&self, cx: &App) -> bool { if !self.closeable { return false; } self.active_panel() .map(|panel| panel.closable(cx)) .unwrap_or(true) } fn zoomable(&self, cx: &App) -> bool { self.active_panel() .map(|panel| panel.zoomable(cx)) .unwrap_or(false) } fn popup_menu(&self, menu: PopupMenu, cx: &App) -> PopupMenu { if let Some(panel) = self.active_panel() { panel.popup_menu(menu, cx) } else { menu } } fn toolbar_buttons(&self, window: &Window, cx: &App) -> Vec