From adf361b374b7a293881795aa24093bac8aae5751 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 16 May 2023 11:49:48 +0200 Subject: [PATCH] Implement zooming for panes and docks --- assets/keymaps/default.json | 2 +- crates/gpui/src/elements.rs | 7 +-- crates/workspace/src/dock.rs | 77 +++++++++++++++++++------- crates/workspace/src/pane.rs | 10 ++-- crates/workspace/src/workspace.rs | 89 +++++++++++++++++++------------ 5 files changed, 122 insertions(+), 63 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 768e0c6ed1..37c010f0f9 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -220,7 +220,7 @@ "alt-cmd-c": "search::ToggleCaseSensitive", "alt-cmd-w": "search::ToggleWholeWord", "alt-cmd-r": "search::ToggleRegex", - "shift-escape": "pane::ToggleZoom" + "shift-escape": "workspace::ToggleZoom" } }, // Bindings from VS Code diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 40b6a50816..a566751fd5 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -33,11 +33,8 @@ use crate::{ rect::RectF, vector::{vec2f, Vector2F}, }, - json, - platform::MouseButton, - scene::MouseDown, - Action, EventContext, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, - WeakViewHandle, WindowContext, + json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, + WindowContext, }; use anyhow::{anyhow, Result}; use collections::HashMap; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 6d0b34dc27..2e4303e70f 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -1,4 +1,4 @@ -use crate::{StatusItemView, Workspace}; +use crate::{StatusItemView, ToggleZoom, Workspace}; use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle, @@ -10,7 +10,7 @@ use settings::Settings; use std::rc::Rc; pub fn init(cx: &mut AppContext) { - cx.capture_action(Dock::toggle_zoom); + cx.add_action(Dock::toggle_zoom); } pub trait Panel: View { @@ -98,6 +98,10 @@ impl From<&dyn PanelHandle> for AnyViewHandle { } } +pub enum Event { + ZoomIn, +} + pub struct Dock { position: DockPosition, panel_entries: Vec, @@ -141,6 +145,7 @@ struct PanelEntry { panel: Rc, size: f32, context_menu: ViewHandle, + zoomed: bool, _subscriptions: [Subscription; 2], } @@ -187,6 +192,25 @@ impl Dock { cx.notify(); } + pub fn set_zoomed(&mut self, zoomed: bool, cx: &mut ViewContext) { + for (ix, entry) in self.panel_entries.iter_mut().enumerate() { + if ix == self.active_panel_index && entry.panel.can_zoom(cx) { + entry.zoomed = zoomed; + } else { + entry.zoomed = false; + } + } + + cx.notify(); + } + + pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + cx.propagate_action(); + if !self.active_entry().map_or(false, |entry| entry.zoomed) { + cx.emit(Event::ZoomIn); + } + } + pub fn add_panel(&mut self, panel: ViewHandle, cx: &mut ViewContext) { let subscriptions = [ cx.observe(&panel, |_, _, cx| cx.notify()), @@ -214,6 +238,7 @@ impl Dock { self.panel_entries.push(PanelEntry { panel: Rc::new(panel), size, + zoomed: false, context_menu: cx.add_view(|cx| { let mut menu = ContextMenu::new(dock_view_id, cx); menu.set_position_mode(OverlayPositionMode::Local); @@ -260,10 +285,22 @@ impl Dock { } pub fn active_panel(&self) -> Option<&Rc> { + let entry = self.active_entry()?; + Some(&entry.panel) + } + + fn active_entry(&self) -> Option<&PanelEntry> { if self.is_open { - self.panel_entries - .get(self.active_panel_index) - .map(|entry| &entry.panel) + self.panel_entries.get(self.active_panel_index) + } else { + None + } + } + + pub fn zoomed_panel(&self) -> Option { + let entry = self.active_entry()?; + if entry.zoomed { + Some(entry.panel.as_any().clone()) } else { None } @@ -305,7 +342,7 @@ impl Dock { } impl Entity for Dock { - type Event = (); + type Event = Event; } impl View for Dock { @@ -314,18 +351,22 @@ impl View for Dock { } fn render(&mut self, cx: &mut ViewContext) -> AnyElement { - if let Some(active_panel) = self.active_panel() { - let size = self.active_panel_size().unwrap(); - let style = &cx.global::().theme.workspace.dock; - ChildView::new(active_panel.as_any(), cx) - .contained() - .with_style(style.container) - .resizable( - self.position.to_resize_handle_side(), - size, - |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx), - ) - .into_any() + if let Some(active_entry) = self.active_entry() { + if active_entry.zoomed { + Empty::new().into_any() + } else { + let size = self.active_panel_size().unwrap(); + let style = &cx.global::().theme.workspace.dock; + ChildView::new(active_entry.panel.as_any(), cx) + .contained() + .with_style(style.container) + .resizable( + self.position.to_resize_handle_side(), + size, + |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx), + ) + .into_any() + } } else { Empty::new().into_any() } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index a216f6181c..6ccd1573bc 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,7 +2,8 @@ mod dragged_item_receiver; use super::{ItemHandle, SplitDirection}; use crate::{ - item::WeakItemHandle, toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, Workspace, + item::WeakItemHandle, toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, ToggleZoom, + Workspace, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -69,7 +70,6 @@ actions!( SplitUp, SplitRight, SplitDown, - ToggleZoom, ] ); @@ -135,7 +135,6 @@ pub enum Event { ChangeItemTitle, Focus, ZoomIn, - ZoomOut, } pub struct Pane { @@ -662,9 +661,8 @@ impl Pane { } pub fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { - if self.zoomed { - cx.emit(Event::ZoomOut); - } else { + cx.propagate_action(); + if !self.zoomed { cx.emit(Event::ZoomIn); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f4ff8d5c91..af8c958db5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -119,7 +119,8 @@ actions!( NewSearch, Feedback, Restart, - Welcome + Welcome, + ToggleZoom, ] ); @@ -181,6 +182,7 @@ pub type WorkspaceId = i64; impl_actions!(workspace, [ActivatePane]); pub fn init(app_state: Arc, cx: &mut AppContext) { + dock::init(cx); pane::init(cx); notifications::init(cx); @@ -230,6 +232,7 @@ pub fn init(app_state: Arc, cx: &mut AppContext) { }, ); cx.add_action(Workspace::toggle_panel); + cx.add_action(Workspace::toggle_zoom); cx.add_action(Workspace::focus_center); cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { workspace.activate_previous_pane(cx) @@ -441,7 +444,6 @@ pub struct Workspace { weak_self: WeakViewHandle, remote_entity_subscription: Option, modal: Option, - zoomed: Option, center: PaneGroup, left_dock: ViewHandle, bottom_dock: ViewHandle, @@ -593,7 +595,7 @@ impl Workspace { active_call = Some((call, subscriptions)); } - let subscriptions = vec![ + let mut subscriptions = vec![ cx.observe_fullscreen(|_, _, cx| cx.notify()), cx.observe_window_activation(Self::on_window_activation_changed), cx.observe_window_bounds(move |_, mut bounds, display, cx| { @@ -613,24 +615,14 @@ impl Workspace { .spawn(DB.set_window_bounds(workspace_id, bounds, display)) .detach_and_log_err(cx); }), - cx.observe(&left_dock, |this, _, cx| { - this.serialize_workspace(cx); - cx.notify(); - }), - cx.observe(&bottom_dock, |this, _, cx| { - this.serialize_workspace(cx); - cx.notify(); - }), - cx.observe(&right_dock, |this, _, cx| { - this.serialize_workspace(cx); - cx.notify(); - }), ]; + subscriptions.extend(Self::register_dock(&left_dock, cx)); + subscriptions.extend(Self::register_dock(&bottom_dock, cx)); + subscriptions.extend(Self::register_dock(&right_dock, cx)); let mut this = Workspace { weak_self: weak_handle.clone(), modal: None, - zoomed: None, center: PaneGroup::new(center_pane.clone()), panes: vec![center_pane.clone()], panes_by_item: Default::default(), @@ -1305,14 +1297,16 @@ impl Workspace { } } - pub fn zoom_in(&mut self, view: AnyViewHandle, cx: &mut ViewContext) { - self.zoomed = Some(view); - cx.notify(); - } - - pub fn zoom_out(&mut self, cx: &mut ViewContext) { - self.zoomed.take(); - cx.notify(); + fn zoomed(&self, cx: &AppContext) -> Option { + self.left_dock + .read(cx) + .zoomed_panel() + .or(self.bottom_dock.read(cx).zoomed_panel()) + .or(self.right_dock.read(cx).zoomed_panel()) + .or_else(|| { + let pane = self.panes.iter().find(|pane| pane.read(cx).is_zoomed())?; + Some(pane.clone().into_any()) + }) } pub fn items<'a>( @@ -1470,11 +1464,46 @@ impl Workspace { cx.notify(); } + fn toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + // Any time the zoom is toggled we will zoom out all panes and docks. Then, + // the dock or pane that was zoomed will emit an event to zoom itself back in. + self.zoom_out(cx); + } + + fn zoom_out(&mut self, cx: &mut ViewContext) { + for pane in &self.panes { + pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); + } + + self.left_dock + .update(cx, |dock, cx| dock.set_zoomed(false, cx)); + self.bottom_dock + .update(cx, |dock, cx| dock.set_zoomed(false, cx)); + self.right_dock + .update(cx, |dock, cx| dock.set_zoomed(false, cx)); + + cx.notify(); + } + pub fn focus_center(&mut self, _: &menu::Cancel, cx: &mut ViewContext) { cx.focus_self(); cx.notify(); } + fn register_dock(dock: &ViewHandle, cx: &mut ViewContext) -> [Subscription; 2] { + [ + cx.observe(dock, |this, _, cx| { + this.serialize_workspace(cx); + cx.notify(); + }), + cx.subscribe(dock, |_, dock, event, cx| { + dock.update(cx, |dock, cx| match event { + dock::Event::ZoomIn => dock.set_zoomed(true, cx), + }) + }), + ] + } + fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { let pane = cx.add_view(|cx| Pane::new(self.weak_handle(), self.app_state.background_actions, cx)); @@ -1699,13 +1728,7 @@ impl Workspace { } pane::Event::ZoomIn => { pane.update(cx, |pane, cx| pane.set_zoomed(true, cx)); - self.zoom_in(pane.into_any(), cx); - } - pane::Event::ZoomOut => { - if self.zoomed.as_ref().map_or(false, |zoomed| *zoomed == pane) { - pane.update(cx, |pane, cx| pane.set_zoomed(false, cx)); - self.zoom_out(cx); - } + cx.notify(); } } @@ -2757,10 +2780,10 @@ impl View for Workspace { }) .with_child(Overlay::new( Stack::new() - .with_children(self.zoomed.as_ref().map(|zoomed| { + .with_children(self.zoomed(cx).map(|zoomed| { enum ZoomBackground {} - ChildView::new(zoomed, cx) + ChildView::new(&zoomed, cx) .contained() .with_style(theme.workspace.zoomed_foreground) .aligned()