Skip to content

Commit

Permalink
WIP: split css things out of render backend
Browse files Browse the repository at this point in the history
  • Loading branch information
Sharktheone committed Sep 18, 2024
1 parent f6538d1 commit 6b952fd
Show file tree
Hide file tree
Showing 33 changed files with 991 additions and 849 deletions.
58 changes: 40 additions & 18 deletions crates/gosub_css3/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ use crate::errors::Error;
use crate::stylesheet::CssStylesheet;
use crate::tokenizer::Tokenizer;

use gosub_shared::{timing_start, timing_stop};
use gosub_shared::byte_stream::{ByteStream, Encoding, Location};
use gosub_shared::traits::Context;
use gosub_shared::traits::css3::CssOrigin;
use gosub_shared::traits::Context;
use gosub_shared::traits::ParserConfig;
use gosub_shared::types::Result;
use gosub_shared::{timing_start, timing_stop};

pub mod ast;
/// This CSS3 parser is heavily based on the MIT licensed CssTree parser written by
/// Roman Dvornov (https://github.com/lahmatiy).
/// The original version can be found at https://github.com/csstree/csstree

pub mod colors;
pub mod ast;
mod errors;
mod functions;
#[allow(dead_code)]
pub mod matcher;
pub mod node;
pub mod parser;
pub mod stylesheet;
mod system;
pub mod tokenizer;
mod unicode;
pub mod walker;
#[allow(dead_code)]
pub mod matcher;
mod errors;
mod functions;

pub struct Css3<'stream> {
/// The tokenizer is responsible for reading the input stream and
Expand All @@ -42,7 +42,12 @@ pub struct Css3<'stream> {

