jay_config/
theme.rs

1//! Tools for configuring the look of the compositor.
2
3use serde::{Deserialize, Serialize};
4
5/// A color.
6///
7/// When specifying RGBA values of a color, the RGB values can either be specified
8/// *straight* or *premultiplied*. Premultiplied means that the RGB values have already
9/// been multiplied by the alpha value.
10///
11/// Given a color, to reduce its opacity by half,
12///
13/// - if you're working with premultiplied values, you would multiply each component by `0.5`;
14/// - if you're working with straight values, you would multiply only the alpha component by `0.5`.
15///
16/// When using hexadecimal notation, `#RRGGBBAA`, the RGB values are usually straight.
17// values are stored premultiplied
18#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
19pub struct Color {
20    r: f32,
21    g: f32,
22    b: f32,
23    a: f32,
24}
25
26fn to_f32(c: u8) -> f32 {
27    c as f32 / 255f32
28}
29
30fn to_u8(c: f32) -> u8 {
31    (c * 255f32) as u8
32}
33
34fn validate_f32(f: f32) -> bool {
35    f >= 0.0 && f <= 1.0
36}
37
38fn validate_f32_all(f: [f32; 4]) -> bool {
39    if !f.into_iter().all(validate_f32) {
40        log::warn!(
41            "f32 values {:?} are not in the valid color range. Using solid black instead xyz",
42            f
43        );
44        return false;
45    }
46    true
47}
48
49impl Color {
50    /// Solid black.
51    pub const BLACK: Self = Self {
52        r: 0.0,
53        g: 0.0,
54        b: 0.0,
55        a: 1.0,
56    };
57
58    /// Creates a new color from `u8` RGB values.
59    pub fn new(r: u8, g: u8, b: u8) -> Self {
60        Self {
61            r: to_f32(r),
62            g: to_f32(g),
63            b: to_f32(b),
64            a: 1.0,
65        }
66    }
67
68    /// Creates a new color from straight `u8` RGBA values.
69    pub fn new_straight(r: u8, g: u8, b: u8, a: u8) -> Self {
70        Self::new_f32_straight(to_f32(r), to_f32(g), to_f32(b), to_f32(a))
71    }
72
73    /// Creates a new color from premultiplied `f32` RGBA values.
74    pub fn new_f32_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
75        if !validate_f32_all([r, g, b, a]) {
76            Self::BLACK
77        } else if r > a || g > a || b > a {
78            log::warn!("f32 values {:?} are not valid valid for a premultiplied color. Using solid black instead.", [r, g, b, a]);
79            Self::BLACK
80        } else {
81            Self { r, g, b, a }
82        }
83    }
84
85    /// Creates a new color from straight `f32` RGBA values.
86    pub fn new_f32_straight(r: f32, g: f32, b: f32, a: f32) -> Self {
87        if !validate_f32_all([r, g, b, a]) {
88            Self::BLACK
89        } else {
90            Self {
91                r: r * a,
92                g: g * a,
93                b: b * a,
94                a,
95            }
96        }
97    }
98
99    /// Creates a new color from `f32` RGB values.
100    pub fn new_f32(r: f32, g: f32, b: f32) -> Self {
101        Self { r, g, b, a: 1.0 }
102    }
103
104    /// Converts the color to its premultiplied `f32` RGBA values.
105    pub fn to_f32_premultiplied(&self) -> [f32; 4] {
106        [self.r, self.g, self.b, self.a]
107    }
108
109    /// Converts the color to its straight `f32` RGBA values.
110    pub fn to_f32_straight(&self) -> [f32; 4] {
111        if self.a == 0.0 {
112            [0.0, 0.0, 0.0, 0.0]
113        } else {
114            let a = self.a;
115            [self.r / a, self.g / a, self.b / a, a]
116        }
117    }
118
119    /// Converts the color to its straight `u8` RGBA values.
120    pub fn to_u8_straight(&self) -> [u8; 4] {
121        let [r, g, b, a] = self.to_f32_straight();
122        [to_u8(r), to_u8(g), to_u8(b), to_u8(a)]
123    }
124}
125
126/// Resets all sizes to their defaults.
127pub fn reset_sizes() {
128    get!().reset_sizes();
129}
130
131/// Resets all colors to their defaults.
132pub fn reset_colors() {
133    get!().reset_colors();
134}
135
136/// Returns the current font.
137pub fn get_font() -> String {
138    get!().get_font()
139}
140
141/// Sets the font.
142///
143/// Default: `monospace 8`.
144///
145/// The font name should be specified in [pango][pango] syntax.
146///
147/// [pango]: https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
148pub fn set_font(font: &str) {
149    get!().set_font(font)
150}
151
152/// Resets the font to the default.
153///
154/// Currently the default is `monospace 8`.
155pub fn reset_font() {
156    get!().reset_font()
157}
158
159/// Elements of the compositor whose color can be changed.
160pub mod colors {
161    use {
162        crate::theme::Color,
163        serde::{Deserialize, Serialize},
164    };
165
166    /// An element of the GUI whose color can be changed.
167    #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
168    pub struct Colorable(#[doc(hidden)] pub u32);
169
170    impl Colorable {
171        /// Sets the color to an RGB value.
172        pub fn set(self, r: u8, g: u8, b: u8) {
173            let color = Color::new(r, g, b);
174            get!().set_color(self, color);
175        }
176
177        /// Sets the color to a `Color` that might contain an alpha component.
178        pub fn set_color(self, color: Color) {
179            get!().set_color(self, color);
180        }
181
182        /// Gets the current color.
183        pub fn get(self) -> Color {
184            get!(Color::BLACK).get_color(self)
185        }
186    }
187
188    macro_rules! colors {
189        ($($(#[$attr:meta])* const $n:expr => $name:ident,)*) => {
190            $(
191                $(#[$attr])*
192                pub const $name: Colorable = Colorable($n);
193            )*
194        }
195    }
196
197    colors! {
198        /// The title background color of an unfocused window.
199        ///
200        /// Default: `#222222`.
201        const 01 => UNFOCUSED_TITLE_BACKGROUND_COLOR,
202        /// The title background color of a focused window.
203        ///
204        /// Default: `#285577`.
205        const 02 => FOCUSED_TITLE_BACKGROUND_COLOR,
206        /// The title background color of an unfocused window that was the last focused
207        /// window in its container.
208        ///
209        /// Default: `#5f676a`.
210        const 03 => FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR,
211        /// The background color of the desktop.
212        ///
213        /// Default: `#001019`.
214        ///
215        /// You can use an application such as [swaybg][swaybg] to further customize the background.
216        ///
217        /// [swaybg]: https://github.com/swaywm/swaybg
218        const 04 => BACKGROUND_COLOR,
219        /// The background color of the bar.
220        ///
221        /// Default: `#000000`.
222        const 05 => BAR_BACKGROUND_COLOR,
223        /// The color of the 1px separator below window titles.
224        ///
225        /// Default: `#333333`.
226        const 06 => SEPARATOR_COLOR,
227        /// The color of the border between windows.
228        ///
229        /// Default: `#3f474a`.
230        const 07 => BORDER_COLOR,
231        /// The title text color of an unfocused window.
232        ///
233        /// Default: `#888888`.
234        const 08 => UNFOCUSED_TITLE_TEXT_COLOR,
235        /// The title text color of a focused window.
236        ///
237        /// Default: `#ffffff`.
238        const 09 => FOCUSED_TITLE_TEXT_COLOR,
239        /// The title text color of an unfocused window that was the last focused
240        /// window in its container.
241        ///
242        /// Default: `#ffffff`.
243        const 10 => FOCUSED_INACTIVE_TITLE_TEXT_COLOR,
244        /// The color of the status text in the bar.
245        ///
246        /// Default: `#ffffff`.
247        const 11 => BAR_STATUS_TEXT_COLOR,
248        /// The title background color of an unfocused window that might be captured.
249        ///
250        /// Default: `#220303`.
251        const 12 => CAPTURED_UNFOCUSED_TITLE_BACKGROUND_COLOR,
252        /// The title background color of a focused window that might be captured.
253        ///
254        /// Default: `#772831`.
255        const 13 => CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR,
256        /// The title background color of a window that has requested attention.
257        ///
258        /// Default: `#23092c`.
259        const 14 => ATTENTION_REQUESTED_BACKGROUND_COLOR,
260        /// Color used to highlight parts of the UI.
261        ///
262        /// Default: `#9d28c67f`.
263        const 15 => HIGHLIGHT_COLOR,
264    }
265
266    /// Sets the color of GUI element.
267    pub fn set_color(element: Colorable, color: Color) {
268        get!().set_color(element, color);
269    }
270
271    /// Gets the color of GUI element.
272    pub fn get_color(element: Colorable) -> Color {
273        get!(Color::BLACK).get_color(element)
274    }
275}
276
277/// Elements of the compositor whose size can be changed.
278pub mod sized {
279    use serde::{Deserialize, Serialize};
280
281    /// An element of the GUI whose size can be changed.
282    #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
283    pub struct Resizable(#[doc(hidden)] pub u32);
284
285    impl Resizable {
286        /// Gets the current size.
287        pub fn get(self) -> i32 {
288            get!(0).get_size(self)
289        }
290
291        /// Sets the size.
292        pub fn set(self, size: i32) {
293            get!().set_size(self, size)
294        }
295    }
296
297    macro_rules! sizes {
298        ($($(#[$attr:meta])* const $n:expr => $name:ident,)*) => {
299            $(
300                $(#[$attr])*
301                pub const $name: Resizable = Resizable($n);
302            )*
303        }
304    }
305
306    sizes! {
307        /// The height of window titles.
308        ///
309        /// Default: 17
310        const 01 => TITLE_HEIGHT,
311        /// The width of borders between windows.
312        ///
313        /// Default: 4
314        const 02 => BORDER_WIDTH,
315    }
316}