Skip to content

Commit

Permalink
Merge pull request #563 from Sharktheone/css/shorthands
Browse files Browse the repository at this point in the history
implement shorthands
  • Loading branch information
Sharktheone authored Aug 27, 2024
2 parents b968b1b + 37a0ba1 commit 8ff3cad
Show file tree
Hide file tree
Showing 7 changed files with 1,129 additions and 115 deletions.
1 change: 1 addition & 0 deletions crates/gosub_styling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use gosub_css3::Css3;
mod errors;
pub mod property_definitions;
pub mod render_tree;
mod shorthands;
pub mod styling;
mod syntax;
mod syntax_matcher;
Expand Down
59 changes: 54 additions & 5 deletions crates/gosub_styling/src/property_definitions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::collections::HashMap;
use std::sync::LazyLock;

use log::warn;

use gosub_css3::stylesheet::CssValue;

use crate::shorthands::{FixList, Shorthands};
use std::sync::LazyLock;

use crate::syntax::GroupCombinators::Juxtaposition;
use crate::syntax::{CssSyntax, SyntaxComponent};
use crate::syntax_matcher::CssSyntaxTree;
Expand Down Expand Up @@ -70,6 +72,8 @@ pub struct PropertyDefinition {
pub initial_value: Option<CssValue>,
// True when this element is resolved
pub resolved: bool,
/// Shorthand resolver, used to expand computed values
pub shorthands: Option<Shorthands>,
}

impl PropertyDefinition {
Expand Down Expand Up @@ -104,6 +108,16 @@ impl PropertyDefinition {
self.syntax.matches(input)
}

pub fn matches_and_shorthands(&self, input: &[CssValue], fix_list: &mut FixList) -> bool {
if let Some(shorthands) = &self.shorthands {
let resolver = shorthands.get_resolver(fix_list);

self.syntax.matches_and_shorthands(input, resolver)
} else {
self.syntax.matches(input)
}
}

pub fn check_expanded_properties(&self, _values: &[CssValue]) -> bool {
// if values.len() != self.expanded_properties.len() {
// return false;
Expand All @@ -119,13 +133,17 @@ impl PropertyDefinition {

true
}

pub fn is_shorthand(&self) -> bool {
self.computed.len() > 1
}
}

/// A syntax definition that can be used to resolve a property definition
#[derive(Debug, Clone)]
pub struct SyntaxDefinition {
/// Actual syntax
syntax: CssSyntaxTree,
pub syntax: CssSyntaxTree,
/// True when the element has already been resolved
resolved: bool,
}
Expand Down Expand Up @@ -222,7 +240,7 @@ impl CssDefinitions {
}

/// Resolve a syntax component
fn resolve_component(
pub fn resolve_component(
&mut self,
component: &SyntaxComponent,
prop_name: &str,
Expand Down Expand Up @@ -267,9 +285,12 @@ impl CssDefinitions {
// If the resolved syntax is just a single element (be it a group, or a single element),
// return that component.
if resolved_prop.syntax.components.len() == 1 {
return resolved_prop.syntax.components[0].clone();
}
let mut component = resolved_prop.syntax.components[0].clone();

component.update_multipliers(multipliers.clone());

return component;
}
// Otherwise, we return a group with the components
return SyntaxComponent::Group {
components: resolved_prop.syntax.components.clone(),
Expand Down Expand Up @@ -352,6 +373,8 @@ fn pars_definition_files() -> CssDefinitions {
properties,
syntax,
};

definitions.index_shorthands();
definitions.resolve();

definitions
Expand Down Expand Up @@ -472,6 +495,7 @@ fn parse_property_file(json: serde_json::Value) -> HashMap<String, PropertyDefin
initial_value,
inherited: obj["inherited"].as_bool().unwrap(),
resolved: false,
shorthands: None,
},
);
}
Expand Down Expand Up @@ -563,6 +587,7 @@ mod tests {
inherited: false,
initial_value: None,
resolved: false,
shorthands: None,
},
);
definitions.resolve();
Expand All @@ -587,6 +612,7 @@ mod tests {
inherited: false,
initial_value: Some(str!("thick".to_string())),
resolved: false,
shorthands: None,
},
);

Expand Down Expand Up @@ -845,4 +871,27 @@ mod tests {
CssValue::Percentage(5.0),
]));
}

#[test]
fn test_margin() {
let definitions = get_css_definitions();
let def = definitions.find_property("margin").unwrap();

println!("margin def: {:?}", def.syntax);

assert!(def.clone().matches(&[unit!(1.0, "px")]));

assert!(def.clone().matches(&[unit!(1.0, "px"), unit!(2.0, "px")]));

assert!(def
.clone()
.matches(&[unit!(1.0, "px"), unit!(2.0, "px"), unit!(3.0, "px")]));

assert!(def.clone().matches(&[
unit!(1.0, "px"),
unit!(2.0, "px"),
unit!(3.0, "px"),
unit!(4.0, "px"),
]));
}
}
86 changes: 60 additions & 26 deletions crates/gosub_styling/src/render_tree.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::collections::HashMap;
use std::fmt::Debug;

use crate::property_definitions::get_css_definitions;
use crate::shorthands::FixList;
use crate::styling::{match_selector, CssProperties, CssProperty, DeclarationProperty};
use log::warn;

