dock: Add PanelControl for pinning the toolbar button (#537)
<img width="387" alt="image" src="https://github.com/user-attachments/assets/b3d3f2a8-dc4e-49e6-8e82-e7f551d70bc7" />
This commit is contained in:
parent
de42cc46a7
commit
ceba66c22d
7 changed files with 81 additions and 43 deletions
|
|
@ -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<PanelControl> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<impl gpui::FocusableView> {
|
||||
Self::view(cx)
|
||||
}
|
||||
|
||||
fn zoomable() -> Option<PanelControl> {
|
||||
Some(PanelControl::Toolbar)
|
||||
}
|
||||
}
|
||||
|
||||
impl ImageStory {
|
||||
|
|
|
|||
|
|
@ -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<AnyView>,
|
||||
story_klass: Option<SharedString>,
|
||||
closable: bool,
|
||||
zoomable: bool,
|
||||
zoomable: Option<PanelControl>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -167,8 +167,8 @@ pub trait Story: FocusableView {
|
|||
fn closable() -> bool {
|
||||
true
|
||||
}
|
||||
fn zoomable() -> bool {
|
||||
true
|
||||
fn zoomable() -> Option<PanelControl> {
|
||||
Some(PanelControl::default())
|
||||
}
|
||||
fn title_bg() -> Option<Hsla> {
|
||||
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<PanelControl>,
|
||||
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<PanelControl> {
|
||||
self.zoomable
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -172,10 +172,6 @@ impl super::Story for SidebarStory {
|
|||
fn new_view(cx: &mut WindowContext) -> View<impl gpui::FocusableView> {
|
||||
Self::view(cx)
|
||||
}
|
||||
|
||||
fn zoomable() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
impl gpui::FocusableView for SidebarStory {
|
||||
fn focus_handle(&self, _: &gpui::AppContext) -> gpui::FocusHandle {
|
||||
|
|
|
|||
|
|
@ -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<PanelControl> {
|
||||
None
|
||||
}
|
||||
}
|
||||
impl gpui::FocusableView for TooltipStory {
|
||||
|
|
|
|||
|
|
@ -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<PanelEvent> + FocusableView {
|
||||
|
|
@ -56,11 +76,11 @@ pub trait Panel: EventEmitter<PanelEvent> + 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<PanelControl> {
|
||||
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<TitleStyle>;
|
||||
fn closable(&self, cx: &AppContext) -> bool;
|
||||
fn zoomable(&self, cx: &AppContext) -> bool;
|
||||
fn zoomable(&self, cx: &AppContext) -> Option<PanelControl>;
|
||||
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<T: Panel> PanelView for View<T> {
|
|||
self.read(cx).closable(cx)
|
||||
}
|
||||
|
||||
fn zoomable(&self, cx: &AppContext) -> bool {
|
||||
fn zoomable(&self, cx: &AppContext) -> Option<PanelControl> {
|
||||
self.read(cx).zoomable(cx)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<PanelControl>,
|
||||
draggable: bool,
|
||||
droppable: bool,
|
||||
active_panel: Option<Arc<dyn PanelView>>,
|
||||
|
|
@ -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<PanelControl> {
|
||||
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<Self>) -> 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<Self>) {
|
||||
if !self.zoomable(cx) {
|
||||
if self.zoomable(cx).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue