diff --git a/README.md b/README.md index 9465a9ad..1feba863 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,8 @@ gpui = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform- ui = { git = "https://github.com/longbridge/gpui-component.git" } ``` +More usage can be found in [story](https://github.com/longbridge/gpui-component/tree/main/crates/story) directory. + ## Demo If you want to see the demo, here is a some demo applications. diff --git a/crates/story/src/input_story.rs b/crates/story/src/input_story.rs index d321a5cf..098db9a0 100644 --- a/crates/story/src/input_story.rs +++ b/crates/story/src/input_story.rs @@ -353,7 +353,6 @@ impl Render for InputStory { .on_action(cx.listener(Self::tab)) .on_action(cx.listener(Self::tab_prev)) .size_full() - .p_4() .justify_start() .gap_3() .child( @@ -429,14 +428,14 @@ impl Render for InputStory { .gap_3() .child( Button::new("btn-submit") - .w_full() + .flex_1() .with_variant(ButtonVariant::Primary) .label("Submit") .on_click(cx.listener(|_, _, cx| cx.dispatch_action(Box::new(Tab)))), ) .child( Button::new("btn-cancel") - .w_full() + .flex_1() .label("Cancel") .into_element(), ), diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 9c50c5e7..f212bca5 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -17,6 +17,7 @@ mod sidebar_story; mod switch_story; mod table_story; mod text_story; +mod title_bar; mod tooltip_story; mod webview_story; @@ -29,10 +30,11 @@ pub use dropdown_story::DropdownStory; pub use form_story::FormStory; use gpui::{ - actions, div, prelude::FluentBuilder as _, px, size, AnyElement, AnyView, AppContext, Bounds, - Context as _, Div, EventEmitter, FocusableView, Global, Hsla, InteractiveElement, IntoElement, - Model, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled as _, View, - ViewContext, VisualContext, WindowBounds, WindowContext, WindowKind, WindowOptions, + actions, div, impl_internal_actions, prelude::FluentBuilder as _, px, size, AnyElement, + AnyView, AppContext, Bounds, Context as _, Div, EventEmitter, FocusableView, Global, Hsla, + InteractiveElement, IntoElement, Model, ParentElement, Render, SharedString, + StatefulInteractiveElement, Styled as _, View, ViewContext, VisualContext, WindowBounds, + WindowContext, WindowKind, WindowOptions, }; pub use icon_story::IconStory; pub use image_story::ImageStory; @@ -48,6 +50,7 @@ pub use sidebar_story::SidebarStory; pub use switch_story::SwitchStory; pub use table_story::TableStory; pub use text_story::TextStory; +pub use title_bar::AppTitleBar; pub use tooltip_story::TooltipStory; pub use webview_story::WebViewStory; @@ -59,9 +62,23 @@ use ui::{ label::Label, notification::Notification, popup_menu::PopupMenu, - v_flex, ActiveTheme, ContextModal, IconName, Root, + scroll::ScrollbarShow, + v_flex, ActiveTheme, ContextModal, IconName, Root, TitleBar, }; +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct SelectScrollbarShow(ScrollbarShow); + +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct SelectLocale(SharedString); + +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct SelectFont(usize); + +impl_internal_actions!(story, [SelectLocale, SelectFont, SelectScrollbarShow,]); + +actions!(story, [Quit, Open, CloseWindow]); + const PANEL_NAME: &str = "StoryContainer"; pub struct AppState { @@ -101,11 +118,7 @@ where cx.spawn(|mut cx| async move { let options = WindowOptions { window_bounds: Some(WindowBounds::Windowed(window_bounds)), - // titlebar: Some(TitlebarOptions { - // title: Some(title.clone()), - // appears_transparent: true, - // traffic_light_position: Some(point(px(9.0), px(9.0))), - // }), + titlebar: Some(TitleBar::title_bar_options()), window_min_size: Some(gpui::Size { width: px(640.), height: px(480.), @@ -121,7 +134,8 @@ where let window = cx .open_window(options, |cx| { let view = crate_view_fn(cx); - cx.new_view(|cx| Root::new(view.into(), cx)) + let root = cx.new_view(|cx| StoryRoot::new(title.clone(), view, cx)); + cx.new_view(|cx| Root::new(root.into(), cx)) }) .expect("failed to open window"); @@ -137,6 +151,34 @@ where .detach(); } +struct StoryRoot { + title_bar: View, + view: AnyView, +} + +impl StoryRoot { + pub fn new( + title: impl Into, + view: impl Into, + cx: &mut ViewContext, + ) -> Self { + let title_bar = cx.new_view(|cx| AppTitleBar::new(title, cx)); + Self { + title_bar, + view: view.into(), + } + } +} + +impl Render for StoryRoot { + fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { + v_flex() + .size_full() + .child(self.title_bar.clone()) + .child(self.view.clone()) + } +} + impl Global for AppState {} pub fn init(cx: &mut AppContext) { diff --git a/crates/story/src/main.rs b/crates/story/src/main.rs index 785a92fd..1d67cb21 100644 --- a/crates/story/src/main.rs +++ b/crates/story/src/main.rs @@ -1,58 +1,33 @@ use anyhow::{Context, Result}; use gpui::*; -use prelude::FluentBuilder as _; use serde::Deserialize; use std::{sync::Arc, time::Duration}; use story::{ - AccordionStory, AppState, Assets, ButtonStory, CalendarStory, DropdownStory, FormStory, - IconStory, ImageStory, InputStory, ListStory, ModalStory, PopupStory, ProgressStory, - ResizableStory, ScrollableStory, SidebarStory, StoryContainer, SwitchStory, TableStory, - TextStory, TooltipStory, + AccordionStory, AppState, AppTitleBar, Assets, ButtonStory, CalendarStory, DropdownStory, + FormStory, IconStory, ImageStory, InputStory, ListStory, ModalStory, Open, PopupStory, + ProgressStory, Quit, ResizableStory, ScrollableStory, SidebarStory, StoryContainer, + SwitchStory, TableStory, TextStory, TooltipStory, }; use ui::{ - badge::Badge, button::{Button, ButtonVariants as _}, - color_picker::{ColorPicker, ColorPickerEvent}, dock::{DockArea, DockAreaState, DockEvent, DockItem, DockPlacement}, popup_menu::PopupMenuExt, - scroll::ScrollbarShow, - ActiveTheme, ContextModal, IconName, Root, Sizable, Theme, TitleBar, + IconName, Root, Sizable, Theme, TitleBar, }; +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct AddPanel(DockPlacement); + +#[derive(Clone, PartialEq, Eq, Deserialize)] +pub struct TogglePanelVisible(SharedString); + +impl_internal_actions!(story, [AddPanel, TogglePanelVisible]); + const MAIN_DOCK_AREA: DockAreaTab = DockAreaTab { id: "main-dock", version: 5, }; -#[derive(Clone, PartialEq, Eq, Deserialize)] -struct SelectScrollbarShow(ScrollbarShow); - -#[derive(Clone, PartialEq, Eq, Deserialize)] -struct SelectLocale(SharedString); - -#[derive(Clone, PartialEq, Eq, Deserialize)] -struct SelectFont(usize); - -#[derive(Clone, PartialEq, Eq, Deserialize)] -struct AddPanel(DockPlacement); - -#[derive(Clone, PartialEq, Eq, Deserialize)] -struct TogglePanelVisible(SharedString); - -impl_internal_actions!( - story, - [ - SelectLocale, - SelectFont, - AddPanel, - SelectScrollbarShow, - TogglePanelVisible - ] -); - -actions!(main_menu, [Quit]); -actions!(workspace, [Open, CloseWindow]); - pub fn init(cx: &mut AppContext) { cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]); @@ -63,11 +38,8 @@ pub fn init(cx: &mut AppContext) { } pub struct StoryWorkspace { - theme_color: Option, + title_bar: View, dock_area: View, - locale_selector: View, - font_size_selector: View, - theme_color_picker: View, last_layout_state: Option, _save_layout_task: Option>, } @@ -118,57 +90,75 @@ impl StoryWorkspace { }) .detach(); - let locale_selector = cx.new_view(LocaleSelector::new); - let font_size_selector = cx.new_view(FontSizeSelector::new); + let title_bar = cx.new_view(|cx| { + AppTitleBar::new("Examples", cx).child({ + move |cx| { + Button::new("add-panel") + .icon(IconName::LayoutDashboard) + .small() + .ghost() + .popup_menu({ + let invisible_panels = AppState::global(cx).invisible_panels.clone(); - let theme_color_picker = cx.new_view(|cx| { - let mut picker = ColorPicker::new("theme-color-picker", cx) - .xsmall() - .anchor(Corner::TopRight) - .label("Theme Color"); - picker.set_value(cx.theme().primary, cx); - picker - }); - cx.subscribe( - &theme_color_picker, - |this, _, ev: &ColorPickerEvent, cx| match ev { - ColorPickerEvent::Change(color) => { - this.set_theme_color(*color, cx); + move |menu, cx| { + menu.menu( + "Add Panel to Center", + Box::new(AddPanel(DockPlacement::Center)), + ) + .separator() + .menu("Add Panel to Left", Box::new(AddPanel(DockPlacement::Left))) + .menu( + "Add Panel to Right", + Box::new(AddPanel(DockPlacement::Right)), + ) + .menu( + "Add Panel to Bottom", + Box::new(AddPanel(DockPlacement::Bottom)), + ) + .separator() + .menu_with_check( + "Sidebar", + !invisible_panels + .read(cx) + .contains(&SharedString::from("Sidebar")), + Box::new(TogglePanelVisible(SharedString::from("Sidebar"))), + ) + .menu_with_check( + "Modal", + !invisible_panels + .read(cx) + .contains(&SharedString::from("SidebModalar")), + Box::new(TogglePanelVisible(SharedString::from("Modal"))), + ) + .menu_with_check( + "Accordion", + !invisible_panels + .read(cx) + .contains(&SharedString::from("Accordion")), + Box::new(TogglePanelVisible(SharedString::from("Accordion"))), + ) + .menu_with_check( + "List", + !invisible_panels + .read(cx) + .contains(&SharedString::from("List")), + Box::new(TogglePanelVisible(SharedString::from("List"))), + ) + } + }) + .anchor(Corner::TopRight) } - }, - ) - .detach(); + }) + }); Self { - theme_color: None, dock_area, - locale_selector, - font_size_selector, - theme_color_picker, + title_bar, last_layout_state: None, _save_layout_task: None, } } - fn set_theme_color(&mut self, color: Option, cx: &mut ViewContext) { - self.theme_color = color; - if let Some(color) = self.theme_color { - let theme = cx.global_mut::(); - theme.apply_color(color); - cx.refresh(); - } - } - - fn change_color_mode(&mut self, _: &ClickEvent, cx: &mut ViewContext) { - let mode = match cx.theme().mode.is_dark() { - true => ui::ThemeMode::Light, - false => ui::ThemeMode::Dark, - }; - - Theme::change(mode, cx); - self.set_theme_color(self.theme_color, cx); - } - fn save_layout(&mut self, dock_area: View, cx: &mut ViewContext) { self._save_layout_task = Some(cx.spawn(|this, mut cx| async move { Timer::after(Duration::from_secs(10)).await; @@ -345,11 +335,7 @@ impl StoryWorkspace { let options = WindowOptions { window_bounds: Some(WindowBounds::Windowed(window_bounds)), #[cfg(not(target_os = "linux"))] - titlebar: Some(TitlebarOptions { - title: None, - appears_transparent: true, - traffic_light_position: Some(point(px(9.0), px(9.0))), - }), + titlebar: Some(TitleBar::title_bar_options()), window_min_size: Some(gpui::Size { width: px(640.), height: px(480.), @@ -450,8 +436,6 @@ impl Render for StoryWorkspace { let drawer_layer = Root::render_drawer_layer(cx); let modal_layer = Root::render_modal_layer(cx); let notification_layer = Root::render_notification_layer(cx); - let notifications_count = cx.notifications().len(); - let invisible_panels = AppState::global(cx).invisible_panels.clone(); div() .id("story-workspace") @@ -461,123 +445,7 @@ impl Render for StoryWorkspace { .size_full() .flex() .flex_col() - .child( - TitleBar::new() - // left side - .child(div().flex().items_center().child("GPUI App")) - .child( - div() - .flex() - .items_center() - .justify_end() - .px_2() - .gap_2() - .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation()) - .child(self.theme_color_picker.clone()) - .child( - Button::new("add-panel") - .icon(IconName::LayoutDashboard) - .small() - .ghost() - .popup_menu(move |menu, cx| { - menu.menu( - "Add Panel to Center", - Box::new(AddPanel(DockPlacement::Center)), - ) - .separator() - .menu( - "Add Panel to Left", - Box::new(AddPanel(DockPlacement::Left)), - ) - .menu( - "Add Panel to Right", - Box::new(AddPanel(DockPlacement::Right)), - ) - .menu( - "Add Panel to Bottom", - Box::new(AddPanel(DockPlacement::Bottom)), - ) - .separator() - .menu_with_check( - "Sidebar", - !invisible_panels - .read(cx) - .contains(&SharedString::from("Sidebar")), - Box::new(TogglePanelVisible(SharedString::from( - "Sidebar", - ))), - ) - .menu_with_check( - "Modal", - !invisible_panels - .read(cx) - .contains(&SharedString::from("SidebModalar")), - Box::new(TogglePanelVisible(SharedString::from( - "Modal", - ))), - ) - .menu_with_check( - "Accordion", - !invisible_panels - .read(cx) - .contains(&SharedString::from("Accordion")), - Box::new(TogglePanelVisible(SharedString::from( - "Accordion", - ))), - ) - .menu_with_check( - "List", - !invisible_panels - .read(cx) - .contains(&SharedString::from("List")), - Box::new(TogglePanelVisible(SharedString::from( - "List", - ))), - ) - }) - .anchor(Corner::TopRight), - ) - .child( - Button::new("theme-mode") - .map(|this| { - if cx.theme().mode.is_dark() { - this.icon(IconName::Sun) - } else { - this.icon(IconName::Moon) - } - }) - .small() - .ghost() - .on_click(cx.listener(Self::change_color_mode)), - ) - .child(self.locale_selector.clone()) - .child(self.font_size_selector.clone()) - .child( - Badge::new().dot().count(1).child( - Button::new("github") - .icon(IconName::GitHub) - .small() - .ghost() - .on_click(|_, cx| { - cx.open_url( - "https://github.com/longbridge/gpui-component", - ) - }), - ), - ) - .child( - div().relative().child( - Badge::new().count(notifications_count).max(99).child( - Button::new("bell") - .small() - .ghost() - .compact() - .icon(IconName::Bell), - ), - ), - ), - ), - ) + .child(self.title_bar.clone()) .child(self.dock_area.clone()) .children(drawer_layer) .children(modal_layer) @@ -585,117 +453,6 @@ impl Render for StoryWorkspace { } } -struct LocaleSelector { - focus_handle: FocusHandle, -} - -impl LocaleSelector { - pub fn new(cx: &mut ViewContext) -> Self { - Self { - focus_handle: cx.focus_handle(), - } - } - - fn on_select_locale(&mut self, locale: &SelectLocale, cx: &mut ViewContext) { - ui::set_locale(&locale.0); - cx.refresh(); - } -} - -impl Render for LocaleSelector { - fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - let focus_handle = self.focus_handle.clone(); - let locale = ui::locale().to_string(); - - div() - .id("locale-selector") - .track_focus(&focus_handle) - .on_action(cx.listener(Self::on_select_locale)) - .child( - Button::new("btn") - .small() - .ghost() - .icon(IconName::Globe) - .popup_menu(move |this, _| { - this.menu_with_check( - "English", - locale == "en", - Box::new(SelectLocale("en".into())), - ) - .menu_with_check( - "简体中文", - locale == "zh-CN", - Box::new(SelectLocale("zh-CN".into())), - ) - }) - .anchor(Corner::TopRight), - ) - } -} - -struct FontSizeSelector { - focus_handle: FocusHandle, -} - -impl FontSizeSelector { - pub fn new(cx: &mut ViewContext) -> Self { - Self { - focus_handle: cx.focus_handle(), - } - } - - fn on_select_font(&mut self, font_size: &SelectFont, cx: &mut ViewContext) { - Theme::global_mut(cx).font_size = font_size.0 as f32; - cx.refresh(); - } - - fn on_select_scrollbar_show(&mut self, show: &SelectScrollbarShow, cx: &mut ViewContext) { - Theme::global_mut(cx).scrollbar_show = show.0; - cx.refresh(); - } -} - -impl Render for FontSizeSelector { - fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { - let focus_handle = self.focus_handle.clone(); - let font_size = cx.theme().font_size as i32; - let scroll_show = cx.theme().scrollbar_show; - - div() - .id("font-size-selector") - .track_focus(&focus_handle) - .on_action(cx.listener(Self::on_select_font)) - .on_action(cx.listener(Self::on_select_scrollbar_show)) - .child( - Button::new("btn") - .small() - .ghost() - .icon(IconName::Settings2) - .popup_menu(move |this, _| { - this.menu_with_check( - "Font Large", - font_size == 18, - Box::new(SelectFont(18)), - ) - .menu_with_check("Font Default", font_size == 16, Box::new(SelectFont(16))) - .menu_with_check("Font Small", font_size == 14, Box::new(SelectFont(14))) - .separator() - .menu_with_check( - "Scrolling to show Scrollbar", - scroll_show == ScrollbarShow::Scrolling, - Box::new(SelectScrollbarShow(ScrollbarShow::Scrolling)), - ) - .menu_with_check( - "Hover to show Scrollbar", - scroll_show == ScrollbarShow::Hover, - Box::new(SelectScrollbarShow(ScrollbarShow::Hover)), - ) - }) - .anchor(Corner::TopRight), - ) - } -} - fn main() { use ui::input::{Copy, Cut, Paste, Redo, Undo}; diff --git a/crates/story/src/title_bar.rs b/crates/story/src/title_bar.rs new file mode 100644 index 00000000..a5994a80 --- /dev/null +++ b/crates/story/src/title_bar.rs @@ -0,0 +1,258 @@ +use std::rc::Rc; + +use gpui::{ + div, AnyElement, ClickEvent, Corner, FocusHandle, Hsla, InteractiveElement as _, IntoElement, + MouseButton, ParentElement as _, Render, SharedString, Styled as _, View, ViewContext, + VisualContext as _, WindowContext, +}; +use ui::{ + badge::Badge, + button::{Button, ButtonVariants as _}, + color_picker::{ColorPicker, ColorPickerEvent}, + popup_menu::PopupMenuExt as _, + prelude::FluentBuilder as _, + scroll::ScrollbarShow, + ActiveTheme as _, ContextModal as _, IconName, Sizable as _, Theme, TitleBar, +}; + +use crate::{SelectFont, SelectLocale, SelectScrollbarShow}; + +pub struct AppTitleBar { + title: SharedString, + theme_color: Option, + locale_selector: View, + font_size_selector: View, + theme_color_picker: View, + child: Rc AnyElement>, +} + +impl AppTitleBar { + pub fn new(title: impl Into, cx: &mut ViewContext) -> Self { + let locale_selector = cx.new_view(LocaleSelector::new); + let font_size_selector = cx.new_view(FontSizeSelector::new); + + let theme_color_picker = cx.new_view(|cx| { + let mut picker = ColorPicker::new("theme-color-picker", cx) + .xsmall() + .anchor(Corner::TopRight) + .label("Theme Color"); + picker.set_value(cx.theme().primary, cx); + picker + }); + cx.subscribe( + &theme_color_picker, + |this, _, ev: &ColorPickerEvent, cx| match ev { + ColorPickerEvent::Change(color) => { + this.set_theme_color(*color, cx); + } + }, + ) + .detach(); + + Self { + title: title.into(), + theme_color: None, + locale_selector, + font_size_selector, + theme_color_picker, + child: Rc::new(|_| div().into_any_element()), + } + } + + pub fn child(mut self, f: F) -> Self + where + E: IntoElement, + F: Fn(&mut WindowContext) -> E + 'static, + { + self.child = Rc::new(move |cx| f(cx).into_any_element()); + self + } + + fn set_theme_color(&mut self, color: Option, cx: &mut ViewContext) { + self.theme_color = color; + if let Some(color) = self.theme_color { + let theme = cx.global_mut::(); + theme.apply_color(color); + cx.refresh(); + } + } + + fn change_color_mode(&mut self, _: &ClickEvent, cx: &mut ViewContext) { + let mode = match cx.theme().mode.is_dark() { + true => ui::ThemeMode::Light, + false => ui::ThemeMode::Dark, + }; + + Theme::change(mode, cx); + self.set_theme_color(self.theme_color, cx); + } +} + +impl Render for AppTitleBar { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let notifications_count = cx.notifications().len(); + + TitleBar::new() + // left side + .child(div().flex().items_center().child(self.title.clone())) + .child( + div() + .flex() + .items_center() + .justify_end() + .px_2() + .gap_2() + .on_mouse_down(MouseButton::Left, |_, cx| cx.stop_propagation()) + .child(self.theme_color_picker.clone()) + .child((self.child.clone())(cx)) + .child( + Button::new("theme-mode") + .map(|this| { + if cx.theme().mode.is_dark() { + this.icon(IconName::Sun) + } else { + this.icon(IconName::Moon) + } + }) + .small() + .ghost() + .on_click(cx.listener(Self::change_color_mode)), + ) + .child(self.locale_selector.clone()) + .child(self.font_size_selector.clone()) + .child( + Badge::new().dot().count(1).child( + Button::new("github") + .icon(IconName::GitHub) + .small() + .ghost() + .on_click(|_, cx| { + cx.open_url("https://github.com/longbridge/gpui-component") + }), + ), + ) + .child( + div().relative().child( + Badge::new().count(notifications_count).max(99).child( + Button::new("bell") + .small() + .ghost() + .compact() + .icon(IconName::Bell), + ), + ), + ), + ) + } +} + +struct LocaleSelector { + focus_handle: FocusHandle, +} + +impl LocaleSelector { + pub fn new(cx: &mut ViewContext) -> Self { + Self { + focus_handle: cx.focus_handle(), + } + } + + fn on_select_locale(&mut self, locale: &SelectLocale, cx: &mut ViewContext) { + ui::set_locale(&locale.0); + cx.refresh(); + } +} + +impl Render for LocaleSelector { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let focus_handle = self.focus_handle.clone(); + let locale = ui::locale().to_string(); + + div() + .id("locale-selector") + .track_focus(&focus_handle) + .on_action(cx.listener(Self::on_select_locale)) + .child( + Button::new("btn") + .small() + .ghost() + .icon(IconName::Globe) + .popup_menu(move |this, _| { + this.menu_with_check( + "English", + locale == "en", + Box::new(SelectLocale("en".into())), + ) + .menu_with_check( + "简体中文", + locale == "zh-CN", + Box::new(SelectLocale("zh-CN".into())), + ) + }) + .anchor(Corner::TopRight), + ) + } +} + +struct FontSizeSelector { + focus_handle: FocusHandle, +} + +impl FontSizeSelector { + pub fn new(cx: &mut ViewContext) -> Self { + Self { + focus_handle: cx.focus_handle(), + } + } + + fn on_select_font(&mut self, font_size: &SelectFont, cx: &mut ViewContext) { + Theme::global_mut(cx).font_size = font_size.0 as f32; + cx.refresh(); + } + + fn on_select_scrollbar_show(&mut self, show: &SelectScrollbarShow, cx: &mut ViewContext) { + Theme::global_mut(cx).scrollbar_show = show.0; + cx.refresh(); + } +} + +impl Render for FontSizeSelector { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let focus_handle = self.focus_handle.clone(); + let font_size = cx.theme().font_size as i32; + let scroll_show = cx.theme().scrollbar_show; + + div() + .id("font-size-selector") + .track_focus(&focus_handle) + .on_action(cx.listener(Self::on_select_font)) + .on_action(cx.listener(Self::on_select_scrollbar_show)) + .child( + Button::new("btn") + .small() + .ghost() + .icon(IconName::Settings2) + .popup_menu(move |this, _| { + this.menu_with_check( + "Font Large", + font_size == 18, + Box::new(SelectFont(18)), + ) + .menu_with_check("Font Default", font_size == 16, Box::new(SelectFont(16))) + .menu_with_check("Font Small", font_size == 14, Box::new(SelectFont(14))) + .separator() + .menu_with_check( + "Scrolling to show Scrollbar", + scroll_show == ScrollbarShow::Scrolling, + Box::new(SelectScrollbarShow(ScrollbarShow::Scrolling)), + ) + .menu_with_check( + "Hover to show Scrollbar", + scroll_show == ScrollbarShow::Hover, + Box::new(SelectScrollbarShow(ScrollbarShow::Hover)), + ) + }) + .anchor(Corner::TopRight), + ) + } +} diff --git a/crates/ui/src/title_bar.rs b/crates/ui/src/title_bar.rs index 73fd72ee..b6a28451 100644 --- a/crates/ui/src/title_bar.rs +++ b/crates/ui/src/title_bar.rs @@ -4,7 +4,7 @@ use crate::{h_flex, ActiveTheme, Icon, IconName, InteractiveElementExt as _, Siz use gpui::{ div, prelude::FluentBuilder as _, px, relative, AnyElement, ClickEvent, Div, Element, Hsla, InteractiveElement as _, IntoElement, MouseButton, ParentElement, Pixels, RenderOnce, Stateful, - StatefulInteractiveElement as _, Style, Styled, WindowContext, + StatefulInteractiveElement as _, Style, Styled, TitlebarOptions, WindowContext, }; pub const TITLE_BAR_HEIGHT: Pixels = px(35.); @@ -32,6 +32,15 @@ impl TitleBar { } } + /// Returns the default title bar options for compatible with the [`crate::TitleBar`]. + pub fn title_bar_options() -> TitlebarOptions { + TitlebarOptions { + title: None, + appears_transparent: true, + traffic_light_position: Some(gpui::point(px(9.0), px(9.0))), + } + } + /// Add custom for close window event, default is None, then click X button will call `cx.remove_window()`. /// Linux only, this will do nothing on other platforms. pub fn on_close_window(