diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs
index 4ef63c5a14..9ab4c94ff1 100644
--- a/crates/collab_ui/src/collab_titlebar_item.rs
+++ b/crates/collab_ui/src/collab_titlebar_item.rs
@@ -13,8 +13,8 @@ use rpc::proto;
use std::sync::Arc;
use theme::ActiveTheme;
use ui::{
- h_flex, platform_titlebar, popover_menu, prelude::*, Avatar, AvatarAudioStatusIndicator,
- Button, ButtonLike, ButtonStyle, ContextMenu, Icon, IconButton, IconName, TintColor, Tooltip,
+ h_flex, popover_menu, prelude::*, Avatar, AvatarAudioStatusIndicator, Button, ButtonLike,
+ ButtonStyle, ContextMenu, Icon, IconButton, IconName, PlatformTitlebar, TintColor, Tooltip,
};
use util::ResultExt;
use vcs_menu::{build_branch_list, BranchList, OpenRecent as ToggleVcsMenu};
@@ -58,8 +58,8 @@ impl Render for CollabTitlebarItem {
let project_id = self.project.read(cx).remote_id();
let workspace = self.workspace.upgrade();
- platform_titlebar("collab-titlebar")
- .titlebar_bg(cx.theme().colors().title_bar_background)
+ PlatformTitlebar::new("collab-titlebar")
+ .background(cx.theme().colors().title_bar_background)
// note: on windows titlebar behaviour is handled by the platform implementation
.when(cfg!(not(windows)), |this| {
this.on_click(|event, cx| {
@@ -68,7 +68,6 @@ impl Render for CollabTitlebarItem {
}
})
})
- .justify_between()
// left side
.child(
h_flex()
diff --git a/crates/storybook/src/story_selector.rs b/crates/storybook/src/story_selector.rs
index c8cda13018..bbda1119ec 100644
--- a/crates/storybook/src/story_selector.rs
+++ b/crates/storybook/src/story_selector.rs
@@ -29,6 +29,7 @@ pub enum ComponentStory {
ListHeader,
ListItem,
OverflowScroll,
+ PlatformTitlebar,
Scroll,
Tab,
TabBar,
@@ -60,6 +61,7 @@ impl ComponentStory {
Self::ListHeader => cx.new_view(|_| ui::ListHeaderStory).into(),
Self::ListItem => cx.new_view(|_| ui::ListItemStory).into(),
Self::OverflowScroll => cx.new_view(|_| crate::stories::OverflowScrollStory).into(),
+ Self::PlatformTitlebar => cx.new_view(|_| ui::PlatformTitlebarStory).into(),
Self::Scroll => ScrollStory::view(cx).into(),
Self::Text => TextStory::view(cx).into(),
Self::Tab => cx.new_view(|_| ui::TabStory).into(),
diff --git a/crates/ui/src/components/platform_titlebar.rs b/crates/ui/src/components/platform_titlebar.rs
index 93a274c9b8..9d889d01df 100644
--- a/crates/ui/src/components/platform_titlebar.rs
+++ b/crates/ui/src/components/platform_titlebar.rs
@@ -1,18 +1,7 @@
-// allowing due to multiple platform conditional code
-#![allow(unused_imports)]
-
-use gpui::{
- div,
- prelude::FluentBuilder,
- px, transparent_black, AnyElement, Div, Element, ElementId, Fill, InteractiveElement,
- Interactivity, IntoElement, ParentElement, Pixels, RenderOnce, Rgba, Stateful,
- StatefulInteractiveElement, StyleRefinement, Styled,
- WindowAppearance::{Dark, Light, VibrantDark, VibrantLight},
- WindowContext,
-};
+use gpui::{transparent_black, AnyElement, Fill, Interactivity, Rgba, Stateful, WindowAppearance};
use smallvec::SmallVec;
-use crate::h_flex;
+use crate::prelude::*;
pub enum PlatformStyle {
Linux,
@@ -47,27 +36,38 @@ impl PlatformStyle {
#[derive(IntoElement)]
pub struct PlatformTitlebar {
platform: PlatformStyle,
- titlebar_bg: Fill,
+ background: Fill,
content: Stateful
,
children: SmallVec<[AnyElement; 2]>,
}
-impl Styled for PlatformTitlebar {
- fn style(&mut self) -> &mut StyleRefinement {
- self.content.style()
- }
-}
-
impl PlatformTitlebar {
- /// Change the platform style used
- pub fn with_platform_style(self, style: PlatformStyle) -> Self {
+ pub fn new(id: impl Into
) -> Self {
Self {
- platform: style,
- ..self
+ platform: PlatformStyle::platform(),
+ background: transparent_black().into(),
+ content: div().id(id.into()),
+ children: SmallVec::new(),
}
}
- fn titlebar_top_padding(&self, cx: &WindowContext) -> Pixels {
+ /// Sets the platform style.
+ pub fn platform_style(mut self, style: PlatformStyle) -> Self {
+ self.platform = style;
+ self
+ }
+
+ /// Sets the background color of the titlebar.
+ pub fn background(mut self, fill: F) -> Self
+ where
+ F: Into,
+ Self: Sized,
+ {
+ self.background = fill.into();
+ self
+ }
+
+ fn top_padding(&self, cx: &WindowContext) -> Pixels {
if self.platform.windows() && cx.is_maximized() {
// todo(windows): get padding from win32 api, need HWND from window context somehow
// should be GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2
@@ -84,106 +84,91 @@ impl PlatformTitlebar {
}
fn render_window_controls_right(&self, cx: &mut WindowContext) -> impl Element {
- if self.platform.windows() {
- let btn_height = titlebar_height(cx) - self.titlebar_top_padding(cx);
- let close_btn_hover_color = Rgba {
- r: 232.0 / 255.0,
- g: 17.0 / 255.0,
- b: 32.0 / 255.0,
- a: 1.0,
- };
-
- let btn_hover_color = match cx.appearance() {
- Light | VibrantLight => Rgba {
- r: 0.1,
- g: 0.1,
- b: 0.1,
- a: 0.2,
- },
- Dark | VibrantDark => Rgba {
- r: 0.9,
- g: 0.9,
- b: 0.9,
- a: 0.1,
- },
- };
-
- fn windows_caption_btn(
- id: &'static str,
- icon_text: &'static str,
- hover_color: Rgba,
- cx: &WindowContext,
- ) -> Stateful {
- let mut active_color = hover_color;
- active_color.a *= 0.2;
- h_flex()
- .id(id)
- .h_full()
- .justify_center()
- .content_center()
- .items_center()
- .w(PlatformTitlebar::windows_caption_button_width(cx))
- .hover(|style| style.bg(hover_color))
- .active(|style| style.bg(active_color))
- .child(icon_text)
- }
-
- div()
- .id("caption-buttons-windows")
- .flex()
- .flex_row()
- .justify_center()
- .content_stretch()
- .max_h(btn_height)
- .min_h(btn_height)
- .font("Segoe Fluent Icons")
- .text_size(px(10.0))
- .children(vec![
- windows_caption_btn("minimize", "\u{e921}", btn_hover_color, cx), // minimize icon
- windows_caption_btn(
- "maximize",
- if cx.is_maximized() {
- "\u{e923}" // restore icon
- } else {
- "\u{e922}" // maximize icon
- },
- btn_hover_color,
- cx,
- ),
- windows_caption_btn("close", "\u{e8bb}", close_btn_hover_color, cx), // close icon
- ])
- } else {
- div().id("caption-buttons-windows")
+ if !self.platform.windows() {
+ return div().id("caption-buttons-windows");
}
- }
- /// Sets the background color of titlebar.
- pub fn titlebar_bg
(mut self, fill: F) -> Self
- where
- F: Into,
- Self: Sized,
- {
- self.titlebar_bg = fill.into();
- self
- }
-}
+ let button_height = titlebar_height(cx) - self.top_padding(cx);
+ let close_button_hover_color = Rgba {
+ r: 232.0 / 255.0,
+ g: 17.0 / 255.0,
+ b: 32.0 / 255.0,
+ a: 1.0,
+ };
-pub fn platform_titlebar(id: impl Into) -> PlatformTitlebar {
- let id = id.into();
- PlatformTitlebar {
- platform: PlatformStyle::platform(),
- titlebar_bg: transparent_black().into(),
- content: div().id(id.clone()),
- children: SmallVec::new(),
+ let button_hover_color = match cx.appearance() {
+ WindowAppearance::Light | WindowAppearance::VibrantLight => Rgba {
+ r: 0.1,
+ g: 0.1,
+ b: 0.1,
+ a: 0.2,
+ },
+ WindowAppearance::Dark | WindowAppearance::VibrantDark => Rgba {
+ r: 0.9,
+ g: 0.9,
+ b: 0.9,
+ a: 0.1,
+ },
+ };
+
+ fn windows_caption_button(
+ id: &'static str,
+ icon_text: &'static str,
+ hover_color: Rgba,
+ cx: &WindowContext,
+ ) -> Stateful {
+ let mut active_color = hover_color;
+ active_color.a *= 0.2;
+ h_flex()
+ .id(id)
+ .h_full()
+ .justify_center()
+ .content_center()
+ .items_center()
+ .w(PlatformTitlebar::windows_caption_button_width(cx))
+ .hover(|style| style.bg(hover_color))
+ .active(|style| style.bg(active_color))
+ .child(icon_text)
+ }
+
+ const MINIMIZE_ICON: &str = "\u{e921}";
+ const RESTORE_ICON: &str = "\u{e923}";
+ const MAXIMIZE_ICON: &str = "\u{e922}";
+ const CLOSE_ICON: &str = "\u{e8bb}";
+
+ div()
+ .id("caption-buttons-windows")
+ .flex()
+ .flex_row()
+ .justify_center()
+ .content_stretch()
+ .max_h(button_height)
+ .min_h(button_height)
+ .font("Segoe Fluent Icons")
+ .text_size(px(10.0))
+ .children(vec![
+ windows_caption_button("minimize", MINIMIZE_ICON, button_hover_color, cx),
+ windows_caption_button(
+ "maximize",
+ if cx.is_maximized() {
+ RESTORE_ICON
+ } else {
+ MAXIMIZE_ICON
+ },
+ button_hover_color,
+ cx,
+ ),
+ windows_caption_button("close", CLOSE_ICON, close_button_hover_color, cx),
+ ])
}
}
impl RenderOnce for PlatformTitlebar {
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let titlebar_height = titlebar_height(cx);
- let titlebar_top_padding = self.titlebar_top_padding(cx);
+ let titlebar_top_padding = self.top_padding(cx);
let window_controls_right = self.render_window_controls_right(cx);
- let macos = self.platform.macos();
+
h_flex()
.id("titlebar")
.w_full()
@@ -192,7 +177,7 @@ impl RenderOnce for PlatformTitlebar {
.map(|this| {
if cx.is_fullscreen() {
this.pl_2()
- } else if macos {
+ } else if self.platform.macos() {
// Use pixels here instead of a rem-based size because the macOS traffic
// lights are a static size, and don't scale with the rest of the UI.
this.pl(px(80.))
@@ -200,14 +185,15 @@ impl RenderOnce for PlatformTitlebar {
this.pl_2()
}
})
- .bg(self.titlebar_bg)
+ .bg(self.background)
.content_stretch()
.child(
self.content
+ .id("titlebar-content")
.flex()
.flex_row()
+ .justify_between()
.w_full()
- .id("titlebar-content")
.children(self.children),
)
.child(window_controls_right)
@@ -219,6 +205,7 @@ impl InteractiveElement for PlatformTitlebar {
self.content.interactivity()
}
}
+
impl StatefulInteractiveElement for PlatformTitlebar {}
impl ParentElement for PlatformTitlebar {
diff --git a/crates/ui/src/components/stories.rs b/crates/ui/src/components/stories.rs
index f321a7525f..8764b39698 100644
--- a/crates/ui/src/components/stories.rs
+++ b/crates/ui/src/components/stories.rs
@@ -10,6 +10,7 @@ mod label;
mod list;
mod list_header;
mod list_item;
+mod platform_titlebar;
mod tab;
mod tab_bar;
mod toggle_button;
@@ -26,6 +27,7 @@ pub use label::*;
pub use list::*;
pub use list_header::*;
pub use list_item::*;
+pub use platform_titlebar::*;
pub use tab::*;
pub use tab_bar::*;
pub use toggle_button::*;
diff --git a/crates/ui/src/components/stories/platform_titlebar.rs b/crates/ui/src/components/stories/platform_titlebar.rs
new file mode 100644
index 0000000000..d2eec79b80
--- /dev/null
+++ b/crates/ui/src/components/stories/platform_titlebar.rs
@@ -0,0 +1,46 @@
+use gpui::Render;
+use story::{StoryContainer, StoryItem, StorySection};
+
+use crate::{prelude::*, PlatformStyle, PlatformTitlebar};
+
+pub struct PlatformTitlebarStory;
+
+impl Render for PlatformTitlebarStory {
+ fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement {
+ StoryContainer::new(
+ "Platform Titlebar",
+ "crates/ui/src/components/stories/platform_titlebar.rs",
+ )
+ .child(
+ StorySection::new().child(
+ StoryItem::new(
+ "Default (macOS)",
+ PlatformTitlebar::new("macos").platform_style(PlatformStyle::MacOs),
+ )
+ .description("")
+ .usage(""),
+ ),
+ )
+ .child(
+ StorySection::new().child(
+ StoryItem::new(
+ "Default (Linux)",
+ PlatformTitlebar::new("linux").platform_style(PlatformStyle::Linux),
+ )
+ .description("")
+ .usage(""),
+ ),
+ )
+ .child(
+ StorySection::new().child(
+ StoryItem::new(
+ "Default (Windows)",
+ PlatformTitlebar::new("windows").platform_style(PlatformStyle::Windows),
+ )
+ .description("")
+ .usage(""),
+ ),
+ )
+ .into_element()
+ }
+}