@@ -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<dyn ItemHandle> },
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<T: View>(&self) -> impl '_ + Iterator<Item = ViewHandle<T>> {
- // self.items
- // .iter()
- // .filter_map(|item| item.as_any().clone().downcast())
- // }
+ pub fn items_of_type<T: Render>(&self) -> impl '_ + Iterator<Item = View<T>> {
+ self.items
+ .iter()
+ .filter_map(|item| item.to_any().downcast().ok())
+ }
pub fn active_item(&self) -> Option<Box<dyn ItemHandle>> {
self.items.get(self.active_item_index).cloned()
}
- // pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Vector2F> {
- // self.items
- // .get(self.active_item_index)?
- // .pixel_position_of_cursor(cx)
- // }
+ pub fn pixel_position_of_cursor(&self, cx: &AppContext) -> Option<Point<Pixels>> {
+ 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<usize> {
- 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<Self>) {
- // // 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);
+ // pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext<Self>) {
+ // // 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);
// }
+ // }
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<Self>) {
- // 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<Self>) {
+ 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<Self>) {
- // 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<Self>) {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // if self.items.is_empty() {
- // return None;
- // }
+ pub fn close_inactive_items(
+ &mut self,
+ _: &CloseInactiveItems,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>,
- // ) -> Task<Result<()>> {
- // 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<Self>,
+ ) -> Task<Result<()>> {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>,
- // ) -> Task<Result<()>> {
- // 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<Self>,
+ ) -> Task<Result<()>> {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // if self.items.is_empty() {
- // return None;
- // }
+ pub fn close_all_items(
+ &mut self,
+ action: &CloseAllItems,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Item = &Box<dyn ItemHandle>>,
@@ -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>,
) {
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<Self> {
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)
@@ -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<PathBuf>,
-// }
+#[derive(Clone, PartialEq)]
+pub struct OpenPaths {
+ pub paths: Vec<PathBuf>,
+}
-// #[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<SaveIntent>,
-// }
+#[derive(Clone, PartialEq, Debug, Deserialize, Action)]
+#[serde(rename_all = "camelCase")]
+pub struct SaveAll {
+ pub save_intent: Option<SaveIntent>,
+}
-// #[derive(Clone, PartialEq, Debug, Deserialize)]
-// #[serde(rename_all = "camelCase")]
-// pub struct Save {
-// pub save_intent: Option<SaveIntent>,
-// }
+#[derive(Clone, PartialEq, Debug, Deserialize, Action)]
+#[serde(rename_all = "camelCase")]
+pub struct Save {
+ pub save_intent: Option<SaveIntent>,
+}
-// #[derive(Clone, PartialEq, Debug, Deserialize, Default)]
-// #[serde(rename_all = "camelCase")]
-// pub struct CloseAllItemsAndPanes {
-// pub save_intent: Option<SaveIntent>,
-// }
+#[derive(Clone, PartialEq, Debug, Deserialize, Default, Action)]
+#[serde(rename_all = "camelCase")]
+pub struct CloseAllItemsAndPanes {
+ pub save_intent: Option<SaveIntent>,
+}
#[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<AppState>, 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<Self>,
weak_self: WeakView<Self>,
workspace_actions: Vec<Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>>,
zoomed: Option<AnyWeakView>,
@@ -455,6 +442,8 @@ pub struct Workspace {
pane_history_timestamp: Arc<AtomicUsize>,
}
+impl EventEmitter<Event> 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::<Workspace>().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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>,
) -> Task<Result<bool>> {
//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::<Workspace>())
- // .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(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();
- // }
- // }
- // }
- // }
+ let workspace_count = cx.update(|_, cx| {
+ cx.windows()
+ .iter()
+ .filter(|window| window.downcast::<Workspace>().is_some())
+ .count()
+ })?;
- Ok(
- false, // this
- // .update(&mut cx, |this, cx| {
- // this.save_all_internal(SaveIntent::Close, cx)
- // })?
- // .await?
- )
+ 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 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(this
+ .update(&mut cx, |this, cx| {
+ this.save_all_internal(SaveIntent::Close, cx)
+ })?
+ .await?)
})
}
- // fn save_all(
- // &mut self,
- // action: &SaveAll,
- // cx: &mut ViewContext<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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<Self>) {
+ 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<Self>,
- // ) -> Task<Result<bool>> {
- // 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::<Vec<_>>();
-
- // 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)
- // })
- // }
+ fn save_all_internal(
+ &mut self,
+ mut save_intent: SaveIntent,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<bool>> {
+ 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::<Vec<_>>();
- // pub fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
- // let mut paths = cx.prompt_for_paths(PathPromptOptions {
- // files: true,
- // directories: true,
- // multiple: 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)
+ })
+ }
- // 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(())
- // }))
- // }
+ pub fn open(&mut self, _: &Open, cx: &mut ViewContext<Self>) {
+ let mut paths = cx.prompt_for_paths(PathPromptOptions {
+ files: true,
+ directories: true,
+ multiple: true,
+ });
- // pub fn open_workspace_for_paths(
- // &mut self,
- // paths: Vec<PathBuf>,
- // cx: &mut ViewContext<Self>,
- // ) -> Task<Result<()>> {
- // let window = cx.window().downcast::<Self>();
- // 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();
+ cx.spawn(|this, mut cx| async move {
+ let Some(paths) = paths.await.log_err().flatten() else {
+ return;
+ };
- // 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(())
- // })
- // }
+ if let Some(task) = this
+ .update(&mut cx, |this, cx| this.open_workspace_for_paths(paths, cx))
+ .log_err()
+ {
+ task.await.log_err();
+ }
+ })
+ .detach()
+ }
+
+ pub fn open_workspace_for_paths(
+ &mut self,
+ paths: Vec<PathBuf>,
+ cx: &mut ViewContext<Self>,
+ ) -> Task<Result<()>> {
+ let window = cx.window_handle().downcast::<Self>();
+ 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<Self>) {
- // 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<Self>) {
+ 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<Project>,
@@ -1417,18 +1397,18 @@ impl Workspace {
self.panes.iter().flat_map(|pane| pane.read(cx).items())
}
- // pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<View<T>> {
- // self.items_of_type(cx).max_by_key(|item| item.id())
- // }
+ pub fn item_of_type<T: Item>(&self, cx: &AppContext) -> Option<View<T>> {
+ 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<Item = View<T>> {
- // 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<Item = View<T>> {
+ self.panes
+ .iter()
+ .flat_map(|pane| pane.read(cx).items_of_type())
+ }
pub fn active_item(&self, cx: &AppContext) -> Option<Box<dyn ItemHandle>> {
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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // self.close_all_internal(true, SaveIntent::Close, cx)
- // }
+ pub fn close_inactive_items_and_panes(
+ &mut self,
+ _: &CloseInactiveTabsAndPanes,
+ cx: &mut ViewContext<Self>,
+ ) {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // 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>,
+ ) {
+ 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<Self>,
- // ) -> Option<Task<Result<()>>> {
- // let current_pane = self.active_pane();
+ fn close_all_internal(
+ &mut self,
+ retain_active_pane: bool,
+ save_intent: SaveIntent,
+ cx: &mut ViewContext<Self>,
+ ) -> Option<Task<Result<()>>> {
+ 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<Self>) {
let dock = match dock_side {
@@ -1634,15 +1616,15 @@ impl Workspace {
None
}
- // pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<View<T>> {
- // for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
- // let dock = dock.read(cx);
- // if let Some(panel) = dock.panel::<T>() {
- // return Some(panel);
- // }
- // }
- // None
- // }
+ pub fn panel<T: Panel>(&self, cx: &WindowContext) -> Option<View<T>> {
+ for dock in [&self.left_dock, &self.bottom_dock, &self.right_dock] {
+ let dock = dock.read(cx);
+ if let Some(panel) = dock.panel::<T>() {
+ return Some(panel);
+ }
+ }
+ None
+ }
fn zoom_out(&mut self, cx: &mut ViewContext<Self>) {
for pane in &self.panes {
@@ -1953,81 +1935,89 @@ impl Workspace {
}
}
- // fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext<Self>) {
- // 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<Self>) {
+ 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<Self>) {
- // 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<Self>) {
+ 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<Self>) {
- // 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<Self>) {
+ 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<Self>,
- // ) {
- // 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<Self>,
+ ) {
+ 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<Self>,
- // ) {
- // 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<Self>,
+ ) {
+ 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<Self>,
- // ) -> Option<&View<Pane>> {
- // 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<Self>,
+ ) -> Option<&View<Pane>> {
+ 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<Pane>, cx: &mut ViewContext<Self>) {
if self.active_pane != pane {