diff --git a/crates/story/src/icon_story.rs b/crates/story/src/icon_story.rs index 11cfad2d..e19e8950 100644 --- a/crates/story/src/icon_story.rs +++ b/crates/story/src/icon_story.rs @@ -36,6 +36,10 @@ impl super::Story for IconStory { fn new_view(cx: &mut WindowContext) -> View { Self::view(cx) } + + fn zoomable() -> bool { + false + } } impl gpui::FocusableView for IconStory { diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 4426fca2..594f3841 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -67,10 +67,12 @@ pub fn init(cx: &mut AppContext) { }; let view = cx.new_view(|cx| { - let (title, description, story) = story_state.to_story(cx); + let (title, description, closeable, zoomable, story) = story_state.to_story(cx); let mut container = StoryContainer::new(cx).story(story, story_state.story_klass); container.name = title.into(); container.description = description.into(); + container.closeable = closeable; + container.zoomable = zoomable; container }); Box::new(view) @@ -106,6 +108,7 @@ pub struct StoryContainer { story: Option, story_klass: Option, closeable: bool, + zoomable: bool, } #[derive(Debug)] @@ -125,6 +128,9 @@ pub trait Story: FocusableView { fn closeable() -> bool { true } + fn zoomable() -> bool { + true + } fn title_bg() -> Option { None } @@ -147,10 +153,10 @@ impl StoryContainer { story: None, story_klass: None, closeable: true, + zoomable: true, } } - #[allow(clippy::too_many_arguments)] pub fn panel(cx: &mut WindowContext) -> View { let name = S::title(); let description = S::description(); @@ -162,6 +168,7 @@ impl StoryContainer { let mut story = Self::new(cx).story(story.into(), story_klass); story.focus_handle = focus_handle; story.closeable = S::closeable(); + story.zoomable = S::zoomable(); story.name = name.into(); story.description = description.into(); story.title_bg = S::title_bg(); @@ -211,12 +218,17 @@ impl StoryState { serde_json::from_value(value).unwrap() } - fn to_story(&self, cx: &mut WindowContext) -> (&'static str, &'static str, AnyView) { + fn to_story( + &self, + cx: &mut WindowContext, + ) -> (&'static str, &'static str, bool, bool, AnyView) { macro_rules! story { ($klass:tt) => { ( $klass::title(), $klass::description(), + $klass::closeable(), + $klass::zoomable(), $klass::view(cx).into(), ) }; @@ -271,6 +283,10 @@ impl Panel for StoryContainer { self.closeable } + fn zoomable(&self, _cx: &WindowContext) -> bool { + self.zoomable + } + fn popup_menu(&self, menu: PopupMenu, _cx: &WindowContext) -> PopupMenu { menu.track_focus(&self.focus_handle) .menu("Info", Box::new(PanelInfo)) diff --git a/crates/story/src/table_story.rs b/crates/story/src/table_story.rs index 068d58a5..c4d7241a 100644 --- a/crates/story/src/table_story.rs +++ b/crates/story/src/table_story.rs @@ -383,6 +383,10 @@ impl super::Story for TableStory { fn new_view(cx: &mut WindowContext) -> View { Self::view(cx) } + + fn closeable() -> bool { + false + } } impl gpui::FocusableView for TableStory { diff --git a/crates/story/src/tooltip_story.rs b/crates/story/src/tooltip_story.rs index 40e0bbc8..4ab69ccb 100644 --- a/crates/story/src/tooltip_story.rs +++ b/crates/story/src/tooltip_story.rs @@ -36,6 +36,10 @@ impl super::Story for TooltipStory { fn new_view(cx: &mut WindowContext) -> View { Self::view(cx) } + + fn zoomable() -> bool { + false + } } impl gpui::FocusableView for TooltipStory { fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle { diff --git a/crates/ui/src/dock/panel.rs b/crates/ui/src/dock/panel.rs index 7bd7e893..cee6afba 100644 --- a/crates/ui/src/dock/panel.rs +++ b/crates/ui/src/dock/panel.rs @@ -44,6 +44,16 @@ pub trait Panel: EventEmitter + FocusableView { true } + /// Return true if the panel is zoomable, default is `false`. + fn zoomable(&self, _cx: &WindowContext) -> bool { + true + } + + /// Return true if the panel is collapsable, default is `false`. + fn collapsible(&self, _cx: &WindowContext) -> bool { + false + } + /// The addition popup menu of the panel, default is `None`. fn popup_menu(&self, this: PopupMenu, _cx: &WindowContext) -> PopupMenu { this @@ -56,11 +66,14 @@ pub trait Panel: EventEmitter + FocusableView { } pub trait PanelView: 'static + Send + Sync { + fn panel_name(&self, _cx: &WindowContext) -> &'static str; fn title(&self, _cx: &WindowContext) -> AnyElement; fn title_style(&self, _cx: &WindowContext) -> Option; fn closeable(&self, cx: &WindowContext) -> bool; + fn zoomable(&self, cx: &WindowContext) -> bool; + fn collapsible(&self, cx: &WindowContext) -> bool; fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu; @@ -72,6 +85,9 @@ pub trait PanelView: 'static + Send + Sync { } impl PanelView for View { + fn panel_name(&self, cx: &WindowContext) -> &'static str { + self.read(cx).panel_name() + } fn title(&self, cx: &WindowContext) -> AnyElement { self.read(cx).title(cx) } @@ -84,6 +100,14 @@ impl PanelView for View { self.read(cx).closeable(cx) } + fn zoomable(&self, cx: &WindowContext) -> bool { + self.read(cx).zoomable(cx) + } + + fn collapsible(&self, cx: &WindowContext) -> bool { + self.read(cx).collapsible(cx) + } + fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu { self.read(cx).popup_menu(menu, cx) } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 57cdb50a..6d7a0f4d 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -85,6 +85,18 @@ impl Panel for TabPanel { .unwrap_or(false) } + fn zoomable(&self, cx: &WindowContext) -> bool { + self.active_panel() + .map(|panel| panel.zoomable(cx)) + .unwrap_or(false) + } + + fn collapsible(&self, cx: &WindowContext) -> bool { + self.active_panel() + .map(|panel| panel.collapsible(cx)) + .unwrap_or(false) + } + fn popup_menu(&self, menu: PopupMenu, cx: &WindowContext) -> PopupMenu { if let Some(panel) = self.active_panel() { panel.popup_menu(menu, cx) @@ -140,6 +152,12 @@ impl TabPanel { /// Add a panel to the end of the tabs pub fn add_panel(&mut self, panel: Arc, cx: &mut ViewContext) { + assert_ne!( + panel.panel_name(cx), + "StackPanel", + "can not allows add `StackPanel` to `TabPanel`" + ); + if self .panels .iter() @@ -228,8 +246,10 @@ impl TabPanel { } fn render_menu_button(&self, cx: &mut ViewContext) -> impl IntoElement { - let is_zoomed = self.is_zoomed; let closeable = self.closeable(cx); + let zoomable = self.zoomable(cx); + + let is_zoomed = self.is_zoomed && zoomable; let view = cx.view().clone(); let build_popup_menu = move |this, cx: &WindowContext| view.read(cx).popup_menu(this, cx); @@ -256,14 +276,14 @@ impl TabPanel { .ghost() .popup_menu(move |this, cx| { build_popup_menu(this, cx) - .menu( - if is_zoomed { + .when(zoomable, |this| { + let name = if is_zoomed { t!("Dock.Zoom Out") } else { t!("Dock.Zoom In") - }, - Box::new(ToggleZoom), - ) + }; + this.separator().menu(name, Box::new(ToggleZoom)) + }) .when(closeable, |this| { this.separator() .menu(t!("Dock.Close"), Box::new(ClosePanel)) @@ -581,6 +601,10 @@ impl TabPanel { } fn on_action_toggle_zoom(&mut self, _: &ToggleZoom, cx: &mut ViewContext) { + if !self.zoomable(cx) { + return; + } + if !self.is_zoomed { cx.emit(PanelEvent::ZoomIn) } else {