use crate::{ CloseWindow, NewFile, NewTerminal, OpenInTerminal, OpenOptions, OpenTerminal, OpenVisible, SplitDirection, ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace, WorkspaceItemBuilder, item::{ ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, ProjectItemKind, ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle, }, move_item, notifications::NotifyResultExt, toolbar::Toolbar, workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings},};use anyhow::Result;use collections::{BTreeSet, HashMap, HashSet, VecDeque};use futures::{StreamExt, stream::FuturesUnordered};use gpui::{ Action, AnyElement, App, AsyncWindowContext, ClickEvent, ClipboardItem, Context, Corner, Div, DragMoveEvent, Entity, EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, Focusable, KeyContext, MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle, Subscription, Task, WeakEntity, WeakFocusHandle, Window, actions, anchored, deferred, impl_actions, prelude::*,};use itertools::Itertools;use language::DiagnosticSeverity;use parking_lot::Mutex;use project::{DirectoryLister, Project, ProjectEntryId, ProjectPath, WorktreeId};use schemars::JsonSchema;use serde::Deserialize;use settings::{Settings, SettingsStore};use std::{ any::Any, cmp, fmt, mem, ops::ControlFlow, path::PathBuf, rc::Rc, sync::{ Arc, atomic::{AtomicUsize, Ordering}, },};use theme::ThemeSettings;use ui::{ ButtonSize, Color, ContextMenu, ContextMenuEntry, ContextMenuItem, DecoratedIcon, IconButton, IconButtonShape, IconDecoration, IconDecorationKind, IconName, IconSize, Indicator, Label, PopoverMenu, PopoverMenuHandle, ScrollableHandle, Tab, TabBar, TabPosition, Tooltip, prelude::*, right_click_menu,};use util::{ResultExt, debug_panic, maybe, truncate_and_remove_front}; /// 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(Clone, Copy, PartialEq, Debug, Deserialize, JsonSchema)]#[serde(rename_all = "snake_case")]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, PartialEq, Debug, Deserialize, JsonSchema, Default)]pub struct ActivateItem(pub usize); #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseActiveItem { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseInactiveItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseAllItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseCleanItems { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseItemsToTheRight { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct CloseItemsToTheLeft { #[serde(default)] pub close_pinned: bool,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct RevealInProjectPanel { #[serde(skip)] pub entry_id: Option<u64>,} #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)]#[serde(deny_unknown_fields)]pub struct DeploySearch { #[serde(default)] pub replace_enabled: bool, #[serde(default)] pub included_files: Option<String>, #[serde(default)] pub excluded_files: Option<String>,} impl_actions!( pane, [ CloseAllItems, CloseActiveItem, CloseCleanItems, CloseItemsToTheLeft, CloseItemsToTheRight, CloseInactiveItems, ActivateItem, RevealInProjectPanel, DeploySearch, ]); actions!( pane, [ ActivatePreviousItem, 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, included_files: None, excluded_files: None, } }} const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; pub enum Event { AddItem { item: Box<dyn ItemHandle>, }, ActivateItem { local: bool, focus_changed: bool, }, Remove { focus_on_pane: Option<Entity<Pane>>, }, RemoveItem { idx: usize, }, RemovedItem { item: Box<dyn ItemHandle>, }, Split(SplitDirection), ItemPinned, ItemUnpinned, 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 } => f .debug_struct("RemovedItem") .field("item", &item.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(), Event::ItemPinned => f.write_str("ItemPinned"), Event::ItemUnpinned => f.write_str("ItemUnpinned"), } }} /// 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: Entity<Toolbar>, pub(crate) workspace: WeakEntity<Workspace>, project: WeakEntity<Project>, pub drag_split_direction: Option<SplitDirection>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool>>, custom_drop_handle: Option< Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>, >, can_split_predicate: Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut Window, &mut Context<Self>) -> bool>>, can_toggle_zoom: bool, should_display_tab_bar: Rc<dyn Fn(&Window, &mut Context<Pane>) -> bool>, render_tab_bar_buttons: Rc< dyn Fn( &mut Pane, &mut Window, &mut Context<Pane>, ) -> (Option<AnyElement>, Option<AnyElement>), >, render_tab_bar: Rc<dyn Fn(&mut Pane, &mut Window, &mut Context<Pane>) -> AnyElement>, show_tab_bar_buttons: bool, _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>, close_pane_if_empty: bool, 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, /// If a certain project item wants to get recreated with specific data, it can persist its data before the recreation here. pub project_item_restoration_data: HashMap<ProjectItemKind, Box<dyn Any + Send>>,} 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: WeakEntity<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: Entity<Pane>, pub item: Box<dyn ItemHandle>, pub ix: usize, pub detail: usize, pub is_active: bool,} impl EventEmitter<Event> for Pane {} pub enum Side { Left, Right,} #[derive(Copy, Clone)]enum PinOperation { Pin, Unpin,} impl Pane { pub fn new( workspace: WeakEntity<Workspace>, project: Entity<Project>, next_timestamp: Arc<AtomicUsize>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>>, double_click_dispatch_action: Box<dyn Action>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let subscriptions = vec![ cx.on_focus(&focus_handle, window, Pane::focus_in), cx.on_focus_in(&focus_handle, window, Pane::focus_in), cx.on_focus_out(&focus_handle, window, Pane::focus_out), cx.observe_global::<SettingsStore>(Self::settings_changed), cx.subscribe(&project, Self::project_events), ]; let handle = cx.entity().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(|_| Toolbar::new()), tab_bar_scroll_handle: ScrollHandle::new(), drag_split_direction: None, workspace, project: project.downgrade(), can_drop_predicate, custom_drop_handle: None, can_split_predicate: None, can_toggle_zoom: true, should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show), render_tab_bar_buttons: Rc::new(default_render_tab_bar_buttons), render_tab_bar: Rc::new(Self::render_tab_bar), show_tab_bar_buttons: TabBarSettings::get_global(cx).show_tab_bar_buttons, display_nav_history_buttons: Some( TabBarSettings::get_global(cx).show_nav_history_buttons, ), _subscriptions: subscriptions, double_click_dispatch_action, save_modals_spawned: HashSet::default(), close_pane_if_empty: true, split_item_context_menu_handle: Default::default(), new_item_context_menu_handle: Default::default(), pinned_tab_count: 0, diagnostics: Default::default(), zoom_out_on_close: true, project_item_restoration_data: HashMap::default(), } } fn alternate_file(&mut self, window: &mut Window, cx: &mut Context<Pane>) { let (_, alternative) = &self.alternate_file_items; if let Some(alternative) = alternative { let existing = self .items() .find_position(|item| item.item_id() == alternative.id()); if let Some((ix, _)) = existing { self.activate_item(ix, true, true, window, cx); } else if let Some(upgraded) = alternative.upgrade() { self.add_item(upgraded, true, true, None, window, cx); } } } pub fn track_alternate_file_items(&mut self) { if let Some(item) = self.active_item().map(|item| item.downgrade_item()) { let (current, _) = &self.alternate_file_items; match current { Some(current) => { if current.id() != item.id() {Lines 1–500 of 5417. Load more
use crate::{ CloseWindow, NewFile, NewTerminal, OpenInTerminal, OpenOptions, OpenTerminal, OpenVisible, SplitDirection, ToggleFileFinder, ToggleProjectSymbols, ToggleZoom, Workspace, WorkspaceItemBuilder, item::{ ActivateOnClose, ClosePosition, Item, ItemHandle, ItemSettings, PreviewTabsSettings, ProjectItemKind, ShowCloseButton, ShowDiagnostics, TabContentParams, TabTooltipContent, WeakItemHandle, }, move_item, notifications::NotifyResultExt, toolbar::Toolbar, workspace_settings::{AutosaveSetting, TabBarSettings, WorkspaceSettings}, }; use anyhow::Result; use collections::{BTreeSet, HashMap, HashSet, VecDeque}; use futures::{StreamExt, stream::FuturesUnordered}; use gpui::{ Action, AnyElement, App, AsyncWindowContext, ClickEvent, ClipboardItem, Context, Corner, Div, DragMoveEvent, Entity, EntityId, EventEmitter, ExternalPaths, FocusHandle, FocusOutEvent, Focusable, KeyContext, MouseButton, MouseDownEvent, NavigationDirection, Pixels, Point, PromptLevel, Render, ScrollHandle, Subscription, Task, WeakEntity, WeakFocusHandle, Window, actions, anchored, deferred, impl_actions, prelude::*, }; use itertools::Itertools; use language::DiagnosticSeverity; use parking_lot::Mutex; use project::{DirectoryLister, Project, ProjectEntryId, ProjectPath, WorktreeId}; use schemars::JsonSchema; use serde::Deserialize; use settings::{Settings, SettingsStore}; use std::{ any::Any, cmp, fmt, mem, ops::ControlFlow, path::PathBuf, rc::Rc, sync::{ Arc, atomic::{AtomicUsize, Ordering}, }, }; use theme::ThemeSettings; use ui::{ ButtonSize, Color, ContextMenu, ContextMenuEntry, ContextMenuItem, DecoratedIcon, IconButton, IconButtonShape, IconDecoration, IconDecorationKind, IconName, IconSize, Indicator, Label, PopoverMenu, PopoverMenuHandle, ScrollableHandle, Tab, TabBar, TabPosition, Tooltip, prelude::*, right_click_menu, }; use util::{ResultExt, debug_panic, maybe, truncate_and_remove_front}; /// 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(Clone, Copy, PartialEq, Debug, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] 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, PartialEq, Debug, Deserialize, JsonSchema, Default)] pub struct ActivateItem(pub usize); #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseActiveItem { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseInactiveItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseAllItems { pub save_intent: Option<SaveIntent>, #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseCleanItems { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseItemsToTheRight { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct CloseItemsToTheLeft { #[serde(default)] pub close_pinned: bool, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct RevealInProjectPanel { #[serde(skip)] pub entry_id: Option<u64>, } #[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema, Default)] #[serde(deny_unknown_fields)] pub struct DeploySearch { #[serde(default)] pub replace_enabled: bool, #[serde(default)] pub included_files: Option<String>, #[serde(default)] pub excluded_files: Option<String>, } impl_actions!( pane, [ CloseAllItems, CloseActiveItem, CloseCleanItems, CloseItemsToTheLeft, CloseItemsToTheRight, CloseInactiveItems, ActivateItem, RevealInProjectPanel, DeploySearch, ] ); actions!( pane, [ ActivatePreviousItem, 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, included_files: None, excluded_files: None, } } } const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; pub enum Event { AddItem { item: Box<dyn ItemHandle>, }, ActivateItem { local: bool, focus_changed: bool, }, Remove { focus_on_pane: Option<Entity<Pane>>, }, RemoveItem { idx: usize, }, RemovedItem { item: Box<dyn ItemHandle>, }, Split(SplitDirection), ItemPinned, ItemUnpinned, 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 } => f .debug_struct("RemovedItem") .field("item", &item.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(), Event::ItemPinned => f.write_str("ItemPinned"), Event::ItemUnpinned => f.write_str("ItemUnpinned"), } } } /// 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: Entity<Toolbar>, pub(crate) workspace: WeakEntity<Workspace>, project: WeakEntity<Project>, pub drag_split_direction: Option<SplitDirection>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool>>, custom_drop_handle: Option< Arc<dyn Fn(&mut Pane, &dyn Any, &mut Window, &mut Context<Pane>) -> ControlFlow<(), ()>>, >, can_split_predicate: Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut Window, &mut Context<Self>) -> bool>>, can_toggle_zoom: bool, should_display_tab_bar: Rc<dyn Fn(&Window, &mut Context<Pane>) -> bool>, render_tab_bar_buttons: Rc< dyn Fn( &mut Pane, &mut Window, &mut Context<Pane>, ) -> (Option<AnyElement>, Option<AnyElement>), >, render_tab_bar: Rc<dyn Fn(&mut Pane, &mut Window, &mut Context<Pane>) -> AnyElement>, show_tab_bar_buttons: bool, _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>, close_pane_if_empty: bool, 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, /// If a certain project item wants to get recreated with specific data, it can persist its data before the recreation here. pub project_item_restoration_data: HashMap<ProjectItemKind, Box<dyn Any + Send>>, } 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: WeakEntity<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: Entity<Pane>, pub item: Box<dyn ItemHandle>, pub ix: usize, pub detail: usize, pub is_active: bool, } impl EventEmitter<Event> for Pane {} pub enum Side { Left, Right, } #[derive(Copy, Clone)] enum PinOperation { Pin, Unpin, } impl Pane { pub fn new( workspace: WeakEntity<Workspace>, project: Entity<Project>, next_timestamp: Arc<AtomicUsize>, can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>>, double_click_dispatch_action: Box<dyn Action>, window: &mut Window, cx: &mut Context<Self>, ) -> Self { let focus_handle = cx.focus_handle(); let subscriptions = vec![ cx.on_focus(&focus_handle, window, Pane::focus_in), cx.on_focus_in(&focus_handle, window, Pane::focus_in), cx.on_focus_out(&focus_handle, window, Pane::focus_out), cx.observe_global::<SettingsStore>(Self::settings_changed), cx.subscribe(&project, Self::project_events), ]; let handle = cx.entity().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(|_| Toolbar::new()), tab_bar_scroll_handle: ScrollHandle::new(), drag_split_direction: None, workspace, project: project.downgrade(), can_drop_predicate, custom_drop_handle: None, can_split_predicate: None, can_toggle_zoom: true, should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show), render_tab_bar_buttons: Rc::new(default_render_tab_bar_buttons), render_tab_bar: Rc::new(Self::render_tab_bar), show_tab_bar_buttons: TabBarSettings::get_global(cx).show_tab_bar_buttons, display_nav_history_buttons: Some( TabBarSettings::get_global(cx).show_nav_history_buttons, ), _subscriptions: subscriptions, double_click_dispatch_action, save_modals_spawned: HashSet::default(), close_pane_if_empty: true, split_item_context_menu_handle: Default::default(), new_item_context_menu_handle: Default::default(), pinned_tab_count: 0, diagnostics: Default::default(), zoom_out_on_close: true, project_item_restoration_data: HashMap::default(), } } fn alternate_file(&mut self, window: &mut Window, cx: &mut Context<Pane>) { let (_, alternative) = &self.alternate_file_items; if let Some(alternative) = alternative { let existing = self .items() .find_position(|item| item.item_id() == alternative.id()); if let Some((ix, _)) = existing { self.activate_item(ix, true, true, window, cx); } else if let Some(upgraded) = alternative.upgrade() { self.add_item(upgraded, true, true, None, window, cx); } } } pub fn track_alternate_file_items(&mut self) { if let Some(item) = self.active_item().map(|item| item.downgrade_item()) { let (current, _) = &self.alternate_file_items; match current { Some(current) => { if current.id() != item.id() {