diff --git a/crates/collab_ui2/src/collab_panel.rs b/crates/collab_ui2/src/collab_panel.rs index 6fb3f03f60..20e77d7023 100644 --- a/crates/collab_ui2/src/collab_panel.rs +++ b/crates/collab_ui2/src/collab_panel.rs @@ -684,6 +684,7 @@ impl CollabPanel { if let Some(serialized_panel) = serialized_panel { panel.update(cx, |panel, cx| { panel.width = serialized_panel.width; + //todo!(collapsed_channels) // panel.collapsed_channels = serialized_panel // .collapsed_channels // .unwrap_or_else(|| Vec::new()); diff --git a/crates/editor2/src/items.rs b/crates/editor2/src/items.rs index 8efac65ed4..cf2bf5b6dc 100644 --- a/crates/editor2/src/items.rs +++ b/crates/editor2/src/items.rs @@ -797,7 +797,7 @@ impl Item for Editor { fn added_to_workspace(&mut self, workspace: &mut Workspace, cx: &mut ViewContext) { let workspace_id = workspace.database_id(); - let item_id = cx.view().entity_id().as_u64() as ItemId; + let item_id = cx.view().item_id().as_u64() as ItemId; self.workspace = Some((workspace.weak_handle(), workspace.database_id())); fn serialize( @@ -828,7 +828,7 @@ impl Item for Editor { serialize( buffer, *workspace_id, - cx.view().entity_id().as_u64() as ItemId, + cx.view().item_id().as_u64() as ItemId, cx, ); } diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index 5b7f8ce590..83b3ccebe7 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -182,6 +182,10 @@ pub struct AsyncWindowContext { } impl AsyncWindowContext { + pub fn window_handle(&self) -> AnyWindowHandle { + self.window + } + pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self { Self { app, window } } diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index a1898bfd6d..e1f039e309 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -343,7 +343,7 @@ where impl Bounds where - T: Clone + Debug + PartialOrd + Add + Sub + Default, + T: Clone + Debug + PartialOrd + Add + Sub + Default + Half, { pub fn intersects(&self, other: &Bounds) -> bool { let my_lower_right = self.lower_right(); @@ -362,6 +362,13 @@ where self.size.width = self.size.width.clone() + double_amount.clone(); self.size.height = self.size.height.clone() + double_amount; } + + pub fn center(&self) -> Point { + Point { + x: self.origin.x.clone() + self.size.width.clone().half(), + y: self.origin.y.clone() + self.size.height.clone().half(), + } + } } impl + Sub> Bounds { @@ -1211,6 +1218,46 @@ impl From<()> for Length { } } +pub trait Half { + fn half(&self) -> Self; +} + +impl Half for f32 { + fn half(&self) -> Self { + self / 2. + } +} + +impl Half for DevicePixels { + fn half(&self) -> Self { + Self(self.0 / 2) + } +} + +impl Half for ScaledPixels { + fn half(&self) -> Self { + Self(self.0 / 2.) + } +} + +impl Half for Pixels { + fn half(&self) -> Self { + Self(self.0 / 2.) + } +} + +impl Half for Rems { + fn half(&self) -> Self { + Self(self.0 / 2.) + } +} + +impl Half for GlobalPixels { + fn half(&self) -> Self { + Self(self.0 / 2.) + } +} + pub trait IsZero { fn is_zero(&self) -> bool; } diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index 87edabab52..7a455fe8ce 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -1579,7 +1579,7 @@ mod tests { path::{Path, PathBuf}, sync::atomic::{self, AtomicUsize}, }; - use workspace::{pane, AppState}; + use workspace::AppState; #[gpui::test] async fn test_visible_list(cx: &mut gpui::TestAppContext) { @@ -2802,7 +2802,6 @@ mod tests { init_settings(cx); language::init(cx); editor::init(cx); - pane::init(cx); crate::init((), cx); workspace::init(app_state.clone(), cx); Project::init_settings(cx); diff --git a/crates/terminal_view2/src/terminal_panel.rs b/crates/terminal_view2/src/terminal_panel.rs index fbb1bd5352..944cd912be 100644 --- a/crates/terminal_view2/src/terminal_panel.rs +++ b/crates/terminal_view2/src/terminal_panel.rs @@ -304,13 +304,13 @@ impl TerminalPanel { .pane .read(cx) .items() - .map(|item| item.id().as_u64()) + .map(|item| item.item_id().as_u64()) .collect::>(); let active_item_id = self .pane .read(cx) .active_item() - .map(|item| item.id().as_u64()); + .map(|item| item.item_id().as_u64()); let height = self.height; let width = self.width; self.pending_serialization = cx.background_executor().spawn( diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index ee45ca862c..64da42cea7 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -217,11 +217,11 @@ impl Dock { // .map_or(false, |panel| panel.has_focus(cx)) // } - // pub fn panel(&self) -> Option> { - // self.panel_entries - // .iter() - // .find_map(|entry| entry.panel.as_any().clone().downcast()) - // } + pub fn panel(&self) -> Option> { + self.panel_entries + .iter() + .find_map(|entry| entry.panel.to_any().clone().downcast().ok()) + } pub fn panel_index_for_type(&self) -> Option { self.panel_entries @@ -416,24 +416,6 @@ impl Dock { cx.notify(); } } - - // pub fn render_placeholder(&self, cx: &WindowContext) -> AnyElement { - // todo!() - // if let Some(active_entry) = self.visible_entry() { - // Empty::new() - // .into_any() - // .contained() - // .with_style(self.style(cx)) - // .resizable::( - // self.position.to_resize_handle_side(), - // active_entry.panel.size(cx), - // |_, _, _| {}, - // ) - // .into_any() - // } else { - // Empty::new().into_any() - // } - // } } impl Render for Dock { @@ -461,40 +443,6 @@ impl Render for Dock { } } -// todo!() -// impl View for Dock { -// fn ui_name() -> &'static str { -// "Dock" -// } - -// fn render(&mut self, cx: &mut ViewContext) -> AnyElement { -// if let Some(active_entry) = self.visible_entry() { -// let style = self.style(cx); -// ChildView::new(active_entry.panel.as_any(), cx) -// .contained() -// .with_style(style) -// .resizable::( -// self.position.to_resize_handle_side(), -// active_entry.panel.size(cx), -// |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx), -// ) -// .into_any() -// } else { -// Empty::new().into_any() -// } -// } - -// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { -// if cx.is_self_focused() { -// if let Some(active_entry) = self.visible_entry() { -// cx.focus(active_entry.panel.as_any()); -// } else { -// cx.focus_parent(); -// } -// } -// } -// } - impl PanelButtons { pub fn new( dock: View, diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs index 7252e7135a..5b37656d96 100644 --- a/crates/workspace2/src/item.rs +++ b/crates/workspace2/src/item.rs @@ -240,7 +240,7 @@ pub trait ItemHandle: 'static + Send { fn deactivated(&self, cx: &mut WindowContext); fn workspace_deactivated(&self, cx: &mut WindowContext); fn navigate(&self, data: Box, cx: &mut WindowContext) -> bool; - fn id(&self) -> EntityId; + fn item_id(&self) -> EntityId; fn to_any(&self) -> AnyView; fn is_dirty(&self, cx: &AppContext) -> bool; fn has_conflict(&self, cx: &AppContext) -> bool; @@ -399,7 +399,7 @@ impl ItemHandle for View { if workspace .panes_by_item - .insert(self.id(), pane.downgrade()) + .insert(self.item_id(), pane.downgrade()) .is_none() { let mut pending_autosave = DelayedDebouncedEditAction::new(); @@ -410,7 +410,7 @@ impl ItemHandle for View { Some(cx.subscribe(self, move |workspace, item, event, cx| { let pane = if let Some(pane) = workspace .panes_by_item - .get(&item.id()) + .get(&item.item_id()) .and_then(|pane| pane.upgrade()) { pane @@ -463,7 +463,7 @@ impl ItemHandle for View { match event { ItemEvent::CloseItem => { pane.update(cx, |pane, cx| { - pane.close_item_by_id(item.id(), crate::SaveIntent::Close, cx) + pane.close_item_by_id(item.item_id(), crate::SaveIntent::Close, cx) }) .detach_and_log_err(cx); return; @@ -502,7 +502,7 @@ impl ItemHandle for View { // }) // .detach(); - let item_id = self.id(); + let item_id = self.item_id(); cx.observe_release(self, move |workspace, _, _| { workspace.panes_by_item.remove(&item_id); event_subscription.take(); @@ -527,7 +527,7 @@ impl ItemHandle for View { self.update(cx, |this, cx| this.navigate(data, cx)) } - fn id(&self) -> EntityId { + fn item_id(&self) -> EntityId { self.entity_id() } @@ -712,7 +712,7 @@ impl FollowableItemHandle for View { self.read(cx).remote_id().or_else(|| { client.peer_id().map(|creator| ViewId { creator, - id: self.id().as_u64(), + id: self.item_id().as_u64(), }) }) } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index b86240f419..d44d347114 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -8,8 +8,8 @@ use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use gpui::{ actions, prelude::*, Action, AppContext, AsyncWindowContext, Component, Div, EntityId, - EventEmitter, FocusHandle, Focusable, FocusableView, Model, PromptLevel, Render, Task, View, - ViewContext, VisualContext, WeakView, WindowContext, + EventEmitter, FocusHandle, Focusable, FocusableView, Model, Pixels, Point, PromptLevel, Render, + Task, View, ViewContext, VisualContext, WeakView, WindowContext, }; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; @@ -102,29 +102,6 @@ actions!( const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; -pub fn init(cx: &mut AppContext) { - // todo!() - // cx.add_action(Pane::toggle_zoom); - // cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { - // pane.activate_item(action.0, true, true, cx); - // }); - // cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| { - // pane.activate_item(pane.items.len() - 1, true, true, cx); - // }); - // cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { - // pane.activate_prev_item(true, cx); - // }); - // cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| { - // pane.activate_next_item(true, cx); - // }); - // cx.add_async_action(Pane::close_active_item); - // cx.add_async_action(Pane::close_inactive_items); - // cx.add_async_action(Pane::close_clean_items); - // cx.add_async_action(Pane::close_items_to_the_left); - // cx.add_async_action(Pane::close_items_to_the_right); - // cx.add_async_action(Pane::close_all_items); -} - pub enum Event { AddItem { item: Box }, ActivateItem { local: bool }, @@ -140,7 +117,10 @@ pub enum Event { 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.id()).finish(), + Event::AddItem { item } => f + .debug_struct("AddItem") + .field("item", &item.item_id()) + .finish(), Event::ActivateItem { local } => f .debug_struct("ActivateItem") .field("local", local) @@ -524,7 +504,7 @@ impl Pane { .0 .lock() .paths_by_item - .insert(item.id(), (project_path, abs_path)); + .insert(item.item_id(), (project_path, abs_path)); } } } @@ -548,7 +528,7 @@ impl Pane { }; let existing_item_index = self.items.iter().position(|existing_item| { - if existing_item.id() == item.id() { + if existing_item.item_id() == item.item_id() { true } else if existing_item.is_singleton(cx) { existing_item @@ -613,21 +593,21 @@ impl Pane { self.items.iter() } - // pub fn items_of_type(&self) -> impl '_ + Iterator> { - // self.items - // .iter() - // .filter_map(|item| item.as_any().clone().downcast()) - // } + pub fn items_of_type(&self) -> impl '_ + Iterator> { + self.items + .iter() + .filter_map(|item| item.to_any().downcast().ok()) + } pub fn active_item(&self) -> Option> { self.items.get(self.active_item_index).cloned() } - // pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option { - // self.items - // .get(self.active_item_index)? - // .pixel_position_of_cursor(cx) - // } + pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option> { + self.items + .get(self.active_item_index)? + .pixel_position_of_cursor(cx) + } pub fn item_for_entry( &self, @@ -644,24 +624,26 @@ impl Pane { } pub fn index_for_item(&self, item: &dyn ItemHandle) -> Option { - self.items.iter().position(|i| i.id() == item.id()) + self.items + .iter() + .position(|i| i.item_id() == item.item_id()) } - // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { - // // Potentially warn the user of the new keybinding - // let workspace_handle = self.workspace().clone(); - // cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) - // .detach(); + // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + // // Potentially warn the user of the new keybinding + // let workspace_handle = self.workspace().clone(); + // cx.spawn(|_, mut cx| async move { notify_of_new_dock(&workspace_handle, &mut cx) }) + // .detach(); - // if self.zoomed { - // cx.emit(Event::ZoomOut); - // } else if !self.items.is_empty() { - // if !self.has_focus { - // cx.focus_self(); - // } - // cx.emit(Event::ZoomIn); + // if self.zoomed { + // cx.emit(Event::ZoomOut); + // } else if !self.items.is_empty() { + // if !self.has_focus { + // cx.focus_self(); // } + // cx.emit(Event::ZoomIn); // } + // } pub fn activate_item( &mut self, @@ -689,9 +671,9 @@ impl Pane { if let Some(newly_active_item) = self.items.get(index) { self.activation_history .retain(|&previously_active_item_id| { - previously_active_item_id != newly_active_item.id() + previously_active_item_id != newly_active_item.item_id() }); - self.activation_history.push(newly_active_item.id()); + self.activation_history.push(newly_active_item.item_id()); } self.update_toolbar(cx); @@ -705,25 +687,25 @@ impl Pane { } } - // pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { - // let mut index = self.active_item_index; - // if index > 0 { - // index -= 1; - // } else if !self.items.is_empty() { - // index = self.items.len() - 1; - // } - // self.activate_item(index, activate_pane, activate_pane, cx); - // } + pub fn activate_prev_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { + let mut index = self.active_item_index; + if index > 0 { + index -= 1; + } else if !self.items.is_empty() { + index = self.items.len() - 1; + } + self.activate_item(index, activate_pane, activate_pane, cx); + } - // pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { - // let mut index = self.active_item_index; - // if index + 1 < self.items.len() { - // index += 1; - // } else { - // index = 0; - // } - // self.activate_item(index, activate_pane, activate_pane, cx); - // } + pub fn activate_next_item(&mut self, activate_pane: bool, cx: &mut ViewContext) { + let mut index = self.active_item_index; + if index + 1 < self.items.len() { + index += 1; + } else { + index = 0; + } + self.activate_item(index, activate_pane, activate_pane, cx); + } pub fn close_active_item( &mut self, @@ -733,7 +715,7 @@ impl Pane { if self.items.is_empty() { return None; } - let active_item_id = self.items[self.active_item_index].id(); + let active_item_id = self.items[self.active_item_index].item_id(); Some(self.close_item_by_id( active_item_id, action.save_intent.unwrap_or(SaveIntent::Close), @@ -750,106 +732,106 @@ impl Pane { self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close) } - // pub fn close_inactive_items( - // &mut self, - // _: &CloseInactiveItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } + pub fn close_inactive_items( + &mut self, + _: &CloseInactiveItems, + cx: &mut ViewContext, + ) -> Option>> { + if self.items.is_empty() { + return None; + } - // let active_item_id = self.items[self.active_item_index].id(); - // Some(self.close_items(cx, SaveIntent::Close, move |item_id| { - // item_id != active_item_id - // })) - // } + let active_item_id = self.items[self.active_item_index].item_id(); + Some(self.close_items(cx, SaveIntent::Close, move |item_id| { + item_id != active_item_id + })) + } - // pub fn close_clean_items( - // &mut self, - // _: &CloseCleanItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // let item_ids: Vec<_> = self - // .items() - // .filter(|item| !item.is_dirty(cx)) - // .map(|item| item.id()) - // .collect(); - // Some(self.close_items(cx, SaveIntent::Close, move |item_id| { - // item_ids.contains(&item_id) - // })) - // } + pub fn close_clean_items( + &mut self, + _: &CloseCleanItems, + cx: &mut ViewContext, + ) -> Option>> { + let item_ids: Vec<_> = self + .items() + .filter(|item| !item.is_dirty(cx)) + .map(|item| item.item_id()) + .collect(); + Some(self.close_items(cx, SaveIntent::Close, move |item_id| { + item_ids.contains(&item_id) + })) + } - // pub fn close_items_to_the_left( - // &mut self, - // _: &CloseItemsToTheLeft, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } - // let active_item_id = self.items[self.active_item_index].id(); - // Some(self.close_items_to_the_left_by_id(active_item_id, cx)) - // } + pub fn close_items_to_the_left( + &mut self, + _: &CloseItemsToTheLeft, + cx: &mut ViewContext, + ) -> Option>> { + if self.items.is_empty() { + return None; + } + let active_item_id = self.items[self.active_item_index].item_id(); + Some(self.close_items_to_the_left_by_id(active_item_id, cx)) + } - // pub fn close_items_to_the_left_by_id( - // &mut self, - // item_id: usize, - // cx: &mut ViewContext, - // ) -> Task> { - // let item_ids: Vec<_> = self - // .items() - // .take_while(|item| item.id() != item_id) - // .map(|item| item.id()) - // .collect(); - // self.close_items(cx, SaveIntent::Close, move |item_id| { - // item_ids.contains(&item_id) - // }) - // } + pub fn close_items_to_the_left_by_id( + &mut self, + item_id: EntityId, + cx: &mut ViewContext, + ) -> Task> { + let item_ids: Vec<_> = self + .items() + .take_while(|item| item.item_id() != item_id) + .map(|item| item.item_id()) + .collect(); + self.close_items(cx, SaveIntent::Close, move |item_id| { + item_ids.contains(&item_id) + }) + } - // pub fn close_items_to_the_right( - // &mut self, - // _: &CloseItemsToTheRight, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } - // let active_item_id = self.items[self.active_item_index].id(); - // Some(self.close_items_to_the_right_by_id(active_item_id, cx)) - // } + pub fn close_items_to_the_right( + &mut self, + _: &CloseItemsToTheRight, + cx: &mut ViewContext, + ) -> Option>> { + if self.items.is_empty() { + return None; + } + let active_item_id = self.items[self.active_item_index].item_id(); + Some(self.close_items_to_the_right_by_id(active_item_id, cx)) + } - // pub fn close_items_to_the_right_by_id( - // &mut self, - // item_id: usize, - // cx: &mut ViewContext, - // ) -> Task> { - // let item_ids: Vec<_> = self - // .items() - // .rev() - // .take_while(|item| item.id() != item_id) - // .map(|item| item.id()) - // .collect(); - // self.close_items(cx, SaveIntent::Close, move |item_id| { - // item_ids.contains(&item_id) - // }) - // } + pub fn close_items_to_the_right_by_id( + &mut self, + item_id: EntityId, + cx: &mut ViewContext, + ) -> Task> { + let item_ids: Vec<_> = self + .items() + .rev() + .take_while(|item| item.item_id() != item_id) + .map(|item| item.item_id()) + .collect(); + self.close_items(cx, SaveIntent::Close, move |item_id| { + item_ids.contains(&item_id) + }) + } - // pub fn close_all_items( - // &mut self, - // action: &CloseAllItems, - // cx: &mut ViewContext, - // ) -> Option>> { - // if self.items.is_empty() { - // return None; - // } + pub fn close_all_items( + &mut self, + action: &CloseAllItems, + cx: &mut ViewContext, + ) -> Option>> { + if self.items.is_empty() { + return None; + } - // Some( - // self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { - // true - // }), - // ) - // } + Some( + self.close_items(cx, action.save_intent.unwrap_or(SaveIntent::Close), |_| { + true + }), + ) + } pub(super) fn file_names_for_prompt( items: &mut dyn Iterator>, @@ -896,7 +878,7 @@ impl Pane { let mut items_to_close = Vec::new(); let mut dirty_items = Vec::new(); for item in &self.items { - if should_close(item.id()) { + if should_close(item.item_id()) { items_to_close.push(item.boxed_clone()); if item.is_dirty(cx) { dirty_items.push(item.boxed_clone()); @@ -949,7 +931,7 @@ impl Pane { for item in workspace.items(cx) { if !items_to_close .iter() - .any(|item_to_close| item_to_close.id() == item.id()) + .any(|item_to_close| item_to_close.item_id() == item.item_id()) { let other_project_item_ids = item.project_item_model_ids(cx); project_item_ids.retain(|id| !other_project_item_ids.contains(id)); @@ -977,7 +959,11 @@ impl Pane { // Remove the item from the pane. pane.update(&mut cx, |pane, cx| { - if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) { + if let Some(item_ix) = pane + .items + .iter() + .position(|i| i.item_id() == item.item_id()) + { pane.remove_item(item_ix, false, cx); } })?; @@ -995,7 +981,7 @@ impl Pane { cx: &mut ViewContext, ) { self.activation_history - .retain(|&history_entry| history_entry != self.items[item_index].id()); + .retain(|&history_entry| history_entry != self.items[item_index].item_id()); if item_index == self.active_item_index { let index_to_activate = self @@ -1003,7 +989,7 @@ impl Pane { .pop() .and_then(|last_activated_item| { self.items.iter().enumerate().find_map(|(index, item)| { - (item.id() == last_activated_item).then_some(index) + (item.item_id() == last_activated_item).then_some(index) }) }) // We didn't have a valid activation history entry, so fallback @@ -1020,7 +1006,9 @@ impl Pane { let item = self.items.remove(item_index); - cx.emit(Event::RemoveItem { item_id: item.id() }); + cx.emit(Event::RemoveItem { + item_id: item.item_id(), + }); if self.items.is_empty() { item.deactivated(cx); self.update_toolbar(cx); @@ -1041,16 +1029,20 @@ impl Pane { .0 .lock() .paths_by_item - .get(&item.id()) + .get(&item.item_id()) .and_then(|(_, abs_path)| abs_path.clone()); self.nav_history .0 .lock() .paths_by_item - .insert(item.id(), (path, abs_path)); + .insert(item.item_id(), (path, abs_path)); } else { - self.nav_history.0.lock().paths_by_item.remove(&item.id()); + self.nav_history + .0 + .lock() + .paths_by_item + .remove(&item.item_id()); } if self.items.is_empty() && self.zoomed { @@ -1323,7 +1315,7 @@ impl Pane { ) -> Option<()> { let (item_index_to_delete, item_id) = self.items().enumerate().find_map(|(i, item)| { if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [entry_id] { - Some((i, item.id())) + Some((i, item.item_id())) } else { None } @@ -1354,10 +1346,10 @@ impl Pane { ) -> impl Component { let label = item.tab_content(Some(detail), cx); let close_icon = || { - let id = item.id(); + let id = item.item_id(); div() - .id(item.id()) + .id(item.item_id()) .invisible() .group_hover("", |style| style.visible()) .child(IconButton::new("close_tab", Icon::Close).on_click( @@ -1387,7 +1379,7 @@ impl Pane { div() .group("") - .id(item.id()) + .id(item.item_id()) .cursor_pointer() .when_some(item.tab_tooltip_text(cx), |div, text| { div.tooltip(move |_, cx| cx.build_view(|cx| Tooltip::new(text.clone())).into()) @@ -1914,6 +1906,25 @@ impl Render for Pane { .on_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)) .on_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx)) .on_action(|pane: &mut Pane, _: &SplitDown, cx| pane.split(SplitDirection::Down, cx)) + // cx.add_action(Pane::toggle_zoom); + // cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { + // pane.activate_item(action.0, true, true, cx); + // }); + // cx.add_action(|pane: &mut Pane, _: &ActivateLastItem, cx| { + // pane.activate_item(pane.items.len() - 1, true, true, cx); + // }); + // cx.add_action(|pane: &mut Pane, _: &ActivatePrevItem, cx| { + // pane.activate_prev_item(true, cx); + // }); + // cx.add_action(|pane: &mut Pane, _: &ActivateNextItem, cx| { + // pane.activate_next_item(true, cx); + // }); + // cx.add_async_action(Pane::close_active_item); + // cx.add_async_action(Pane::close_inactive_items); + // cx.add_async_action(Pane::close_clean_items); + // cx.add_async_action(Pane::close_items_to_the_left); + // cx.add_async_action(Pane::close_items_to_the_right); + // cx.add_async_action(Pane::close_all_items); .size_full() .on_action(|pane: &mut Self, action: &CloseActiveItem, cx| { pane.close_active_item(action, cx) diff --git a/crates/workspace2/src/searchable.rs b/crates/workspace2/src/searchable.rs index 2a393a9f6d..78c3b5a6ce 100644 --- a/crates/workspace2/src/searchable.rs +++ b/crates/workspace2/src/searchable.rs @@ -240,7 +240,7 @@ impl From<&Box> for AnyView { impl PartialEq for Box { fn eq(&self, other: &Self) -> bool { - self.id() == other.id() + self.item_id() == other.item_id() } } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index a01883b9a0..c7a27848ce 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -32,8 +32,9 @@ use gpui::{ actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveComponent, KeyContext, Model, ModelContext, - ParentComponent, Point, Render, Size, Styled, Subscription, Task, View, ViewContext, - VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions, + ParentComponent, PathPromptOptions, Point, PromptLevel, Render, Size, Styled, Subscription, + Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, + WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -49,7 +50,7 @@ pub use persistence::{ WorkspaceDb, DB, }; use postage::stream::Stream; -use project2::{Project, ProjectEntryId, ProjectPath, Worktree}; +use project2::{Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; use serde::Deserialize; use settings2::Settings; use status_bar::StatusBar; @@ -57,7 +58,7 @@ pub use status_bar::StatusItemView; use std::{ any::TypeId, borrow::Cow, - env, + cmp, env, path::{Path, PathBuf}, sync::{atomic::AtomicUsize, Arc}, time::Duration, @@ -84,8 +85,8 @@ lazy_static! { .and_then(parse_pixel_position_env_var); } -// #[derive(Clone, PartialEq)] -// pub struct RemoveWorktreeFromProject(pub WorktreeId); +#[derive(Clone, PartialEq)] +pub struct RemoveWorktreeFromProject(pub WorktreeId); actions!( Open, @@ -114,40 +115,40 @@ actions!( CloseAllDocks, ); -// #[derive(Clone, PartialEq)] -// pub struct OpenPaths { -// pub paths: Vec, -// } +#[derive(Clone, PartialEq)] +pub struct OpenPaths { + pub paths: Vec, +} -// #[derive(Clone, Deserialize, PartialEq)] -// pub struct ActivatePane(pub usize); +#[derive(Clone, Deserialize, PartialEq, Action)] +pub struct ActivatePane(pub usize); -// #[derive(Clone, Deserialize, PartialEq)] -// pub struct ActivatePaneInDirection(pub SplitDirection); +#[derive(Clone, Deserialize, PartialEq, Action)] +pub struct ActivatePaneInDirection(pub SplitDirection); -// #[derive(Clone, Deserialize, PartialEq)] -// pub struct SwapPaneInDirection(pub SplitDirection); +#[derive(Clone, Deserialize, PartialEq, Action)] +pub struct SwapPaneInDirection(pub SplitDirection); -// #[derive(Clone, Deserialize, PartialEq)] -// pub struct NewFileInDirection(pub SplitDirection); +#[derive(Clone, Deserialize, PartialEq, Action)] +pub struct NewFileInDirection(pub SplitDirection); -// #[derive(Clone, PartialEq, Debug, Deserialize)] -// #[serde(rename_all = "camelCase")] -// pub struct SaveAll { -// pub save_intent: Option, -// } +#[derive(Clone, PartialEq, Debug, Deserialize, Action)] +#[serde(rename_all = "camelCase")] +pub struct SaveAll { + pub save_intent: Option, +} -// #[derive(Clone, PartialEq, Debug, Deserialize)] -// #[serde(rename_all = "camelCase")] -// pub struct Save { -// pub save_intent: Option, -// } +#[derive(Clone, PartialEq, Debug, Deserialize, Action)] +#[serde(rename_all = "camelCase")] +pub struct Save { + pub save_intent: Option, +} -// #[derive(Clone, PartialEq, Debug, Deserialize, Default)] -// #[serde(rename_all = "camelCase")] -// pub struct CloseAllItemsAndPanes { -// pub save_intent: Option, -// } +#[derive(Clone, PartialEq, Debug, Deserialize, Default, Action)] +#[serde(rename_all = "camelCase")] +pub struct CloseAllItemsAndPanes { + pub save_intent: Option, +} #[derive(Deserialize)] pub struct Toast { @@ -199,20 +200,6 @@ pub struct OpenTerminal { pub working_directory: PathBuf, } -// impl_actions!( -// workspace, -// [ -// ActivatePane, -// ActivatePaneInDirection, -// SwapPaneInDirection, -// NewFileInDirection, -// Toast, -// SaveAll, -// Save, -// CloseAllItemsAndPanes, -// ] -// ); - pub type WorkspaceId = i64; pub fn init_settings(cx: &mut AppContext) { @@ -222,7 +209,6 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(app_state: Arc, cx: &mut AppContext) { init_settings(cx); - pane::init(cx); notifications::init(cx); // cx.add_global_action({ @@ -423,6 +409,7 @@ pub enum Event { } pub struct Workspace { + window_self: WindowHandle, weak_self: WeakView, workspace_actions: Vec) -> Div>>, zoomed: Option, @@ -455,6 +442,8 @@ pub struct Workspace { pane_history_timestamp: Arc, } +impl EventEmitter for Workspace {} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub struct ViewId { pub creator: PeerId, @@ -532,8 +521,8 @@ impl Workspace { ) }); cx.subscribe(¢er_pane, Self::handle_pane_event).detach(); - // todo!() - // cx.focus(¢er_pane); + + cx.focus_view(¢er_pane); cx.emit(Event::PaneAdded(center_pane.clone())); let window_handle = cx.window_handle().downcast::().unwrap(); @@ -636,10 +625,16 @@ impl Workspace { this.serialize_workspace(cx); cx.notify(); }), + cx.on_release(|this, cx| { + this.app_state.workspace_store.update(cx, |store, _| { + store.workspaces.remove(&this.window_self); + }) + }), ]; cx.defer(|this, cx| this.update_window_title(cx)); Workspace { + window_self: window_handle, weak_self: weak_handle.clone(), zoomed: None, zoomed_position: None, @@ -779,19 +774,6 @@ impl Workspace { })? }; - // todo!() Ask how to do this - // let weak_view = window.update(&mut cx, |_, cx| cx.view().downgrade())?; - // let async_cx = window.update(&mut cx, |_, cx| cx.to_async())?; - - // (app_state.initialize_workspace)( - // weak_view, - // serialized_workspace.is_some(), - // app_state.clone(), - // async_cx, - // ) - // .await - // .log_err(); - window .update(&mut cx, |_, cx| cx.activate_window()) .log_err(); @@ -964,12 +946,12 @@ impl Workspace { if let Some((project_entry_id, build_item)) = task.log_err() { let prev_active_item_id = pane.update(&mut cx, |pane, _| { pane.nav_history_mut().set_mode(mode); - pane.active_item().map(|p| p.id()) + pane.active_item().map(|p| p.item_id()) })?; pane.update(&mut cx, |pane, cx| { let item = pane.open_item(project_entry_id, true, cx, build_item); - navigated |= Some(item.id()) != prev_active_item_id; + navigated |= Some(item.item_id()) != prev_active_item_id; pane.nav_history_mut().set_mode(NavigationMode::Normal); if let Some(data) = entry.data { navigated |= item.navigate(data, cx); @@ -1077,35 +1059,40 @@ impl Workspace { } } - // pub fn close_global(_: &CloseWindow, cx: &mut AppContext) { - // cx.spawn(|mut cx| async move { - // let window = cx - // .windows() - // .into_iter() - // .find(|window| window.is_active(&cx).unwrap_or(false)); - // if let Some(window) = window { - // //This can only get called when the window's project connection has been lost - // //so we don't need to prompt the user for anything and instead just close the window - // window.remove(&mut cx); - // } - // }) - // .detach(); - // } + // todo!(Non-window-actions) + pub fn close_global(_: &CloseWindow, cx: &mut AppContext) { + cx.windows().iter().find(|window| { + window + .update(cx, |_, window| { + if window.is_window_active() { + //This can only get called when the window's project connection has been lost + //so we don't need to prompt the user for anything and instead just close the window + window.remove_window(); + true + } else { + false + } + }) + .unwrap_or(false) + }); + } - // pub fn close( - // &mut self, - // _: &CloseWindow, - // cx: &mut ViewContext, - // ) -> Option>> { - // let window = cx.window(); - // let prepare = self.prepare_to_close(false, cx); - // Some(cx.spawn(|_, mut cx| async move { - // if prepare.await? { - // window.remove(&mut cx); - // } - // Ok(()) - // })) - // } + pub fn close( + &mut self, + _: &CloseWindow, + cx: &mut ViewContext, + ) -> Option>> { + let window = cx.window_handle(); + let prepare = self.prepare_to_close(false, cx); + Some(cx.spawn(|_, mut cx| async move { + if prepare.await? { + window.update(&mut cx, |_, cx| { + cx.remove_window(); + })?; + } + Ok(()) + })) + } pub fn prepare_to_close( &mut self, @@ -1113,184 +1100,177 @@ impl Workspace { cx: &mut ViewContext, ) -> Task> { //todo!(saveing) - // let active_call = self.active_call().cloned(); - // let window = cx.window(); + let active_call = self.active_call().cloned(); + let window = cx.window_handle(); cx.spawn(|this, mut cx| async move { - // let workspace_count = cx - // .windows() - // .into_iter() - // .filter(|window| window.root_is::()) - // .count(); + let workspace_count = cx.update(|_, cx| { + cx.windows() + .iter() + .filter(|window| window.downcast::().is_some()) + .count() + })?; - // if let Some(active_call) = active_call { - // if !quitting - // && workspace_count == 1 - // && active_call.read_with(&cx, |call, _| call.room().is_some()) - // { - // let answer = window.prompt( - // PromptLevel::Warning, - // "Do you want to leave the current call?", - // &["Close window and hang up", "Cancel"], - // &mut cx, - // ); + if let Some(active_call) = active_call { + if !quitting + && workspace_count == 1 + && active_call.read_with(&cx, |call, _| call.room().is_some())? + { + let answer = window.update(&mut cx, |_, cx| { + cx.prompt( + PromptLevel::Warning, + "Do you want to leave the current call?", + &["Close window and hang up", "Cancel"], + ) + })?; - // if let Some(mut answer) = answer { - // if answer.next().await == Some(1) { - // return anyhow::Ok(false); - // } else { - // active_call - // .update(&mut cx, |call, cx| call.hang_up(cx)) - // .await - // .log_err(); - // } - // } - // } - // } + if answer.await.log_err() == Some(1) { + return anyhow::Ok(false); + } else { + active_call + .update(&mut cx, |call, cx| call.hang_up(cx))? + .await + .log_err(); + } + } + } - Ok( - false, // this - // .update(&mut cx, |this, cx| { - // this.save_all_internal(SaveIntent::Close, cx) - // })? - // .await? - ) + Ok(this + .update(&mut cx, |this, cx| { + this.save_all_internal(SaveIntent::Close, cx) + })? + .await?) }) } - // fn save_all( - // &mut self, - // action: &SaveAll, - // cx: &mut ViewContext, - // ) -> Option>> { - // let save_all = - // self.save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx); - // Some(cx.foreground().spawn(async move { - // save_all.await?; - // Ok(()) - // })) - // } + fn save_all(&mut self, action: &SaveAll, cx: &mut ViewContext) { + let save_all = self + .save_all_internal(action.save_intent.unwrap_or(SaveIntent::SaveAll), cx) + .detach_and_log_err(cx); + } - // fn save_all_internal( - // &mut self, - // mut save_intent: SaveIntent, - // cx: &mut ViewContext, - // ) -> Task> { - // if self.project.read(cx).is_read_only() { - // return Task::ready(Ok(true)); - // } - // let dirty_items = self - // .panes - // .iter() - // .flat_map(|pane| { - // pane.read(cx).items().filter_map(|item| { - // if item.is_dirty(cx) { - // Some((pane.downgrade(), item.boxed_clone())) - // } else { - // None - // } - // }) - // }) - // .collect::>(); + fn save_all_internal( + &mut self, + mut save_intent: SaveIntent, + cx: &mut ViewContext, + ) -> Task> { + if self.project.read(cx).is_read_only() { + return Task::ready(Ok(true)); + } + let dirty_items = self + .panes + .iter() + .flat_map(|pane| { + pane.read(cx).items().filter_map(|item| { + if item.is_dirty(cx) { + Some((pane.downgrade(), item.boxed_clone())) + } else { + None + } + }) + }) + .collect::>(); - // let project = self.project.clone(); - // cx.spawn(|workspace, mut cx| async move { - // // Override save mode and display "Save all files" prompt - // if save_intent == SaveIntent::Close && dirty_items.len() > 1 { - // let mut answer = workspace.update(&mut cx, |_, cx| { - // let prompt = Pane::file_names_for_prompt( - // &mut dirty_items.iter().map(|(_, handle)| handle), - // dirty_items.len(), - // cx, - // ); - // cx.prompt( - // PromptLevel::Warning, - // &prompt, - // &["Save all", "Discard all", "Cancel"], - // ) - // })?; - // match answer.next().await { - // Some(0) => save_intent = SaveIntent::SaveAll, - // Some(1) => save_intent = SaveIntent::Skip, - // _ => {} - // } - // } - // for (pane, item) in dirty_items { - // let (singleton, project_entry_ids) = - // cx.read(|cx| (item.is_singleton(cx), item.project_entry_ids(cx))); - // if singleton || !project_entry_ids.is_empty() { - // if let Some(ix) = - // pane.read_with(&cx, |pane, _| pane.index_for_item(item.as_ref()))? - // { - // if !Pane::save_item( - // project.clone(), - // &pane, - // ix, - // &*item, - // save_intent, - // &mut cx, - // ) - // .await? - // { - // return Ok(false); - // } - // } - // } - // } - // Ok(true) - // }) - // } + let project = self.project.clone(); + cx.spawn(|workspace, mut cx| async move { + // Override save mode and display "Save all files" prompt + if save_intent == SaveIntent::Close && dirty_items.len() > 1 { + let mut answer = workspace.update(&mut cx, |_, cx| { + let prompt = Pane::file_names_for_prompt( + &mut dirty_items.iter().map(|(_, handle)| handle), + dirty_items.len(), + cx, + ); + cx.prompt( + PromptLevel::Warning, + &prompt, + &["Save all", "Discard all", "Cancel"], + ) + })?; + match answer.await.log_err() { + Some(0) => save_intent = SaveIntent::SaveAll, + Some(1) => save_intent = SaveIntent::Skip, + _ => {} + } + } + for (pane, item) in dirty_items { + let (singleton, project_entry_ids) = + cx.update(|_, cx| (item.is_singleton(cx), item.project_entry_ids(cx)))?; + if singleton || !project_entry_ids.is_empty() { + if let Some(ix) = + pane.update(&mut cx, |pane, _| pane.index_for_item(item.as_ref()))? + { + if !Pane::save_item( + project.clone(), + &pane, + ix, + &*item, + save_intent, + &mut cx, + ) + .await? + { + return Ok(false); + } + } + } + } + Ok(true) + }) + } - // pub fn open(&mut self, _: &Open, cx: &mut ViewContext) -> Option>> { - // let mut paths = cx.prompt_for_paths(PathPromptOptions { - // files: true, - // directories: true, - // multiple: true, - // }); + pub fn open(&mut self, _: &Open, cx: &mut ViewContext) { + let mut paths = cx.prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }); - // Some(cx.spawn(|this, mut cx| async move { - // if let Some(paths) = paths.recv().await.flatten() { - // if let Some(task) = this - // .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx)) - // .log_err() - // { - // task.await? - // } - // } - // Ok(()) - // })) - // } + cx.spawn(|this, mut cx| async move { + let Some(paths) = paths.await.log_err().flatten() else { + return; + }; - // pub fn open_workspace_for_paths( - // &mut self, - // paths: Vec, - // cx: &mut ViewContext, - // ) -> Task> { - // let window = cx.window().downcast::(); - // let is_remote = self.project.read(cx).is_remote(); - // let has_worktree = self.project.read(cx).worktrees(cx).next().is_some(); - // let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); - // let close_task = if is_remote || has_worktree || has_dirty_items { - // None - // } else { - // Some(self.prepare_to_close(false, cx)) - // }; - // let app_state = self.app_state.clone(); + if let Some(task) = this + .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx)) + .log_err() + { + task.await.log_err(); + } + }) + .detach() + } - // cx.spawn(|_, mut cx| async move { - // let window_to_replace = if let Some(close_task) = close_task { - // if !close_task.await? { - // return Ok(()); - // } - // window - // } else { - // None - // }; - // cx.update(|cx| open_paths(&paths, &app_state, window_to_replace, cx)) - // .await?; - // Ok(()) - // }) - // } + pub fn open_workspace_for_paths( + &mut self, + paths: Vec, + cx: &mut ViewContext, + ) -> Task> { + let window = cx.window_handle().downcast::(); + let is_remote = self.project.read(cx).is_remote(); + let has_worktree = self.project.read(cx).worktrees().next().is_some(); + let has_dirty_items = self.items(cx).any(|item| item.is_dirty(cx)); + let close_task = if is_remote || has_worktree || has_dirty_items { + None + } else { + Some(self.prepare_to_close(false, cx)) + }; + let app_state = self.app_state.clone(); + + cx.spawn(|_, mut cx| async move { + let window_to_replace = if let Some(close_task) = close_task { + if !close_task.await? { + return Ok(()); + } + window + } else { + None + }; + cx.update(|_, cx| open_paths(&paths, &app_state, window_to_replace, cx))? + .await?; + Ok(()) + }) + } #[allow(clippy::type_complexity)] pub fn open_paths( @@ -1368,25 +1348,25 @@ impl Workspace { }) } - // fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext) { - // let mut paths = cx.prompt_for_paths(PathPromptOptions { - // files: false, - // directories: true, - // multiple: true, - // }); - // cx.spawn(|this, mut cx| async move { - // if let Some(paths) = paths.recv().await.flatten() { - // let results = this - // .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))? - // .await; - // for result in results.into_iter().flatten() { - // result.log_err(); - // } - // } - // anyhow::Ok(()) - // }) - // .detach_and_log_err(cx); - // } + fn add_folder_to_project(&mut self, _: &AddFolderToProject, cx: &mut ViewContext) { + let mut paths = cx.prompt_for_paths(PathPromptOptions { + files: false, + directories: true, + multiple: true, + }); + cx.spawn(|this, mut cx| async move { + if let Some(paths) = paths.await.log_err().flatten() { + let results = this + .update(&mut cx, |this, cx| this.open_paths(paths, true, cx))? + .await; + for result in results.into_iter().flatten() { + result.log_err(); + } + } + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } fn project_path_for_path( project: Model, @@ -1417,18 +1397,18 @@ impl Workspace { self.panes.iter().flat_map(|pane| pane.read(cx).items()) } - // pub fn item_of_type(&self, cx: &AppContext) -> Option> { - // self.items_of_type(cx).max_by_key(|item| item.id()) - // } + pub fn item_of_type(&self, cx: &AppContext) -> Option> { + self.items_of_type(cx).max_by_key(|item| item.item_id()) + } - // pub fn items_of_type<'a, T: Item>( - // &'a self, - // cx: &'a AppContext, - // ) -> impl 'a + Iterator> { - // self.panes - // .iter() - // .flat_map(|pane| pane.read(cx).items_of_type()) - // } + pub fn items_of_type<'a, T: Item>( + &'a self, + cx: &'a AppContext, + ) -> impl 'a + Iterator> { + self.panes + .iter() + .flat_map(|pane| pane.read(cx).items_of_type()) + } pub fn active_item(&self, cx: &AppContext) -> Option> { self.active_pane().read(cx).active_item() @@ -1465,68 +1445,70 @@ impl Workspace { }) } - // pub fn close_inactive_items_and_panes( - // &mut self, - // _: &CloseInactiveTabsAndPanes, - // cx: &mut ViewContext, - // ) -> Option>> { - // self.close_all_internal(true, SaveIntent::Close, cx) - // } + pub fn close_inactive_items_and_panes( + &mut self, + _: &CloseInactiveTabsAndPanes, + cx: &mut ViewContext, + ) { + self.close_all_internal(true, SaveIntent::Close, cx) + .map(|task| task.detach_and_log_err(cx)); + } - // pub fn close_all_items_and_panes( - // &mut self, - // action: &CloseAllItemsAndPanes, - // cx: &mut ViewContext, - // ) -> Option>> { - // self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) - // } + pub fn close_all_items_and_panes( + &mut self, + action: &CloseAllItemsAndPanes, + cx: &mut ViewContext, + ) { + self.close_all_internal(false, action.save_intent.unwrap_or(SaveIntent::Close), cx) + .map(|task| task.detach_and_log_err(cx)); + } - // fn close_all_internal( - // &mut self, - // retain_active_pane: bool, - // save_intent: SaveIntent, - // cx: &mut ViewContext, - // ) -> Option>> { - // let current_pane = self.active_pane(); + fn close_all_internal( + &mut self, + retain_active_pane: bool, + save_intent: SaveIntent, + cx: &mut ViewContext, + ) -> Option>> { + let current_pane = self.active_pane(); - // let mut tasks = Vec::new(); + let mut tasks = Vec::new(); - // if retain_active_pane { - // if let Some(current_pane_close) = current_pane.update(cx, |pane, cx| { - // pane.close_inactive_items(&CloseInactiveItems, cx) - // }) { - // tasks.push(current_pane_close); - // }; - // } + if retain_active_pane { + if let Some(current_pane_close) = current_pane.update(cx, |pane, cx| { + pane.close_inactive_items(&CloseInactiveItems, cx) + }) { + tasks.push(current_pane_close); + }; + } - // for pane in self.panes() { - // if retain_active_pane && pane.id() == current_pane.id() { - // continue; - // } + for pane in self.panes() { + if retain_active_pane && pane.entity_id() == current_pane.entity_id() { + continue; + } - // if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| { - // pane.close_all_items( - // &CloseAllItems { - // save_intent: Some(save_intent), - // }, - // cx, - // ) - // }) { - // tasks.push(close_pane_items) - // } - // } + if let Some(close_pane_items) = pane.update(cx, |pane: &mut Pane, cx| { + pane.close_all_items( + &CloseAllItems { + save_intent: Some(save_intent), + }, + cx, + ) + }) { + tasks.push(close_pane_items) + } + } - // if tasks.is_empty() { - // None - // } else { - // Some(cx.spawn(|_, _| async move { - // for task in tasks { - // task.await? - // } - // Ok(()) - // })) - // } - // } + if tasks.is_empty() { + None + } else { + Some(cx.spawn(|_, _| async move { + for task in tasks { + task.await? + } + Ok(()) + })) + } + } pub fn toggle_dock(&mut self, dock_side: DockPosition, cx: &mut ViewContext) { let dock = match dock_side { @@ -1634,15 +1616,15 @@ impl Workspace { None } - // pub fn panel(&self, cx: &WindowContext) -> Option> { - // for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { - // let dock = dock.read(cx); - // if let Some(panel) = dock.panel::() { - // return Some(panel); - // } - // } - // None - // } + pub fn panel(&self, cx: &WindowContext) -> Option> { + for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] { + let dock = dock.read(cx); + if let Some(panel) = dock.panel::() { + return Some(panel); + } + } + None + } fn zoom_out(&mut self, cx: &mut ViewContext) { for pane in &self.panes { @@ -1953,81 +1935,89 @@ impl Workspace { } } - // fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext) { - // let panes = self.center.panes(); - // if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { - // cx.focus(&pane); - // } else { - // self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx); - // } - // } + fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext) { + let panes = self.center.panes(); + if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { + cx.focus_view(&pane); + } else { + self.split_and_clone(self.active_pane.clone(), SplitDirection::Right, cx); + } + } - // pub fn activate_next_pane(&mut self, cx: &mut ViewContext) { - // let panes = self.center.panes(); - // if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { - // let next_ix = (ix + 1) % panes.len(); - // let next_pane = panes[next_ix].clone(); - // cx.focus(&next_pane); - // } - // } + pub fn activate_next_pane(&mut self, cx: &mut ViewContext) { + let panes = self.center.panes(); + if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { + let next_ix = (ix + 1) % panes.len(); + let next_pane = panes[next_ix].clone(); + cx.focus_view(&next_pane); + } + } - // pub fn activate_previous_pane(&mut self, cx: &mut ViewContext) { - // let panes = self.center.panes(); - // if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { - // let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1); - // let prev_pane = panes[prev_ix].clone(); - // cx.focus(&prev_pane); - // } - // } + pub fn activate_previous_pane(&mut self, cx: &mut ViewContext) { + let panes = self.center.panes(); + if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) { + let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1); + let prev_pane = panes[prev_ix].clone(); + cx.focus_view(&prev_pane); + } + } - // pub fn activate_pane_in_direction( - // &mut self, - // direction: SplitDirection, - // cx: &mut ViewContext, - // ) { - // if let Some(pane) = self.find_pane_in_direction(direction, cx) { - // cx.focus(pane); - // } - // } + pub fn activate_pane_in_direction( + &mut self, + direction: SplitDirection, + cx: &mut ViewContext, + ) { + if let Some(pane) = self.find_pane_in_direction(direction, cx) { + cx.focus_view(pane); + } + } - // pub fn swap_pane_in_direction( - // &mut self, - // direction: SplitDirection, - // cx: &mut ViewContext, - // ) { - // if let Some(to) = self - // .find_pane_in_direction(direction, cx) - // .map(|pane| pane.clone()) - // { - // self.center.swap(&self.active_pane.clone(), &to); - // cx.notify(); - // } - // } + pub fn swap_pane_in_direction( + &mut self, + direction: SplitDirection, + cx: &mut ViewContext, + ) { + if let Some(to) = self + .find_pane_in_direction(direction, cx) + .map(|pane| pane.clone()) + { + self.center.swap(&self.active_pane.clone(), &to); + cx.notify(); + } + } - // fn find_pane_in_direction( - // &mut self, - // direction: SplitDirection, - // cx: &mut ViewContext, - // ) -> Option<&View> { - // let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else { - // return None; - // }; - // let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx); - // let center = match cursor { - // Some(cursor) if bounding_box.contains_point(cursor) => cursor, - // _ => bounding_box.center(), - // }; + fn find_pane_in_direction( + &mut self, + direction: SplitDirection, + cx: &mut ViewContext, + ) -> Option<&View> { + let Some(bounding_box) = self.center.bounding_box_for_pane(&self.active_pane) else { + return None; + }; + let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx); + let center = match cursor { + Some(cursor) if bounding_box.contains_point(&cursor) => cursor, + _ => bounding_box.center(), + }; - // let distance_to_next = theme::current(cx).workspace.pane_divider.width + 1.; + let distance_to_next = 1.; //todo(pane dividers styling) - // let target = match direction { - // SplitDirection::Left => vec2f(bounding_box.origin_x() - distance_to_next, center.y()), - // SplitDirection::Right => vec2f(bounding_box.max_x() + distance_to_next, center.y()), - // SplitDirection::Up => vec2f(center.x(), bounding_box.origin_y() - distance_to_next), - // SplitDirection::Down => vec2f(center.x(), bounding_box.max_y() + distance_to_next), - // }; - // self.center.pane_at_pixel_position(target) - // } + let target = match direction { + SplitDirection::Left => { + Point::new(bounding_box.origin.x - distance_to_next.into(), center.y) + } + SplitDirection::Right => { + Point::new(bounding_box.right() + distance_to_next.into(), center.y) + } + SplitDirection::Up => { + Point::new(center.x, bounding_box.origin.y - distance_to_next.into()) + } + SplitDirection::Down => { + Point::new(center.x, bounding_box.top() + distance_to_next.into()) + } + }; + self.center.pane_at_pixel_position(target) + } fn handle_pane_focused(&mut self, pane: View, cx: &mut ViewContext) { if self.active_pane != pane { @@ -2199,7 +2189,7 @@ impl Workspace { .read(cx) .items() .enumerate() - .find(|(_, item_handle)| item_handle.id() == item_id_to_move); + .find(|(_, item_handle)| item_handle.item_id() == item_id_to_move); if item_to_move.is_none() { log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop"); @@ -2228,7 +2218,7 @@ impl Workspace { self.unfollow(&pane, cx); self.last_leaders_by_pane.remove(&pane.downgrade()); for removed_item in pane.read(cx).items() { - self.panes_by_item.remove(&removed_item.id()); + self.panes_by_item.remove(&removed_item.item_id()); } cx.notify(); @@ -2972,14 +2962,14 @@ impl Workspace { fn serialize_pane_handle(pane_handle: &View, cx: &WindowContext) -> SerializedPane { let (items, active) = { let pane = pane_handle.read(cx); - let active_item_id = pane.active_item().map(|item| item.id()); + let active_item_id = pane.active_item().map(|item| item.item_id()); ( pane.items() .filter_map(|item_handle| { Some(SerializedItem { kind: Arc::from(item_handle.serialized_item_kind()?), - item_id: item_handle.id().as_u64(), - active: Some(item_handle.id()) == active_item_id, + item_id: item_handle.item_id().as_u64(), + active: Some(item_handle.item_id()) == active_item_id, }) }) .collect::>(), @@ -3213,52 +3203,39 @@ impl Workspace { } fn actions(div: Div) -> Div { - div - // cx.add_async_action(Workspace::open); + div.on_action(Self::open) // cx.add_async_action(Workspace::follow_next_collaborator); // cx.add_async_action(Workspace::close); - // cx.add_async_action(Workspace::close_inactive_items_and_panes); - // cx.add_async_action(Workspace::close_all_items_and_panes); + .on_action(Self::close_inactive_items_and_panes) + .on_action(Self::close_all_items_and_panes) // cx.add_global_action(Workspace::close_global); // cx.add_global_action(restart); - // cx.add_async_action(Workspace::save_all); - // cx.add_action(Workspace::add_folder_to_project); - // cx.add_action( - // |workspace: &mut Workspace, _: &Unfollow, cx: &mut ViewContext| { - // let pane = workspace.active_pane().clone(); - // workspace.unfollow(&pane, cx); - // }, - // ); - // cx.add_action( - // |workspace: &mut Workspace, action: &Save, cx: &mut ViewContext| { - // workspace - // .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) - // .detach_and_log_err(cx); - // }, - // ); - // cx.add_action( - // |workspace: &mut Workspace, _: &SaveAs, cx: &mut ViewContext| { - // workspace - // .save_active_item(SaveIntent::SaveAs, cx) - // .detach_and_log_err(cx); - // }, - // ); - // cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { - // workspace.activate_previous_pane(cx) - // }); - // cx.add_action(|workspace: &mut Workspace, _: &ActivateNextPane, cx| { - // workspace.activate_next_pane(cx) - // }); - // cx.add_action( - // |workspace: &mut Workspace, action: &ActivatePaneInDirection, cx| { - // workspace.activate_pane_in_direction(action.0, cx) - // }, - // ); - // cx.add_action( - // |workspace: &mut Workspace, action: &SwapPaneInDirection, cx| { - // workspace.swap_pane_in_direction(action.0, cx) - // }, - // ); + .on_action(Self::save_all) + .on_action(Self::add_folder_to_project) + .on_action(|workspace, _: &Unfollow, cx| { + let pane = workspace.active_pane().clone(); + workspace.unfollow(&pane, cx); + }) + .on_action(|workspace, action: &Save, cx| { + workspace + .save_active_item(action.save_intent.unwrap_or(SaveIntent::Save), cx) + .detach_and_log_err(cx); + }) + .on_action(|workspace, _: &SaveAs, cx| { + workspace + .save_active_item(SaveIntent::SaveAs, cx) + .detach_and_log_err(cx); + }) + .on_action(|workspace, _: &ActivatePreviousPane, cx| { + workspace.activate_previous_pane(cx) + }) + .on_action(|workspace, _: &ActivateNextPane, cx| workspace.activate_next_pane(cx)) + .on_action(|workspace, action: &ActivatePaneInDirection, cx| { + workspace.activate_pane_in_direction(action.0, cx) + }) + .on_action(|workspace, action: &SwapPaneInDirection, cx| { + workspace.swap_pane_in_direction(action.0, cx) + }) .on_action(|this, e: &ToggleLeftDock, cx| { this.toggle_dock(DockPosition::Left, cx); }) @@ -3374,6 +3351,12 @@ impl Workspace { } fn add_workspace_actions_listeners(&self, mut div: Div) -> Div { + let mut div = div + .on_action(Self::close_inactive_items_and_panes) + .on_action(Self::close_all_items_and_panes) + .on_action(Self::add_folder_to_project) + .on_action(Self::save_all) + .on_action(Self::open); for action in self.workspace_actions.iter() { div = (action)(div) } @@ -3599,8 +3582,6 @@ fn notify_if_database_failed(workspace: WindowHandle, cx: &mut AsyncA .log_err(); } -impl EventEmitter for Workspace {} - impl FocusableView for Workspace { fn focus_handle(&self, cx: &AppContext) -> FocusHandle { self.active_pane.focus_handle(cx) @@ -3624,7 +3605,7 @@ impl Render for Workspace { cx.set_rem_size(ui_font_size); - self.add_workspace_actions_listeners(div()) + Self::actions(self.add_workspace_actions_listeners(div())) .key_context(context) .relative() .size_full() @@ -3692,20 +3673,6 @@ impl Render for Workspace { ), ) .child(self.status_bar.clone()) - // .when(self.debug.show_toast, |this| { - // this.child(Toast::new(ToastOrigin::Bottom).child(Label::new("A toast"))) - // }) - // .children( - // Some( - // div() - // .absolute() - // .top(px(50.)) - // .left(px(640.)) - // .z_index(8) - // .child(LanguageSelector::new("language-selector")), - // ) - // .filter(|_| self.is_language_selector_open()), - // ) .z_index(8) // Debug .child( @@ -3717,43 +3684,12 @@ impl Render for Workspace { .top_20() .left_1_4() .w_40() - .gap_2(), // .when(self.show_debug, |this| { - // this.child(Button::::new("Toggle User Settings").on_click( - // Arc::new(|workspace, cx| workspace.debug_toggle_user_settings(cx)), - // )) - // .child( - // Button::::new("Toggle Toasts").on_click(Arc::new( - // |workspace, cx| workspace.debug_toggle_toast(cx), - // )), - // ) - // .child( - // Button::::new("Toggle Livestream").on_click(Arc::new( - // |workspace, cx| workspace.debug_toggle_livestream(cx), - // )), - // ) - // }) - // .child( - // Button::::new("Toggle Debug") - // .on_click(Arc::new(|workspace, cx| workspace.toggle_debug(cx))), - // ), + .gap_2(), ) } } -// todo!() -// impl Entity for Workspace { -// type Event = Event; - -// fn release(&mut self, cx: &mut AppContext) { -// self.app_state.workspace_store.update(cx, |store, _| { -// store.workspaces.remove(&self.weak_self); -// }) -// } -// } // impl View for Workspace { -// fn ui_name() -> &'static str { -// "Workspace" -// } // fn render(&mut self, cx: &mut ViewContext) -> AnyElement { // let theme = theme::current(cx).clone(); @@ -3879,12 +3815,6 @@ impl Render for Workspace { // .into_any_named("workspace") // } -// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { -// if cx.is_self_focused() { -// cx.focus(&self.active_pane); -// } -// } - // fn modifiers_changed(&mut self, e: &ModifiersChangedEvent, cx: &mut ViewContext) -> bool { // DragAndDrop::::update_modifiers(e.modifiers, cx) // } @@ -4061,13 +3991,13 @@ impl WorkspaceHandle for View { } } -// impl std::fmt::Debug for OpenPaths { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// f.debug_struct("OpenPaths") -// .field("paths", &self.paths) -// .finish() -// } -// } +impl std::fmt::Debug for OpenPaths { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OpenPaths") + .field("paths", &self.paths) + .finish() + } +} pub struct WorkspaceCreated(pub WeakView); @@ -4530,7 +4460,7 @@ fn parse_pixel_size_env_var(value: &str) -> Option> { // mod tests { // use super::*; // use crate::{ -// dock::test::{TestPanel, TestPanelEvent}, +// dock::test::TestPanel, // item::test::{TestItem, TestItemEvent, TestProjectItem}, // }; // use fs::FakeFs; @@ -4615,7 +4545,7 @@ fn parse_pixel_size_env_var(value: &str) -> Option> { // let workspace = window.root(cx); // let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // let worktree_id = project.read_with(cx, |project, cx| { -// project.worktrees(cx).next().unwrap().read(cx).id() +// project.worktrees().next().unwrap().read(cx).id() // }); // let item1 = window.build_view(cx, |cx| {