1use 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
14pub 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 pub fn extension_css(mut self, extension: impl Into<String>) -> Self {
36 self.theme_extensions.push(extension.into());
37 self
38 }
39
40 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 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#[derive(Clone, PartialEq, Default, Debug)]
84pub struct Theme {
85 parent: Option<Arc<Theme>>,
86 rules: Vec<Rule>,
87}
88
89impl Theme {
90 pub fn create() -> ThemeBuilder {
92 ThemeBuilder {
93 theme_css: None,
94 ..Default::default()
95 }
96 }
97
98 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 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 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 Token::IDHash(ref id_name) => {
350 selector.id = Some(id_name.to_string());
351 }
352
353 Token::Delim('*') => {}
355
356 Token::Delim('.') => {
358 selector.classes.insert(input.expect_ident()?.into_owned());
359 }
360
361 Token::Colon => {
363 selector
364 .pseudo_classes
365 .insert(input.expect_ident()?.into_owned());
366 }
367
368 Token::Comma => {
370 selectors.push(selector);
371 selector = Selector::default();
372 first_token_in_selector = true;
373 continue; }
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}