Skip to main content

qframe/theme/
style.rs

1//! Style properties set by theme rules and typography roles.
2
3use std::collections::BTreeMap;
4use std::sync::Arc;
5
6use super::paint::{Expr, Paint};
7
8/// The value of one style property.
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub enum PropValue {
11    /// A colour: `fg = "$text"`.
12    Paint(Paint),
13    /// A switch: `bold = true`.
14    Flag(bool),
15    /// A cell count: `gap = 1`.
16    Cells(u16),
17    /// Vertical and horizontal cell counts: `padding = [0, 2]`.
18    Pair(u16, u16),
19    /// One of a fixed set of words: `style = "thin"`. Only keys listed in [`WORD_PROPS`] hold
20    /// words.
21    Word(&'static str),
22}
23
24/// Style keys whose value is a word from a fixed list, as `(widget, key, allowed words)`.
25/// Theme files are checked against this list; any other word is reported with its location.
26pub const WORD_PROPS: [(&str, &str, &[&str]); 1] = [("scrollbar", "style", &["block", "half", "thin", "dots"])];
27
28/// The allowed words of `key` in rules for `widget`, when that key holds a word.
29pub(crate) fn allowed_words(widget: &str, key: &str) -> Option<&'static [&'static str]> {
30    WORD_PROPS.iter().find(|(w, k, _)| *w == widget && *k == key).map(|(_, _, words)| *words)
31}
32
33/// Style properties after all matching rules have been layered.
34///
35/// Clones share their storage, so handing out a theme's remembered style every frame copies
36/// nothing.
37#[derive(Debug, Clone, Default, PartialEq)]
38pub struct StyleProps {
39    values: Arc<BTreeMap<String, PropValue>>,
40}
41
42impl StyleProps {
43    /// The raw value of `key`.
44    #[must_use]
45    pub fn get(&self, key: &str) -> Option<PropValue> {
46        self.values.get(key).copied()
47    }
48
49    /// The paint stored under `key`, if `key` holds a colour.
50    #[must_use]
51    pub fn paint(&self, key: &str) -> Option<Paint> {
52        match self.get(key)? {
53            PropValue::Paint(paint) => Some(paint),
54            _ => None,
55        }
56    }
57
58    /// The flag stored under `key`; `false` when unset.
59    #[must_use]
60    pub fn flag(&self, key: &str) -> bool {
61        matches!(self.get(key), Some(PropValue::Flag(true)))
62    }
63
64    /// The cell count stored under `key`.
65    #[must_use]
66    pub fn cells(&self, key: &str) -> Option<u16> {
67        match self.get(key)? {
68            PropValue::Cells(n) => Some(n),
69            _ => None,
70        }
71    }
72
73    /// The `(vertical, horizontal)` pair stored under `key`.
74    #[must_use]
75    pub fn pair(&self, key: &str) -> Option<(u16, u16)> {
76        match self.get(key)? {
77            PropValue::Pair(v, h) => Some((v, h)),
78            _ => None,
79        }
80    }
81
82    /// The word stored under `key`, such as a scrollbar `style`.
83    #[must_use]
84    pub fn word(&self, key: &str) -> Option<&'static str> {
85        match self.get(key)? {
86            PropValue::Word(word) => Some(word),
87            _ => None,
88        }
89    }
90
91    /// Whether any property uses an animated paint.
92    #[must_use]
93    pub fn is_animated(&self) -> bool {
94        self.values.values().any(|value| matches!(value, PropValue::Paint(paint) if paint.is_animated()))
95    }
96
97    /// Whether no property is set.
98    #[must_use]
99    pub fn is_empty(&self) -> bool {
100        self.values.is_empty()
101    }
102
103    /// All properties, sorted by name.
104    pub fn iter(&self) -> impl Iterator<Item = (&str, PropValue)> {
105        self.values.iter().map(|(key, value)| (key.as_str(), *value))
106    }
107
108    pub(crate) fn set(&mut self, key: &str, value: PropValue) {
109        Arc::make_mut(&mut self.values).insert(key.to_owned(), value);
110    }
111
112    /// Drops `key`, as if no rule had set it.
113    pub(crate) fn remove(&mut self, key: &str) {
114        if self.values.contains_key(key) {
115            Arc::make_mut(&mut self.values).remove(key);
116        }
117    }
118
119    /// Copies every property of `other` over this one.
120    pub(crate) fn overlay(&mut self, other: &Self) {
121        if other.values.is_empty() {
122            return;
123        }
124        let values = Arc::make_mut(&mut self.values);
125        for (key, value) in other.values.iter() {
126            values.insert(key.clone(), *value);
127        }
128    }
129}
130
131/// A property value as written in the file, before colour tokens are resolved.
132#[derive(Debug, Clone, PartialEq)]
133pub(crate) enum RawProp {
134    Expr(Expr),
135    Flag(bool),
136    Cells(u16),
137    Pair(u16, u16),
138    Word(&'static str),
139}