impl<'stream> Css3<'stream> {
/// Creates a new parser with the given byte stream so only parse() needs to be called.
fn new(stream: &'stream mut ByteStream, config: ParserConfig, origin: CssOrigin, source: &str) -> Self {
fn new(
stream: &'stream mut ByteStream,
config: ParserConfig,
origin: CssOrigin,
source: &str,
) -> Self {
Self {
tokenizer: Tokenizer::new(stream, Location::default()),
allow_values_in_argument_list: Vec::new(),
Expand All @@ -53,7 +58,12 @@ impl<'stream> Css3<'stream> {
}

/// Parses a direct string to a CssStyleSheet
pub fn parse_str(data: &str, config: ParserConfig, origin: CssOrigin, source_url: &str) -> Result<CssStylesheet> {
pub fn parse_str(
data: &str,
config: ParserConfig,
origin: CssOrigin,
source_url: &str,
) -> Result<CssStylesheet> {
let mut stream = ByteStream::new(Encoding::UTF8, None);
stream.read_from_str(data, Some(Encoding::UTF8));
stream.close();
Expand All @@ -62,13 +72,22 @@ impl<'stream> Css3<'stream> {
}

/// Parses a direct stream to a CssStyleSheet
pub fn parse_stream(stream: &mut ByteStream, config: ParserConfig, origin: CssOrigin, source_url: &str) -> Result<CssStylesheet> {
pub fn parse_stream(
stream: &mut ByteStream,
config: ParserConfig,
origin: CssOrigin,
source_url: &str,
) -> Result<CssStylesheet> {
Css3::new(stream, config, origin, source_url).parse()
}

fn parse(&mut self) -> Result<CssStylesheet> {
if self.config.context != Context::Stylesheet {
return Err(Error::Parse("Expected a stylesheet context".to_string(), Location::default()).into());
return Err(Error::Parse(
"Expected a stylesheet context".to_string(),
Location::default(),
)
.into());
}

let t_id = timing_start!("css3.parse", self.config.source.as_deref().unwrap_or(""));
Expand All @@ -87,9 +106,13 @@ impl<'stream> Css3<'stream> {
timing_stop!(t_id);

match node_tree {
Ok(None) => return Err(Error::Parse("No node tree found".to_string(), Location::default()).into()),
Ok(Some(node)) => convert_ast_to_stylesheet(&node, self.origin.clone(), self.source.clone().as_str()),
Err(e) => Err(e.into()),
Ok(None) => {
Err(Error::Parse("No node tree found".to_string(), Location::default()).into())
}
Ok(Some(node)) => {
convert_ast_to_stylesheet(&node, self.origin, self.source.clone().as_str())
}
Err(e) => Err(e),
}
}
}
Expand All @@ -106,10 +129,10 @@ pub fn load_default_useragent_stylesheet() -> CssStylesheet {
};

let css_data = include_str!("../resources/useragent.css");
Css3::parse_str(css_data, config, CssOrigin::UserAgent, url).expect("Could not parse useragent stylesheet")
Css3::parse_str(css_data, config, CssOrigin::UserAgent, url)
.expect("Could not parse useragent stylesheet")
}


#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -133,7 +156,6 @@ mod tests {
let res = Css3::parse_str(css.as_str(), config, CssOrigin::Author, filename);
if res.is_err() {
println!("{:?}", res.err().unwrap());
return;
}

// let binding = res.unwrap();
Expand Down
132 changes: 119 additions & 13 deletions crates/gosub_css3/src/matcher/styling.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
use core::fmt::Debug;
use itertools::Itertools;
use std::cmp::Ordering;
use std::collections::HashMap;

use gosub_shared::traits::node::ElementDataType;
use gosub_shared::document::DocumentHandle;
use gosub_shared::node::NodeId;
use gosub_shared::traits::css3::{CssOrigin, CssPropertyMap, CssSystem};
use gosub_shared::traits::document::Document;
use gosub_shared::traits::node::ElementDataType;
use gosub_shared::traits::node::Node;
use gosub_shared::document::DocumentHandle;
use gosub_shared::traits::css3::{CssOrigin, CssSystem};
use itertools::Itertools;
use nom::Parser;
use std::cmp::Ordering;
use std::collections::HashMap;

use crate::matcher::property_definitions::get_css_definitions;
use crate::stylesheet::{Combinator, CssSelector, CssSelectorPart, CssValue, MatcherType, Specificity};
use crate::stylesheet::{
Combinator, CssSelector, CssSelectorPart, CssValue, MatcherType, Specificity,
};

// Matches a complete selector (all parts) against the given node(id)
pub(crate) fn match_selector<D: Document<C>, C: CssSystem>(
Expand Down Expand Up @@ -100,14 +102,19 @@ fn match_selector_part<'a, D: Document<C>, C: CssSystem>(
if !current_node.is_element_node() {
return false;
}
current_node.get_element_data().unwrap().classes().contains(name)
current_node
.get_element_data()
.unwrap()
.classes()
.contains(name)
}
CssSelectorPart::Id(name) => {
if !current_node.is_element_node() {
return false;
}
current_node
.get_element_data().unwrap()
.get_element_data()
.unwrap()
.attributes()
.get("id")
.unwrap_or(&"".to_string())
Expand Down Expand Up @@ -269,7 +276,6 @@ fn match_selector_part<'a, D: Document<C>, C: CssSystem>(
match_selector_part(last, prev, doc, next_node, parts)
}
Combinator::SubsequentSibling => {

let parent_node = doc.node_by_id(current_node.parent_id().unwrap());
let Some(children) = parent_node.map(|p| p.children()) else {
return false;
Expand Down Expand Up @@ -310,7 +316,10 @@ fn match_selector_part<'a, D: Document<C>, C: CssSystem>(
return false;
};

current_node.get_element_data().unwrap().is_namespace(namespace)
current_node
.get_element_data()
.unwrap()
.is_namespace(namespace)
}
Combinator::Column => {
//TODO
Expand Down Expand Up @@ -531,6 +540,75 @@ impl CssProperty {
}
}

impl gosub_shared::traits::css3::CssProperty for CssProperty {
type Value = CssValue;

fn compute_value(&mut self) {
self.compute_value();
}
fn unit_to_px(&self) -> f32 {
self.actual.unit_to_px()
}

fn as_string(&self) -> Option<&str> {
if let CssValue::String(str) = &self.actual {
Some(str)
} else {
None
}
}

fn as_percentage(&self) -> Option<f32> {
if let CssValue::Percentage(percent) = &self.actual {
Some(*percent)
} else {
None
}
}

fn as_unit(&self) -> Option<(f32, &str)> {
if let CssValue::Unit(value, unit) = &self.actual {
Some((*value, unit))
} else {
None
}
}

fn as_color(&self) -> Option<(f32, f32, f32, f32)> {
if let CssValue::Color(color) = &self.actual {
Some((color.r, color.g, color.b, color.a))
} else {
None
}
}

fn parse_color(&self) -> Option<(f32, f32, f32, f32)> {
self.actual
.to_color()
.map(|color| (color.r, color.g, color.b, color.a))
}

fn as_number(&self) -> Option<f32> {
if let CssValue::Number(num) = &self.actual {
Some(*num)
} else {
None
}
}

fn as_list(&self) -> Option<Vec<Self::Value>> {
if let CssValue::List(list) = &self.actual {
Some(list.iter().cloned().collect())
} else {
None
}
}

fn is_none(&self) -> bool {
matches!(self.actual, CssValue::None)
}
}

/// Map of all declared values for a single node. Note that these are only the defined properties, not
/// the non-existing properties.
#[derive(Debug)]
Expand All @@ -556,6 +634,34 @@ impl CssProperties {
}
}

impl CssPropertyMap for CssProperties {
type Property = CssProperty;

fn get(&self, name: &str) -> Option<&Self::Property> {
todo!()
}

fn get_mut(&mut self, name: &str) -> Option<&mut Self::Property> {
todo!()
}

fn make_dirty(&mut self) {
todo!()
}

fn iter(&self) -> impl Iterator<Item = (&str, &Self::Property)> + '_ {
Vec::new().into_iter()
}

fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut Self::Property)> + '_ {
Vec::new().into_iter()
}

fn make_clean(&mut self) {
todo!()
}
}

pub fn prop_is_inherit(name: &str) -> bool {
get_css_definitions()
.find_property(name)
Expand All @@ -565,8 +671,8 @@ pub fn prop_is_inherit(name: &str) -> bool {

#[cfg(test)]
mod tests {
use crate::colors::RgbColor;
use super::*;
use crate::colors::RgbColor;

#[test]
fn css_props() {
Expand Down
Loading

0 comments on commit 6b952fd

Please sign in to comment.