Commit 5a827093 authored by Florian Blasius's avatar Florian Blasius 🤘

[#157] refactor theme code to move it to

own sub crate.
parent 03aef195
This diff is collapsed.
......@@ -32,6 +32,7 @@ orbclient = "0.3.21"
[dependencies]
orbtk-structs = { version = "0.1.0", path = "crates/structs" }
orbtk-css-engine = { version = "0.1.0", path = "crates/css-engine" }
cssparser = "0.17.0"
lazy_static = "1.3.0"
# dces = { path = "../dces-rust"}
......
[package]
name = "orbtk-css-engine"
version = "0.1.0"
authors = ["Florian Blasius <flovanpt@posteo.de>"]
description = "CSS engine used to theme OrbTk."
repository = "https://gitlab.redox-os.org/redox-os/orbtk"
license = "MIT"
keywords = ["css", "theme", "styling", "ui"]
edition = "2018"
[dependencies]
orbtk-structs = { version = "0.1.0", path = "../structs" }
cssparser = "0.17.0"
\ No newline at end of file
pub use selector::*;
pub use theme::*;
pub mod prelude;
mod selector;
mod theme;
pub use crate::*;
\ No newline at end of file
use std::{collections::HashSet, ops::Add};
use std::fmt;
#[derive(Clone, Debug)]
pub enum SelectorRelation {
Ancestor(Selector),
Parent(Selector),
}
/// Describes the specificity of a selector.
///
/// The indexes are as follows:
/// 0 - number of IDs (most important)
/// 1 - number of classes and pseudo-classes
/// 2 - number of elements (least important)
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Specificity([u8; 3]);
impl Add<Self> for Specificity {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Specificity([
self.0[0] + rhs.0[0],
self.0[1] + rhs.0[1],
self.0[2] + rhs.0[2],
])
}
}
/// Describes a css selector.
#[derive(Debug, Default)]
pub struct Selector {
pub id: Option<String>,
pub element: Option<String>,
pub classes: HashSet<String>,
pub pseudo_classes: HashSet<String>,
pub relation: Option<Box<SelectorRelation>>,
pub dirty: bool,
}
/// Inner selector value.
impl Selector {
pub fn new() -> Self {
Selector {
id: None,
element: None,
classes: HashSet::new(),
pseudo_classes: HashSet::new(),
relation: None,
dirty: true,
}
}
pub fn is_empty(&self) -> bool {
self.element.is_none()
&& self.id.is_none()
&& self.classes.is_empty()
&& self.pseudo_classes.is_empty()
}
pub fn dirty(&self) -> bool {
self.dirty
}
pub fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty;
}
pub fn specificity(&self) -> Specificity {
let s = Specificity([
if self.id.is_some() { 1 } else { 0 },
(self.classes.len() + self.pseudo_classes.len()) as u8,
if self.element.is_some() { 1 } else { 0 },
]);
if let Some(ref relation) = self.relation {
match **relation {
SelectorRelation::Ancestor(ref x) | SelectorRelation::Parent(ref x) => {
return x.specificity() + s;
}
}
}
s
}
pub fn matches(&self, other: &Selector) -> bool {
if self.id.is_some() && self.id != other.id {
return false;
}
if self.element.is_some() && self.element != other.element {
return false;
}
if !other.classes.is_superset(&self.classes) {
return false;
}
if !other.pseudo_classes.is_superset(&self.pseudo_classes) {
return false;
}
true
}
pub fn with<S: Into<String>>(mut self, element: S) -> Self {
self.element = Some(element.into());
self
}
pub fn id<S: Into<String>>(mut self, id: S) -> Self {
self.id = Some(id.into());
self
}
pub fn class<S: Into<String>>(mut self, class: S) -> Self {
self.classes.insert(class.into());
self
}
pub fn without_class<S: Into<String>>(mut self, class: S) -> Self {
self.classes.remove(&class.into());
self
}
pub fn pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.pseudo_classes.insert(pseudo_class.into());
self
}
pub fn without_pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.pseudo_classes.remove(&pseudo_class.into());
self
}
}
impl PartialEq for Selector {
fn eq(&self, other: &Selector) -> bool {
self.id == other.id
}
}
impl Clone for Selector {
fn clone(&self) -> Self {
Selector {
id: self.id.clone(),
element: self.element.clone(),
classes: self.classes.clone(),
pseudo_classes: self.pseudo_classes.clone(),
relation: self.relation.clone(),
dirty: self.dirty,
}
}
}
impl fmt::Display for Selector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(element) = &self.element {
return write!(f, ", css: {}", element);
}
write!(f, "")
}
}
\ No newline at end of file
......@@ -9,6 +9,6 @@ keywords = ["windowing", "wasm"]
edition = "2018"
[dependencies]
stdweb = "0.4.15"
stdweb = "0.4.16"
orbtk-structs = { version = "0.1.0", path = "../structs" }
......@@ -11,7 +11,7 @@ use crate::{
backend::{FontMeasure, FONT_MEASURE},
properties::{
Bounds, Constraint, Font, FontIcon, FontSize, HorizontalAlignment, IconFont, IconSize,
Image, Text, VerticalAlignment, Visibility, VisibilityValue, WaterMark, ImageExtension, ConstraintExtension,
Image, Text, VerticalAlignment, Visibility, VisibilityValue, WaterMark, ImageExt, ConstraintExt,
},
enums::Alignment,
structs::{DirtySize, Size},
......
......@@ -9,7 +9,7 @@ use dces::prelude::{Entity, EntityComponentManager};
use crate::{
application::Tree,
properties::{
Bounds, Constraint, HorizontalAlignment, Margin, Padding, VerticalAlignment, Visibility, VisibilityValue, ConstraintExtension,
Bounds, Constraint, HorizontalAlignment, Margin, Padding, VerticalAlignment, Visibility, VisibilityValue, ConstraintExt,
},
enums::Alignment,
structs::{DirtySize, Position, Size, Spacer},
......
......@@ -24,8 +24,10 @@ pub use crate::theme::*;
pub use crate::widgets::*;
pub use dces::prelude::*;
pub use orbtk_css_engine::prelude as css_engine;
pub use orbtk_structs::prelude as structs;
pub mod application;
pub mod backend;
pub mod enums;
......
......@@ -20,5 +20,6 @@ pub use crate::{
theme::*,
widgets::*,
widget,
property
property,
css_engine::Selector as SelectorValue
};
\ No newline at end of file
......@@ -8,7 +8,7 @@ property!(
// --- Trait implementations ---
/// Contains different methods to check the bounds.
pub trait BoundsExtension {
pub trait BoundsExt {
/// Check if this rect contains the given `point`.
fn contains(&self, point: (f64, f64)) -> bool;
......@@ -19,7 +19,7 @@ pub trait BoundsExtension {
fn intersects(&self, rect: &Bounds) -> bool;
}
impl BoundsExtension for Bounds {
impl BoundsExt for Bounds {
fn contains(&self, point: (f64, f64)) -> bool {
point.0 >= self.0.x
&& point.0 < self.0.x + self.0.width
......
......@@ -171,7 +171,7 @@ property!(
// --- Trait implementations ---
/// Provides additional operations on grid columns.
pub trait ColumnExtension {
pub trait ColumnExt {
fn create() -> ColumnsBuilder;
/// Returns the number of elements in the columns list, also referred to as its 'length'.
......@@ -190,7 +190,7 @@ pub trait ColumnExtension {
fn iter_mut(&mut self) -> IterMut<Column>;
}
impl ColumnExtension for Columns {
impl ColumnExt for Columns {
/// Creates a new `ColumnsBuilder` object with default values.
fn create() -> ColumnsBuilder {
ColumnsBuilder::new()
......
......@@ -124,7 +124,7 @@ property!(
// --- Trait implementations ---
/// Provides operations on a box constraint.
pub trait ConstraintExtension {
pub trait ConstraintExt {
/// Returns a constraint builder.
fn create() -> ConstraintBuilder;
......@@ -186,7 +186,7 @@ pub trait ConstraintExtension {
fn perform(&self, size: (f64, f64)) -> (f64, f64);
}
impl ConstraintExtension for BoxConstraint {
impl ConstraintExt for BoxConstraint {
fn create() -> ConstraintBuilder {
ConstraintBuilder::new()
}
......@@ -285,7 +285,7 @@ impl ConstraintExtension for BoxConstraint {
}
}
impl ConstraintExtension for Constraint {
impl ConstraintExt for Constraint {
fn create() -> ConstraintBuilder {
ConstraintBuilder::new()
}
......
......@@ -10,7 +10,7 @@ property!(
// --- Trait implementations ---
/// Used to align the position of a widget vertical.
pub trait HorizontalAlignmentExtension {
pub trait HorizontalAlignmentExt {
/// Calculates the x position of the widget depending on the available width, the goal width
/// margin and Horizontal alignment.
fn align_x(&self, available_height: f64, height: f64, margin: Margin) -> f64;
......@@ -20,7 +20,7 @@ pub trait HorizontalAlignmentExtension {
fn align_width(&self, available_height: f64, height: f64, margin: Margin) -> f64;
}
impl HorizontalAlignmentExtension for HorizontalAlignment {
impl HorizontalAlignmentExt for HorizontalAlignment {
fn align_x(&self, available_height: f64, height: f64, margin: Margin) -> f64 {
self.0
.align_position(available_height, height, margin.left(), margin.right())
......
......@@ -170,7 +170,7 @@ property!(
// --- Trait implementations ---
/// Provides additional operations on grid rows.
pub trait RowExtension {
pub trait RowExt {
/// Returns a new Rows Builder.
fn create() -> RowsBuilder;
......@@ -190,7 +190,7 @@ pub trait RowExtension {
fn iter_mut(&mut self) -> IterMut<Row>;
}
impl RowExtension for Rows {
impl RowExt for Rows {
/// Creates a new `RowsBuilder` object with default values.
fn create() -> RowsBuilder {
RowsBuilder::new()
......
......@@ -8,7 +8,7 @@ property!(
// --- Trait implementations ---
/// Used to align the position of a widget vertical.
pub trait VerticalAlignmentExtension {
pub trait VerticalAlignmentExt {
/// Calculates the y position of the widget depending on the available height, the goal height
/// margin and Vertical alignment.
fn align_y(&self, available_height: f64, height: f64, margin: Margin) -> f64;
......@@ -18,7 +18,7 @@ pub trait VerticalAlignmentExtension {
fn align_height(&self, available_height: f64, height: f64, margin: Margin) -> f64;
}
impl VerticalAlignmentExtension for VerticalAlignment {
impl VerticalAlignmentExt for VerticalAlignment {
fn align_y(&self, available_height: f64, height: f64, margin: Margin) -> f64 {
self.0
.align_position(available_height, height, margin.top(), margin.bottom())
......
......@@ -38,11 +38,19 @@ impl From<Box<[u8]>> for InnerFont {
}
}
property!(/// `Font` describes the text font of a widget.
Font(InnerFont));
property!(
/// `Font` describes the text font of a widget.
Font(InnerFont)
);
// --- Conversions ---
impl From<OrbFont> for Font {
fn from(s: OrbFont) -> Font {
Font::from(InnerFont::from(s))
}
}
impl From<&str> for Font {
fn from(s: &str) -> Font {
Font::from(InnerFont::from(OrbFont::from_path(s).unwrap()))
......
......@@ -9,6 +9,12 @@ property!(
// --- Conversions ---
impl From<OrbFont> for IconFont {
fn from(s: OrbFont) -> IconFont {
IconFont::from(InnerFont::from(s))
}
}
impl From<&str> for IconFont {
fn from(s: &str) -> IconFont {
IconFont::from(InnerFont::from(OrbFont::from_path(s).unwrap()))
......
use std::{collections::HashSet, ops::Add};
use std::fmt;
use crate::prelude::*;
#[derive(Clone, Debug)]
pub enum SelectorRelation {
Ancestor(SelectorValue),
Parent(SelectorValue),
}
/// Describes the specificity of a selector.
///
/// The indexes are as follows:
/// 0 - number of IDs (most important)
/// 1 - number of classes and pseudo-classes
/// 2 - number of elements (least important)
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Specificity([u8; 3]);
impl Add<Self> for Specificity {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Specificity([
self.0[0] + rhs.0[0],
self.0[1] + rhs.0[1],
self.0[2] + rhs.0[2],
])
}
}
#[derive(Debug, Default)]
pub struct SelectorValue {
pub id: Option<String>,
pub element: Option<String>,
pub classes: HashSet<String>,
pub pseudo_classes: HashSet<String>,
pub relation: Option<Box<SelectorRelation>>,
pub dirty: bool,
}
impl fmt::Display for SelectorValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(element) = &self.element {
return write!(f, ", css: {}", element);
}
write!(f, "")
}
}
/// Extends the selector.
pub trait SelectorExtension {
fn dirty(&self) -> bool;
fn set_dirty(&mut self, dirty: bool);
fn specificity(&self) -> Specificity;
fn matches(&self, other: &SelectorValue) -> bool;
fn with<S: Into<String>>(self, element: S) -> Self;
fn id<S: Into<String>>(self, id: S) -> Self;
fn class<S: Into<String>>(self, class: S) -> Self;
fn without_class<S: Into<String>>(self, class: S) -> Self;
fn pseudo_class<S: Into<String>>(self, pseudo_class: S) -> Self;
fn without_pseudo_class<S: Into<String>>(self, pseudo_class: S) -> Self;
}
/// Inner selector value.
impl SelectorValue {
pub fn new() -> Self {
SelectorValue {
id: None,
element: None,
classes: HashSet::new(),
pseudo_classes: HashSet::new(),
relation: None,
dirty: true,
}
}
}
impl SelectorExtension for SelectorValue {
fn dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self, dirty: bool) {
self.dirty = dirty;
}
fn specificity(&self) -> Specificity {
let s = Specificity([
if self.id.is_some() { 1 } else { 0 },
(self.classes.len() + self.pseudo_classes.len()) as u8,
if self.element.is_some() { 1 } else { 0 },
]);
if let Some(ref relation) = self.relation {
match **relation {
SelectorRelation::Ancestor(ref x) | SelectorRelation::Parent(ref x) => {
return x.specificity() + s;
}
}
}
s
}
fn matches(&self, other: &SelectorValue) -> bool {
if self.id.is_some() && self.id != other.id {
return false;
}
if self.element.is_some() && self.element != other.element {
return false;
}
if !other.classes.is_superset(&self.classes) {
return false;
}
if !other.pseudo_classes.is_superset(&self.pseudo_classes) {
return false;
}
true
}
fn with<S: Into<String>>(mut self, element: S) -> Self {
self.element = Some(element.into());
self
}
fn id<S: Into<String>>(mut self, id: S) -> Self {
self.id = Some(id.into());
self
}
fn class<S: Into<String>>(mut self, class: S) -> Self {
self.classes.insert(class.into());
self
}
fn without_class<S: Into<String>>(mut self, class: S) -> Self {
self.classes.remove(&class.into());
self
}
fn pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.pseudo_classes.insert(pseudo_class.into());
self
}
fn without_pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.pseudo_classes.remove(&pseudo_class.into());
self
}
}
impl PartialEq for SelectorValue {
fn eq(&self, other: &SelectorValue) -> bool {
self.id == other.id
}
}
impl SelectorValue {
pub fn is_empty(&self) -> bool {
self.element.is_none()
&& self.id.is_none()
&& self.classes.is_empty()
&& self.pseudo_classes.is_empty()
}
}
impl Clone for SelectorValue {
fn clone(&self) -> Self {
SelectorValue {
id: self.id.clone(),
element: self.element.clone(),
classes: self.classes.clone(),
pseudo_classes: self.pseudo_classes.clone(),
relation: self.relation.clone(),
dirty: self.dirty,
}
}
}
use crate::{
Entity,
EntityComponentManager,
css_engine::{Selector as SelectorValue, Specificity},
properties::{get_property, PropertySource}
};
property!(
/// `Selector` describes the css selector of a widget.
......@@ -201,80 +12,50 @@ property!(
// --- Trait implementations ---
impl SelectorExtension for Selector {
fn dirty(&self) -> bool {
self.0.dirty
impl Selector {
pub fn dirty(&self) -> bool {
self.0.dirty()
}
fn set_dirty(&mut self, dirty: bool) {
self.0.dirty = dirty;
pub fn set_dirty(&mut self, dirty: bool) {
self.0.set_dirty(dirty);
}
fn specificity(&self) -> Specificity {
let s = Specificity([
if self.0.id.is_some() { 1 } else { 0 },
(self.0.classes.len() + self.0.pseudo_classes.len()) as u8,
if self.0.element.is_some() { 1 } else { 0 },
]);
if let Some(ref relation) = self.0.relation {
match **relation {
SelectorRelation::Ancestor(ref x) | SelectorRelation::Parent(ref x) => {
return x.specificity() + s;
}
}
}
s
pub fn specificity(&self) -> Specificity {
self.0.specificity()
}
fn matches(&self, other: &SelectorValue) -> bool {
if self.0.id.is_some() && self.0.id != other.id {
return false;
}
if self.0.element.is_some() && self.0.element != other.element {
return false;
}
if !other.classes.is_superset(&self.0.classes) {
return false;
}
if !other.pseudo_classes.is_superset(&self.0.pseudo_classes) {
return false;
}
true
pub fn matches(&self, other: &SelectorValue) -> bool {
self.0.matches(other)
}
fn with<S: Into<String>>(mut self, element: S) -> Self {
self.0.element = Some(element.into());
pub fn with<S: Into<String>>(mut self, element: S) -> Self {
self.0 = self.0.with(element);
self
}
fn id<S: Into<String>>(mut self, id: S) -> Self {
self.0.id = Some(id.into());
pub fn id<S: Into<String>>(mut self, id: S) -> Self {
self.0 = self.0.id(id);
self
}
fn class<S: Into<String>>(mut self, class: S) -> Self {
self.0.classes.insert(class.into());
pub fn class<S: Into<String>>(mut self, class: S) -> Self {
self.0 = self.0.class(class);
self
}
fn without_class<S: Into<String>>(mut self, class: S) -> Self {
self.0.classes.remove(&class.into());
pub fn without_class<S: Into<String>>(mut self, class: S) -> Self {
self.0 = self.0.without_class(class);
self
}
fn pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.0.pseudo_classes.insert(pseudo_class.into());
pub fn pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.0 = self.0.pseudo_class(pseudo_class);
self
}
fn without_pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {
self.0.pseudo_classes.remove(&pseudo_class.into());
pub fn without_pseudo_class<S: Into<String>>(mut self, pseudo_class: S) -> Self {