diff --git a/crates/gpui2/src/app/entity_map.rs b/crates/gpui2/src/app/entity_map.rs index 4a3cca040b..f1e7fad6a1 100644 --- a/crates/gpui2/src/app/entity_map.rs +++ b/crates/gpui2/src/app/entity_map.rs @@ -71,11 +71,12 @@ impl EntityMap { #[track_caller] pub fn lease<'a, T>(&mut self, model: &'a Model) -> Lease<'a, T> { self.assert_valid_context(model); - let entity = Some( - self.entities - .remove(model.entity_id) - .expect("Circular entity lease. Is the entity already being updated?"), - ); + let entity = Some(self.entities.remove(model.entity_id).unwrap_or_else(|| { + panic!( + "Circular entity lease of {}. Is it already being updated?", + std::any::type_name::() + ) + })); Lease { model, entity, diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index f9560f2c53..a37e3dee2a 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -1124,9 +1124,14 @@ where } } } + // if self.hover_style.is_some() { if bounds.contains_point(&mouse_position) { + // eprintln!("div hovered {bounds:?} {mouse_position:?}"); style.refine(&self.hover_style); + } else { + // eprintln!("div NOT hovered {bounds:?} {mouse_position:?}"); } + // } if let Some(drag) = cx.active_drag.take() { for (state_type, group_drag_style) in &self.group_drag_over_styles { diff --git a/crates/gpui2/src/platform/mac/window.rs b/crates/gpui2/src/platform/mac/window.rs index 03782d13a8..bb3a659a62 100644 --- a/crates/gpui2/src/platform/mac/window.rs +++ b/crates/gpui2/src/platform/mac/window.rs @@ -1205,10 +1205,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) { InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return, - InputEvent::MouseUp(MouseUpEvent { - button: MouseButton::Left, - .. - }) => { + InputEvent::MouseUp(MouseUpEvent { .. }) => { lock.synthetic_drag_counter += 1; } diff --git a/crates/gpui2/src/view.rs b/crates/gpui2/src/view.rs index 6b8c8a3eef..a26812c7ae 100644 --- a/crates/gpui2/src/view.rs +++ b/crates/gpui2/src/view.rs @@ -191,6 +191,10 @@ impl AnyView { self.model.entity_type } + pub fn entity_id(&self) -> EntityId { + self.model.entity_id() + } + pub(crate) fn draw( &self, origin: Point, diff --git a/crates/project_panel2/src/project_panel.rs b/crates/project_panel2/src/project_panel.rs index 7a455fe8ce..97f2253c5e 100644 --- a/crates/project_panel2/src/project_panel.rs +++ b/crates/project_panel2/src/project_panel.rs @@ -1,6 +1,6 @@ pub mod file_associations; mod project_panel_settings; -use settings::Settings; +use settings::{Settings, SettingsStore}; use db::kvp::KEY_VALUE_STORE; use editor::{scroll::autoscroll::Autoscroll, Cancel, Editor}; @@ -34,7 +34,7 @@ use ui::{h_stack, v_stack, IconElement, Label}; use unicase::UniCase; use util::{maybe, ResultExt, TryFutureExt}; use workspace::{ - dock::{DockPosition, PanelEvent}, + dock::{DockPosition, Panel, PanelEvent}, Workspace, }; @@ -148,7 +148,6 @@ pub enum Event { SplitEntry { entry_id: ProjectEntryId, }, - DockPositionChanged, Focus, NewSearchInDirectory { dir_entry: Entry, @@ -244,16 +243,17 @@ impl ProjectPanel { this.update_visible_entries(None, cx); // Update the dock position when the setting changes. - // todo!() - // let mut old_dock_position = this.position(cx); - // cx.observe_global::(move |this, cx| { - // let new_dock_position = this.position(cx); - // if new_dock_position != old_dock_position { - // old_dock_position = new_dock_position; - // cx.emit(Event::DockPositionChanged); - // } - // }) - // .detach(); + let mut old_dock_position = this.position(cx); + ProjectPanelSettings::register(cx); + cx.observe_global::(move |this, cx| { + dbg!("OLA!"); + let new_dock_position = this.position(cx); + if new_dock_position != old_dock_position { + old_dock_position = new_dock_position; + cx.emit(PanelEvent::ChangePosition); + } + }) + .detach(); this }); @@ -1485,7 +1485,7 @@ impl EventEmitter for ProjectPanel {} impl EventEmitter for ProjectPanel {} -impl workspace::dock::Panel for ProjectPanel { +impl Panel for ProjectPanel { fn position(&self, cx: &WindowContext) -> DockPosition { match ProjectPanelSettings::get_global(cx).dock { ProjectPanelDockPosition::Left => DockPosition::Left, diff --git a/crates/settings2/src/settings_file.rs b/crates/settings2/src/settings_file.rs index fc4ad5882e..c28e281895 100644 --- a/crates/settings2/src/settings_file.rs +++ b/crates/settings2/src/settings_file.rs @@ -77,6 +77,7 @@ pub fn handle_settings_file_changes( }); cx.spawn(move |mut cx| async move { while let Some(user_settings_content) = user_settings_file_rx.next().await { + eprintln!("settings file changed"); let result = cx.update_global(|store: &mut SettingsStore, cx| { store .set_user_settings(&user_settings_content, cx) diff --git a/crates/terminal_view2/src/terminal_view.rs b/crates/terminal_view2/src/terminal_view.rs index b6ab7e86b9..56de1ee7ef 100644 --- a/crates/terminal_view2/src/terminal_view.rs +++ b/crates/terminal_view2/src/terminal_view.rs @@ -32,7 +32,7 @@ use workspace::{ notifications::NotifyResultExt, register_deserializable_item, searchable::{SearchEvent, SearchOptions, SearchableItem}, - ui::{ContextMenu, Label}, + ui::{ContextMenu, Label, ListEntry}, CloseActiveItem, NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId, }; @@ -85,7 +85,7 @@ pub struct TerminalView { has_new_content: bool, //Currently using iTerm bell, show bell emoji in tab until input is received has_bell: bool, - context_menu: Option>, + context_menu: Option>>, blink_state: bool, blinking_on: bool, blinking_paused: bool, @@ -300,11 +300,10 @@ impl TerminalView { position: gpui::Point, cx: &mut ViewContext, ) { - self.context_menu = Some(cx.build_view(|cx| { - ContextMenu::new(cx) - .entry(Label::new("Clear"), Box::new(Clear)) - .entry( - Label::new("Close"), + self.context_menu = Some(ContextMenu::build(cx, |menu, _| { + menu.action(ListEntry::new(Label::new("Clear")), Box::new(Clear)) + .action( + ListEntry::new(Label::new("Close")), Box::new(CloseActiveItem { save_intent: None }), ) })); diff --git a/crates/ui2/src/components/context_menu.rs b/crates/ui2/src/components/context_menu.rs index d3214cbff1..8bbc0c1b0c 100644 --- a/crates/ui2/src/components/context_menu.rs +++ b/crates/ui2/src/components/context_menu.rs @@ -6,42 +6,74 @@ use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHea use gpui::{ overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div, FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View, + VisualContext, WeakView, }; -pub struct ContextMenu { - items: Vec, - focus_handle: FocusHandle, +pub enum ContextMenuItem { + Separator(ListSeparator), + Header(ListSubHeader), + Entry( + ListEntry>, + Rc)>, + ), } -impl ManagedView for ContextMenu { +pub struct ContextMenu { + items: Vec>, + focus_handle: FocusHandle, + handle: WeakView, +} + +impl ManagedView for ContextMenu { fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle { self.focus_handle.clone() } } -impl ContextMenu { - pub fn new(cx: &mut WindowContext) -> Self { - Self { - items: Default::default(), - focus_handle: cx.focus_handle(), - } +impl ContextMenu { + pub fn build( + cx: &mut ViewContext, + f: impl FnOnce(Self, &mut ViewContext) -> Self, + ) -> View { + let handle = cx.view().downgrade(); + cx.build_view(|cx| { + f( + Self { + handle, + items: Default::default(), + focus_handle: cx.focus_handle(), + }, + cx, + ) + }) } pub fn header(mut self, title: impl Into) -> Self { - self.items.push(ListItem::Header(ListSubHeader::new(title))); + self.items + .push(ContextMenuItem::Header(ListSubHeader::new(title))); self } pub fn separator(mut self) -> Self { - self.items.push(ListItem::Separator(ListSeparator)); + self.items.push(ContextMenuItem::Separator(ListSeparator)); self } - pub fn entry(mut self, label: Label, action: Box) -> Self { - self.items.push(ListEntry::new(label).action(action).into()); + pub fn entry( + mut self, + view: ListEntry, + on_click: impl Fn(&mut V, &mut ViewContext) + 'static, + ) -> Self { + self.items + .push(ContextMenuItem::Entry(view, Rc::new(on_click))); self } + pub fn action(self, view: ListEntry, action: Box) -> Self { + // todo: add the keybindings to the list entry + self.entry(view, move |_, cx| cx.dispatch_action(action.boxed_clone())) + } + pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext) { // todo!() cx.emit(Dismiss); @@ -52,9 +84,9 @@ impl ContextMenu { } } -impl Render for ContextMenu { +impl Render for ContextMenu { type Element = Div; - // todo!() + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { div().elevation_2(cx).flex().flex_row().child( v_stack() @@ -71,7 +103,25 @@ impl Render for ContextMenu { // .bg(cx.theme().colors().elevated_surface_background) // .border() // .border_color(cx.theme().colors().border) - .child(List::new(self.items.clone())), + .child(List::new( + self.items + .iter() + .map(|item| match item { + ContextMenuItem::Separator(separator) => { + ListItem::Separator(separator.clone()) + } + ContextMenuItem::Header(header) => ListItem::Header(header.clone()), + ContextMenuItem::Entry(entry, callback) => { + let callback = callback.clone(); + let handle = self.handle.clone(); + ListItem::Entry(entry.clone().on_click(move |this, cx| { + handle.update(cx, |view, cx| callback(view, cx)).ok(); + cx.emit(Dismiss); + })) + } + }) + .collect(), + )), ) } } @@ -232,6 +282,7 @@ impl Element for MenuHandle { } }) .detach(); + cx.focus_view(&new_menu); *menu.borrow_mut() = Some(new_menu); *position.borrow_mut() = if attach.is_some() && child_layout_id.is_some() { @@ -260,16 +311,25 @@ pub use stories::*; mod stories { use super::*; use crate::story::Story; - use gpui::{actions, Div, Render, VisualContext}; + use gpui::{actions, Div, Render}; - actions!(PrintCurrentDate); + actions!(PrintCurrentDate, PrintBestFood); - fn build_menu(cx: &mut WindowContext, header: impl Into) -> View { - cx.build_view(|cx| { - ContextMenu::new(cx).header(header).separator().entry( - Label::new("Print current time"), - PrintCurrentDate.boxed_clone(), - ) + fn build_menu( + cx: &mut ViewContext, + header: impl Into, + ) -> View> { + let handle = cx.view().clone(); + ContextMenu::build(cx, |menu, _| { + menu.header(header) + .separator() + .entry(ListEntry::new(Label::new("Print current time")), |v, cx| { + println!("dispatching PrintCurrentTime action"); + cx.dispatch_action(PrintCurrentDate.boxed_clone()) + }) + .entry(ListEntry::new(Label::new("Print best food")), |v, cx| { + cx.dispatch_action(PrintBestFood.boxed_clone()) + }) }) } @@ -281,10 +341,14 @@ mod stories { fn render(&mut self, cx: &mut ViewContext) -> Self::Element { Story::container(cx) .on_action(|_, _: &PrintCurrentDate, _| { + println!("printing unix time!"); if let Ok(unix_time) = std::time::UNIX_EPOCH.elapsed() { println!("Current Unix time is {:?}", unix_time.as_secs()); } }) + .on_action(|_, _: &PrintBestFood, _| { + println!("burrito"); + }) .flex() .flex_row() .justify_between() diff --git a/crates/ui2/src/components/list.rs b/crates/ui2/src/components/list.rs index b9508c5413..07ff577ce0 100644 --- a/crates/ui2/src/components/list.rs +++ b/crates/ui2/src/components/list.rs @@ -1,4 +1,6 @@ -use gpui::{div, Action}; +use std::rc::Rc; + +use gpui::{div, Div, Stateful, StatefulInteractiveComponent}; use crate::settings::user_settings; use crate::{ @@ -172,35 +174,35 @@ pub enum ListEntrySize { Medium, } -#[derive(Component, Clone)] -pub enum ListItem { - Entry(ListEntry), +#[derive(Clone)] +pub enum ListItem { + Entry(ListEntry), Separator(ListSeparator), Header(ListSubHeader), } -impl From for ListItem { - fn from(entry: ListEntry) -> Self { +impl From> for ListItem { + fn from(entry: ListEntry) -> Self { Self::Entry(entry) } } -impl From for ListItem { +impl From for ListItem { fn from(entry: ListSeparator) -> Self { Self::Separator(entry) } } -impl From for ListItem { +impl From for ListItem { fn from(entry: ListSubHeader) -> Self { Self::Header(entry) } } -impl ListItem { - fn render(self, view: &mut V, cx: &mut ViewContext) -> impl Component { +impl ListItem { + fn render(self, view: &mut V, ix: usize, cx: &mut ViewContext) -> impl Component { match self { - ListItem::Entry(entry) => div().child(entry.render(view, cx)), + ListItem::Entry(entry) => div().child(entry.render(ix, cx)), ListItem::Separator(separator) => div().child(separator.render(view, cx)), ListItem::Header(header) => div().child(header.render(view, cx)), } @@ -210,7 +212,7 @@ impl ListItem { Self::Entry(ListEntry::new(label)) } - pub fn as_entry(&mut self) -> Option<&mut ListEntry> { + pub fn as_entry(&mut self) -> Option<&mut ListEntry> { if let Self::Entry(entry) = self { Some(entry) } else { @@ -219,8 +221,7 @@ impl ListItem { } } -#[derive(Component)] -pub struct ListEntry { +pub struct ListEntry { disabled: bool, // TODO: Reintroduce this // disclosure_control_style: DisclosureControlVisibility, @@ -231,15 +232,13 @@ pub struct ListEntry { size: ListEntrySize, toggle: Toggle, variant: ListItemVariant, - on_click: Option>, + on_click: Option) + 'static>>, } -impl Clone for ListEntry { +impl Clone for ListEntry { fn clone(&self) -> Self { Self { disabled: self.disabled, - // TODO: Reintroduce this - // disclosure_control_style: DisclosureControlVisibility, indent_level: self.indent_level, label: self.label.clone(), left_slot: self.left_slot.clone(), @@ -247,12 +246,12 @@ impl Clone for ListEntry { size: self.size, toggle: self.toggle, variant: self.variant, - on_click: self.on_click.as_ref().map(|opt| opt.boxed_clone()), + on_click: self.on_click.clone(), } } } -impl ListEntry { +impl ListEntry { pub fn new(label: Label) -> Self { Self { disabled: false, @@ -267,8 +266,8 @@ impl ListEntry { } } - pub fn action(mut self, action: impl Into>) -> Self { - self.on_click = Some(action.into()); + pub fn on_click(mut self, handler: impl Fn(&mut V, &mut ViewContext) + 'static) -> Self { + self.on_click = Some(Rc::new(handler)); self } @@ -307,7 +306,7 @@ impl ListEntry { self } - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + fn render(self, ix: usize, cx: &mut ViewContext) -> Stateful> { let settings = user_settings(cx); let left_content = match self.left_slot.clone() { @@ -328,21 +327,21 @@ impl ListEntry { ListEntrySize::Medium => div().h_7(), }; div() + .id(ix) .relative() .hover(|mut style| { style.background = Some(cx.theme().colors().editor_background.into()); style }) - .on_mouse_down(gpui::MouseButton::Left, { - let action = self.on_click.map(|action| action.boxed_clone()); + .on_click({ + let on_click = self.on_click.clone(); - move |entry: &mut V, event, cx| { - if let Some(action) = action.as_ref() { - cx.dispatch_action(action.boxed_clone()); + move |view: &mut V, event, cx| { + if let Some(on_click) = &on_click { + (on_click)(view, cx) } } }) - .group("") .bg(cx.theme().colors().surface_background) // TODO: Add focus state // .when(self.state == InteractionState::Focused, |this| { @@ -391,8 +390,8 @@ impl ListSeparator { } #[derive(Component)] -pub struct List { - items: Vec, +pub struct List { + items: Vec>, /// Message to display when the list is empty /// Defaults to "No items" empty_message: SharedString, @@ -400,8 +399,8 @@ pub struct List { toggle: Toggle, } -impl List { - pub fn new(items: Vec) -> Self { +impl List { + pub fn new(items: Vec>) -> Self { Self { items, empty_message: "No items".into(), @@ -425,9 +424,14 @@ impl List { self } - fn render(self, _view: &mut V, cx: &mut ViewContext) -> impl Component { + fn render(self, view: &mut V, cx: &mut ViewContext) -> impl Component { let list_content = match (self.items.is_empty(), self.toggle) { - (false, _) => div().children(self.items), + (false, _) => div().children( + self.items + .into_iter() + .enumerate() + .map(|(ix, item)| item.render(view, ix, cx)), + ), (true, Toggle::Toggled(false)) => div(), (true, _) => { div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted)) diff --git a/crates/ui2/src/static_data.rs b/crates/ui2/src/static_data.rs index bb81d6230f..dd296cee5d 100644 --- a/crates/ui2/src/static_data.rs +++ b/crates/ui2/src/static_data.rs @@ -478,7 +478,7 @@ pub fn static_new_notification_items_2() -> Vec> { ] } -pub fn static_project_panel_project_items() -> Vec { +pub fn static_project_panel_project_items() -> Vec> { vec![ ListEntry::new(Label::new("zed")) .left_icon(Icon::FolderOpen.into()) @@ -605,7 +605,7 @@ pub fn static_project_panel_project_items() -> Vec { .collect() } -pub fn static_project_panel_single_items() -> Vec { +pub fn static_project_panel_single_items() -> Vec> { vec![ ListEntry::new(Label::new("todo.md")) .left_icon(Icon::FileDoc.into()) @@ -622,7 +622,7 @@ pub fn static_project_panel_single_items() -> Vec { .collect() } -pub fn static_collab_panel_current_call() -> Vec { +pub fn static_collab_panel_current_call() -> Vec> { vec![ ListEntry::new(Label::new("as-cii")).left_avatar("http://github.com/as-cii.png?s=50"), ListEntry::new(Label::new("nathansobo")) @@ -635,7 +635,7 @@ pub fn static_collab_panel_current_call() -> Vec { .collect() } -pub fn static_collab_panel_channels() -> Vec { +pub fn static_collab_panel_channels() -> Vec> { vec![ ListEntry::new(Label::new("zed")) .left_icon(Icon::Hash.into()) diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index 9603875aed..5df9ce7f45 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -8,7 +8,9 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::sync::Arc; use theme2::ActiveTheme; -use ui::{h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Tooltip}; +use ui::{ + h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Label, ListEntry, Tooltip, +}; pub enum PanelEvent { ChangePosition, @@ -40,7 +42,7 @@ pub trait Panel: FocusableView + EventEmitter { } pub trait PanelHandle: Send + Sync { - fn id(&self) -> EntityId; + fn entity_id(&self) -> EntityId; fn persistent_name(&self) -> &'static str; fn position(&self, cx: &WindowContext) -> DockPosition; fn position_is_valid(&self, position: DockPosition, cx: &WindowContext) -> bool; @@ -62,8 +64,8 @@ impl PanelHandle for View where T: Panel, { - fn id(&self) -> EntityId { - self.entity_id() + fn entity_id(&self) -> EntityId { + Entity::entity_id(self) } fn persistent_name(&self) -> &'static str { @@ -254,20 +256,19 @@ impl Dock { } } - // todo!() - // pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext) { - // for entry in &mut self.panel_entries { - // if entry.panel.as_any() == panel { - // if zoomed != entry.panel.is_zoomed(cx) { - // entry.panel.set_zoomed(zoomed, cx); - // } - // } else if entry.panel.is_zoomed(cx) { - // entry.panel.set_zoomed(false, cx); - // } - // } + pub fn set_panel_zoomed(&mut self, panel: &AnyView, zoomed: bool, cx: &mut ViewContext) { + for entry in &mut self.panel_entries { + if entry.panel.entity_id() == panel.entity_id() { + if zoomed != entry.panel.is_zoomed(cx) { + entry.panel.set_zoomed(zoomed, cx); + } + } else if entry.panel.is_zoomed(cx) { + entry.panel.set_zoomed(false, cx); + } + } - // cx.notify(); - // } + cx.notify(); + } pub fn zoom_out(&mut self, cx: &mut ViewContext) { for entry in &mut self.panel_entries { @@ -277,42 +278,91 @@ impl Dock { } } - pub(crate) fn add_panel(&mut self, panel: View, cx: &mut ViewContext) { + pub(crate) fn add_panel( + &mut self, + panel: View, + workspace: WeakView, + cx: &mut ViewContext, + ) { let subscriptions = [ cx.observe(&panel, |_, _, cx| cx.notify()), - cx.subscribe(&panel, |this, panel, event, cx| { - match event { - PanelEvent::ChangePosition => { - //todo!() - // see: Workspace::add_panel_with_extra_event_handler - } - PanelEvent::ZoomIn => { - //todo!() - // see: Workspace::add_panel_with_extra_event_handler - } - PanelEvent::ZoomOut => { - // todo!() - // // see: Workspace::add_panel_with_extra_event_handler - } - PanelEvent::Activate => { - if let Some(ix) = this - .panel_entries - .iter() - .position(|entry| entry.panel.id() == panel.id()) - { - this.set_open(true, cx); - this.activate_panel(ix, cx); - //` todo!() - // cx.focus(&panel); + cx.subscribe(&panel, move |this, panel, event, cx| match event { + PanelEvent::ChangePosition => { + let new_position = panel.read(cx).position(cx); + + let Ok(new_dock) = workspace.update(cx, |workspace, cx| { + if panel.is_zoomed(cx) { + workspace.zoomed_position = Some(new_position); } - } - PanelEvent::Close => { - if this.visible_panel().map_or(false, |p| p.id() == panel.id()) { - this.set_open(false, cx); + match new_position { + DockPosition::Left => &workspace.left_dock, + DockPosition::Bottom => &workspace.bottom_dock, + DockPosition::Right => &workspace.right_dock, } - } - PanelEvent::Focus => todo!(), + .clone() + }) else { + return; + }; + + let was_visible = this.is_open() + && this.visible_panel().map_or(false, |active_panel| { + active_panel.entity_id() == Entity::entity_id(&panel) + }); + + this.remove_panel(&panel, cx); + + new_dock.update(cx, |new_dock, cx| { + new_dock.add_panel(panel.clone(), workspace.clone(), cx); + if was_visible { + new_dock.set_open(true, cx); + new_dock.activate_panel(this.panels_len() - 1, cx); + } + }); } + PanelEvent::ZoomIn => { + this.set_panel_zoomed(&panel.to_any(), true, cx); + if !panel.has_focus(cx) { + cx.focus_view(&panel); + } + workspace + .update(cx, |workspace, cx| { + workspace.zoomed = Some(panel.downgrade().into()); + workspace.zoomed_position = Some(panel.read(cx).position(cx)); + }) + .ok(); + } + PanelEvent::ZoomOut => { + this.set_panel_zoomed(&panel.to_any(), false, cx); + workspace + .update(cx, |workspace, cx| { + if workspace.zoomed_position == Some(this.position) { + workspace.zoomed = None; + workspace.zoomed_position = None; + } + cx.notify(); + }) + .ok(); + } + PanelEvent::Activate => { + if let Some(ix) = this + .panel_entries + .iter() + .position(|entry| entry.panel.entity_id() == Entity::entity_id(&panel)) + { + this.set_open(true, cx); + this.activate_panel(ix, cx); + cx.focus_view(&panel); + } + } + PanelEvent::Close => { + if this + .visible_panel() + .map_or(false, |p| p.entity_id() == Entity::entity_id(&panel)) + { + this.set_open(false, cx); + } + } + PanelEvent::Focus => todo!(), }), ]; @@ -335,7 +385,7 @@ impl Dock { if let Some(panel_ix) = self .panel_entries .iter() - .position(|entry| entry.panel.id() == panel.id()) + .position(|entry| entry.panel.entity_id() == Entity::entity_id(panel)) { if panel_ix == self.active_panel_index { self.active_panel_index = 0; @@ -396,7 +446,7 @@ impl Dock { pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option { self.panel_entries .iter() - .find(|entry| entry.panel.id() == panel.id()) + .find(|entry| entry.panel.entity_id() == panel.entity_id()) .map(|entry| entry.panel.size(cx)) } @@ -620,6 +670,7 @@ impl Render for PanelButtons { let dock = self.dock.read(cx); let active_index = dock.active_panel_index; let is_open = dock.is_open; + let dock_position = dock.position; let (menu_anchor, menu_attach) = match dock.position { DockPosition::Left => (AnchorCorner::BottomLeft, AnchorCorner::TopLeft), @@ -632,9 +683,10 @@ impl Render for PanelButtons { .panel_entries .iter() .enumerate() - .filter_map(|(i, panel)| { - let icon = panel.panel.icon(cx)?; - let name = panel.panel.persistent_name(); + .filter_map(|(i, entry)| { + let icon = entry.panel.icon(cx)?; + let name = entry.panel.persistent_name(); + let panel = entry.panel.clone(); let mut button: IconButton = if i == active_index && is_open { let action = dock.toggle_action(); @@ -645,7 +697,7 @@ impl Render for PanelButtons { .action(action.boxed_clone()) .tooltip(move |_, cx| Tooltip::for_action(tooltip.clone(), &*action, cx)) } else { - let action = panel.panel.toggle_action(cx); + let action = entry.panel.toggle_action(cx); IconButton::new(name, icon) .action(action.boxed_clone()) @@ -656,7 +708,30 @@ impl Render for PanelButtons { menu_handle() .id(name) .menu(move |_, cx| { - cx.build_view(|cx| ContextMenu::new(cx).header("SECTION")) + const POSITIONS: [DockPosition; 3] = [ + DockPosition::Left, + DockPosition::Right, + DockPosition::Bottom, + ]; + ContextMenu::build(cx, |mut menu, cx| { + for position in POSITIONS { + if position != dock_position + && panel.position_is_valid(position, cx) + { + let panel = panel.clone(); + menu = menu.entry( + ListEntry::new(Label::new(format!( + "Dock {}", + position.to_label() + ))), + move |_, cx| { + panel.set_position(position, cx); + }, + ) + } + } + menu + }) }) .anchor(menu_anchor) .attach(menu_attach) diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 08d248f6f2..c6d6d9c404 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -813,7 +813,9 @@ impl Workspace { DockPosition::Right => &self.right_dock, }; - dock.update(cx, |dock, cx| dock.add_panel(panel, cx)); + dock.update(cx, |dock, cx| { + dock.add_panel(panel, self.weak_self.clone(), cx) + }); } pub fn status_bar(&self) -> &View {