use std::rc::Rc; use gpui::{ AnyElement, App, AppContext, Context, Corner, Entity, FocusHandle, InteractiveElement as _, IntoElement, MouseButton, ParentElement as _, Render, SharedString, Styled as _, Subscription, Window, div, px, }; use gpui_component::{ ActiveTheme as _, ContextModal as _, IconName, PixelsExt, Sizable as _, Theme, TitleBar, badge::Badge, button::{Button, ButtonVariants as _}, menu::AppMenuBar, menu::DropdownMenu as _, scroll::ScrollbarShow, }; use crate::{SelectFont, SelectRadius, SelectScrollbarShow, app_menus}; pub struct AppTitleBar { app_menu_bar: Entity, font_size_selector: Entity, child: Rc AnyElement>, _subscriptions: Vec, } impl AppTitleBar { pub fn new( title: impl Into, window: &mut Window, cx: &mut Context, ) -> Self { app_menus::init(title, cx); let font_size_selector = cx.new(|cx| FontSizeSelector::new(window, cx)); let app_menu_bar = AppMenuBar::new(window, cx); Self { app_menu_bar, font_size_selector, child: Rc::new(|_, _| div().into_any_element()), _subscriptions: vec![], } } pub fn child(mut self, f: F) -> Self where E: IntoElement, F: Fn(&mut Window, &mut App) -> E + 'static, { self.child = Rc::new(move |window, cx| f(window, cx).into_any_element()); self } } impl Render for AppTitleBar { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let notifications_count = window.notifications(cx).len(); TitleBar::new() // left side .child(div().flex().items_center().child(self.app_menu_bar.clone())) .child( div() .flex() .items_center() .justify_end() .px_2() .gap_2() .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) .child((self.child.clone())(window, cx)) .child(self.font_size_selector.clone()) .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 FontSizeSelector { focus_handle: FocusHandle, } impl FontSizeSelector { pub fn new(_: &mut Window, cx: &mut Context) -> Self { Self { focus_handle: cx.focus_handle(), } } fn on_select_font( &mut self, font_size: &SelectFont, window: &mut Window, cx: &mut Context, ) { Theme::global_mut(cx).font_size = px(font_size.0 as f32); window.refresh(); } fn on_select_radius( &mut self, radius: &SelectRadius, window: &mut Window, cx: &mut Context, ) { Theme::global_mut(cx).radius = px(radius.0 as f32); window.refresh(); } fn on_select_scrollbar_show( &mut self, show: &SelectScrollbarShow, window: &mut Window, cx: &mut Context, ) { Theme::global_mut(cx).scrollbar_show = show.0; window.refresh(); } } impl Render for FontSizeSelector { fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { let focus_handle = self.focus_handle.clone(); let font_size = cx.theme().font_size.as_f32() as i32; let radius = cx.theme().radius.as_f32() 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_radius)) .on_action(cx.listener(Self::on_select_scrollbar_show)) .child( Button::new("btn") .small() .ghost() .icon(IconName::Settings2) .dropdown_menu(move |this, _, _| { this.scrollable() .max_h(px(480.)) .label("Font Size") .menu_with_check("Large", font_size == 18, Box::new(SelectFont(18))) .menu_with_check( "Medium (default)", font_size == 16, Box::new(SelectFont(16)), ) .menu_with_check("Small", font_size == 14, Box::new(SelectFont(14))) .separator() .label("Border Radius") .menu_with_check("8px", radius == 8, Box::new(SelectRadius(8))) .menu_with_check( "6px (default)", radius == 6, Box::new(SelectRadius(6)), ) .menu_with_check("4px", radius == 4, Box::new(SelectRadius(4))) .menu_with_check("0px", radius == 0, Box::new(SelectRadius(0))) .separator() .label("Scrollbar") .menu_with_check( "Scrolling to show", scroll_show == ScrollbarShow::Scrolling, Box::new(SelectScrollbarShow(ScrollbarShow::Scrolling)), ) .menu_with_check( "Hover to show", scroll_show == ScrollbarShow::Hover, Box::new(SelectScrollbarShow(ScrollbarShow::Hover)), ) .menu_with_check( "Always show", scroll_show == ScrollbarShow::Always, Box::new(SelectScrollbarShow(ScrollbarShow::Always)), ) }) .anchor(Corner::TopRight), ) } }