Merge remote-tracking branch 'origin/main' into assistant-2

This commit is contained in:
Antonio Scandurra
2023-12-06 12:53:02 +01:00
59 changed files with 4270 additions and 1325 deletions
+25 -23
View File
@@ -78,7 +78,7 @@ impl Settings for ItemSettings {
}
}
#[derive(Eq, PartialEq, Hash, Debug)]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum ItemEvent {
CloseItem,
UpdateTab,
@@ -92,7 +92,9 @@ pub struct BreadcrumbText {
pub highlights: Option<Vec<(Range<usize>, HighlightStyle)>>,
}
pub trait Item: FocusableView + EventEmitter<ItemEvent> {
pub trait Item: FocusableView + EventEmitter<Self::Event> {
type Event;
fn deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn workspace_deactivated(&mut self, _: &mut ViewContext<Self>) {}
fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
@@ -155,6 +157,8 @@ pub trait Item: FocusableView + EventEmitter<ItemEvent> {
unimplemented!("reload() must be implemented if can_save() returns true")
}
fn to_item_events(event: &Self::Event, f: impl FnMut(ItemEvent));
fn act_as_type<'a>(
&'a self,
type_id: TypeId,
@@ -206,12 +210,12 @@ pub trait Item: FocusableView + EventEmitter<ItemEvent> {
}
pub trait ItemHandle: 'static + Send {
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
fn subscribe_to_item_events(
&self,
cx: &mut WindowContext,
handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
) -> gpui::Subscription;
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle;
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
fn tab_content(&self, detail: Option<usize>, cx: &WindowContext) -> AnyElement;
@@ -285,20 +289,20 @@ impl dyn ItemHandle {
}
impl<T: Item> ItemHandle for View<T> {
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
self.focus_handle(cx)
}
fn subscribe_to_item_events(
&self,
cx: &mut WindowContext,
handler: Box<dyn Fn(&ItemEvent, &mut WindowContext) + Send>,
handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
) -> gpui::Subscription {
cx.subscribe(self, move |_, event, cx| {
handler(event, cx);
T::to_item_events(event, |item_event| handler(item_event, cx));
})
}
fn focus_handle(&self, cx: &WindowContext) -> FocusHandle {
self.focus_handle(cx)
}
fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
self.read(cx).tab_tooltip_text(cx)
}
@@ -461,7 +465,7 @@ impl<T: Item> ItemHandle for View<T> {
}
}
match event {
T::to_item_events(event, |event| match event {
ItemEvent::CloseItem => {
pane.update(cx, |pane, cx| {
pane.close_item_by_id(item.item_id(), crate::SaveIntent::Close, cx)
@@ -489,7 +493,7 @@ impl<T: Item> ItemHandle for View<T> {
}
_ => {}
}
});
}));
cx.on_blur(&self.focus_handle(cx), move |workspace, cx| {
@@ -655,12 +659,7 @@ pub enum FollowEvent {
Unfollow,
}
pub trait FollowableEvents {
fn to_follow_event(&self) -> Option<FollowEvent>;
}
pub trait FollowableItem: Item {
type FollowableEvent: FollowableEvents;
fn remote_id(&self) -> Option<ViewId>;
fn to_state_proto(&self, cx: &WindowContext) -> Option<proto::view::Variant>;
fn from_state_proto(
@@ -670,9 +669,10 @@ pub trait FollowableItem: Item {
state: &mut Option<proto::view::Variant>,
cx: &mut WindowContext,
) -> Option<Task<Result<View<Self>>>>;
fn to_follow_event(event: &Self::Event) -> Option<FollowEvent>;
fn add_event_to_update_proto(
&self,
event: &Self::FollowableEvent,
event: &Self::Event,
update: &mut Option<proto::update_view::Variant>,
cx: &WindowContext,
) -> bool;
@@ -683,7 +683,6 @@ pub trait FollowableItem: Item {
cx: &mut ViewContext<Self>,
) -> Task<Result<()>>;
fn is_project_item(&self, cx: &WindowContext) -> bool;
fn set_leader_peer_id(&mut self, leader_peer_id: Option<PeerId>, cx: &mut ViewContext<Self>);
}
@@ -739,10 +738,7 @@ impl<T: FollowableItem> FollowableItemHandle for View<T> {
}
fn to_follow_event(&self, event: &dyn Any) -> Option<FollowEvent> {
event
.downcast_ref()
.map(T::FollowableEvent::to_follow_event)
.flatten()
T::to_follow_event(event.downcast_ref()?)
}
fn apply_update_proto(
@@ -929,6 +925,12 @@ pub mod test {
}
impl Item for TestItem {
type Event = ItemEvent;
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
f(*event)
}
fn tab_description(&self, detail: usize, _: &AppContext) -> Option<SharedString> {
self.tab_descriptions.as_ref().and_then(|descriptions| {
let description = *descriptions.get(detail).or_else(|| descriptions.last())?;
+208 -189
View File
@@ -1,5 +1,5 @@
use crate::{
item::{Item, ItemHandle, ItemSettings, WeakItemHandle},
item::{ClosePosition, Item, ItemHandle, ItemSettings, WeakItemHandle},
toolbar::Toolbar,
workspace_settings::{AutosaveSetting, WorkspaceSettings},
NewCenterTerminal, NewFile, NewSearch, SplitDirection, ToggleZoom, Workspace,
@@ -27,7 +27,8 @@ use std::{
};
use ui::{
h_stack, prelude::*, right_click_menu, Color, Icon, IconButton, IconElement, Label, Tooltip,
h_stack, prelude::*, right_click_menu, ButtonSize, Color, Icon, IconButton, IconSize,
Indicator, Label, Tooltip,
};
use ui::{v_stack, ContextMenu};
use util::truncate_and_remove_front;
@@ -537,18 +538,21 @@ impl Pane {
pub(crate) fn open_item(
&mut self,
project_entry_id: ProjectEntryId,
project_entry_id: Option<ProjectEntryId>,
focus_item: bool,
cx: &mut ViewContext<Self>,
build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
) -> Box<dyn ItemHandle> {
let mut existing_item = None;
for (index, item) in self.items.iter().enumerate() {
if item.is_singleton(cx) && item.project_entry_ids(cx).as_slice() == [project_entry_id]
{
let item = item.boxed_clone();
existing_item = Some((index, item));
break;
if let Some(project_entry_id) = project_entry_id {
for (index, item) in self.items.iter().enumerate() {
if item.is_singleton(cx)
&& item.project_entry_ids(cx).as_slice() == [project_entry_id]
{
let item = item.boxed_clone();
existing_item = Some((index, item));
break;
}
}
}
@@ -1415,22 +1419,7 @@ impl Pane {
cx: &mut ViewContext<'_, Pane>,
) -> impl IntoElement {
let label = item.tab_content(Some(detail), cx);
let close_icon = || {
let id = item.item_id();
div()
.id(ix)
.invisible()
.group_hover("", |style| style.visible())
.child(
IconButton::new("close_tab", Icon::Close).on_click(cx.listener(
move |pane, _, cx| {
pane.close_item_by_id(id, SaveIntent::Close, cx)
.detach_and_log_err(cx);
},
)),
)
};
let close_side = &ItemSettings::get_global(cx).close_position;
let (text_color, tab_bg, tab_hover_bg, tab_active_bg) = match ix == self.active_item_index {
false => (
@@ -1447,102 +1436,129 @@ impl Pane {
),
};
let close_right = ItemSettings::get_global(cx).close_position.right();
let is_active = ix == self.active_item_index;
let indicator = {
let indicator_color = match (item.has_conflict(cx), item.is_dirty(cx)) {
(true, _) => Some(Color::Warning),
(_, true) => Some(Color::Accent),
(false, false) => None,
};
h_stack()
.w_3()
.h_3()
.justify_center()
.absolute()
.map(|this| match close_side {
ClosePosition::Left => this.right_1(),
ClosePosition::Right => this.left_1(),
})
.when_some(indicator_color, |this, indicator_color| {
this.child(Indicator::dot().color(indicator_color))
})
};
let close_button = {
let id = item.item_id();
h_stack()
.invisible()
.w_3()
.h_3()
.justify_center()
.absolute()
.map(|this| match close_side {
ClosePosition::Left => this.left_1(),
ClosePosition::Right => this.right_1(),
})
.group_hover("", |style| style.visible())
.child(
// TODO: Fix button size
IconButton::new("close tab", Icon::Close)
.icon_color(Color::Muted)
.size(ButtonSize::None)
.icon_size(IconSize::XSmall)
.on_click(cx.listener(move |pane, _, cx| {
pane.close_item_by_id(id, SaveIntent::Close, cx)
.detach_and_log_err(cx);
})),
)
};
let tab = div()
.group("")
.id(ix)
.cursor_pointer()
.when_some(item.tab_tooltip_text(cx), |div, text| {
div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
})
.on_click(cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)))
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
// .on_drop(|_view, state: View<DraggedTab>, cx| {
// eprintln!("{:?}", state.read(cx));
// })
.flex()
.items_center()
.justify_center()
// todo!("Nate - I need to do some work to balance all the items in the tab once things stablize")
.map(|this| {
if close_right {
this.pl_3().pr_1()
} else {
this.pr_1().pr_3()
}
})
.py_1()
.bg(tab_bg)
.border_color(cx.theme().colors().border)
.text_color(if is_active {
cx.theme().colors().text
} else {
cx.theme().colors().text_muted
})
.bg(tab_bg)
// 30px @ 16px/rem
.h(rems(1.875))
.map(|this| {
let is_first_item = ix == 0;
let is_last_item = ix == self.items.len() - 1;
match ix.cmp(&self.active_item_index) {
cmp::Ordering::Less => this.border_l().mr_px(),
cmp::Ordering::Greater => {
if is_last_item {
this.mr_px().ml_px()
cmp::Ordering::Less => {
if is_first_item {
this.pl_px().pr_px().border_b()
} else {
this.border_r().ml_px()
this.border_l().pr_px().border_b()
}
}
cmp::Ordering::Greater => {
if is_last_item {
this.pr_px().pl_px().border_b()
} else {
this.border_r().pl_px().border_b()
}
}
cmp::Ordering::Equal => {
if is_first_item {
this.pl_px().border_r().pb_px()
} else {
this.border_l().border_r().pb_px()
}
}
cmp::Ordering::Equal => this.border_l().border_r(),
}
})
// .hover(|h| h.bg(tab_hover_bg))
// .active(|a| a.bg(tab_active_bg))
.child(
div()
.flex()
.items_center()
h_stack()
.group("")
.id(ix)
.relative()
.h_full()
.cursor_pointer()
.when_some(item.tab_tooltip_text(cx), |div, text| {
div.tooltip(move |cx| cx.build_view(|cx| Tooltip::new(text.clone())).into())
})
.on_click(
cx.listener(move |v: &mut Self, e, cx| v.activate_item(ix, true, true, cx)),
)
// .on_drag(move |pane, cx| pane.render_tab(ix, item.boxed_clone(), detail, cx))
// .drag_over::<DraggedTab>(|d| d.bg(cx.theme().colors().element_drop_target))
// .on_drop(|_view, state: View<DraggedTab>, cx| {
// eprintln!("{:?}", state.read(cx));
// })
.px_5()
// .hover(|h| h.bg(tab_hover_bg))
// .active(|a| a.bg(tab_active_bg))
.gap_1()
.text_color(text_color)
.children(
item.has_conflict(cx)
.then(|| {
div().border().border_color(gpui::red()).child(
IconElement::new(Icon::ExclamationTriangle)
.size(ui::IconSize::Small)
.color(Color::Warning),
)
})
.or(item.is_dirty(cx).then(|| {
div().border().border_color(gpui::red()).child(
IconElement::new(Icon::ExclamationTriangle)
.size(ui::IconSize::Small)
.color(Color::Info),
)
})),
)
.children((!close_right).then(|| close_icon()))
.child(label)
.children(close_right.then(|| close_icon())),
.child(indicator)
.child(close_button)
.child(label),
);
right_click_menu(ix).trigger(tab).menu(|cx| {
ContextMenu::build(cx, |menu, cx| {
menu.action(
"Close Active Item",
CloseActiveItem { save_intent: None }.boxed_clone(),
)
.action("Close Inactive Items", CloseInactiveItems.boxed_clone())
.action("Close Clean Items", CloseCleanItems.boxed_clone())
.action("Close Items To The Left", CloseItemsToTheLeft.boxed_clone())
.action(
"Close Items To The Right",
CloseItemsToTheRight.boxed_clone(),
)
.action(
"Close All Items",
CloseAllItems { save_intent: None }.boxed_clone(),
)
menu.action("Close", CloseActiveItem { save_intent: None }.boxed_clone())
.action("Close Others", CloseInactiveItems.boxed_clone())
.separator()
.action("Close Left", CloseItemsToTheLeft.boxed_clone())
.action("Close Right", CloseItemsToTheRight.boxed_clone())
.separator()
.action("Close Clean", CloseCleanItems.boxed_clone())
.action(
"Close All",
CloseAllItems { save_intent: None }.boxed_clone(),
)
})
})
}
@@ -1562,116 +1578,118 @@ impl Pane {
// Left Side
.child(
h_stack()
.px_2()
.flex()
.flex_none()
.gap_1()
.px_1()
.border_b()
.border_r()
.border_color(cx.theme().colors().border)
// Nav Buttons
.child(
div().border().border_color(gpui::red()).child(
IconButton::new("navigate_backward", Icon::ArrowLeft)
.on_click({
let view = cx.view().clone();
move |_, cx| view.update(cx, Self::navigate_backward)
})
.disabled(!self.can_navigate_backward()),
),
IconButton::new("navigate_backward", Icon::ArrowLeft)
.icon_size(IconSize::Small)
.on_click({
let view = cx.view().clone();
move |_, cx| view.update(cx, Self::navigate_backward)
})
.disabled(!self.can_navigate_backward()),
)
.child(
div().border().border_color(gpui::red()).child(
IconButton::new("navigate_forward", Icon::ArrowRight)
.on_click({
let view = cx.view().clone();
move |_, cx| view.update(cx, Self::navigate_backward)
})
.disabled(!self.can_navigate_forward()),
),
IconButton::new("navigate_forward", Icon::ArrowRight)
.icon_size(IconSize::Small)
.on_click({
let view = cx.view().clone();
move |_, cx| view.update(cx, Self::navigate_backward)
})
.disabled(!self.can_navigate_forward()),
),
)
.child(
div().flex_1().h_full().child(
div().id("tabs").flex().overflow_x_scroll().children(
self.items
.iter()
.enumerate()
.zip(self.tab_details(cx))
.map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
div()
.relative()
.flex_1()
.h_full()
.overflow_hidden_x()
.child(
div()
.absolute()
.top_0()
.left_0()
.z_index(1)
.size_full()
.border_b()
.border_color(cx.theme().colors().border),
)
.child(
h_stack().id("tabs").z_index(2).children(
self.items
.iter()
.enumerate()
.zip(self.tab_details(cx))
.map(|((ix, item), detail)| self.render_tab(ix, item, detail, cx)),
),
),
),
)
// Right Side
.child(
div()
.px_1()
h_stack()
.flex()
.flex_none()
.gap_2()
// Nav Buttons
.gap_1()
.px_1()
.border_b()
.border_l()
.border_color(cx.theme().colors().border)
.child(
div()
.flex()
.items_center()
.gap_px()
.child(
div()
.bg(gpui::blue())
.border()
.border_color(gpui::red())
.child(IconButton::new("plus", Icon::Plus).on_click(
cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action("New File", NewFile.boxed_clone())
.action(
"New Terminal",
NewCenterTerminal.boxed_clone(),
)
.action("New Search", NewSearch.boxed_clone())
});
cx.subscribe(
&menu,
|this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.new_item_menu = None;
},
)
.detach();
this.new_item_menu = Some(menu);
}),
))
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
el.child(Self::render_menu_overlay(new_item_menu))
}),
IconButton::new("plus", Icon::Plus)
.icon_size(IconSize::Small)
.on_click(cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action("New File", NewFile.boxed_clone())
.action(
"New Terminal",
NewCenterTerminal.boxed_clone(),
)
.action("New Search", NewSearch.boxed_clone())
});
cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.new_item_menu = None;
})
.detach();
this.new_item_menu = Some(menu);
})),
)
.when_some(self.new_item_menu.as_ref(), |el, new_item_menu| {
el.child(Self::render_menu_overlay(new_item_menu))
})
.child(
div()
.border()
.border_color(gpui::red())
.child(IconButton::new("split", Icon::Split).on_click(
cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action("Split Right", SplitRight.boxed_clone())
.action("Split Left", SplitLeft.boxed_clone())
.action("Split Up", SplitUp.boxed_clone())
.action("Split Down", SplitDown.boxed_clone())
});
cx.subscribe(
&menu,
|this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.split_item_menu = None;
},
)
.detach();
this.split_item_menu = Some(menu);
}),
))
.when_some(
self.split_item_menu.as_ref(),
|el, split_item_menu| {
el.child(Self::render_menu_overlay(split_item_menu))
},
),
),
IconButton::new("split", Icon::Split)
.icon_size(IconSize::Small)
.on_click(cx.listener(|this, _, cx| {
let menu = ContextMenu::build(cx, |menu, cx| {
menu.action("Split Right", SplitRight.boxed_clone())
.action("Split Left", SplitLeft.boxed_clone())
.action("Split Up", SplitUp.boxed_clone())
.action("Split Down", SplitDown.boxed_clone())
});
cx.subscribe(&menu, |this, _, event: &DismissEvent, cx| {
this.focus(cx);
this.split_item_menu = None;
})
.detach();
this.split_item_menu = Some(menu);
})),
)
.when_some(self.split_item_menu.as_ref(), |el, split_item_menu| {
el.child(Self::render_menu_overlay(split_item_menu))
}),
),
)
}
@@ -2089,6 +2107,8 @@ impl Render for Pane {
v_stack()
.key_context("Pane")
.track_focus(&self.focus_handle)
.size_full()
.overflow_hidden()
.on_focus_in({
let this = this.clone();
move |event, cx| {
@@ -2156,7 +2176,6 @@ impl Render for Pane {
pane.close_all_items(action, cx)
.map(|task| task.detach_and_log_err(cx));
}))
.size_full()
.on_action(
cx.listener(|pane: &mut Self, action: &CloseActiveItem, cx| {
pane.close_active_item(action, cx)
+9 -1
View File
@@ -59,7 +59,6 @@ impl SharedScreen {
}
impl EventEmitter<Event> for SharedScreen {}
impl EventEmitter<ItemEvent> for SharedScreen {}
impl FocusableView for SharedScreen {
fn focus_handle(&self, _: &AppContext) -> FocusHandle {
@@ -79,9 +78,12 @@ impl Render for SharedScreen {
}
impl Item for SharedScreen {
type Event = Event;
fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
@@ -111,4 +113,10 @@ impl Item for SharedScreen {
let track = self.track.upgrade()?;
Some(cx.build_view(|cx| Self::new(&track, self.peer_id, self.user.clone(), cx)))
}
fn to_item_events(event: &Self::Event, mut f: impl FnMut(ItemEvent)) {
match event {
Event::Close => f(ItemEvent::CloseItem),
}
}
}
+3 -21
View File
@@ -1,10 +1,10 @@
use crate::ItemHandle;
use gpui::{
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
ViewContext, WindowContext,
};
use ui::prelude::*;
use ui::{h_stack, v_stack, Icon, IconButton};
use ui::{h_stack, v_stack};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -87,25 +87,7 @@ impl Render for Toolbar {
.child(
h_stack()
.justify_between()
// Toolbar left side
.children(self.items.iter().map(|(child, _)| child.to_any()))
// Toolbar right side
.child(
h_stack()
.p_1()
.child(
div()
.border()
.border_color(gpui::red())
.child(IconButton::new("buffer-search", Icon::MagnifyingGlass)),
)
.child(
div()
.border()
.border_color(gpui::red())
.child(IconButton::new("inline-assist", Icon::MagicWand)),
),
),
.children(self.items.iter().map(|(child, _)| child.to_any())),
)
}
}
+80 -72
View File
@@ -10,7 +10,7 @@ mod persistence;
pub mod searchable;
// todo!()
mod modal_layer;
mod shared_screen;
pub mod shared_screen;
mod status_bar;
mod toolbar;
mod workspace_settings;
@@ -212,27 +212,31 @@ pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
notifications::init(cx);
// cx.add_global_action({
// let app_state = Arc::downgrade(&app_state);
// move |_: &Open, cx: &mut AppContext| {
// let mut paths = cx.prompt_for_paths(PathPromptOptions {
// files: true,
// directories: true,
// multiple: true,
// });
cx.on_action(Workspace::close_global);
cx.on_action(restart);
// if let Some(app_state) = app_state.upgrade() {
// cx.spawn(move |mut cx| async move {
// if let Some(paths) = paths.recv().await.flatten() {
// cx.update(|cx| {
// open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
// });
// }
// })
// .detach();
// }
// }
// });
cx.on_action({
let app_state = Arc::downgrade(&app_state);
move |_: &Open, cx: &mut AppContext| {
let mut paths = cx.prompt_for_paths(PathPromptOptions {
files: true,
directories: true,
multiple: true,
});
if let Some(app_state) = app_state.upgrade() {
cx.spawn(move |mut cx| async move {
if let Some(paths) = paths.await.log_err().flatten() {
cx.update(|cx| {
open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx)
})
.ok();
}
})
.detach();
}
}
});
}
type ProjectItemBuilders =
@@ -1076,7 +1080,6 @@ impl Workspace {
}
}
// todo!(Non-window-actions)
pub fn close_global(_: &CloseWindow, cx: &mut AppContext) {
cx.windows().iter().find(|window| {
window
@@ -1094,21 +1097,18 @@ impl Workspace {
});
}
pub fn close(
&mut self,
_: &CloseWindow,
cx: &mut ViewContext<Self>,
) -> Option<Task<Result<()>>> {
pub fn close_window(&mut self, _: &CloseWindow, cx: &mut ViewContext<Self>) {
let window = cx.window_handle();
let prepare = self.prepare_to_close(false, cx);
Some(cx.spawn(|_, mut cx| async move {
cx.spawn(|_, mut cx| async move {
if prepare.await? {
window.update(&mut cx, |_, cx| {
cx.remove_window();
})?;
}
Ok(())
}))
anyhow::Ok(())
})
.detach_and_log_err(cx)
}
pub fn prepare_to_close(
@@ -1855,13 +1855,13 @@ impl Workspace {
})
}
pub(crate) fn load_path(
fn load_path(
&mut self,
path: ProjectPath,
cx: &mut ViewContext<Self>,
) -> Task<
Result<(
ProjectEntryId,
Option<ProjectEntryId>,
impl 'static + Send + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
)>,
> {
@@ -2327,42 +2327,44 @@ impl Workspace {
}))
}
// pub fn follow_next_collaborator(
// &mut self,
// _: &FollowNextCollaborator,
// cx: &mut ViewContext<Self>,
// ) -> Option<Task<Result<()>>> {
// let collaborators = self.project.read(cx).collaborators();
// let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
// let mut collaborators = collaborators.keys().copied();
// for peer_id in collaborators.by_ref() {
// if peer_id == leader_id {
// break;
// }
// }
// collaborators.next()
// } else if let Some(last_leader_id) =
// self.last_leaders_by_pane.get(&self.active_pane.downgrade())
// {
// if collaborators.contains_key(last_leader_id) {
// Some(*last_leader_id)
// } else {
// None
// pub fn follow_next_collaborator(
// &mut self,
// _: &FollowNextCollaborator,
// cx: &mut ViewContext<Self>,
// ) {
// let collaborators = self.project.read(cx).collaborators();
// let next_leader_id = if let Some(leader_id) = self.leader_for_pane(&self.active_pane) {
// let mut collaborators = collaborators.keys().copied();
// for peer_id in collaborators.by_ref() {
// if peer_id == leader_id {
// break;
// }
// }
// collaborators.next()
// } else if let Some(last_leader_id) =
// self.last_leaders_by_pane.get(&self.active_pane.downgrade())
// {
// if collaborators.contains_key(last_leader_id) {
// Some(*last_leader_id)
// } else {
// None
// };
// let pane = self.active_pane.clone();
// let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
// else {
// return None;
// };
// if Some(leader_id) == self.unfollow(&pane, cx) {
// return None;
// }
// self.follow(leader_id, cx)
// } else {
// None
// };
// let pane = self.active_pane.clone();
// let Some(leader_id) = next_leader_id.or_else(|| collaborators.keys().copied().next())
// else {
// return;
// };
// if Some(leader_id) == self.unfollow(&pane, cx) {
// return;
// }
// if let Some(task) = self.follow(leader_id, cx) {
// task.detach();
// }
// }
pub fn follow(
&mut self,
@@ -2411,6 +2413,18 @@ impl Workspace {
self.start_following(leader_id, cx)
}
// // if you're already following, find the right pane and focus it.
// for (pane, state) in &self.follower_states {
// if leader_id == state.leader_id {
// cx.focus(pane);
// return None;
// }
// }
// // Otherwise, follow.
// self.start_following(leader_id, cx)
// }
pub fn unfollow(&mut self, pane: &View<Pane>, cx: &mut ViewContext<Self>) -> Option<PeerId> {
let state = self.follower_states.remove(pane)?;
let leader_id = state.leader_id;
@@ -2627,8 +2641,6 @@ impl Workspace {
update: proto::UpdateFollowers,
cx: &mut AsyncWindowContext,
) -> Result<()> {
dbg!("process_leader_update", &update);
match update.variant.ok_or_else(|| anyhow!("invalid update"))? {
proto::update_followers::Variant::UpdateActiveView(update_active_view) => {
this.update(cx, |this, _| {
@@ -3223,13 +3235,8 @@ impl Workspace {
fn actions(&self, div: Div, cx: &mut ViewContext<Self>) -> Div {
self.add_workspace_actions_listeners(div, cx)
// cx.add_async_action(Workspace::open);
// cx.add_async_action(Workspace::follow_next_collaborator);
// cx.add_async_action(Workspace::close);
.on_action(cx.listener(Self::close_inactive_items_and_panes))
.on_action(cx.listener(Self::close_all_items_and_panes))
// cx.add_global_action(Workspace::close_global);
// cx.add_global_action(restart);
.on_action(cx.listener(Self::save_all))
.on_action(cx.listener(Self::add_folder_to_project))
.on_action(cx.listener(|workspace, _: &Unfollow, cx| {
@@ -3278,6 +3285,9 @@ impl Workspace {
workspace.close_all_docks(cx);
}),
)
.on_action(cx.listener(Workspace::open))
.on_action(cx.listener(Workspace::close_window))
// cx.add_action(Workspace::activate_pane_at_index);
// cx.add_action(|workspace: &mut Workspace, _: &ReopenClosedItem, cx| {
// workspace.reopen_closed_item(cx).detach();
@@ -3882,8 +3892,6 @@ impl WorkspaceStore {
let leader_id = envelope.original_sender_id()?;
let update = envelope.payload;
dbg!("handle_upate_followers");
this.update(&mut cx, |this, cx| {
for workspace in &this.workspaces {
workspace.update(cx, |workspace, cx| {