use gpui::{ div, prelude::FluentBuilder, px, rems, App, AppContext, Context, Corner, DefiniteLength, DismissEvent, DragMoveEvent, Empty, Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement as _, IntoElement, ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, WeakEntity, Window, }; use std::sync::Arc; use theme::ActiveTheme; 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}, v_flex, AxisExt, IconName, Placement, Selectable, Sizable, StyledExt, }; #[derive(Clone)] struct TabState { closable: bool, zoomable: bool, draggable: bool, droppable: bool, active_panel: Option>, } #[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(cx.theme().radius) .text_xs() .shadow_lg() .bg(cx.theme().background) .text_color(cx.theme().text_accent) .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) closable: 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(cx) .map(|panel| panel.title(cx)) .unwrap_or("Empty Tab".into_any_element()) } fn closable(&self, cx: &App) -> bool { if !self.closable { return false; } self.active_panel(cx) .map(|panel| panel.closable(cx)) .unwrap_or(true) } fn zoomable(&self, cx: &App) -> bool { self.active_panel(cx) .map(|panel| panel.zoomable(cx)) .unwrap_or(false) } fn visible(&self, cx: &App) -> bool { self.visible_panels(cx).next().is_some() } fn popup_menu(&self, menu: PopupMenu, cx: &App) -> PopupMenu { if let Some(panel) = self.active_panel(cx) { panel.popup_menu(menu, cx) } else { menu } } fn toolbar_buttons(&self, window: &Window, cx: &App) -> Vec