use crate::{ item::{ ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, ShowDiagnostics, TabContentParams, WeakItemHandle, }, move_item, notifications::NotifyResultExt, toolbar::Toolbar, workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings}, CloseWindow, CopyPath, CopyRelativePath, NewFile, NewTerminal, OpenInTerminal, OpenTerminal, OpenVisible, SplitDirection, ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace,};use anyhow::Result;use collections::{BTreeSet, HashMap, HashSet, VecDeque};use futures::{stream::FuturesUnordered, StreamExt};use gpui::{ actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement, AppContext, AsyncWindowContext, ClickEvent, ClipboardItem, Div, DragMoveEvent, EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, FocusableView, KeyContext, Model, MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakView, WindowContext,};use itertools::Itertools;use language::DiagnosticSeverity;use parking_lot::Mutex;use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};use serde::Deserialize;use settings::{Settings, SettingsStore};use std::{ any::Any, cmp, fmt, mem, ops::ControlFlow, path::PathBuf, rc::Rc, sync::{ atomic::{AtomicUsize, Ordering}, Arc, },};use theme::ThemeSettings;use ui::{ prelude::*, right_click_menu, ButtonSize, Color, DecoratedIcon, IconButton, IconButtonShape, IconDecoration, IconDecorationKind, IconName, IconSize, Indicator, Label, PopoverMenu, PopoverMenuHandle, Tab, TabBar, TabPosition, Tooltip,};use ui::{v_flex, ContextMenu};use util::{debug_panic, maybe, truncate_and_remove_front, ResultExt}; /// A selected entry in e.g. project panel.#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]pub struct SelectedEntry { pub worktree_id: WorktreeId, pub entry_id: ProjectEntryId,} /// A group of selected entries from project panel.#[derive(Debug)]pub struct DraggedSelection { pub active_selection: SelectedEntry, pub marked_selections: Arc<BTreeSet<SelectedEntry>>,} impl DraggedSelection { pub fn items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a SelectedEntry> + 'a> { if self.marked_selections.contains(&self.active_selection) { Box::new(self.marked_selections.iter()) } else { Box::new(std::iter::once(&self.active_selection)) } }} #[derive(PartialEq, Clone, Copy, Deserialize, Debug)]#[serde(rename_all = "camelCase")]pub enum SaveIntent { /// write all files (even if unchanged) /// prompt before overwriting on-disk changes Save, /// same as Save, but without auto formatting SaveWithoutFormat, /// write any files that have local changes /// prompt before overwriting on-disk changes SaveAll, /// always prompt for a new path SaveAs, /// prompt "you have unsaved changes" before writing Close, /// write all dirty files, don't prompt on conflict Overwrite, /// skip all save-related behavior Skip,} #[derive(Clone, Deserialize, PartialEq, Debug)]pub struct ActivateItem(pub usize); #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseActiveItem { pub save_intent: Option<SaveIntent>,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseInactiveItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseAllItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseCleanItems { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseItemsToTheRight { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct CloseItemsToTheLeft { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, Default)]#[serde(rename_all = "camelCase")]pub struct RevealInProjectPanel { pub entry_id: Option<u64>,} #[derive(Default, PartialEq, Clone, Deserialize)]pub struct DeploySearch { #[serde(default)] pub replace_enabled: bool,} impl_actions!( pane, [ CloseAllItems, CloseActiveItem, CloseCleanItems, CloseItemsToTheLeft, CloseItemsToTheRight, CloseInactiveItems, ActivateItem, RevealInProjectPanel, DeploySearch, ]); actions!( pane, [ ActivatePrevItem, ActivateNextItem, ActivateLastItem, AlternateFile, GoBack, GoForward, JoinIntoNext, JoinAll, ReopenClosedItem, SplitLeft, SplitUp, SplitRight, SplitDown, SplitHorizontal, SplitVertical, SwapItemLeft, SwapItemRight, TogglePreviewTab, TogglePinTab, ]); impl DeploySearch { pub fn find() -> Self { Self { replace_enabled: false, } }} const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; pub enum Event { AddItem { item: Box<dyn ItemHandle>, }, ActivateItem { local: bool, }, Remove { focus_on_pane: Option<View<Pane>>, }, RemoveItem { idx: usize, }, RemovedItem { item_id: EntityId, }, Split(SplitDirection), JoinAll, JoinIntoNext, ChangeItemTitle, Focus, ZoomIn, ZoomOut, UserSavedItem { item: Box<dyn WeakItemHandle>, save_intent: SaveIntent, },} impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Event::AddItem { item } => f .debug_struct("AddItem") .field("item", &item.item_id()) .finish(), Event::ActivateItem { local } => f .debug_struct("ActivateItem") .field("local", local) .finish(), Event::Remove { .. } => f.write_str("Remove"), Event::RemoveItem { idx } => f.debug_struct("RemoveItem").field("idx", idx).finish(), Event::RemovedItem { item_id } => f .debug_struct("RemovedItem") .field("item_id", item_id) .finish(), Event::Split(direction) => f .debug_struct("Split") .field("direction", direction) .finish(), Event::JoinAll => f.write_str("JoinAll"), Event::JoinIntoNext => f.write_str("JoinIntoNext"), Event::ChangeItemTitle => f.write_str("ChangeItemTitle"), Event::Focus => f.write_str("Focus"), Event::ZoomIn => f.write_str("ZoomIn"), Event::ZoomOut => f.write_str("ZoomOut"), Event::UserSavedItem { item, save_intent } => f .debug_struct("UserSavedItem") .field("item", &item.id()) .field("save_intent", save_intent) .finish(), } }} /// A container for 0 to many items that are open in the workspace./// Treats all items uniformly via the [`ItemHandle`] trait, whether it's an editor, search results multibuffer, terminal or something else,/// responsible for managing item tabs, focus and zoom states and drag and drop features./// Can be split, see `PaneGroup` for more details.pub struct Pane { alternate_file_items: ( Option<Box<dyn WeakItemHandle>>, Option<Box<dyn WeakItemHandle>>, ), focus_handle: FocusHandle, items: Vec<Box<dyn ItemHandle>>, activation_history: Vec<ActivationHistoryEntry>, next_activation_timestamp: Arc<AtomicUsize>, zoomed: bool, was_focused: bool, active_item_index: usize, preview_item_id: Option<EntityId>, last_focus_handle_by_item: HashMap<EntityId, WeakFocusHandle>, nav_history: NavHistory, toolbar: View<Toolbar>, pub(crate) workspace: WeakView<Workspace>, project: Model<Project>, drag_split_direction: Option<SplitDirection>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>>, custom_drop_handle: Option<Arc<dyn Fn(&mut Pane, &dyn Any, &mut ViewContext<Pane>) -> ControlFlow<(), ()>>>, can_split_predicate: Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut ViewContext<Self>) -> bool>>, should_display_tab_bar: Rc<dyn Fn(&ViewContext<Pane>) -> bool>, render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> (Option<AnyElement>, Option<AnyElement>)>, _subscriptions: Vec<Subscription>, tab_bar_scroll_handle: ScrollHandle, /// Is None if navigation buttons are permanently turned off (and should not react to setting changes). /// Otherwise, when `display_nav_history_buttons` is Some, it determines whether nav buttons should be displayed. display_nav_history_buttons: Option<bool>, double_click_dispatch_action: Box<dyn Action>, save_modals_spawned: HashSet<EntityId>, pub new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>, pub split_item_context_menu_handle: PopoverMenuHandle<ContextMenu>, pinned_tab_count: usize, diagnostics: HashMap<ProjectPath, DiagnosticSeverity>, zoom_out_on_close: bool,} pub struct ActivationHistoryEntry { pub entity_id: EntityId, pub timestamp: usize,} pub struct ItemNavHistory { history: NavHistory, item: Arc<dyn WeakItemHandle>, is_preview: bool,} #[derive(Clone)]pub struct NavHistory(Arc<Mutex<NavHistoryState>>); struct NavHistoryState { mode: NavigationMode, backward_stack: VecDeque<NavigationEntry>, forward_stack: VecDeque<NavigationEntry>, closed_stack: VecDeque<NavigationEntry>, paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>, pane: WeakView<Pane>, next_timestamp: Arc<AtomicUsize>,} #[derive(Debug, Copy, Clone)]pub enum NavigationMode { Normal, GoingBack, GoingForward, ClosingItem, ReopeningClosedItem, Disabled,} impl Default for NavigationMode { fn default() -> Self { Self::Normal }} pub struct NavigationEntry { pub item: Arc<dyn WeakItemHandle>, pub data: Option<Box<dyn Any + Send>>, pub timestamp: usize, pub is_preview: bool,} #[derive(Clone)]pub struct DraggedTab { pub pane: View<Pane>, pub item: Box<dyn ItemHandle>, pub ix: usize, pub detail: usize, pub is_active: bool,} impl EventEmitter<Event> for Pane {} impl Pane { pub fn new( workspace: WeakView<Workspace>, project: Model<Project>, next_timestamp: Arc<AtomicUsize>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>>, double_click_dispatch_action: Box<dyn Action>, cx: &mut ViewContext<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let subscriptions = vec![ cx.on_focus(&focus_handle, Pane::focus_in), cx.on_focus_in(&focus_handle, Pane::focus_in), cx.on_focus_out(&focus_handle, Pane::focus_out), cx.observe_global::<SettingsStore>(Self::settings_changed), cx.subscribe(&project, Self::project_events), ]; let handle = cx.view().downgrade(); Self { alternate_file_items: (None, None), focus_handle, items: Vec::new(), activation_history: Vec::new(), next_activation_timestamp: next_timestamp.clone(), was_focused: false, zoomed: false, active_item_index: 0, preview_item_id: None, last_focus_handle_by_item: Default::default(), nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState { mode: NavigationMode::Normal, backward_stack: Default::default(), forward_stack: Default::default(), closed_stack: Default::default(), paths_by_item: Default::default(), pane: handle.clone(), next_timestamp, }))), toolbar: cx.new_view(|_| Toolbar::new()), tab_bar_scroll_handle: ScrollHandle::new(), drag_split_direction: None, workspace, project, can_drop_predicate, custom_drop_handle: None, can_split_predicate: None, should_display_tab_bar: Rc::new(|cx| TabBarSettings::get_global(cx).show), render_tab_bar_buttons: Rc::new(move |pane, cx| { if !pane.has_focus(cx) && !pane.context_menu_focused(cx) { return (None, None); } // Ideally we would return a vec of elements here to pass directly to the [TabBar]'s // `end_slot`, but due to needing a view here that isn't possible. let right_children = h_flex() // Instead we need to replicate the spacing from the [TabBar]'s `end_slot` here. .gap(DynamicSpacing::Base04.rems(cx)) .child( PopoverMenu::new("pane-tab-bar-popover-menu") .trigger( IconButton::new("plus", IconName::Plus) .icon_size(IconSize::Small) .tooltip(|cx| Tooltip::text("New...", cx)), ) .anchor(AnchorCorner::TopRight) .with_handle(pane.new_item_context_menu_handle.clone()) .menu(move |cx| { Some(ContextMenu::build(cx, |menu, _| { menu.action("New File", NewFile.boxed_clone()) .action( "Open File", ToggleFileFinder::default().boxed_clone(), ) .separator() .action( "Search Project", DeploySearch { replace_enabled: false, } .boxed_clone(), ) .action( "Search Symbols", ToggleProjectSymbols.boxed_clone(), ) .separator() .action("New Terminal", NewTerminal.boxed_clone()) })) }), ) .child( PopoverMenu::new("pane-tab-bar-split") .trigger( IconButton::new("split", IconName::Split) .icon_size(IconSize::Small) .tooltip(|cx| Tooltip::text("Split Pane", cx)), ) .anchor(AnchorCorner::TopRight) .with_handle(pane.split_item_context_menu_handle.clone()) .menu(move |cx| { ContextMenu::build(cx, |menu, _| { menu.action("Split Right", SplitRight.boxed_clone()) .action("Split Left", SplitLeft.boxed_clone()) .action("Split Up", SplitUp.boxed_clone()) .action("Split Down", SplitDown.boxed_clone()) }) .into() }), ) .child({ let zoomed = pane.is_zoomed(); IconButton::new("toggle_zoom", IconName::Maximize) .icon_size(IconSize::Small) .toggle_state(zoomed) .selected_icon(IconName::Minimize) .on_click(cx.listener(|pane, _, cx| { pane.toggle_zoom(&crate::ToggleZoom, cx); })) .tooltip(move |cx| { Tooltip::for_action( if zoomed { "Zoom Out" } else { "Zoom In" }, &ToggleZoom, cx, ) }) }) .into_any_element() .into(); (None, right_children) }),Lines 1–500 of 4064. Load more
use crate::{ item::{ ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, ShowDiagnostics, TabContentParams, WeakItemHandle, }, move_item, notifications::NotifyResultExt, toolbar::Toolbar, workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings}, CloseWindow, CopyPath, CopyRelativePath, NewFile, NewTerminal, OpenInTerminal, OpenTerminal, OpenVisible, SplitDirection, ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace, }; use anyhow::Result; use collections::{BTreeSet, HashMap, HashSet, VecDeque}; use futures::{stream::FuturesUnordered, StreamExt}; use gpui::{ actions, anchored, deferred, impl_actions, prelude::*, Action, AnchorCorner, AnyElement, AppContext, AsyncWindowContext, ClickEvent, ClipboardItem, Div, DragMoveEvent, EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, FocusableView, KeyContext, Model, MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle, Subscription, Task, View, ViewContext, VisualContext, WeakFocusHandle, WeakView, WindowContext, }; use itertools::Itertools; use language::DiagnosticSeverity; use parking_lot::Mutex; use project::{Project, ProjectEntryId, ProjectPath, WorktreeId}; use serde::Deserialize; use settings::{Settings, SettingsStore}; use std::{ any::Any, cmp, fmt, mem, ops::ControlFlow, path::PathBuf, rc::Rc, sync::{ atomic::{AtomicUsize, Ordering}, Arc, }, }; use theme::ThemeSettings; use ui::{ prelude::*, right_click_menu, ButtonSize, Color, DecoratedIcon, IconButton, IconButtonShape, IconDecoration, IconDecorationKind, IconName, IconSize, Indicator, Label, PopoverMenu, PopoverMenuHandle, Tab, TabBar, TabPosition, Tooltip, }; use ui::{v_flex, ContextMenu}; use util::{debug_panic, maybe, truncate_and_remove_front, ResultExt}; /// A selected entry in e.g. project panel. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SelectedEntry { pub worktree_id: WorktreeId, pub entry_id: ProjectEntryId, } /// A group of selected entries from project panel. #[derive(Debug)] pub struct DraggedSelection { pub active_selection: SelectedEntry, pub marked_selections: Arc<BTreeSet<SelectedEntry>>, } impl DraggedSelection { pub fn items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a SelectedEntry> + 'a> { if self.marked_selections.contains(&self.active_selection) { Box::new(self.marked_selections.iter()) } else { Box::new(std::iter::once(&self.active_selection)) } } } #[derive(PartialEq, Clone, Copy, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub enum SaveIntent { /// write all files (even if unchanged) /// prompt before overwriting on-disk changes Save, /// same as Save, but without auto formatting SaveWithoutFormat, /// write any files that have local changes /// prompt before overwriting on-disk changes SaveAll, /// always prompt for a new path SaveAs, /// prompt "you have unsaved changes" before writing Close, /// write all dirty files, don't prompt on conflict Overwrite, /// skip all save-related behavior Skip, } #[derive(Clone, Deserialize, PartialEq, Debug)] pub struct ActivateItem(pub usize); #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseActiveItem { pub save_intent: Option<SaveIntent>, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseInactiveItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseAllItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseCleanItems { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseItemsToTheRight { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct CloseItemsToTheLeft { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct RevealInProjectPanel { pub entry_id: Option<u64>, } #[derive(Default, PartialEq, Clone, Deserialize)] pub struct DeploySearch { #[serde(default)] pub replace_enabled: bool, } impl_actions!( pane, [ CloseAllItems, CloseActiveItem, CloseCleanItems, CloseItemsToTheLeft, CloseItemsToTheRight, CloseInactiveItems, ActivateItem, RevealInProjectPanel, DeploySearch, ] ); actions!( pane, [ ActivatePrevItem, ActivateNextItem, ActivateLastItem, AlternateFile, GoBack, GoForward, JoinIntoNext, JoinAll, ReopenClosedItem, SplitLeft, SplitUp, SplitRight, SplitDown, SplitHorizontal, SplitVertical, SwapItemLeft, SwapItemRight, TogglePreviewTab, TogglePinTab, ] ); impl DeploySearch { pub fn find() -> Self { Self { replace_enabled: false, } } } const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; pub enum Event { AddItem { item: Box<dyn ItemHandle>, }, ActivateItem { local: bool, }, Remove { focus_on_pane: Option<View<Pane>>, }, RemoveItem { idx: usize, }, RemovedItem { item_id: EntityId, }, Split(SplitDirection), JoinAll, JoinIntoNext, ChangeItemTitle, Focus, ZoomIn, ZoomOut, UserSavedItem { item: Box<dyn WeakItemHandle>, save_intent: SaveIntent, }, } impl fmt::Debug for Event { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Event::AddItem { item } => f .debug_struct("AddItem") .field("item", &item.item_id()) .finish(), Event::ActivateItem { local } => f .debug_struct("ActivateItem") .field("local", local) .finish(), Event::Remove { .. } => f.write_str("Remove"), Event::RemoveItem { idx } => f.debug_struct("RemoveItem").field("idx", idx).finish(), Event::RemovedItem { item_id } => f .debug_struct("RemovedItem") .field("item_id", item_id) .finish(), Event::Split(direction) => f .debug_struct("Split") .field("direction", direction) .finish(), Event::JoinAll => f.write_str("JoinAll"), Event::JoinIntoNext => f.write_str("JoinIntoNext"), Event::ChangeItemTitle => f.write_str("ChangeItemTitle"), Event::Focus => f.write_str("Focus"), Event::ZoomIn => f.write_str("ZoomIn"), Event::ZoomOut => f.write_str("ZoomOut"), Event::UserSavedItem { item, save_intent } => f .debug_struct("UserSavedItem") .field("item", &item.id()) .field("save_intent", save_intent) .finish(), } } } /// A container for 0 to many items that are open in the workspace. /// Treats all items uniformly via the [`ItemHandle`] trait, whether it's an editor, search results multibuffer, terminal or something else, /// responsible for managing item tabs, focus and zoom states and drag and drop features. /// Can be split, see `PaneGroup` for more details. pub struct Pane { alternate_file_items: ( Option<Box<dyn WeakItemHandle>>, Option<Box<dyn WeakItemHandle>>, ), focus_handle: FocusHandle, items: Vec<Box<dyn ItemHandle>>, activation_history: Vec<ActivationHistoryEntry>, next_activation_timestamp: Arc<AtomicUsize>, zoomed: bool, was_focused: bool, active_item_index: usize, preview_item_id: Option<EntityId>, last_focus_handle_by_item: HashMap<EntityId, WeakFocusHandle>, nav_history: NavHistory, toolbar: View<Toolbar>, pub(crate) workspace: WeakView<Workspace>, project: Model<Project>, drag_split_direction: Option<SplitDirection>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool>>, custom_drop_handle: Option<Arc<dyn Fn(&mut Pane, &dyn Any, &mut ViewContext<Pane>) -> ControlFlow<(), ()>>>, can_split_predicate: Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut ViewContext<Self>) -> bool>>, should_display_tab_bar: Rc<dyn Fn(&ViewContext<Pane>) -> bool>, render_tab_bar_buttons: Rc<dyn Fn(&mut Pane, &mut ViewContext<Pane>) -> (Option<AnyElement>, Option<AnyElement>)>, _subscriptions: Vec<Subscription>, tab_bar_scroll_handle: ScrollHandle, /// Is None if navigation buttons are permanently turned off (and should not react to setting changes). /// Otherwise, when `display_nav_history_buttons` is Some, it determines whether nav buttons should be displayed. display_nav_history_buttons: Option<bool>, double_click_dispatch_action: Box<dyn Action>, save_modals_spawned: HashSet<EntityId>, pub new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>, pub split_item_context_menu_handle: PopoverMenuHandle<ContextMenu>, pinned_tab_count: usize, diagnostics: HashMap<ProjectPath, DiagnosticSeverity>, zoom_out_on_close: bool, } pub struct ActivationHistoryEntry { pub entity_id: EntityId, pub timestamp: usize, } pub struct ItemNavHistory { history: NavHistory, item: Arc<dyn WeakItemHandle>, is_preview: bool, } #[derive(Clone)] pub struct NavHistory(Arc<Mutex<NavHistoryState>>); struct NavHistoryState { mode: NavigationMode, backward_stack: VecDeque<NavigationEntry>, forward_stack: VecDeque<NavigationEntry>, closed_stack: VecDeque<NavigationEntry>, paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>, pane: WeakView<Pane>, next_timestamp: Arc<AtomicUsize>, } #[derive(Debug, Copy, Clone)] pub enum NavigationMode { Normal, GoingBack, GoingForward, ClosingItem, ReopeningClosedItem, Disabled, } impl Default for NavigationMode { fn default() -> Self { Self::Normal } } pub struct NavigationEntry { pub item: Arc<dyn WeakItemHandle>, pub data: Option<Box<dyn Any + Send>>, pub timestamp: usize, pub is_preview: bool, } #[derive(Clone)] pub struct DraggedTab { pub pane: View<Pane>, pub item: Box<dyn ItemHandle>, pub ix: usize, pub detail: usize, pub is_active: bool, } impl EventEmitter<Event> for Pane {} impl Pane { pub fn new( workspace: WeakView<Workspace>, project: Model<Project>, next_timestamp: Arc<AtomicUsize>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut WindowContext) -> bool + 'static>>, double_click_dispatch_action: Box<dyn Action>, cx: &mut ViewContext<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let subscriptions = vec![ cx.on_focus(&focus_handle, Pane::focus_in), cx.on_focus_in(&focus_handle, Pane::focus_in), cx.on_focus_out(&focus_handle, Pane::focus_out), cx.observe_global::<SettingsStore>(Self::settings_changed), cx.subscribe(&project, Self::project_events), ]; let handle = cx.view().downgrade(); Self { alternate_file_items: (None, None), focus_handle, items: Vec::new(), activation_history: Vec::new(), next_activation_timestamp: next_timestamp.clone(), was_focused: false, zoomed: false, active_item_index: 0, preview_item_id: None, last_focus_handle_by_item: Default::default(), nav_history: NavHistory(Arc::new(Mutex::new(NavHistoryState { mode: NavigationMode::Normal, backward_stack: Default::default(), forward_stack: Default::default(), closed_stack: Default::default(), paths_by_item: Default::default(), pane: handle.clone(), next_timestamp, }))), toolbar: cx.new_view(|_| Toolbar::new()), tab_bar_scroll_handle: ScrollHandle::new(), drag_split_direction: None, workspace, project, can_drop_predicate, custom_drop_handle: None, can_split_predicate: None, should_display_tab_bar: Rc::new(|cx| TabBarSettings::get_global(cx).show), render_tab_bar_buttons: Rc::new(move |pane, cx| { if !pane.has_focus(cx) && !pane.context_menu_focused(cx) { return (None, None); } // Ideally we would return a vec of elements here to pass directly to the [TabBar]'s // `end_slot`, but due to needing a view here that isn't possible. let right_children = h_flex() // Instead we need to replicate the spacing from the [TabBar]'s `end_slot` here. .gap(DynamicSpacing::Base04.rems(cx)) .child( PopoverMenu::new("pane-tab-bar-popover-menu") .trigger( IconButton::new("plus", IconName::Plus) .icon_size(IconSize::Small) .tooltip(|cx| Tooltip::text("New...", cx)), ) .anchor(AnchorCorner::TopRight) .with_handle(pane.new_item_context_menu_handle.clone()) .menu(move |cx| { Some(ContextMenu::build(cx, |menu, _| { menu.action("New File", NewFile.boxed_clone()) .action( "Open File", ToggleFileFinder::default().boxed_clone(), ) .separator() .action( "Search Project", DeploySearch { replace_enabled: false, } .boxed_clone(), ) .action( "Search Symbols", ToggleProjectSymbols.boxed_clone(), ) .separator() .action("New Terminal", NewTerminal.boxed_clone()) })) }), ) .child( PopoverMenu::new("pane-tab-bar-split") .trigger( IconButton::new("split", IconName::Split) .icon_size(IconSize::Small) .tooltip(|cx| Tooltip::text("Split Pane", cx)), ) .anchor(AnchorCorner::TopRight) .with_handle(pane.split_item_context_menu_handle.clone()) .menu(move |cx| { ContextMenu::build(cx, |menu, _| { menu.action("Split Right", SplitRight.boxed_clone()) .action("Split Left", SplitLeft.boxed_clone()) .action("Split Up", SplitUp.boxed_clone()) .action("Split Down", SplitDown.boxed_clone()) }) .into() }), ) .child({ let zoomed = pane.is_zoomed(); IconButton::new("toggle_zoom", IconName::Maximize) .icon_size(IconSize::Small) .toggle_state(zoomed) .selected_icon(IconName::Minimize) .on_click(cx.listener(|pane, _, cx| { pane.toggle_zoom(&crate::ToggleZoom, cx); })) .tooltip(move |cx| { Tooltip::for_action( if zoomed { "Zoom Out" } else { "Zoom In" }, &ToggleZoom, cx, ) }) }) .into_any_element() .into(); (None, right_children) }),