use gosub_css3::stylesheet::{CssDeclaration, CssSelector, CssStylesheet, CssValue};
use gosub_css3::stylesheet::{CssDeclaration, CssOrigin, CssSelector, CssStylesheet, CssValue};
use gosub_html5::node::data::element::ElementData;
use gosub_html5::node::{NodeData, NodeId};
use gosub_html5::parser::document::{DocumentHandle, TreeIterator};
Expand All @@ -13,9 +16,6 @@ use gosub_render_backend::{PreRenderText, RenderBackend};
use gosub_shared::types::Result;
use gosub_typeface::DEFAULT_FS;

use crate::property_definitions::get_css_definitions;
use crate::styling::{match_selector, CssProperties, CssProperty, DeclarationProperty};

mod desc;

/// Map of all declared values for all nodes in the document
Expand Down Expand Up @@ -238,6 +238,18 @@ impl<B: RenderBackend, L: Layouter> RenderTree<B, L> {
fn generate_from(&mut self, document: DocumentHandle) {
// Iterate the complete document tree
let tree_iterator = TreeIterator::new(&document);

{
let doc = document.get();

println!("Stylesheets: {:?}", doc.stylesheets.len());

for stylesheet in &doc.stylesheets {
println!(" {:?}", stylesheet.location);
println!(" {:?}", stylesheet.origin);
}
}

for current_node_id in tree_iterator {
let mut css_map_entry = CssProperties::new();

Expand All @@ -248,7 +260,13 @@ impl<B: RenderBackend, L: Layouter> RenderTree<B, L> {

let definitions = get_css_definitions();

let mut fix_list = FixList::new();

for sheet in document.get().stylesheets.iter() {
if sheet.origin == CssOrigin::UserAgent {
continue;
}

for rule in sheet.rules.iter() {
for selector in rule.selectors().iter() {
if !match_selector(
Expand All @@ -262,18 +280,19 @@ impl<B: RenderBackend, L: Layouter> RenderTree<B, L> {
// Selector matched, so we add all declared values to the map
for declaration in rule.declarations().iter() {
// Step 1: find the property in our CSS definition list
let definition = definitions.find_property(&declaration.property);
// If not found, we skip this declaration
if definition.is_none() {
let Some(definition) = definitions.find_property(&declaration.property)
else {
// If not found, we skip this declaration
warn!(
"Definition is not found for property {:?}",
declaration.property
);
continue;
}
};

// Check if the declaration matches the definition and return the "expanded" order
let res = definition.unwrap().matches(&declaration.value);
let res = definition
.matches_and_shorthands(&declaration.value, &mut fix_list);
if !res {
warn!("Declaration does not match definition: {:?}", declaration);
continue;
Expand All @@ -293,6 +312,10 @@ impl<B: RenderBackend, L: Layouter> RenderTree<B, L> {
}
}

fix_list.resolve_nested(definitions);

fix_list.apply(&mut css_map_entry);

let binding = document.get();
let current_node = binding.get_node_by_id(current_node_id).unwrap();

Expand Down Expand Up @@ -490,29 +513,29 @@ impl<B: RenderBackend, L: Layouter> RenderTree<B, L> {
}

// Generates a declaration property and adds it to the css_map_entry
fn add_property_to_map(
pub fn add_property_to_map(
css_map_entry: &mut CssProperties,
sheet: &CssStylesheet,
selector: &CssSelector,
declaration: &CssDeclaration,
) {
let property_name = declaration.property.clone();
let entry = CssProperty::new(property_name.as_str());
// let entry = CssProperty::new(property_name.as_str());

// If the property is a shorthand css property, we need fetch the individual properties
// It's possible that need to recurse here as these individual properties can be shorthand as well
if entry.is_shorthand() {
for property_name in entry.get_props_from_shorthand() {
let decl = CssDeclaration {
property: property_name.to_string(),
value: declaration.value.clone(),
important: declaration.important,
};

add_property_to_map(css_map_entry, sheet, selector, &decl);
}
}

// if entry.is_shorthand() {
// for property_name in entry.get_props_from_shorthand() {
// let decl = CssDeclaration {
// property: property_name.to_string(),
// value: declaration.value.clone(),
// important: declaration.important,
// };
//
// add_property_to_map(css_map_entry, sheet, selector, &decl);
// }
// }
//
let declaration = DeclarationProperty {
// @todo: this seems wrong. We only get the first values from the declared values
value: declaration.value.first().unwrap().clone(),
Expand Down Expand Up @@ -674,10 +697,21 @@ impl<B: RenderBackend, L: Layouter> RenderTreeNode<B, L> {
return true;
}

return self
.properties
let tag_name = self.name.to_lowercase();

const INLINE_ELEMENTS: [&str; 31] = [
"a", "abbr", "acronym", "b", "bdo", "big", "br", "button", "cite", "code", "dfn", "em",
"i", "img", "input", "kbd", "label", "map", "object", "q", "samp", "script", "select",
"small", "span", "strong", "sub", "sup", "textarea", "tt", "var",
];

if INLINE_ELEMENTS.contains(&tag_name.as_str()) {
return true;
}

self.properties
.get("display")
.map_or(false, |prop| prop.compute_value().to_string() == "inline");
.map_or(false, |prop| prop.compute_value().to_string() == "inline")
}
}

Expand Down
Loading

0 comments on commit 8ff3cad

Please sign in to comment.