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!(
79 "f32 values {:?} are not valid for a premultiplied color. Using solid black instead.",
80 [r, g, b, a]
81 );
82 Self::BLACK
83 } else {
84 Self { r, g, b, a }
85 }
86 }
87
88 /// Creates a new color from straight `f32` RGBA values.
89 pub fn new_f32_straight(r: f32, g: f32, b: f32, a: f32) -> Self {
90 if !validate_f32_all([r, g, b, a]) {
91 Self::BLACK
92 } else {
93 Self {
94 r: r * a,
95 g: g * a,
96 b: b * a,
97 a,
98 }
99 }
100 }
101
102 /// Creates a new color from `f32` RGB values.
103 pub fn new_f32(r: f32, g: f32, b: f32) -> Self {
104 Self { r, g, b, a: 1.0 }
105 }
106
107 /// Converts the color to its premultiplied `f32` RGBA values.
108 pub fn to_f32_premultiplied(&self) -> [f32; 4] {
109 [self.r, self.g, self.b, self.a]
110 }
111
112 /// Converts the color to its straight `f32` RGBA values.
113 pub fn to_f32_straight(&self) -> [f32; 4] {
114 if self.a == 0.0 {
115 [0.0, 0.0, 0.0, 0.0]
116 } else {
117 let a = self.a;
118 [self.r / a, self.g / a, self.b / a, a]
119 }
120 }
121
122 /// Converts the color to its straight `u8` RGBA values.
123 pub fn to_u8_straight(&self) -> [u8; 4] {
124 let [r, g, b, a] = self.to_f32_straight();
125 [to_u8(r), to_u8(g), to_u8(b), to_u8(a)]
126 }
127}
128
129/// Resets all sizes to their defaults.
130pub fn reset_sizes() {
131 get!().reset_sizes();
132}
133
134/// Resets all colors to their defaults.
135pub fn reset_colors() {
136 get!().reset_colors();
137}
138
139/// Returns the current font.
140pub fn get_font() -> String {
141 get!().get_font()
142}
143
144/// Sets the font.
145///
146/// Default: `monospace 8`.
147///
148/// See also [`set_bar_font`] and [`set_title_font`].
149///
150/// The font name should be specified in [pango][pango] syntax.
151///
152/// [pango]: https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
153pub fn set_font(font: &str) {
154 get!().set_font(font)
155}
156
157/// Sets the font used by the bar.
158///
159/// If this function is not called, the font set by [`set_font`] is used. See that
160/// function for more details.
161pub fn set_bar_font(font: &str) {
162 get!().set_bar_font(font)
163}
164
165/// Sets the font used by window titles.
166///
167/// If this function is not called, the font set by [`set_font`] is used. See that
168/// function for more details.
169pub fn set_title_font(font: &str) {
170 get!().set_title_font(font)
171}
172
173/// Resets the fonts to the defaults.
174///
175/// Currently the default is `monospace 8`.
176pub fn reset_font() {
177 get!().reset_font()
178}
179
180#[non_exhaustive]
181#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)]
182pub enum BarPosition {
183 #[default]
184 Top,
185 Bottom,
186}
187
188/// Sets the position of the bar.
189///
190/// Default: `Top`.
191pub fn set_bar_position(position: BarPosition) {
192 get!().set_bar_position(position);
193}
194
195/// Gets the position of the bar.
196pub fn get_bar_position() -> BarPosition {
197 get!(BarPosition::Top).get_bar_position()
198}
199
200/// Sets the proportional fonts used by egui windows.
201///
202/// The default is `["sans-serif", "Noto Sans", "Noto Color Emoji"]`.
203pub fn set_egui_proportional_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
204 get!().set_egui_fonts(Some(fonts.into_iter().collect()), None);
205}
206
207/// Sets the monospace fonts used by egui windows.
208///
209/// The default is `["monospace", "Noto Sans Mono", "Noto Color Emoji"]`.
210pub fn set_egui_monospace_fonts<'a>(fonts: impl IntoIterator<Item = &'a str>) {
211 get!().set_egui_fonts(None, Some(fonts.into_iter().collect()));
212}
213
214/// Sets whether window icons set by the client are shown.
215///
216/// The default is `true`.
217pub fn set_show_window_icons(show: bool) {
218 get!().set_show_window_icons(show);
219}
220
221/// Sets whether window icons set by the client are rendered as grayscale.
222///
223/// This is only supported on the Vulkan renderer.
224///
225/// The default is `false`.
226pub fn set_window_icons_grayscale(grayscale: bool) {
227 get!().set_window_icons_grayscale(grayscale);
228}
229
230/// Elements of the compositor whose color can be changed.
231pub mod colors {
232 use {
233 crate::theme::Color,
234 serde::{Deserialize, Serialize},
235 };
236
237 /// An element of the GUI whose color can be changed.
238 #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
239 pub struct Colorable(#[doc(hidden)] pub u32);
240
241 impl Colorable {
242 /// Sets the color to an RGB value.
243 pub fn set(self, r: u8, g: u8, b: u8) {
244 let color = Color::new(r, g, b);
245 get!().set_color(self, color);
246 }
247
248 /// Sets the color to a `Color` that might contain an alpha component.
249 pub fn set_color(self, color: Color) {
250 get!().set_color(self, color);
251 }
252
253 /// Gets the current color.
254 pub fn get(self) -> Color {
255 get!(Color::BLACK).get_color(self)
256 }
257 }
258
259 macro_rules! colors {
260 ($($(#[$attr:meta])* const $n:expr => $name:ident,)*) => {
261 $(
262 $(#[$attr])*
263 pub const $name: Colorable = Colorable($n);
264 )*
265 }
266 }
267
268 colors! {
269 /// The title background color of an unfocused window.
270 ///
271 /// Default: `#222222`.
272 const 01 => UNFOCUSED_TITLE_BACKGROUND_COLOR,
273 /// The title background color of a focused window.
274 ///
275 /// Default: `#285577`.
276 const 02 => FOCUSED_TITLE_BACKGROUND_COLOR,
277 /// The title background color of an unfocused window that was the last focused
278 /// window in its container.
279 ///
280 /// Default: `#5f676a`.
281 const 03 => FOCUSED_INACTIVE_TITLE_BACKGROUND_COLOR,
282 /// The background color of the desktop.
283 ///
284 /// Default: `#001019`.
285 ///
286 /// You can use an application such as [swaybg][swaybg] to further customize the background.
287 ///
288 /// [swaybg]: https://github.com/swaywm/swaybg
289 const 04 => BACKGROUND_COLOR,
290 /// The background color of the bar.
291 ///
292 /// Default: `#000000`.
293 const 05 => BAR_BACKGROUND_COLOR,
294 /// The color of the 1px separator below window titles.
295 ///
296 /// Default: `#333333`.
297 const 06 => SEPARATOR_COLOR,
298 /// The color of the border between windows.
299 ///
300 /// Default: `#3f474a`.
301 const 07 => BORDER_COLOR,
302 /// The title text color of an unfocused window.
303 ///
304 /// Default: `#888888`.
305 const 08 => UNFOCUSED_TITLE_TEXT_COLOR,
306 /// The title text color of a focused window.
307 ///
308 /// Default: `#ffffff`.
309 const 09 => FOCUSED_TITLE_TEXT_COLOR,
310 /// The title text color of an unfocused window that was the last focused
311 /// window in its container.
312 ///
313 /// Default: `#ffffff`.
314 const 10 => FOCUSED_INACTIVE_TITLE_TEXT_COLOR,
315 /// The color of the status text in the bar.
316 ///
317 /// Default: `#ffffff`.
318 const 11 => BAR_STATUS_TEXT_COLOR,
319 /// The title background color of an unfocused window that might be captured.
320 ///
321 /// Default: `#220303`.
322 const 12 => CAPTURED_UNFOCUSED_TITLE_BACKGROUND_COLOR,
323 /// The title background color of a focused window that might be captured.
324 ///
325 /// Default: `#772831`.
326 const 13 => CAPTURED_FOCUSED_TITLE_BACKGROUND_COLOR,
327 /// The title background color of a window that has requested attention.
328 ///
329 /// Default: `#23092c`.
330 const 14 => ATTENTION_REQUESTED_BACKGROUND_COLOR,
331 /// Color used to highlight parts of the UI.
332 ///
333 /// Default: `#9d28c67f`.
334 const 15 => HIGHLIGHT_COLOR,
335 }
336
337 /// Sets the color of GUI element.
338 pub fn set_color(element: Colorable, color: Color) {
339 get!().set_color(element, color);
340 }
341
342 /// Gets the color of GUI element.
343 pub fn get_color(element: Colorable) -> Color {
344 get!(Color::BLACK).get_color(element)
345 }
346}
347
348/// Elements of the compositor whose size can be changed.
349pub mod sized {
350 use serde::{Deserialize, Serialize};
351
352 /// An element of the GUI whose size can be changed.
353 #[derive(Serialize, Deserialize, Copy, Clone, Debug, Hash, Eq, PartialEq)]
354 pub struct Resizable(#[doc(hidden)] pub u32);
355
356 impl Resizable {
357 /// Gets the current size.
358 pub fn get(self) -> i32 {
359 get!(0).get_size(self)
360 }
361
362 /// Sets the size.
363 pub fn set(self, size: i32) {
364 get!().set_size(self, size)
365 }
366 }
367
368 macro_rules! sizes {
369 ($($(#[$attr:meta])* const $n:expr => $name:ident,)*) => {
370 $(
371 $(#[$attr])*
372 pub const $name: Resizable = Resizable($n);
373 )*
374 }
375 }
376
377 sizes! {
378 /// The height of window titles.
379 ///
380 /// Default: 17
381 const 01 => TITLE_HEIGHT,
382 /// The width of borders between windows.
383 ///
384 /// Default: 4
385 const 02 => BORDER_WIDTH,
386 /// The height of the bar.
387 ///
388 /// Defaults to the TITLE_HEIGHT if not set explicitly.
389 ///
390 /// Default: 17
391 const 03 => BAR_HEIGHT,
392 /// The width of the bar's separator.
393 ///
394 /// Default: 1
395 const 04 => BAR_SEPARATOR_WIDTH,
396 }
397}