diff --git a/crates/story/src/icon_story.rs b/crates/story/src/icon_story.rs index c4fb3374..a79352a3 100644 --- a/crates/story/src/icon_story.rs +++ b/crates/story/src/icon_story.rs @@ -3,6 +3,7 @@ use gpui::{ }; use ui::{ button::{Button, ButtonVariant, ButtonVariants}, + dock::PanelControl, h_flex, theme::ActiveTheme as _, v_flex, Icon, IconName, @@ -33,8 +34,8 @@ impl super::Story for IconStory { Self::view(cx) } - fn zoomable() -> bool { - false + fn zoomable() -> Option { + None } } diff --git a/crates/story/src/image_story.rs b/crates/story/src/image_story.rs index b204f44c..07bc1623 100644 --- a/crates/story/src/image_story.rs +++ b/crates/story/src/image_story.rs @@ -1,5 +1,5 @@ use gpui::{px, ParentElement as _, Render, Styled, View, VisualContext as _, WindowContext}; -use ui::{h_flex, v_flex, SvgImg}; +use ui::{dock::PanelControl, h_flex, v_flex, SvgImg}; const GOOGLE_LOGO: &str = include_str!("./fixtures/google.svg"); const PIE_JSON: &str = include_str!("./fixtures/pie.json"); @@ -19,6 +19,10 @@ impl super::Story for ImageStory { fn new_view(cx: &mut WindowContext) -> View { Self::view(cx) } + + fn zoomable() -> Option { + Some(PanelControl::Toolbar) + } } impl ImageStory { diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 4c92bdf5..73226156 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -52,7 +52,7 @@ use gpui::{ use ui::{ button::Button, divider::Divider, - dock::{register_panel, Panel, PanelEvent, PanelInfo, PanelState, TitleStyle}, + dock::{register_panel, Panel, PanelControl, PanelEvent, PanelInfo, PanelState, TitleStyle}, h_flex, label::Label, notification::Notification, @@ -147,7 +147,7 @@ pub struct StoryContainer { story: Option, story_klass: Option, closable: bool, - zoomable: bool, + zoomable: Option, } #[derive(Debug)] @@ -167,8 +167,8 @@ pub trait Story: FocusableView { fn closable() -> bool { true } - fn zoomable() -> bool { - true + fn zoomable() -> Option { + Some(PanelControl::default()) } fn title_bg() -> Option { None @@ -192,7 +192,7 @@ impl StoryContainer { story: None, story_klass: None, closable: true, - zoomable: true, + zoomable: Some(PanelControl::default()), } } @@ -260,7 +260,13 @@ impl StoryState { fn to_story( &self, cx: &mut WindowContext, - ) -> (&'static str, &'static str, bool, bool, AnyView) { + ) -> ( + &'static str, + &'static str, + bool, + Option, + AnyView, + ) { macro_rules! story { ($klass:tt) => { ( @@ -324,7 +330,7 @@ impl Panel for StoryContainer { self.closable } - fn zoomable(&self, _cx: &AppContext) -> bool { + fn zoomable(&self, _cx: &AppContext) -> Option { self.zoomable } diff --git a/crates/story/src/sidebar_story.rs b/crates/story/src/sidebar_story.rs index 474e6188..bbd3bfc9 100644 --- a/crates/story/src/sidebar_story.rs +++ b/crates/story/src/sidebar_story.rs @@ -172,10 +172,6 @@ impl super::Story for SidebarStory { fn new_view(cx: &mut WindowContext) -> View { Self::view(cx) } - - fn zoomable() -> bool { - true - } } impl gpui::FocusableView for SidebarStory { fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle { diff --git a/crates/story/src/tooltip_story.rs b/crates/story/src/tooltip_story.rs index d1b9889e..65933c71 100644 --- a/crates/story/src/tooltip_story.rs +++ b/crates/story/src/tooltip_story.rs @@ -6,6 +6,7 @@ use gpui::{ use ui::{ button::{Button, ButtonVariant, ButtonVariants}, checkbox::Checkbox, + dock::PanelControl, h_flex, label::Label, tooltip::Tooltip, @@ -37,8 +38,8 @@ impl super::Story for TooltipStory { Self::view(cx) } - fn zoomable() -> bool { - false + fn zoomable() -> Option { + None } } impl gpui::FocusableView for TooltipStory { diff --git a/crates/ui/src/dock/panel.rs b/crates/ui/src/dock/panel.rs index 8efd6469..63c5df0c 100644 --- a/crates/ui/src/dock/panel.rs +++ b/crates/ui/src/dock/panel.rs @@ -30,6 +30,26 @@ pub struct TitleStyle { pub foreground: Hsla, } +#[derive(Clone, Copy, Default)] +pub enum PanelControl { + Both, + #[default] + Menu, + Toolbar, +} + +impl PanelControl { + #[inline] + pub fn toolbar_visible(&self) -> bool { + matches!(self, PanelControl::Both | PanelControl::Toolbar) + } + + #[inline] + pub fn menu_visible(&self) -> bool { + matches!(self, PanelControl::Both | PanelControl::Menu) + } +} + /// The Panel trait used to define the panel. #[allow(unused_variables)] pub trait Panel: EventEmitter + FocusableView { @@ -56,11 +76,11 @@ pub trait Panel: EventEmitter + FocusableView { true } - /// Return true if the panel is zoomable, default is `false`. + /// Return `PanelControl` if the panel is zoomable, default is `None`. /// /// This method called in Panel render, we should make sure it is fast. - fn zoomable(&self, cx: &AppContext) -> bool { - true + fn zoomable(&self, cx: &AppContext) -> Option { + Some(PanelControl::Menu) } /// Return false to hide panel, true to show panel, default is `true`. @@ -108,7 +128,7 @@ pub trait PanelView: 'static + Send + Sync { fn title(&self, cx: &WindowContext) -> AnyElement; fn title_style(&self, cx: &AppContext) -> Option; fn closable(&self, cx: &AppContext) -> bool; - fn zoomable(&self, cx: &AppContext) -> bool; + fn zoomable(&self, cx: &AppContext) -> Option; fn visible(&self, cx: &AppContext) -> bool; fn set_active(&self, active: bool, cx: &mut WindowContext); fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext); @@ -136,7 +156,7 @@ impl PanelView for View { self.read(cx).closable(cx) } - fn zoomable(&self, cx: &AppContext) -> bool { + fn zoomable(&self, cx: &AppContext) -> Option { self.read(cx).zoomable(cx) } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 02726a81..aa823e39 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -20,14 +20,14 @@ use crate::{ }; use super::{ - ClosePanel, DockArea, DockPlacement, Panel, PanelEvent, PanelState, PanelStyle, PanelView, - StackPanel, ToggleZoom, + ClosePanel, DockArea, DockPlacement, Panel, PanelControl, PanelEvent, PanelState, PanelStyle, + PanelView, StackPanel, ToggleZoom, }; #[derive(Clone)] struct TabState { closable: bool, - zoomable: bool, + zoomable: Option, draggable: bool, droppable: bool, active_panel: Option>, @@ -104,10 +104,8 @@ impl Panel for TabPanel { .unwrap_or(false) } - fn zoomable(&self, cx: &AppContext) -> bool { - self.active_panel(cx) - .map(|panel| panel.zoomable(cx)) - .unwrap_or(false) + fn zoomable(&self, cx: &AppContext) -> Option { + self.active_panel(cx).and_then(|panel| panel.zoomable(cx)) } fn visible(&self, cx: &AppContext) -> bool { @@ -377,12 +375,12 @@ impl TabPanel { } fn render_toolbar(&self, state: &TabState, cx: &mut ViewContext) -> impl IntoElement { - let is_zoomed = self.is_zoomed && state.zoomable; + let is_zoomed = self.is_zoomed; let view = cx.view().clone(); let build_popup_menu = move |this, cx: &WindowContext| view.read(cx).popup_menu(this, cx); + let zoomable_toolbar_visible = state.zoomable.map_or(false, |v| v.toolbar_visible()); // TODO: Do not show MenuButton if there is no menu items - h_flex() .gap_2() .occlude() @@ -390,17 +388,29 @@ impl TabPanel { .when_some(self.toolbar_buttons(cx), |this, buttons| { this.children(buttons.into_iter().map(|btn| btn.xsmall().ghost())) }) - .when(self.is_zoomed, |this| { - this.child( - Button::new("zoom") - .icon(IconName::Minimize) - .xsmall() - .ghost() - .tooltip(t!("Dock.Zoom Out")) - .on_click( - cx.listener(|view, _, cx| view.on_action_toggle_zoom(&ToggleZoom, cx)), - ), - ) + .map(|this| { + let value = if is_zoomed { + Some(("zoom-out", IconName::Minimize, t!("Dock.Zoom Out"))) + } else if zoomable_toolbar_visible { + Some(("zoom-in", IconName::Maximize, t!("Dock.Zoom In"))) + } else { + None + }; + + if let Some((id, icon, tooltip)) = value { + this.child( + Button::new(id) + .icon(icon) + .xsmall() + .ghost() + .tooltip(tooltip) + .on_click(cx.listener(|view, _, cx| { + view.on_action_toggle_zoom(&ToggleZoom, cx) + })), + ) + } else { + this + } }) .child( Button::new("menu") @@ -408,7 +418,7 @@ impl TabPanel { .xsmall() .ghost() .popup_menu({ - let zoomable = state.zoomable; + let zoomable = state.zoomable.map_or(false, |v| v.menu_visible()); let closable = state.closable; move |this, cx| { @@ -932,7 +942,7 @@ impl TabPanel { } fn on_action_toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { - if !self.zoomable(cx) { + if self.zoomable(cx).is_none() { return; }