Theme · Platform: one platform for buttons and caps, icon themes, frosted panes
theme::Platform moves out of typography and shell's ControlsStyle folds into it; the theme's platform starts as the system's. Window buttons Ely draws, the title bar's layout and every key cap follow it, secondary included, through typography::keys::keystroke; a title bar whose buttons the OS draws follows the OS. primitives::IconTheme lays an owner's rules by file name and extension over Ely's map, for every file icon. shell::Vibrancy fills a pane with the palette's new glass, translucent so a window opened Blurred shows its backdrop, solid under high contrast. gpui 0.2.2 has no Mica; that line stays blocked.
This commit is contained in:
1 parent
7f2205c811
commit
d3cfc71ead
30 files changed
+593
-162
No files matched your search
+28
-45
@@ -9,30 +9,9 @@ use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
primitives::{Icon, IconName},
|
||||
theme::{ActiveTheme, ControlSize, IconSize, TextSize},
|
||||
theme::{ActiveTheme, ControlSize, IconSize, Platform, TextSize},
|
||||
};
|
||||
|
||||
/// Which system's window buttons to draw.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ControlsStyle {
|
||||
Mac,
|
||||
Windows,
|
||||
Linux,
|
||||
}
|
||||
|
||||
impl ControlsStyle {
|
||||
/// The style of the system this build targets.
|
||||
pub fn current() -> Self {
|
||||
if cfg!(target_os = "macos") {
|
||||
Self::Mac
|
||||
} else if cfg!(target_os = "windows") {
|
||||
Self::Windows
|
||||
} else {
|
||||
Self::Linux
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Drags the window, and zooms it on double-click.
|
||||
pub fn drag_region(id: impl Into<ElementId>, window: &mut Window, cx: &mut App) -> Stateful<Div> {
|
||||
let id = id.into();
|
||||
@@ -91,7 +70,7 @@ fn run(control: Control, on_close: Option<&OnClose>, window: &mut Window, cx: &m
|
||||
#[derive(IntoElement)]
|
||||
pub struct WindowControls {
|
||||
id: ElementId,
|
||||
style: ControlsStyle,
|
||||
platform: Option<Platform>,
|
||||
on_close: Option<OnClose>,
|
||||
}
|
||||
|
||||
@@ -99,13 +78,14 @@ impl WindowControls {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
id: id.into(),
|
||||
style: ControlsStyle::current(),
|
||||
platform: None,
|
||||
on_close: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn style(mut self, style: ControlsStyle) -> Self {
|
||||
self.style = style;
|
||||
/// Draws this platform's buttons instead of the theme's.
|
||||
pub fn platform(mut self, platform: Platform) -> Self {
|
||||
self.platform = Some(platform);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -118,10 +98,10 @@ impl WindowControls {
|
||||
|
||||
impl RenderOnce for WindowControls {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
match self.style {
|
||||
ControlsStyle::Mac => traffic_lights(self.id, self.on_close, window, cx),
|
||||
ControlsStyle::Windows => caption_buttons(self.id, self.on_close, window, cx),
|
||||
ControlsStyle::Linux => round_buttons(self.id, self.on_close, window, cx),
|
||||
match self.platform.unwrap_or(cx.theme().platform) {
|
||||
Platform::Mac => traffic_lights(self.id, self.on_close, window, cx),
|
||||
Platform::Windows => caption_buttons(self.id, self.on_close, window, cx),
|
||||
Platform::Linux => round_buttons(self.id, self.on_close, window, cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,7 +284,7 @@ fn route_close(id: &ElementId, handler: Option<OnClose>, window: &mut Window, cx
|
||||
pub struct TitleBar {
|
||||
id: ElementId,
|
||||
title: Option<SharedString>,
|
||||
style: Option<ControlsStyle>,
|
||||
platform: Option<Platform>,
|
||||
on_close: Option<OnClose>,
|
||||
leading: SmallVec<[AnyElement; 2]>,
|
||||
actions: SmallVec<[AnyElement; 2]>,
|
||||
@@ -316,7 +296,7 @@ impl TitleBar {
|
||||
Self {
|
||||
id: id.into(),
|
||||
title: None,
|
||||
style: None,
|
||||
platform: None,
|
||||
on_close: None,
|
||||
leading: SmallVec::new(),
|
||||
actions: SmallVec::new(),
|
||||
@@ -328,13 +308,13 @@ impl TitleBar {
|
||||
self
|
||||
}
|
||||
|
||||
/// Draws this style's controls instead of the system's.
|
||||
pub fn style(mut self, style: ControlsStyle) -> Self {
|
||||
self.style = Some(style);
|
||||
/// Draws this platform's controls instead of the system's.
|
||||
pub fn platform(mut self, platform: Platform) -> Self {
|
||||
self.platform = Some(platform);
|
||||
self
|
||||
}
|
||||
|
||||
/// Runs instead of closing; without a forced style, system closes run it too.
|
||||
/// Runs instead of closing; without a forced platform, system closes run it too.
|
||||
pub fn on_close(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self {
|
||||
self.on_close = Some(Rc::new(handler));
|
||||
self
|
||||
@@ -355,19 +335,22 @@ impl ParentElement for TitleBar {
|
||||
|
||||
impl RenderOnce for TitleBar {
|
||||
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
if self.style.is_none() {
|
||||
if self.platform.is_none() {
|
||||
route_close(&self.id, self.on_close.clone(), window, cx);
|
||||
}
|
||||
let bar = drag_region(self.id, window, cx);
|
||||
let theme = cx.theme();
|
||||
let system = self.style.is_none() && cfg!(target_os = "macos");
|
||||
let style = self.style.unwrap_or_else(ControlsStyle::current);
|
||||
let system = self.platform.is_none() && cfg!(target_os = "macos");
|
||||
let style = match system {
|
||||
true => Platform::Mac,
|
||||
false => self.platform.unwrap_or(theme.platform),
|
||||
};
|
||||
let mut trailing = (!system).then_some(WindowControls {
|
||||
id: "window-controls".into(),
|
||||
style,
|
||||
platform: Some(style),
|
||||
on_close: self.on_close,
|
||||
});
|
||||
let leading = if style == ControlsStyle::Mac {
|
||||
let leading = if style == Platform::Mac {
|
||||
trailing.take()
|
||||
} else {
|
||||
None
|
||||
@@ -379,7 +362,7 @@ impl RenderOnce for TitleBar {
|
||||
.text_color(theme.colors.fg_muted)
|
||||
.child(title)
|
||||
});
|
||||
let centered = style != ControlsStyle::Windows;
|
||||
let centered = style != Platform::Windows;
|
||||
let inset = theme.traffic_light_inset();
|
||||
let fullscreen = window.is_fullscreen();
|
||||
let bar = bar
|
||||
@@ -396,9 +379,9 @@ impl RenderOnce for TitleBar {
|
||||
let bar = match (system, style) {
|
||||
(true, _) if fullscreen => bar.pl_3().pr_2(),
|
||||
(true, _) => bar.pl(inset).pr_2(),
|
||||
(false, ControlsStyle::Mac) => bar.pl_3().pr_2().children(leading),
|
||||
(false, ControlsStyle::Windows) => bar.pl_3(),
|
||||
(false, ControlsStyle::Linux) => bar.pl_3().pr_2(),
|
||||
(false, Platform::Mac) => bar.pl_3().pr_2().children(leading),
|
||||
(false, Platform::Windows) => bar.pl_3(),
|
||||
(false, Platform::Linux) => bar.pl_3().pr_2(),
|
||||
};
|
||||
let bar = bar.children(self.leading);
|
||||
let bar = match (centered, title) {
|
||||
|
||||
+3
-1
@@ -11,11 +11,12 @@ mod tabs;
|
||||
#[cfg(all(test, feature = "test-support"))]
|
||||
mod tests;
|
||||
mod update;
|
||||
mod vibrancy;
|
||||
mod windows;
|
||||
|
||||
pub use activity::{ActivityBar, ActivityItem};
|
||||
pub use bars::{StatusBar, StatusBarItem, Toolbar, ToolbarGroup, ToolbarSeparator};
|
||||
pub use chrome::{ControlsStyle, TitleBar, WindowControls, drag_region};
|
||||
pub use chrome::{TitleBar, WindowControls, drag_region};
|
||||
pub use crash::CrashReporter;
|
||||
pub use dialogs::{AboutDialog, SplashScreen};
|
||||
pub use frame::ResizeBorder;
|
||||
@@ -25,4 +26,5 @@ pub use status::{Connectivity, OfflineIndicator, ZoomControl};
|
||||
pub use switcher::WindowSwitcher;
|
||||
pub use tabs::{TabBar, WindowTab};
|
||||
pub use update::{UpdateBanner, UpdateDialog, UpdateState};
|
||||
pub use vibrancy::Vibrancy;
|
||||
pub use windows::{Corner, Hosted, MiniWindow, WindowManager, open_hosted};
|
||||
@@ -88,3 +88,45 @@ fn the_switcher_switches_on_release_so_its_opener_stays_shut(cx: &mut TestAppCon
|
||||
assert!(!desk.read_with(cx, |desk, _| desk.open));
|
||||
assert_eq!(desk.read_with(cx, |desk, _| desk.enters_above), above);
|
||||
}
|
||||
|
||||
struct Platformed;
|
||||
|
||||
impl Render for Platformed {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.child(
|
||||
div()
|
||||
.debug_selector(|| "controls".into())
|
||||
.child(super::WindowControls::new("controls")),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.debug_selector(|| "caps".into())
|
||||
.child(crate::typography::Kbd::new("secondary-s")),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Window buttons and key caps follow the theme's platform: Windows draws three wide caption buttons and spells Ctrl.
|
||||
#[gpui::test]
|
||||
fn the_themes_platform_draws_the_buttons_and_the_caps(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
cx.update(|cx| Theme::update(cx, |theme| theme.platform = crate::theme::Platform::Mac));
|
||||
let (_, cx) = cx.add_window_view(|_, _| Platformed);
|
||||
settle(cx);
|
||||
let width = |name: &'static str, cx: &mut VisualTestContext| {
|
||||
cx.debug_bounds(name).expect("a measured part").size.width
|
||||
};
|
||||
let (lights, command) = (width("controls", cx), width("caps", cx));
|
||||
cx.update(|_, cx| Theme::update(cx, |theme| theme.platform = crate::theme::Platform::Windows));
|
||||
settle(cx);
|
||||
assert!(
|
||||
width("controls", cx) > lights * 2.0,
|
||||
"caption buttons wider than lights"
|
||||
);
|
||||
assert!(
|
||||
width("caps", cx) > command,
|
||||
"Ctrl wider than the command mark"
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
use gpui::{
|
||||
AnyElement, App, Div, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window,
|
||||
div,
|
||||
};
|
||||
|
||||
use crate::theme::ActiveTheme;
|
||||
|
||||
/// A pane the window's blur shows through: vibrancy on macOS, acrylic on Windows. Its window opens with `WindowBackgroundAppearance::Blurred` and nothing opaque beneath the pane; high contrast fills it solid.
|
||||
#[derive(IntoElement)]
|
||||
pub struct Vibrancy {
|
||||
base: Div,
|
||||
}
|
||||
|
||||
impl Vibrancy {
|
||||
pub fn new() -> Self {
|
||||
Self { base: div() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Vibrancy {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Styled for Vibrancy {
|
||||
fn style(&mut self) -> &mut StyleRefinement {
|
||||
self.base.style()
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for Vibrancy {
|
||||
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
|
||||
self.base.extend(elements);
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Vibrancy {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
self.base.bg(cx.theme().colors.glass)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user