title_bar.rs

  1use gpui::{AnyElement, Interactivity, Stateful};
  2use smallvec::SmallVec;
  3
  4use crate::components::title_bar::windows_window_controls::WindowsWindowControls;
  5use crate::prelude::*;
  6
  7#[derive(IntoElement)]
  8pub struct TitleBar {
  9    platform_style: PlatformStyle,
 10    content: Stateful<Div>,
 11    children: SmallVec<[AnyElement; 2]>,
 12}
 13
 14impl TitleBar {
 15    #[cfg(not(target_os = "windows"))]
 16    pub fn height(cx: &mut WindowContext) -> Pixels {
 17        (1.75 * cx.rem_size()).max(px(32.))
 18    }
 19
 20    #[cfg(target_os = "windows")]
 21    pub fn height(_cx: &mut WindowContext) -> Pixels {
 22        // todo(windows) instead of hard coded size report the actual size to the Windows platform API
 23        px(32.)
 24    }
 25
 26    #[cfg(not(target_os = "windows"))]
 27    fn top_padding(_cx: &WindowContext) -> Pixels {
 28        px(0.)
 29    }
 30
 31    #[cfg(target_os = "windows")]
 32    fn top_padding(cx: &WindowContext) -> Pixels {
 33        use windows::Win32::UI::{
 34            HiDpi::GetSystemMetricsForDpi,
 35            WindowsAndMessaging::{SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI},
 36        };
 37
 38        // This top padding is not dependent on the title bar style and is instead a quirk of maximized windows on Windows:
 39        // https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543
 40        let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) };
 41        if cx.is_maximized() {
 42            px((padding * 2) as f32)
 43        } else {
 44            px(0.)
 45        }
 46    }
 47
 48    pub fn new(id: impl Into<ElementId>) -> Self {
 49        Self {
 50            platform_style: PlatformStyle::platform(),
 51            content: div().id(id.into()),
 52            children: SmallVec::new(),
 53        }
 54    }
 55
 56    /// Sets the platform style.
 57    pub fn platform_style(mut self, style: PlatformStyle) -> Self {
 58        self.platform_style = style;
 59        self
 60    }
 61}
 62
 63impl InteractiveElement for TitleBar {
 64    fn interactivity(&mut self) -> &mut Interactivity {
 65        self.content.interactivity()
 66    }
 67}
 68
 69impl StatefulInteractiveElement for TitleBar {}
 70
 71impl ParentElement for TitleBar {
 72    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
 73        self.children.extend(elements)
 74    }
 75}
 76
 77impl RenderOnce for TitleBar {
 78    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
 79        let height = Self::height(cx);
 80        h_flex()
 81            .id("titlebar")
 82            .w_full()
 83            .pt(Self::top_padding(cx))
 84            .h(height + Self::top_padding(cx))
 85            .map(|this| {
 86                if cx.is_fullscreen() {
 87                    this.pl_2()
 88                } else if self.platform_style == PlatformStyle::Mac {
 89                    // Use pixels here instead of a rem-based size because the macOS traffic
 90                    // lights are a static size, and don't scale with the rest of the UI.
 91                    this.pl(px(80.))
 92                } else {
 93                    this.pl_2()
 94                }
 95            })
 96            .bg(cx.theme().colors().title_bar_background)
 97            .content_stretch()
 98            .child(
 99                self.content
100                    .id("titlebar-content")
101                    .flex()
102                    .flex_row()
103                    .justify_between()
104                    .w_full()
105                    .children(self.children),
106            )
107            .when(self.platform_style == PlatformStyle::Windows, |title_bar| {
108                title_bar.child(WindowsWindowControls::new(height))
109            })
110    }
111}