diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 9442f5f1bb..60c1c12bed 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -14,7 +14,7 @@ pub use test_context::*; use crate::{ current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AnyWindowHandle, - AppMetadata, AssetSource, ClipboardItem, Context, DispatchPhase, DisplayId, Executor, + AppMetadata, AssetSource, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext, @@ -694,13 +694,17 @@ impl AppContext { self.globals_by_type.insert(global_type, lease.global); } - pub fn observe_release( + pub fn observe_release( &mut self, - handle: &Model, - mut on_release: impl FnMut(&mut E, &mut AppContext) + Send + 'static, - ) -> Subscription { + handle: &E, + mut on_release: impl FnMut(&mut T, &mut AppContext) + Send + 'static, + ) -> Subscription + where + E: Entity, + T: 'static, + { self.release_listeners.insert( - handle.entity_id, + handle.entity_id(), Box::new(move |entity, cx| { let entity = entity.downcast_mut().expect("invalid entity type"); on_release(entity, cx) diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index b54afa8dea..246ef33ee7 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -314,7 +314,7 @@ impl AnyView { .map_err(|_| self) } - pub(crate) fn entity_type(&self) -> TypeId { + pub fn entity_type(&self) -> TypeId { self.0.entity_type() } diff --git a/crates/workspace2/src/item.rs b/crates/workspace2/src/item.rs index 5995487f07..554a7aadb6 100644 --- a/crates/workspace2/src/item.rs +++ b/crates/workspace2/src/item.rs @@ -1,88 +1,80 @@ -// use crate::{ -// pane, persistence::model::ItemId, searchable::SearchableItemHandle, FollowableItemBuilders, -// ItemNavHistory, Pane, ToolbarItemLocation, ViewId, Workspace, WorkspaceId, -// }; -// use crate::{AutosaveSetting, DelayedDebouncedEditAction, WorkspaceSettings}; +use crate::{ + pane::{self, Pane}, + persistence::model::ItemId, + searchable::SearchableItemHandle, + workspace_settings::{AutosaveSetting, WorkspaceSettings}, + DelayedDebouncedEditAction, FollowableItemBuilders, ItemNavHistory, ToolbarItemLocation, + ViewId, Workspace, WorkspaceId, +}; use anyhow::Result; use client2::{ proto::{self, PeerId}, Client, }; +use gpui2::{ + AnyElement, AnyView, AppContext, EventEmitter, HighlightStyle, Model, Pixels, Point, Render, + SharedString, Task, View, ViewContext, WeakView, WindowContext, +}; +use parking_lot::Mutex; +use project2::{Project, ProjectEntryId, ProjectPath}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; use settings2::Settings; +use smallvec::SmallVec; +use std::{ + any::{Any, TypeId}, + ops::Range, + path::PathBuf, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; use theme2::Theme; -// use client2::{ -// proto::{self, PeerId}, -// Client, -// }; -// use gpui2::geometry::vector::Vector2F; -// use gpui2::AnyWindowHandle; -// use gpui2::{ -// fonts::HighlightStyle, AnyElement, AnyViewHandle, AppContext, Model, Task, View, -// ViewContext, View, WeakViewHandle, WindowContext, -// }; -// use project2::{Project, ProjectEntryId, ProjectPath}; -// use schemars::JsonSchema; -// use serde_derive::{Deserialize, Serialize}; -// use settings2::Setting; -// use smallvec::SmallVec; -// use std::{ -// any::{Any, TypeId}, -// borrow::Cow, -// cell::RefCell, -// fmt, -// ops::Range, -// path::PathBuf, -// rc::Rc, -// sync::{ -// atomic::{AtomicBool, Ordering}, -// Arc, -// }, -// time::Duration, -// }; -// use theme2::Theme; -// #[derive(Deserialize)] -// pub struct ItemSettings { -// pub git_status: bool, -// pub close_position: ClosePosition, -// } +#[derive(Deserialize)] +pub struct ItemSettings { + pub git_status: bool, + pub close_position: ClosePosition, +} -// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -// #[serde(rename_all = "lowercase")] -// pub enum ClosePosition { -// Left, -// #[default] -// Right, -// } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "lowercase")] +pub enum ClosePosition { + Left, + #[default] + Right, +} -// impl ClosePosition { -// pub fn right(&self) -> bool { -// match self { -// ClosePosition::Left => false, -// ClosePosition::Right => true, -// } -// } -// } +impl ClosePosition { + pub fn right(&self) -> bool { + match self { + ClosePosition::Left => false, + ClosePosition::Right => true, + } + } +} -// #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] -// pub struct ItemSettingsContent { -// git_status: Option, -// close_position: Option, -// } +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] +pub struct ItemSettingsContent { + git_status: Option, + close_position: Option, +} -// impl Setting for ItemSettings { -// const KEY: Option<&'static str> = Some("tabs"); +impl Settings for ItemSettings { + const KEY: Option<&'static str> = Some("tabs"); -// type FileContent = ItemSettingsContent; + type FileContent = ItemSettingsContent; -// fn load( -// default_value: &Self::FileContent, -// user_values: &[&Self::FileContent], -// _: &gpui2::AppContext, -// ) -> anyhow::Result { -// Self::load_via_json_merge(default_value, user_values) -// } -// } + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &mut AppContext, + ) -> Result { + Self::load_via_json_merge(default_value, user_values) + } +} #[derive(Eq, PartialEq, Hash, Debug)] pub enum ItemEvent { @@ -165,18 +157,18 @@ pub trait Item: Render + EventEmitter + Send { false } - // fn act_as_type<'a>( - // &'a self, - // type_id: TypeId, - // self_handle: &'a View, - // _: &'a AppContext, - // ) -> Option<&AnyViewHandle> { - // if TypeId::of::() == type_id { - // Some(self_handle) - // } else { - // None - // } - // } + fn act_as_type<'a>( + &'a self, + type_id: TypeId, + self_handle: &'a View, + _: &'a AppContext, + ) -> Option { + if TypeId::of::() == type_id { + Some(self_handle.clone().into_any()) + } else { + None + } + } fn as_searchable(&self, _: &View) -> Option> { None @@ -215,35 +207,6 @@ pub trait Item: Render + EventEmitter + Send { } } -use std::{ - any::Any, - cell::RefCell, - ops::Range, - path::PathBuf, - rc::Rc, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, - }, - time::Duration, -}; - -use gpui2::{ - AnyElement, AnyView, AnyWindowHandle, AppContext, EventEmitter, HighlightStyle, Model, Pixels, - Point, Render, SharedString, Task, View, ViewContext, WeakView, WindowContext, -}; -use project2::{Project, ProjectEntryId, ProjectPath}; -use smallvec::SmallVec; - -use crate::{ - pane::{self, Pane}, - persistence::model::ItemId, - searchable::SearchableItemHandle, - workspace_settings::{AutosaveSetting, WorkspaceSettings}, - DelayedDebouncedEditAction, FollowableItemBuilders, ItemNavHistory, ToolbarItemLocation, - ViewId, Workspace, WorkspaceId, -}; - pub trait ItemHandle: 'static + Send { fn subscribe_to_item_events( &self, @@ -275,7 +238,6 @@ pub trait ItemHandle: 'static + Send { fn workspace_deactivated(&self, cx: &mut WindowContext); fn navigate(&self, data: Box, cx: &mut WindowContext) -> bool; fn id(&self) -> usize; - fn window(&self) -> AnyWindowHandle; fn to_any(&self) -> AnyView; fn is_dirty(&self, cx: &AppContext) -> bool; fn has_conflict(&self, cx: &AppContext) -> bool; @@ -288,12 +250,12 @@ pub trait ItemHandle: 'static + Send { cx: &mut WindowContext, ) -> Task>; fn reload(&self, project: Model, cx: &mut WindowContext) -> Task>; - // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle>; todo!() + fn act_as_type(&self, type_id: TypeId, cx: &AppContext) -> Option; fn to_followable_item_handle(&self, cx: &AppContext) -> Option>; fn on_release( &self, cx: &mut AppContext, - callback: Box, + callback: Box, ) -> gpui2::Subscription; fn to_searchable_item_handle(&self, cx: &AppContext) -> Option>; fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation; @@ -303,23 +265,21 @@ pub trait ItemHandle: 'static + Send { fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option>; } -pub trait WeakItemHandle: Send { +pub trait WeakItemHandle: Send + Sync { fn id(&self) -> usize; - fn window(&self) -> AnyWindowHandle; fn upgrade(&self) -> Option>; } -// todo!() -// impl dyn ItemHandle { -// pub fn downcast(&self) -> Option> { -// self.as_any().clone().downcast() -// } +impl dyn ItemHandle { + pub fn downcast(&self) -> Option> { + self.to_any().downcast().ok() + } -// pub fn act_as(&self, cx: &AppContext) -> Option> { -// self.act_as_type(TypeId::of::(), cx) -// .and_then(|t| t.clone().downcast()) -// } -// } + pub fn act_as(&self, cx: &AppContext) -> Option> { + self.act_as_type(TypeId::of::(), cx) + .and_then(|t| t.downcast().ok()) + } +} impl ItemHandle for View { fn subscribe_to_item_events( @@ -438,8 +398,8 @@ impl ItemHandle for View { .is_none() { let mut pending_autosave = DelayedDebouncedEditAction::new(); - let pending_update = Rc::new(RefCell::new(None)); - let pending_update_scheduled = Rc::new(AtomicBool::new(false)); + let pending_update = Arc::new(Mutex::new(None)); + let pending_update_scheduled = Arc::new(AtomicBool::new(false)); let mut event_subscription = Some(cx.subscribe(self, move |workspace, item, event, cx| { @@ -462,33 +422,31 @@ impl ItemHandle for View { workspace.unfollow(&pane, cx); } - if item.add_event_to_update_proto( - event, - &mut *pending_update.borrow_mut(), - cx, - ) && !pending_update_scheduled.load(Ordering::SeqCst) + if item.add_event_to_update_proto(event, &mut *pending_update.lock(), cx) + && !pending_update_scheduled.load(Ordering::SeqCst) { pending_update_scheduled.store(true, Ordering::SeqCst); - cx.after_window_update({ - let pending_update = pending_update.clone(); - let pending_update_scheduled = pending_update_scheduled.clone(); - move |this, cx| { - pending_update_scheduled.store(false, Ordering::SeqCst); - this.update_followers( - is_project_item, - proto::update_followers::Variant::UpdateView( - proto::UpdateView { - id: item - .remote_id(&this.app_state.client, cx) - .map(|id| id.to_proto()), - variant: pending_update.borrow_mut().take(), - leader_id, - }, - ), - cx, - ); - } - }); + todo!("replace with on_next_frame?"); + // cx.after_window_update({ + // let pending_update = pending_update.clone(); + // let pending_update_scheduled = pending_update_scheduled.clone(); + // move |this, cx| { + // pending_update_scheduled.store(false, Ordering::SeqCst); + // this.update_followers( + // is_project_item, + // proto::update_followers::Variant::UpdateView( + // proto::UpdateView { + // id: item + // .remote_id(&this.app_state.client, cx) + // .map(|id| id.to_proto()), + // variant: pending_update.borrow_mut().take(), + // leader_id, + // }, + // ), + // cx, + // ); + // } + // }); } } @@ -525,15 +483,16 @@ impl ItemHandle for View { } })); - cx.observe_focus(self, move |workspace, item, focused, cx| { - if !focused - && WorkspaceSettings::get_global(cx).autosave == AutosaveSetting::OnFocusChange - { - Pane::autosave_item(&item, workspace.project.clone(), cx) - .detach_and_log_err(cx); - } - }) - .detach(); + todo!("observe focus"); + // cx.observe_focus(self, move |workspace, item, focused, cx| { + // if !focused + // && WorkspaceSettings::get_global(cx).autosave == AutosaveSetting::OnFocusChange + // { + // Pane::autosave_item(&item, workspace.project.clone(), cx) + // .detach_and_log_err(cx); + // } + // }) + // .detach(); let item_id = self.id(); cx.observe_release(self, move |workspace, _, _| { @@ -564,11 +523,6 @@ impl ItemHandle for View { self.id() } - fn window(&self) -> AnyWindowHandle { - todo!() - // AnyViewHandle::window(self) - } - fn to_any(&self) -> AnyView { self.clone().into_any() } @@ -602,16 +556,15 @@ impl ItemHandle for View { self.update(cx, |item, cx| item.reload(project, cx)) } - // todo!() - // fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option<&'a AnyViewHandle> { - // self.read(cx).act_as_type(type_id, self, cx) - // } + fn act_as_type<'a>(&'a self, type_id: TypeId, cx: &'a AppContext) -> Option { + self.read(cx).act_as_type(type_id, self, cx) + } fn to_followable_item_handle(&self, cx: &AppContext) -> Option> { if cx.has_global::() { let builders = cx.global::(); - let item = self.as_any(); - Some(builders.get(&item.view_type())?.1(item)) + let item = self.to_any(); + Some(builders.get(&item.entity_type())?.1(&item)) } else { None } @@ -620,7 +573,7 @@ impl ItemHandle for View { fn on_release( &self, cx: &mut AppContext, - callback: Box, + callback: Box, ) -> gpui2::Subscription { cx.observe_release(self, move |_, cx| callback(cx)) } @@ -673,10 +626,6 @@ impl WeakItemHandle for WeakView { self.id() } - fn window(&self) -> AnyWindowHandle { - self.window() - } - fn upgrade(&self) -> Option> { self.upgrade().map(|v| Box::new(v) as Box) } diff --git a/crates/workspace2/src/pane.rs b/crates/workspace2/src/pane.rs index ca42b0ef2e..22aa61e6cd 100644 --- a/crates/workspace2/src/pane.rs +++ b/crates/workspace2/src/pane.rs @@ -2,10 +2,15 @@ use crate::{ item::{Item, ItemHandle, WeakItemHandle}, + toolbar::Toolbar, SplitDirection, Workspace, }; +use anyhow::Result; use collections::{HashMap, VecDeque}; -use gpui2::{EventEmitter, Model, View, ViewContext, WeakView}; +use gpui2::{ + AppContext, EventEmitter, Model, Task, View, ViewContext, VisualContext, WeakView, + WindowContext, +}; use parking_lot::Mutex; use project2::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; @@ -68,6 +73,7 @@ pub enum SaveIntent { // pub save_intent: Option, // } +// todo!() // actions!( // pane, // [ @@ -90,8 +96,9 @@ pub enum SaveIntent { // impl_actions!(pane, [ActivateItem, CloseActiveItem, CloseAllItems]); -// const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; +const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; +// todo!() // pub fn init(cx: &mut AppContext) { // cx.add_action(Pane::toggle_zoom); // cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { @@ -330,7 +337,7 @@ impl Pane { pane: handle.clone(), next_timestamp, }))), - // toolbar: cx.add_view(|_| Toolbar::new()), + toolbar: cx.build_view(|_| Toolbar::new()), // tab_bar_context_menu: TabBarContextMenu { // kind: TabBarContextMenuKind::New, // handle: context_menu, @@ -447,33 +454,33 @@ impl Pane { } } - // pub fn nav_history(&self) -> &NavHistory { - // &self.nav_history - // } + pub fn nav_history(&self) -> &NavHistory { + &self.nav_history + } - // pub fn nav_history_mut(&mut self) -> &mut NavHistory { - // &mut self.nav_history - // } + pub fn nav_history_mut(&mut self) -> &mut NavHistory { + &mut self.nav_history + } - // pub fn disable_history(&mut self) { - // self.nav_history.disable(); - // } + pub fn disable_history(&mut self) { + self.nav_history.disable(); + } - // pub fn enable_history(&mut self) { - // self.nav_history.enable(); - // } + pub fn enable_history(&mut self) { + self.nav_history.enable(); + } - // pub fn can_navigate_backward(&self) -> bool { - // !self.nav_history.0.borrow().backward_stack.is_empty() - // } + pub fn can_navigate_backward(&self) -> bool { + !self.nav_history.0.lock().backward_stack.is_empty() + } - // pub fn can_navigate_forward(&self) -> bool { - // !self.nav_history.0.borrow().forward_stack.is_empty() - // } + pub fn can_navigate_forward(&self) -> bool { + !self.nav_history.0.lock().forward_stack.is_empty() + } - // fn history_updated(&mut self, cx: &mut ViewContext) { - // self.toolbar.update(cx, |_, cx| cx.notify()); - // } + fn history_updated(&mut self, cx: &mut ViewContext) { + self.toolbar.update(cx, |_, cx| cx.notify()); + } pub(crate) fn open_item( &mut self, @@ -736,115 +743,115 @@ impl Pane { // )) // } - // pub fn close_item_by_id( - // &mut self, - // item_id_to_close: usize, - // save_intent: SaveIntent, - // cx: &mut ViewContext, - // ) -> Task> { - // self.close_items(cx, save_intent, move |view_id| view_id == item_id_to_close) + pub fn close_item_by_id( + &mut self, + item_id_to_close: usize, + save_intent: SaveIntent, + cx: &mut ViewContext, + ) -> Task> { + 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].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_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_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_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_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_all_items( + // &mut self, + // action: &CloseAllItems, + // cx: &mut ViewContext, + // ) -> Option>> { + // if self.items.is_empty() { + // return None; // } - // 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_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_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_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_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_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>, @@ -1156,28 +1163,29 @@ impl Pane { // Ok(true) // } - // fn can_autosave_item(item: &dyn ItemHandle, cx: &AppContext) -> bool { - // let is_deleted = item.project_entry_ids(cx).is_empty(); - // item.is_dirty(cx) && !item.has_conflict(cx) && item.can_save(cx) && !is_deleted - // } + fn can_autosave_item(item: &dyn ItemHandle, cx: &AppContext) -> bool { + let is_deleted = item.project_entry_ids(cx).is_empty(); + item.is_dirty(cx) && !item.has_conflict(cx) && item.can_save(cx) && !is_deleted + } - // pub fn autosave_item( - // item: &dyn ItemHandle, - // project: Model, - // cx: &mut WindowContext, - // ) -> Task> { - // if Self::can_autosave_item(item, cx) { - // item.save(project, cx) - // } else { - // Task::ready(Ok(())) - // } - // } + pub fn autosave_item( + item: &dyn ItemHandle, + project: Model, + cx: &mut WindowContext, + ) -> Task> { + if Self::can_autosave_item(item, cx) { + item.save(project, cx) + } else { + Task::ready(Ok(())) + } + } - // pub fn focus_active_item(&mut self, cx: &mut ViewContext) { - // if let Some(active_item) = self.active_item() { - // cx.focus(active_item.as_any()); - // } - // } + pub fn focus_active_item(&mut self, cx: &mut ViewContext) { + todo!(); + // if let Some(active_item) = self.active_item() { + // cx.focus(active_item.as_any()); + // } + } // pub fn split(&mut self, direction: SplitDirection, cx: &mut ViewContext) { // cx.emit(Event::Split(direction)); @@ -1979,7 +1987,7 @@ impl NavHistory { cx: &AppContext, mut f: impl FnMut(&NavigationEntry, (ProjectPath, Option)), ) { - let borrowed_history = self.0.borrow(); + let borrowed_history = self.0.lock(); borrowed_history .forward_stack .iter() @@ -1990,7 +1998,7 @@ impl NavHistory { borrowed_history.paths_by_item.get(&entry.item.id()) { f(entry, project_and_abs_path.clone()); - } else if let Some(item) = entry.item.upgrade(cx) { + } else if let Some(item) = entry.item.upgrade() { if let Some(path) = item.project_path(cx) { f(entry, (path, None)); } @@ -1999,23 +2007,23 @@ impl NavHistory { } pub fn set_mode(&mut self, mode: NavigationMode) { - self.0.borrow_mut().mode = mode; + self.0.lock().mode = mode; } pub fn mode(&self) -> NavigationMode { - self.0.borrow().mode + self.0.lock().mode } pub fn disable(&mut self) { - self.0.borrow_mut().mode = NavigationMode::Disabled; + self.0.lock().mode = NavigationMode::Disabled; } pub fn enable(&mut self) { - self.0.borrow_mut().mode = NavigationMode::Normal; + self.0.lock().mode = NavigationMode::Normal; } pub fn pop(&mut self, mode: NavigationMode, cx: &mut WindowContext) -> Option { - let mut state = self.0.borrow_mut(); + let mut state = self.0.lock(); let entry = match mode { NavigationMode::Normal | NavigationMode::Disabled | NavigationMode::ClosingItem => { return None @@ -2034,10 +2042,10 @@ impl NavHistory { pub fn push( &mut self, data: Option, - item: Rc, + item: Arc, cx: &mut WindowContext, ) { - let state = &mut *self.0.borrow_mut(); + let state = &mut *self.0.lock(); match state.mode { NavigationMode::Disabled => {} NavigationMode::Normal | NavigationMode::ReopeningClosedItem => { @@ -2086,7 +2094,7 @@ impl NavHistory { } pub fn remove_item(&mut self, item_id: usize) { - let mut state = self.0.borrow_mut(); + let mut state = self.0.lock(); state.paths_by_item.remove(&item_id); state .backward_stack @@ -2100,19 +2108,19 @@ impl NavHistory { } pub fn path_for_item(&self, item_id: usize) -> Option<(ProjectPath, Option)> { - self.0.borrow().paths_by_item.get(&item_id).cloned() + self.0.lock().paths_by_item.get(&item_id).cloned() } } -// impl NavHistoryState { -// pub fn did_update(&self, cx: &mut WindowContext) { -// if let Some(pane) = self.pane.upgrade(cx) { -// cx.defer(move |cx| { -// pane.update(cx, |pane, cx| pane.history_updated(cx)); -// }); -// } -// } -// } +impl NavHistoryState { + pub fn did_update(&self, cx: &mut WindowContext) { + if let Some(pane) = self.pane.upgrade() { + cx.defer(move |cx| { + pane.update(cx, |pane, cx| pane.history_updated(cx)); + }); + } + } +} // pub struct PaneBackdrop { // child_view: usize, diff --git a/crates/workspace2/src/toolbar.rs b/crates/workspace2/src/toolbar.rs index 4357c6a49d..49e3bd1d98 100644 --- a/crates/workspace2/src/toolbar.rs +++ b/crates/workspace2/src/toolbar.rs @@ -1,7 +1,7 @@ use crate::ItemHandle; -use gpui2::{AppContext, EventEmitter, View, ViewContext, WindowContext}; +use gpui2::{AnyView, AppContext, EventEmitter, Render, View, ViewContext, WindowContext}; -pub trait ToolbarItemView: EventEmitter + Sized { +pub trait ToolbarItemView: Render + EventEmitter { fn set_active_pane_item( &mut self, active_pane_item: Option<&dyn crate::ItemHandle>, @@ -22,14 +22,14 @@ pub trait ToolbarItemView: EventEmitter + Sized { /// Number of times toolbar's height will be repeated to get the effective height. /// Useful when multiple rows one under each other are needed. /// The rows have the same width and act as a whole when reacting to resizes and similar events. - fn row_count(&self, _cx: &ViewContext) -> usize { + fn row_count(&self, _cx: &WindowContext) -> usize { 1 } } -trait ToolbarItemViewHandle { +trait ToolbarItemViewHandle: Send { fn id(&self) -> usize; - // fn as_any(&self) -> &AnyViewHandle; todo!() + fn to_any(&self) -> AnyView; fn set_active_pane_item( &self, active_pane_item: Option<&dyn ItemHandle>, @@ -249,7 +249,7 @@ impl Toolbar { pub fn item_of_type(&self) -> Option> { self.items .iter() - .find_map(|(item, _)| item.as_any().clone().downcast()) + .find_map(|(item, _)| item.to_any().downcast().ok()) } pub fn hidden(&self) -> bool { @@ -262,10 +262,9 @@ impl ToolbarItemViewHandle for View { self.id() } - // todo!() - // fn as_any(&self) -> &AnyViewHandle { - // self - // } + fn to_any(&self) -> AnyView { + self.clone().into_any() + } fn set_active_pane_item( &self, @@ -285,7 +284,7 @@ impl ToolbarItemViewHandle for View { } fn row_count(&self, cx: &WindowContext) -> usize { - self.read_with(cx, |this, cx| this.row_count(cx)) + self.read(cx).row_count(cx) } } diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index d00ef2c26a..723a922bc6 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -36,6 +36,10 @@ use std::{ pub use toolbar::{ToolbarItemLocation, ToolbarItemView}; use util::ResultExt; +use crate::persistence::model::{ + DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup, +}; + // lazy_static! { // static ref ZED_WINDOW_SIZE: Option = env::var("ZED_WINDOW_SIZE") // .ok() @@ -514,9 +518,9 @@ pub struct Workspace { // zoomed: Option, // zoomed_position: Option, // center: PaneGroup, - // left_dock: View, - // bottom_dock: View, - // right_dock: View, + left_dock: View, + bottom_dock: View, + right_dock: View, panes: Vec>, panes_by_item: HashMap>, // active_pane: View, @@ -526,8 +530,8 @@ pub struct Workspace { // titlebar_item: Option, // notifications: Vec<(TypeId, usize, Box)>, project: Model, - // follower_states: HashMap, FollowerState>, - // last_leaders_by_pane: HashMap, PeerId>, + follower_states: HashMap, FollowerState>, + last_leaders_by_pane: HashMap, PeerId>, // window_edited: bool, // active_call: Option<(ModelHandle, Vec)>, // leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, @@ -2613,37 +2617,33 @@ impl Workspace { // self.start_following(leader_id, cx) // } - // pub fn unfollow( - // &mut self, - // pane: &View, - // cx: &mut ViewContext, - // ) -> Option { - // let state = self.follower_states.remove(pane)?; - // let leader_id = state.leader_id; - // for (_, item) in state.items_by_leader_view_id { - // item.set_leader_peer_id(None, cx); - // } + pub fn unfollow(&mut self, pane: &View, cx: &mut ViewContext) -> Option { + let state = self.follower_states.remove(pane)?; + let leader_id = state.leader_id; + for (_, item) in state.items_by_leader_view_id { + item.set_leader_peer_id(None, cx); + } - // if self - // .follower_states - // .values() - // .all(|state| state.leader_id != state.leader_id) - // { - // let project_id = self.project.read(cx).remote_id(); - // let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); - // self.app_state - // .client - // .send(proto::Unfollow { - // room_id, - // project_id, - // leader_id: Some(leader_id), - // }) - // .log_err(); - // } + if self + .follower_states + .values() + .all(|state| state.leader_id != state.leader_id) + { + let project_id = self.project.read(cx).remote_id(); + let room_id = self.active_call()?.read(cx).room()?.read(cx).id(); + self.app_state + .client + .send(proto::Unfollow { + room_id, + project_id, + leader_id: Some(leader_id), + }) + .log_err(); + } - // cx.notify(); - // Some(leader_id) - // } + cx.notify(); + Some(leader_id) + } // pub fn is_being_followed(&self, peer_id: PeerId) -> bool { // self.follower_states @@ -3210,137 +3210,134 @@ impl Workspace { // })); // } - // fn serialize_workspace(&self, cx: &ViewContext) { - // fn serialize_pane_handle( - // pane_handle: &View, - // cx: &AppContext, - // ) -> SerializedPane { - // let (items, active) = { - // let pane = pane_handle.read(cx); - // let active_item_id = pane.active_item().map(|item| item.id()); - // ( - // pane.items() - // .filter_map(|item_handle| { - // Some(SerializedItem { - // kind: Arc::from(item_handle.serialized_item_kind()?), - // item_id: item_handle.id(), - // active: Some(item_handle.id()) == active_item_id, - // }) - // }) - // .collect::>(), - // pane.has_focus(), - // ) - // }; + fn serialize_workspace(&self, cx: &ViewContext) { + fn serialize_pane_handle(pane_handle: &View, cx: &AppContext) -> SerializedPane { + let (items, active) = { + let pane = pane_handle.read(cx); + let active_item_id = pane.active_item().map(|item| item.id()); + ( + pane.items() + .filter_map(|item_handle| { + Some(SerializedItem { + kind: Arc::from(item_handle.serialized_item_kind()?), + item_id: item_handle.id(), + active: Some(item_handle.id()) == active_item_id, + }) + }) + .collect::>(), + pane.has_focus(), + ) + }; - // SerializedPane::new(items, active) - // } + SerializedPane::new(items, active) + } - // fn build_serialized_pane_group( - // pane_group: &Member, - // cx: &AppContext, - // ) -> SerializedPaneGroup { - // match pane_group { - // Member::Axis(PaneAxis { - // axis, - // members, - // flexes, - // bounding_boxes: _, - // }) => SerializedPaneGroup::Group { - // axis: *axis, - // children: members - // .iter() - // .map(|member| build_serialized_pane_group(member, cx)) - // .collect::>(), - // flexes: Some(flexes.borrow().clone()), - // }, - // Member::Pane(pane_handle) => { - // SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx)) - // } - // } - // } + fn build_serialized_pane_group( + pane_group: &Member, + cx: &AppContext, + ) -> SerializedPaneGroup { + match pane_group { + Member::Axis(PaneAxis { + axis, + members, + flexes, + bounding_boxes: _, + }) => SerializedPaneGroup::Group { + axis: *axis, + children: members + .iter() + .map(|member| build_serialized_pane_group(member, cx)) + .collect::>(), + flexes: Some(flexes.borrow().clone()), + }, + Member::Pane(pane_handle) => { + SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx)) + } + } + } - // fn build_serialized_docks(this: &Workspace, cx: &ViewContext) -> DockStructure { - // let left_dock = this.left_dock.read(cx); - // let left_visible = left_dock.is_open(); - // let left_active_panel = left_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let left_dock_zoom = left_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + fn build_serialized_docks(this: &Workspace, cx: &ViewContext) -> DockStructure { + let left_dock = this.left_dock.read(cx); + let left_visible = left_dock.is_open(); + let left_active_panel = left_dock.visible_panel().and_then(|panel| { + Some( + cx.view_ui_name(panel.as_any().window(), panel.id())? + .to_string(), + ) + }); + let left_dock_zoom = left_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // let right_dock = this.right_dock.read(cx); - // let right_visible = right_dock.is_open(); - // let right_active_panel = right_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let right_dock_zoom = right_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + let right_dock = this.right_dock.read(cx); + let right_visible = right_dock.is_open(); + let right_active_panel = right_dock.visible_panel().and_then(|panel| { + Some( + cx.view_ui_name(panel.as_any().window(), panel.id())? + .to_string(), + ) + }); + let right_dock_zoom = right_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // let bottom_dock = this.bottom_dock.read(cx); - // let bottom_visible = bottom_dock.is_open(); - // let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { - // Some( - // cx.view_ui_name(panel.as_any().window(), panel.id())? - // .to_string(), - // ) - // }); - // let bottom_dock_zoom = bottom_dock - // .visible_panel() - // .map(|panel| panel.is_zoomed(cx)) - // .unwrap_or(false); + let bottom_dock = this.bottom_dock.read(cx); + let bottom_visible = bottom_dock.is_open(); + let bottom_active_panel = bottom_dock.visible_panel().and_then(|panel| { + Some( + cx.view_ui_name(panel.as_any().window(), panel.id())? + .to_string(), + ) + }); + let bottom_dock_zoom = bottom_dock + .visible_panel() + .map(|panel| panel.is_zoomed(cx)) + .unwrap_or(false); - // DockStructure { - // left: DockData { - // visible: left_visible, - // active_panel: left_active_panel, - // zoom: left_dock_zoom, - // }, - // right: DockData { - // visible: right_visible, - // active_panel: right_active_panel, - // zoom: right_dock_zoom, - // }, - // bottom: DockData { - // visible: bottom_visible, - // active_panel: bottom_active_panel, - // zoom: bottom_dock_zoom, - // }, - // } - // } + DockStructure { + left: DockData { + visible: left_visible, + active_panel: left_active_panel, + zoom: left_dock_zoom, + }, + right: DockData { + visible: right_visible, + active_panel: right_active_panel, + zoom: right_dock_zoom, + }, + bottom: DockData { + visible: bottom_visible, + active_panel: bottom_active_panel, + zoom: bottom_dock_zoom, + }, + } + } - // if let Some(location) = self.location(cx) { - // // Load bearing special case: - // // - with_local_workspace() relies on this to not have other stuff open - // // when you open your log - // if !location.paths().is_empty() { - // let center_group = build_serialized_pane_group(&self.center.root, cx); - // let docks = build_serialized_docks(self, cx); + if let Some(location) = self.location(cx) { + // Load bearing special case: + // - with_local_workspace() relies on this to not have other stuff open + // when you open your log + if !location.paths().is_empty() { + let center_group = build_serialized_pane_group(&self.center.root, cx); + let docks = build_serialized_docks(self, cx); - // let serialized_workspace = SerializedWorkspace { - // id: self.database_id, - // location, - // center_group, - // bounds: Default::default(), - // display: Default::default(), - // docks, - // }; + let serialized_workspace = SerializedWorkspace { + id: self.database_id, + location, + center_group, + bounds: Default::default(), + display: Default::default(), + docks, + }; - // cx.background() - // .spawn(persistence::DB.save_workspace(serialized_workspace)) - // .detach(); - // } - // } - // } + cx.background() + .spawn(persistence::DB.save_workspace(serialized_workspace)) + .detach(); + } + } + } // pub(crate) fn load_workspace( // workspace: WeakView,