cosmic/config/
mod.rs

1// Copyright 2024 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4//! Configurations available to libcosmic applications.
5
6use crate::cosmic_theme::Density;
7use cosmic_config::cosmic_config_derive::CosmicConfigEntry;
8use cosmic_config::{Config, CosmicConfigEntry};
9use serde::{Deserialize, Serialize};
10use std::collections::BTreeSet;
11use std::sync::{LazyLock, RwLock};
12
13/// ID for the `CosmicTk` config.
14pub const ID: &str = "com.system76.CosmicTk";
15
16const MONO_FAMILY_DEFAULT: &str = "Noto Sans Mono";
17const SANS_FAMILY_DEFAULT: &str = "Open Sans";
18
19pub static COSMIC_TK: LazyLock<RwLock<CosmicTk>> = LazyLock::new(|| {
20    RwLock::new(
21        CosmicTk::config()
22            .map(|c| {
23                CosmicTk::get_entry(&c).unwrap_or_else(|(errors, mode)| {
24                    for why in errors.into_iter().filter(cosmic_config::Error::is_err) {
25                        if let cosmic_config::Error::GetKey(_, err) = &why {
26                            if err.kind() == std::io::ErrorKind::NotFound {
27                                // No system default config installed; don't error
28                                continue;
29                            }
30                        }
31                        tracing::error!(?why, "CosmicTk config entry error");
32                    }
33                    mode
34                })
35            })
36            .unwrap_or_default(),
37    )
38});
39
40/// Apply the theme to other toolkits.
41#[allow(clippy::missing_panics_doc)]
42pub fn apply_theme_global() -> bool {
43    COSMIC_TK.read().unwrap().apply_theme_global
44}
45
46/// Show minimize button in window header.
47#[allow(clippy::missing_panics_doc)]
48pub fn show_minimize() -> bool {
49    COSMIC_TK.read().unwrap().show_minimize
50}
51
52/// Show maximize button in window header.
53#[allow(clippy::missing_panics_doc)]
54pub fn show_maximize() -> bool {
55    COSMIC_TK.read().unwrap().show_maximize
56}
57
58/// Preferred icon theme.
59#[allow(clippy::missing_panics_doc)]
60pub fn icon_theme() -> String {
61    COSMIC_TK.read().unwrap().icon_theme.clone()
62}
63
64/// Density of CSD/SSD header bars.
65#[allow(clippy::missing_panics_doc)]
66pub fn header_size() -> Density {
67    COSMIC_TK.read().unwrap().header_size
68}
69
70/// Interface density.
71#[allow(clippy::missing_panics_doc)]
72pub fn interface_density() -> Density {
73    COSMIC_TK.read().unwrap().interface_density
74}
75
76#[allow(clippy::missing_panics_doc)]
77pub fn interface_font() -> FontConfig {
78    COSMIC_TK.read().unwrap().interface_font.clone()
79}
80
81#[allow(clippy::missing_panics_doc)]
82pub fn monospace_font() -> FontConfig {
83    COSMIC_TK.read().unwrap().monospace_font.clone()
84}
85
86#[derive(Clone, CosmicConfigEntry, Debug, Eq, PartialEq)]
87#[version = 1]
88pub struct CosmicTk {
89    /// Apply the theme to other toolkits.
90    pub apply_theme_global: bool,
91
92    /// Show minimize button in window header.
93    pub show_minimize: bool,
94
95    /// Show maximize button in window header.
96    pub show_maximize: bool,
97
98    /// Preferred icon theme.
99    pub icon_theme: String,
100
101    /// Density of CSD/SSD header bars.
102    pub header_size: Density,
103
104    /// Interface density.
105    pub interface_density: Density,
106
107    /// Interface font family
108    pub interface_font: FontConfig,
109
110    /// Mono font family
111    pub monospace_font: FontConfig,
112}
113
114impl Default for CosmicTk {
115    fn default() -> Self {
116        Self {
117            apply_theme_global: false,
118            show_minimize: true,
119            show_maximize: true,
120            icon_theme: String::from("Cosmic"),
121            header_size: Density::Standard,
122            interface_density: Density::Standard,
123            interface_font: FontConfig {
124                family: SANS_FAMILY_DEFAULT.to_owned(),
125                weight: iced::font::Weight::Normal,
126                stretch: iced::font::Stretch::Normal,
127                style: iced::font::Style::Normal,
128            },
129            monospace_font: FontConfig {
130                family: MONO_FAMILY_DEFAULT.to_owned(),
131                weight: iced::font::Weight::Normal,
132                stretch: iced::font::Stretch::Normal,
133                style: iced::font::Style::Normal,
134            },
135        }
136    }
137}
138
139impl CosmicTk {
140    #[inline]
141    pub fn config() -> Result<Config, cosmic_config::Error> {
142        Config::new(ID, Self::VERSION)
143    }
144}
145
146#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
147pub struct FontConfig {
148    pub family: String,
149    pub weight: iced::font::Weight,
150    pub stretch: iced::font::Stretch,
151    pub style: iced::font::Style,
152}
153
154impl From<FontConfig> for iced::Font {
155    fn from(font: FontConfig) -> Self {
156        /// Stores static strings of the family names for `iced::Font` compatibility.
157        static FAMILY_MAP: LazyLock<RwLock<BTreeSet<&'static str>>> =
158            LazyLock::new(RwLock::default);
159
160        let read_guard = FAMILY_MAP.read().unwrap();
161        let name: Option<&'static str> = read_guard.get(font.family.as_str()).copied();
162        drop(read_guard);
163
164        let name = name.unwrap_or_else(|| {
165            let value: &'static str = font.family.clone().leak();
166            FAMILY_MAP.write().unwrap().insert(value);
167            value
168        });
169
170        Self {
171            family: iced::font::Family::Name(name),
172            weight: font.weight,
173            stretch: font.stretch,
174            style: font.style,
175        }
176    }
177}