mod loader;
mod parser;
pub mod properties;
mod selector;
mod stylesheet;
mod value;
pub use loader::{StyleManager, StyleBuilder, CssLoadError};
pub use parser::CssParser;
pub use properties::{ComputedStyle, StyleProperty, AlignItems, FlexDirection, JustifyContent};
pub use selector::{Selector, SelectorPart, PseudoClass, Specificity};
pub use stylesheet::{StyleSheet, StyleRule, StyleSheetBuilder, RuleBuilder};
pub use value::{CssValue, Length, LengthUnit};
use crate::theme::ThemeData;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct StyleContext<'a> {
pub theme: &'a ThemeData,
pub style_manager: Option<Arc<StyleManager>>,
pub parent_font_size: f32,
pub viewport_width: f32,
pub viewport_height: f32,
}
impl<'a> StyleContext<'a> {
pub fn new(theme: &'a ThemeData) -> Self {
Self {
theme,
style_manager: None,
parent_font_size: theme.typography.base_size,
viewport_width: 1920.0,
viewport_height: 1080.0,
}
}
pub fn with_styles(theme: &'a ThemeData, styles: Arc<StyleManager>) -> Self {
Self {
theme,
style_manager: Some(styles),
parent_font_size: theme.typography.base_size,
viewport_width: 1920.0,
viewport_height: 1080.0,
}
}
pub fn with_viewport(mut self, width: f32, height: f32) -> Self {
self.viewport_width = width;
self.viewport_height = height;
self
}
pub fn resolve_var(&self, name: &str) -> Option<String> {
if let Some(sm) = &self.style_manager {
if let Some(value) = sm.get_variable(name) {
return Some(value.clone());
}
}
self.theme.resolve_var(name)
}
pub fn combined_stylesheet(&self) -> StyleSheet {
if let Some(sm) = &self.style_manager {
sm.combined_stylesheet()
} else {
StyleSheet::default()
}
}
pub fn compute_style(
&self,
widget_type: &str,
widget_id: Option<&str>,
classes: &[String],
state: &WidgetState,
) -> ComputedStyle {
let stylesheet = self.combined_stylesheet();
stylesheet.compute_style(widget_type, widget_id, classes, state, self)
}
pub fn to_pixels(&self, length: &Length) -> f32 {
match length.unit {
LengthUnit::Px => length.value,
LengthUnit::Rem => length.value * self.theme.typography.base_size,
LengthUnit::Em => length.value * self.parent_font_size,
LengthUnit::Percent => length.value / 100.0, LengthUnit::Vw => length.value * self.viewport_width / 100.0,
LengthUnit::Vh => length.value * self.viewport_height / 100.0,
LengthUnit::Vmin => length.value * self.viewport_width.min(self.viewport_height) / 100.0,
LengthUnit::Vmax => length.value * self.viewport_width.max(self.viewport_height) / 100.0,
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct WidgetState {
pub hovered: bool,
pub pressed: bool,
pub focused: bool,
pub disabled: bool,
pub checked: bool,
pub first_child: bool,
pub last_child: bool,
pub nth_child: usize,
}
impl WidgetState {
pub fn matches(&self, pseudo: &PseudoClass) -> bool {
match pseudo {
PseudoClass::Hover => self.hovered,
PseudoClass::Active => self.pressed,
PseudoClass::Focus => self.focused,
PseudoClass::FocusVisible => self.focused, PseudoClass::Disabled => self.disabled,
PseudoClass::Enabled => !self.disabled,
PseudoClass::Checked => self.checked,
PseudoClass::FirstChild => self.first_child,
PseudoClass::LastChild => self.last_child,
PseudoClass::NthChild(n) => self.nth_child == *n,
PseudoClass::Not(_) => true, }
}
}
#[derive(Debug, Clone, Default)]
pub struct ClassList {
classes: Vec<String>,
}
impl ClassList {
pub fn new() -> Self {
Self { classes: Vec::new() }
}
pub fn add(&mut self, class: impl Into<String>) {
let class = class.into();
if !self.classes.contains(&class) {
self.classes.push(class);
}
}
pub fn remove(&mut self, class: &str) {
self.classes.retain(|c| c != class);
}
pub fn toggle(&mut self, class: impl Into<String>) {
let class = class.into();
if self.classes.contains(&class) {
self.remove(&class);
} else {
self.classes.push(class);
}
}
pub fn contains(&self, class: &str) -> bool {
self.classes.contains(&class.to_string())
}
pub fn iter(&self) -> impl Iterator<Item = &String> {
self.classes.iter()
}
}
impl From<&str> for ClassList {
fn from(s: &str) -> Self {
let classes = s
.split_whitespace()
.map(|s| s.to_string())
.collect();
Self { classes }
}
}
impl From<Vec<String>> for ClassList {
fn from(classes: Vec<String>) -> Self {
Self { classes }
}
}
#[derive(Debug, Clone, Default)]
pub struct InlineStyle {
properties: HashMap<String, CssValue>,
}
impl InlineStyle {
pub fn new() -> Self {
Self {
properties: HashMap::new(),
}
}
pub fn set(&mut self, property: impl Into<String>, value: CssValue) {
self.properties.insert(property.into(), value);
}
pub fn get(&self, property: &str) -> Option<&CssValue> {
self.properties.get(property)
}
pub fn remove(&mut self, property: &str) {
self.properties.remove(property);
}
}