orbtk_css_engine/
theme.rs

1//! This module contains all css theming related resources.
2
3use std::{fs::File, io::BufReader, io::Read, mem, path::Path, sync::Arc};
4
5use cssparser::{
6    self, BasicParseError, CompactCowStr, DeclarationListParser, ParseError, Parser, ParserInput,
7    Token,
8};
9
10use orbtk_utils::prelude::*;
11
12use crate::prelude::*;
13
14/// Used to build a theme, specifying additional details.
15pub struct ThemeBuilder {
16    theme_css: Option<String>,
17    theme_path: Option<String>,
18    theme_extensions: Vec<String>,
19    theme_extension_paths: Vec<String>,
20}
21
22impl Default for ThemeBuilder {
23    fn default() -> Self {
24        ThemeBuilder {
25            theme_css: None,
26            theme_path: None,
27            theme_extensions: Vec::new(),
28            theme_extension_paths: Vec::new(),
29        }
30    }
31}
32
33impl ThemeBuilder {
34    /// Inserts a theme css extension.
35    pub fn extension_css(mut self, extension: impl Into<String>) -> Self {
36        self.theme_extensions.push(extension.into());
37        self
38    }
39
40    /// Inserts a theme extension by path.
41    pub fn extension_path(mut self, extension_path: impl Into<String>) -> Self {
42        self.theme_extension_paths.push(extension_path.into());
43        self
44    }
45
46    /// Builds the theme.
47    pub fn build(self) -> Theme {
48        let mut theme = String::new();
49
50        for css_extension in self.theme_extensions.iter().rev() {
51            theme.push_str(&css_extension);
52        }
53
54        for extension_path in self.theme_extension_paths.iter().rev() {
55            if let Ok(file) = File::open(extension_path) {
56                let mut reader = BufReader::new(file);
57                let mut css = String::new();
58                let _ = reader.read_to_string(&mut css).unwrap();
59
60                theme.push_str(&css);
61            }
62        }
63
64        if let Some(css) = self.theme_css {
65            theme.push_str(&css);
66        };
67
68        if let Some(path) = self.theme_path {
69            if let Ok(file) = File::open(path) {
70                let mut reader = BufReader::new(file);
71                let mut css = String::new();
72                let _ = reader.read_to_string(&mut css).unwrap();
73
74                theme.push_str(&css);
75            }
76        };
77
78        Theme::parse(&theme)
79    }
80}
81
82/// `Theme` is the representation of a css styling.
83#[derive(Clone, PartialEq, Default, Debug)]
84pub struct Theme {
85    parent: Option<Arc<Theme>>,
86    rules: Vec<Rule>,
87}
88
89impl Theme {
90    /// Creates a new `ThemeBuilder` object with default theme as base.
91    pub fn create() -> ThemeBuilder {
92        ThemeBuilder {
93            theme_css: None,
94            ..Default::default()
95        }
96    }
97
98    /// Creates a new `ThemeBuilder` with the given css as base.
99    pub fn create_from_css(css: impl Into<String>) -> ThemeBuilder {
100        ThemeBuilder {
101            theme_css: Some(css.into()),
102            ..Default::default()
103        }
104    }
105
106    /// Creates a new `ThemeBuilder` with the given css path as base.
107    pub fn create_from_path(path: impl Into<String>) -> ThemeBuilder {
108        ThemeBuilder {
109            theme_path: Some(path.into()),
110            ..Default::default()
111        }
112    }
113
114    fn parse(s: &str) -> Self {
115        Theme {
116            parent: None,
117            rules: parse(s),
118        }
119    }
120
121    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Theme, String> {
122        let file = File::open(path).map_err(|err| format!("failed to open css: {}", err))?;
123        let mut reader = BufReader::new(file);
124        let mut css = String::new();
125        let res = reader
126            .read_to_string(&mut css)
127            .map_err(|err| format!("failed to read css: {}", err));
128        match res {
129            Ok(_) => Ok(Theme::parse(&css)),
130            Err(err) => Err(err),
131        }
132    }
133
134    fn all_rules(&self) -> Vec<Rule> {
135        if let Some(ref parent) = self.parent {
136            self.rules
137                .iter()
138                .chain(parent.rules.iter())
139                .cloned()
140                .collect()
141        } else {
142            self.rules.clone()
143        }
144    }
145
146    pub fn get(&self, property: &str, query: &Selector) -> Option<Value> {
147        let mut matches: Vec<(bool, Specificity, Value)> = Vec::new();
148
149        for rule in self.all_rules().iter().rev() {
150            let matching_selectors = rule
151                .selectors
152                .iter()
153                .filter(|x| x.matches(&query))
154                .collect::<Vec<_>>();
155
156            if !matching_selectors.is_empty() {
157                if let Some(decl) = rule
158                    .declarations
159                    .iter()
160                    .find(|decl| decl.property == property)
161                {
162                    let highest_specifity = matching_selectors
163                        .iter()
164                        .map(|sel| sel.specificity())
165                        .max()
166                        .unwrap();
167                    matches.push((decl.important, highest_specifity, decl.value.clone()));
168                }
169            }
170        }
171
172        matches.sort_by_key(|x| (x.0, x.1));
173        matches.last().map(|x| x.2.clone())
174    }
175
176    pub fn brush(&self, property: &str, query: &Selector) -> Option<Brush> {
177        self.get(property, query).and_then(|v| v.brush())
178    }
179
180    pub fn uint(&self, property: &str, query: &Selector) -> Option<u32> {
181        self.get(property, query).and_then(|v| v.uint())
182    }
183
184    pub fn float(&self, property: &str, query: &Selector) -> Option<f32> {
185        self.get(property, query).and_then(|v| v.float())
186    }
187
188    pub fn string(&self, property: &str, query: &Selector) -> Option<String> {
189        self.get(property, query).and_then(|v| v.string())
190    }
191}
192
193#[derive(Clone, Default, PartialEq, Debug)]
194pub struct Rule {
195    pub selectors: Vec<Selector>,
196    pub declarations: Vec<Declaration>,
197}
198
199#[derive(Clone, Default, PartialEq, Debug)]
200pub struct Declaration {
201    pub property: String,
202    pub value: Value,
203    pub important: bool,
204}
205
206#[derive(Clone, PartialEq, Debug)]
207pub enum Value {
208    UInt(u32),
209    Float(f32),
210    Brush(Brush),
211    Str(String),
212}
213
214impl Default for Value {
215    fn default() -> Self {
216        Value::UInt(0)
217    }
218}
219
220impl Value {
221    pub fn uint(&self) -> Option<u32> {
222        match *self {
223            Value::UInt(x) => Some(x),
224            _ => None,
225        }
226    }
227
228    pub fn float(&self) -> Option<f32> {
229        match *self {
230            Value::Float(x) => Some(x),
231            _ => None,
232        }
233    }
234
235    pub fn brush(&self) -> Option<Brush> {
236        match self {
237            Value::Brush(x) => Some(x.clone()),
238            _ => None,
239        }
240    }
241
242    pub fn string(&self) -> Option<String> {
243        match self {
244            Value::Str(x) => Some(x.clone()),
245            _ => None,
246        }
247    }
248}
249
250#[derive(Clone, Debug)]
251pub enum CustomParseError {
252    InvalidColorName(String),
253    InvalidColorHex(String),
254    InvalidStringName(String),
255}
256
257impl<'t> From<CustomParseError> for ParseError<'t, CustomParseError> {
258    fn from(e: CustomParseError) -> Self {
259        ParseError::Custom(e)
260    }
261}
262
263struct RuleParser;
264
265impl RuleParser {
266    fn new() -> Self {
267        RuleParser {}
268    }
269}
270
271impl<'i> cssparser::QualifiedRuleParser<'i> for RuleParser {
272    type Prelude = Vec<Selector>;
273    type QualifiedRule = Rule;
274    type Error = CustomParseError;
275
276    fn parse_prelude<'t>(
277        &mut self,
278        input: &mut Parser<'i, 't>,
279    ) -> Result<Self::Prelude, ParseError<'i, Self::Error>> {
280        let res = parse_selectors(input)?;
281        Ok(res)
282    }
283
284    fn parse_block<'t>(
285        &mut self,
286        selectors: Self::Prelude,
287        input: &mut Parser<'i, 't>,
288    ) -> Result<Self::QualifiedRule, ParseError<'i, Self::Error>> {
289        let decl_parser = DeclarationParser {};
290
291        let decls = DeclarationListParser::new(input, decl_parser).collect::<Vec<_>>();
292
293        for decl in &decls {
294            match *decl {
295                Ok(_) => {}
296                Err(ref e) => {
297                    match e.error {
298                        ParseError::Basic(ref e) => eprintln!("{:?}", e),
299                        ParseError::Custom(ref e) => eprintln!("{:?}", e),
300                    }
301                    println!("Error occured in `{}`", input.slice(e.span.clone()));
302                }
303            }
304        }
305
306        let decls = decls.into_iter().filter_map(|decl| decl.ok()).collect();
307
308        Ok(Rule {
309            selectors,
310            declarations: decls,
311        })
312    }
313}
314
315impl<'i> cssparser::AtRuleParser<'i> for RuleParser {
316    type Prelude = ();
317    type AtRule = Rule;
318    type Error = CustomParseError;
319}
320
321fn parse_selectors<'i, 't>(
322    input: &mut Parser<'i, 't>,
323) -> Result<Vec<Selector>, ParseError<'i, CustomParseError>> {
324    let mut selectors = Vec::new();
325
326    let mut selector = Selector::default();
327
328    let mut first_token_in_selector = true;
329    while let Ok(t) = input.next() {
330        match t {
331            // Element
332            Token::Ident(ref element_name) => {
333                if first_token_in_selector {
334                    selector.element = Some(element_name.to_string())
335                } else {
336                    let mut old_selector = Selector::new().with(element_name.to_string());
337                    mem::swap(&mut old_selector, &mut selector);
338                    selector.relation = Some(Box::new(SelectorRelation::Ancestor(old_selector)));
339                }
340            }
341
342            Token::Delim('>') => {
343                let mut old_selector = Selector::new().with(input.expect_ident()?.to_string());
344                mem::swap(&mut old_selector, &mut selector);
345                selector.relation = Some(Box::new(SelectorRelation::Parent(old_selector)));
346            }
347
348            // Id
349            Token::IDHash(ref id_name) => {
350                selector.id = Some(id_name.to_string());
351            }
352
353            // Any element
354            Token::Delim('*') => {}
355
356            // Class
357            Token::Delim('.') => {
358                selector.classes.insert(input.expect_ident()?.into_owned());
359            }
360
361            // Pseudo-class
362            Token::Colon => {
363                selector
364                    .pseudo_classes
365                    .insert(input.expect_ident()?.into_owned());
366            }
367
368            // This selector is done, on to the next one
369            Token::Comma => {
370                selectors.push(selector);
371                selector = Selector::default();
372                first_token_in_selector = true;
373                continue; // need to continue to avoid `first_token_in_selector` being set to false
374            }
375
376            t => {
377                let basic_error = BasicParseError::UnexpectedToken(t);
378                return Err(basic_error.into());
379            }
380        }
381
382        first_token_in_selector = false;
383    }
384
385    selectors.push(selector);
386
387    if selectors.iter().any(|sel| sel.relation.is_some()) {
388        eprintln!("WARNING: Complex selector relations not implemented");
389    }
390
391    Ok(selectors)
392}
393
394struct DeclarationParser;
395
396impl<'i> cssparser::DeclarationParser<'i> for DeclarationParser {
397    type Declaration = Declaration;
398    type Error = CustomParseError;
399
400    fn parse_value<'t>(
401        &mut self,
402        name: CompactCowStr<'i>,
403        input: &mut Parser<'i, 't>,
404    ) -> Result<Self::Declaration, ParseError<'i, Self::Error>> {
405        let value = match &*name {
406            "color" | "border-color" | "icon-color" => Value::Brush(parse_basic_color(input)?),
407
408            "background" | "foreground" => Value::Brush(parse_basic_color(input)?),
409
410            "font-family" | "icon-family" => Value::Str(parse_string(input)?),
411
412            "border-radius" | "border-width" | "font-size" | "icon-size" | "icon-margin"
413            | "padding" | "padding-left" | "padding-top" | "padding-right" | "padding-bottom"
414            | "width" | "height" | "min-width" | "min-height" | "max-width" | "max-height"
415            | "spacing" => match input.next()? {
416                Token::Number {
417                    int_value: Some(x),
418                    has_sign,
419                    ..
420                } if !has_sign && x >= 0 => Value::UInt(x as u32),
421                t => return Err(BasicParseError::UnexpectedToken(t).into()),
422            },
423
424            "opacity" => match input.next()? {
425                Token::Number { value: x, .. } => Value::Float(x as f32),
426                t => return Err(BasicParseError::UnexpectedToken(t).into()),
427            },
428
429            _ => return Err(BasicParseError::UnexpectedToken(input.next()?).into()),
430        };
431
432        Ok(Declaration {
433            property: name.into_owned(),
434            value,
435            important: input.r#try(cssparser::parse_important).is_ok(),
436        })
437    }
438}
439
440impl<'i> cssparser::AtRuleParser<'i> for DeclarationParser {
441    type Prelude = ();
442    type AtRule = Declaration;
443    type Error = CustomParseError;
444}
445
446fn css_color(name: &str) -> Option<Brush> {
447    Some(match name {
448        "transparent" => Brush::from(name),
449
450        "black" => Brush::from("#000000"),
451        "silver" => Brush::from("#C0C0C0"),
452        "gray" | "grey" => Brush::from("#808080"),
453        "white" => Brush::from("#FFFFFF"),
454        "maroon" => Brush::from("#800000"),
455        "red" => Brush::from("#FF0000"),
456        "purple" => Brush::from("#800080"),
457        "fuchsia" => Brush::from("#FF00FF"),
458        "green" => Brush::from("#008000"),
459        "lime" => Brush::from("#00FF00"),
460        "olive" => Brush::from("#808000"),
461        "yellow" => Brush::from("#FFFF00"),
462        "navy" => Brush::from("#000080"),
463        "blue" => Brush::from("#0000FF"),
464        "teal" => Brush::from("#008080"),
465        "aqua" => Brush::from("#00FFFF"),
466        _ => return None,
467    })
468}
469
470fn css_string(name: &str) -> Option<String> {
471    Some(String::from(name))
472}
473
474fn parse_string<'i, 't>(
475    input: &mut Parser<'i, 't>,
476) -> Result<String, ParseError<'i, CustomParseError>> {
477    Ok(match input.next()? {
478        Token::QuotedString(s) => match css_string(&s) {
479            Some(string) => string,
480            None => return Err(CustomParseError::InvalidStringName(s.into_owned()).into()),
481        },
482
483        t => {
484            let basic_error = BasicParseError::UnexpectedToken(t);
485            return Err(basic_error.into());
486        }
487    })
488}
489
490fn parse_basic_color<'i, 't>(
491    input: &mut Parser<'i, 't>,
492) -> Result<Brush, ParseError<'i, CustomParseError>> {
493    Ok(match input.next()? {
494        Token::Ident(s) => match css_color(&s) {
495            Some(color) => color,
496            None => return Err(CustomParseError::InvalidColorName(s.into_owned()).into()),
497        },
498
499        Token::IDHash(hash) | Token::Hash(hash) => Brush::from(hash.into_owned()),
500
501        t => {
502            let basic_error = BasicParseError::UnexpectedToken(t);
503            return Err(basic_error.into());
504        }
505    })
506}
507
508pub fn parse(s: &str) -> Vec<Rule> {
509    let mut input = ParserInput::new(s);
510    let mut parser = Parser::new(&mut input);
511    let rule_parser = RuleParser::new();
512
513    let rules = {
514        let rule_list_parser =
515            cssparser::RuleListParser::new_for_stylesheet(&mut parser, rule_parser);
516        rule_list_parser.collect::<Vec<_>>()
517    };
518
519    for rule in &rules {
520        match *rule {
521            Ok(_) => {}
522            Err(ref e) => {
523                match e.error {
524                    ParseError::Basic(ref e) => eprintln!("{:?}", e),
525                    ParseError::Custom(ref e) => eprintln!("{:?}", e),
526                }
527                println!("Error occured in `{}`", parser.slice(e.span.clone()));
528            }
529        }
530    }
531
532    rules.into_iter().filter_map(|rule| rule.ok()).collect()
533}