gpui/colors.rs
1use crate::{App, Global, Window, WindowAppearance, rgb};
2use palette::rgb::Rgba;
3use std::ops::Deref;
4use std::sync::Arc;
5
6/// A pair of light and dark base palettes for application components to share.
7/// GPUI's built-in elements do not read this value automatically.
8///
9/// Choose a palette for a window with [`Colors::for_appearance`]. For one app-wide palette,
10/// call [`App::init_colors`] and read it with [`DefaultColors::default_colors`].
11///
12/// # Examples
13///
14/// ```
15/// use gpui::{colors::Colors, rgb};
16///
17/// let light = Colors::light();
18/// assert_eq!(light.background, rgb(0xffffff));
19///
20/// let dark = Colors::dark();
21/// assert_eq!(dark.text, rgb(0xffffff));
22/// ```
23#[derive(Clone, Debug)]
24pub struct Colors {
25 /// The default color for text.
26 pub text: Rgba,
27 /// The color for text on a [`Colors::selected`] background.
28 pub selected_text: Rgba,
29 /// The color of the window background.
30 pub background: Rgba,
31 /// The color for disabled controls and their text.
32 pub disabled: Rgba,
33 /// The color for selected items and highlighted controls.
34 pub selected: Rgba,
35 /// The color for borders around controls.
36 pub border: Rgba,
37 /// The color for dividers between items.
38 pub separator: Rgba,
39 /// The color for raised surfaces, such as cards and panels, drawn over [`Colors::background`].
40 pub container: Rgba,
41}
42
43impl Default for Colors {
44 /// Returns [`Colors::light`].
45 fn default() -> Self {
46 Self::light()
47 }
48}
49
50impl Colors {
51 /// Returns [`Colors::light`] or [`Colors::dark`] for the window's [`WindowAppearance`].
52 ///
53 /// The returned palette is a snapshot. Ask again after the window appearance changes.
54 pub fn for_appearance(window: &Window) -> Self {
55 match window.appearance() {
56 WindowAppearance::Light | WindowAppearance::VibrantLight => Self::light(),
57 WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::dark(),
58 }
59 }
60
61 /// Returns the colors for dark appearances.
62 pub fn dark() -> Self {
63 Self {
64 text: rgb(0xffffff),
65 selected_text: rgb(0xffffff),
66 disabled: rgb(0x565656),
67 selected: rgb(0x2457ca),
68 background: rgb(0x222222),
69 border: rgb(0x000000),
70 separator: rgb(0xd9d9d9),
71 container: rgb(0x262626),
72 }
73 }
74
75 /// Returns the colors for light appearances.
76 ///
77 /// This is the set that [`Colors::default`] returns.
78 pub fn light() -> Self {
79 Self {
80 text: rgb(0x252525),
81 selected_text: rgb(0xffffff),
82 background: rgb(0xffffff),
83 disabled: rgb(0xb0b0b0),
84 selected: rgb(0x2a63d9),
85 border: rgb(0xd9d9d9),
86 separator: rgb(0xe6e6e6),
87 container: rgb(0xf4f5f5),
88 }
89 }
90
91 /// Returns the app-wide colors that [`App::init_colors`] set.
92 ///
93 /// # Panics
94 ///
95 /// Panics if [`App::init_colors`] has not been called.
96 pub fn get_global(cx: &App) -> &Arc<Colors> {
97 &cx.global::<GlobalColors>().0
98 }
99}
100
101/// The app-wide [`Colors`], stored as a global on the [`App`].
102///
103/// [`App::init_colors`] sets it. Read the colors through [`DefaultColors::default_colors`]
104/// or [`Colors::get_global`] rather than this type. It dereferences to the shared [`Colors`].
105#[derive(Clone, Debug)]
106pub struct GlobalColors(pub Arc<Colors>);
107
108impl Deref for GlobalColors {
109 type Target = Arc<Colors>;
110
111 fn deref(&self) -> &Self::Target {
112 &self.0
113 }
114}
115
116impl Global for GlobalColors {}
117
118/// Gives access to the app-wide [`Colors`] through `cx.default_colors()`.
119///
120/// [`App`] implements this trait. The colors exist only after [`App::init_colors`] runs.
121///
122/// # Panics
123///
124/// Calling [`DefaultColors::default_colors`] panics if [`App::init_colors`] has not been called.
125pub trait DefaultColors {
126 /// Returns the app-wide [`Colors`] that [`App::init_colors`] set.
127 fn default_colors(&self) -> &Arc<Colors>;
128}
129
130impl DefaultColors for App {
131 fn default_colors(&self) -> &Arc<Colors> {
132 &self.global::<GlobalColors>().0
133 }
134}
135
136/// Whether GPUI's base colors use the light or the dark set.
137///
138/// This is a two-value form of [`WindowAppearance`], which also has vibrant variants. The
139/// conversion from [`WindowAppearance`] maps each vibrant variant to its base set. Use
140/// [`Colors::light`] for `Light` and [`Colors::dark`] for `Dark`.
141#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
142pub enum DefaultAppearance {
143 /// Use the set of colors for light appearances.
144 #[default]
145 Light,
146 /// Use the set of colors for dark appearances.
147 Dark,
148}
149
150impl From<WindowAppearance> for DefaultAppearance {
151 fn from(appearance: WindowAppearance) -> Self {
152 match appearance {
153 WindowAppearance::Light | WindowAppearance::VibrantLight => Self::Light,
154 WindowAppearance::Dark | WindowAppearance::VibrantDark => Self::Dark,
155 }
156 }
157}