From 8f12489937ce86d997b1f6fc73783f7231995a79 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 9 May 2023 18:57:25 +0200 Subject: [PATCH] WIP: Allow panels to be moved Co-Authored-By: Nathan Sobo --- crates/project_panel/src/project_panel.rs | 32 +++- crates/terminal_view/src/terminal_panel.rs | 2 +- crates/workspace/src/dock.rs | 161 +++++++++++---------- crates/workspace/src/item.rs | 30 +++- crates/workspace/src/workspace.rs | 11 +- crates/zed/src/zed.rs | 13 +- 6 files changed, 157 insertions(+), 92 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index cecf6c114f..f326d665d0 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -28,7 +28,7 @@ use std::{ }; use theme::ProjectPanelEntry; use unicase::UniCase; -use workspace::Workspace; +use workspace::{dock::DockPosition, Workspace}; const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX; @@ -1327,7 +1327,35 @@ impl Entity for ProjectPanel { type Event = Event; } -impl workspace::dock::Panel for ProjectPanel {} +impl workspace::dock::Panel for ProjectPanel { + fn position(&self, cx: &gpui::WindowContext) -> DockPosition { + todo!() + } + + fn position_is_valid(&self, position: DockPosition) -> bool { + matches!(position, DockPosition::Left | DockPosition::Right) + } + + fn icon_path(&self) -> &'static str { + "icons/folder_tree_16.svg" + } + + fn icon_tooltip(&self) -> String { + "Project Panel".into() + } + + fn should_change_position_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + todo!() + } + + fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + false + } + + fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + false + } +} impl ClipboardEntry { fn is_cut(&self) -> bool { diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index d963025f89..be3c3de9d0 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -139,7 +139,7 @@ impl Panel for TerminalPanel { matches!(event, Event::Close) } - fn label(&self, cx: &AppContext) -> Option { + fn icon_label(&self, cx: &AppContext) -> Option { let count = self.pane.read(cx).items_len(); if count == 0 { None diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 492f61c86f..a378e00ec3 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -8,20 +8,25 @@ use settings::Settings; use std::rc::Rc; pub trait Panel: View { - fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { - false - } - fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { - false - } - fn label(&self, _: &AppContext) -> Option { + fn position(&self, cx: &WindowContext) -> DockPosition; + fn position_is_valid(&self, position: DockPosition) -> bool; + fn icon_path(&self) -> &'static str; + fn icon_tooltip(&self) -> String; + fn icon_label(&self, _: &AppContext) -> Option { None } + fn should_change_position_on_event(&self, _: &Self::Event, _: &AppContext) -> bool; + fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool; + fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool; } pub trait PanelHandle { fn id(&self) -> usize; - fn label(&self, cx: &WindowContext) -> Option; + fn position(&self, cx: &WindowContext) -> DockPosition; + fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool; + fn icon_path(&self, cx: &WindowContext) -> &'static str; + fn icon_tooltip(&self, cx: &WindowContext) -> String; + fn icon_label(&self, cx: &WindowContext) -> Option; fn is_focused(&self, cx: &WindowContext) -> bool; fn as_any(&self) -> &AnyViewHandle; } @@ -34,8 +39,24 @@ where self.id() } - fn label(&self, cx: &WindowContext) -> Option { - self.read(cx).label(cx) + fn position(&self, cx: &WindowContext) -> DockPosition { + self.read(cx).position(cx) + } + + fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool { + self.read(cx).position_is_valid(position) + } + + fn icon_path(&self, cx: &WindowContext) -> &'static str { + self.read(cx).icon_path() + } + + fn icon_tooltip(&self, cx: &WindowContext) -> String { + self.read(cx).icon_tooltip() + } + + fn icon_label(&self, cx: &WindowContext) -> Option { + self.read(cx).icon_label(cx) } fn is_focused(&self, cx: &WindowContext) -> bool { @@ -82,8 +103,6 @@ impl DockPosition { } struct Item { - icon_path: &'static str, - tooltip: String, view: Rc, _subscriptions: [Subscription; 2], } @@ -132,13 +151,7 @@ impl Dock { cx.notify(); } - pub fn add_item( - &mut self, - icon_path: &'static str, - tooltip: String, - view: ViewHandle, - cx: &mut ViewContext, - ) { + pub fn add_panel(&mut self, view: ViewHandle, cx: &mut ViewContext) { let subscriptions = [ cx.observe(&view, |_, _, cx| cx.notify()), cx.subscribe(&view, |this, view, event, cx| { @@ -157,8 +170,6 @@ impl Dock { ]; self.items.push(Item { - icon_path, - tooltip, view: Rc::new(view), _subscriptions: subscriptions, }); @@ -235,15 +246,15 @@ impl Entity for PanelButtons { impl View for PanelButtons { fn ui_name() -> &'static str { - "DockToggleButton" + "PanelButtons" } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { let theme = &cx.global::().theme; let tooltip_style = theme.tooltip.clone(); let theme = &theme.workspace.status_bar.panel_buttons; - let dock = self.dock.read(cx); let item_style = theme.button.clone(); + let dock = self.dock.read(cx); let active_ix = dock.active_item_ix; let is_open = dock.is_open; let dock_position = dock.position; @@ -253,69 +264,65 @@ impl View for PanelButtons { DockPosition::Right => theme.group_right, }; - #[allow(clippy::needless_collect)] let items = dock .items .iter() - .map(|item| (item.icon_path, item.tooltip.clone(), item.view.clone())) + .map(|item| item.view.clone()) .collect::>(); - Flex::row() - .with_children(items.into_iter().enumerate().map( - |(ix, (icon_path, tooltip, item_view))| { - let action = TogglePanel { - dock_position, - item_index: ix, - }; - MouseEventHandler::::new(ix, cx, |state, cx| { - let is_active = is_open && ix == active_ix; - let style = item_style.style_for(state, is_active); - Flex::row() - .with_child( - Svg::new(icon_path) - .with_color(style.icon_color) - .constrained() - .with_width(style.icon_size) + .with_children(items.into_iter().enumerate().map(|(ix, view)| { + let action = TogglePanel { + dock_position, + item_index: ix, + }; + MouseEventHandler::::new(ix, cx, |state, cx| { + let is_active = is_open && ix == active_ix; + let style = item_style.style_for(state, is_active); + Flex::row() + .with_child( + Svg::new(view.icon_path(cx)) + .with_color(style.icon_color) + .constrained() + .with_width(style.icon_size) + .aligned(), + ) + .with_children(if let Some(label) = view.icon_label(cx) { + Some( + Label::new(label, style.label.text.clone()) + .contained() + .with_style(style.label.container) .aligned(), ) - .with_children(if let Some(label) = item_view.label(cx) { - Some( - Label::new(label, style.label.text.clone()) - .contained() - .with_style(style.label.container) - .aligned(), - ) - } else { - None - }) - .constrained() - .with_height(style.icon_size) - .contained() - .with_style(style.container) - }) - .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, { - let action = action.clone(); - move |_, this, cx| { - if let Some(workspace) = this.workspace.upgrade(cx) { - let action = action.clone(); - cx.window_context().defer(move |cx| { - workspace.update(cx, |workspace, cx| { - workspace.toggle_panel(&action, cx) - }); + } else { + None + }) + .constrained() + .with_height(style.icon_size) + .contained() + .with_style(style.container) + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, { + let action = action.clone(); + move |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let action = action.clone(); + cx.window_context().defer(move |cx| { + workspace.update(cx, |workspace, cx| { + workspace.toggle_panel(&action, cx) }); - } + }); } - }) - .with_tooltip::( - ix, - tooltip, - Some(Box::new(action)), - tooltip_style.clone(), - cx, - ) - }, - )) + } + }) + .with_tooltip::( + ix, + view.icon_tooltip(cx), + Some(Box::new(action)), + tooltip_style.clone(), + cx, + ) + })) .contained() .with_style(group_style) .into_any() diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 0ae9e4f0ea..ddee068d46 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -1060,5 +1060,33 @@ pub(crate) mod test { } } - impl Panel for TestItem {} + impl Panel for TestItem { + fn position(&self, cx: &gpui::WindowContext) -> crate::dock::DockPosition { + unimplemented!() + } + + fn position_is_valid(&self, position: crate::dock::DockPosition) -> bool { + unimplemented!() + } + + fn icon_path(&self) -> &'static str { + unimplemented!() + } + + fn icon_tooltip(&self) -> String { + unimplemented!() + } + + fn should_change_position_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + unimplemented!() + } + + fn should_activate_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + unimplemented!() + } + + fn should_close_on_event(&self, _: &Self::Event, _: &AppContext) -> bool { + unimplemented!() + } + } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 59a8e97f3d..d86b25b746 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -60,7 +60,7 @@ use crate::{ notifications::simple_message_notification::MessageNotification, persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace}, }; -use dock::{Dock, DockPosition, PanelButtons, TogglePanel}; +use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle, TogglePanel}; use lazy_static::lazy_static; use notifications::{NotificationHandle, NotifyResultExt}; pub use pane::*; @@ -834,6 +834,15 @@ impl Workspace { &self.right_dock } + pub fn add_panel(&mut self, panel: ViewHandle, cx: &mut ViewContext) { + let dock = match panel.position(cx) { + DockPosition::Left => &mut self.left_dock, + DockPosition::Bottom => &mut self.bottom_dock, + DockPosition::Right => &mut self.right_dock, + }; + dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); + } + pub fn status_bar(&self) -> &ViewHandle { &self.status_bar } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 662638301d..394661da41 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -311,19 +311,12 @@ pub fn initialize_workspace( cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx)); workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx); - let project_panel = ProjectPanel::new(workspace, cx); - workspace.left_dock().update(cx, |dock, cx| { - dock.add_item( - "icons/folder_tree_16.svg", - "Project Panel".to_string(), - project_panel, - cx, - ); - }); + let project_panel = cx.add_view(|cx| ProjectPanel::new(workspace, cx)); + workspace.add_panel(panel, cx); let terminal_panel = cx.add_view(|cx| TerminalPanel::new(workspace, cx)); workspace.bottom_dock().update(cx, |dock, cx| { - dock.add_item( + dock.add_panel( "icons/terminal_12.svg", "Terminals".to_string(), terminal_panel,