extern crate alloc;
use alloc::{boxed::Box, collections::BTreeMap, string::String, vec::Vec};
use crate::dom::NodeType;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CssPropertyOrigin {
Inherited,
Own,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CssPropertyWithOrigin {
pub property: CssProperty,
pub origin: CssPropertyOrigin,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CssDependencyChainStep {
Percent { source_node: NodeId, factor: f32 },
Em { source_node: NodeId, factor: f32 },
Rem { factor: f32 },
Absolute { pixels: f32 },
}
#[derive(Debug, Clone, PartialEq)]
pub struct CssDependencyChain {
pub property_type: CssPropertyType,
pub steps: Vec<CssDependencyChainStep>,
pub cached_pixels: Option<f32>,
}
impl CssDependencyChain {
pub fn absolute(property_type: CssPropertyType, pixels: f32) -> Self {
Self {
property_type,
steps: vec![CssDependencyChainStep::Absolute { pixels }],
cached_pixels: Some(pixels),
}
}
pub fn percent(property_type: CssPropertyType, source_node: NodeId, factor: f32) -> Self {
Self {
property_type,
steps: vec![CssDependencyChainStep::Percent {
source_node,
factor,
}],
cached_pixels: None,
}
}
pub fn em(property_type: CssPropertyType, source_node: NodeId, factor: f32) -> Self {
Self {
property_type,
steps: vec![CssDependencyChainStep::Em {
source_node,
factor,
}],
cached_pixels: None,
}
}
pub fn rem(property_type: CssPropertyType, factor: f32) -> Self {
Self {
property_type,
steps: vec![CssDependencyChainStep::Rem { factor }],
cached_pixels: None,
}
}
pub fn depends_on(&self, node_id: NodeId) -> bool {
self.steps.iter().any(|step| match step {
CssDependencyChainStep::Percent { source_node, .. } => *source_node == node_id,
CssDependencyChainStep::Em { source_node, .. } => *source_node == node_id,
_ => false,
})
}
pub fn resolve<F>(&mut self, mut resolve_node_value: F, root_font_size: f32) -> Option<f32>
where
F: FnMut(NodeId, CssPropertyType) -> Option<f32>,
{
let mut current_value: Option<f32> = None;
for step in &self.steps {
match step {
CssDependencyChainStep::Absolute { pixels } => {
current_value = Some(*pixels);
}
CssDependencyChainStep::Percent {
source_node,
factor,
} => {
let source_val = resolve_node_value(*source_node, self.property_type)?;
current_value = Some(source_val * factor);
}
CssDependencyChainStep::Em {
source_node,
factor,
} => {
let font_size = resolve_node_value(*source_node, CssPropertyType::FontSize)?;
current_value = Some(font_size * factor);
}
CssDependencyChainStep::Rem { factor } => {
current_value = Some(root_font_size * factor);
}
}
}
self.cached_pixels = current_value;
current_value
}
}
use azul_css::{
css::{Css, CssPath},
props::{
basic::{StyleFontFamily, StyleFontFamilyVec, StyleFontSize},
layout::{LayoutDisplay, LayoutHeight, LayoutWidth},
property::{
BoxDecorationBreakValue, BreakInsideValue, CaretAnimationDurationValue,
CaretColorValue, CaretWidthValue, ClipPathValue, ColumnCountValue, ColumnFillValue,
ColumnRuleColorValue, ColumnRuleStyleValue, ColumnRuleWidthValue, ColumnSpanValue,
ColumnWidthValue, ContentValue, CounterIncrementValue, CounterResetValue, CssProperty,
CssPropertyType, FlowFromValue, FlowIntoValue, LayoutAlignContentValue,
LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutBorderBottomWidthValue,
LayoutBorderLeftWidthValue, LayoutBorderRightWidthValue, LayoutBorderSpacingValue,
LayoutBorderTopWidthValue, LayoutBoxSizingValue, LayoutClearValue,
LayoutColumnGapValue, LayoutDisplayValue, LayoutFlexBasisValue,
LayoutFlexDirectionValue, LayoutFlexGrowValue, LayoutFlexShrinkValue,
LayoutFlexWrapValue, LayoutFloatValue, LayoutGapValue, LayoutGridAutoColumnsValue,
LayoutGridAutoFlowValue, LayoutGridAutoRowsValue, LayoutGridColumnValue,
LayoutGridRowValue, LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
LayoutHeightValue, LayoutInsetBottomValue, LayoutJustifyContentValue,
LayoutJustifyItemsValue, LayoutJustifySelfValue, LayoutLeftValue,
LayoutMarginBottomValue, LayoutMarginLeftValue, LayoutMarginRightValue,
LayoutMarginTopValue, LayoutMaxHeightValue, LayoutMaxWidthValue, LayoutMinHeightValue,
LayoutMinWidthValue, LayoutOverflowValue, LayoutPaddingBottomValue,
LayoutPaddingLeftValue, LayoutPaddingRightValue, LayoutPaddingTopValue,
LayoutPositionValue, LayoutRightValue, LayoutRowGapValue, LayoutScrollbarWidthValue,
LayoutTableLayoutValue, LayoutTextJustifyValue, LayoutTopValue, LayoutWidthValue,
LayoutWritingModeValue, LayoutZIndexValue, OrphansValue, PageBreakValue,
ScrollbarStyleValue, SelectionBackgroundColorValue, SelectionColorValue,
SelectionRadiusValue, ShapeImageThresholdValue, ShapeInsideValue, ShapeMarginValue,
ShapeOutsideValue, StringSetValue, StyleBackfaceVisibilityValue,
StyleBackgroundContentVecValue, StyleBackgroundPositionVecValue,
StyleBackgroundRepeatVecValue, StyleBackgroundSizeVecValue,
StyleBorderBottomColorValue, StyleBorderBottomLeftRadiusValue,
StyleBorderBottomRightRadiusValue, StyleBorderBottomStyleValue,
StyleBorderCollapseValue, StyleBorderLeftColorValue, StyleBorderLeftStyleValue,
StyleBorderRightColorValue, StyleBorderRightStyleValue, StyleBorderTopColorValue,
StyleBorderTopLeftRadiusValue, StyleBorderTopRightRadiusValue,
StyleBorderTopStyleValue, StyleBoxShadowValue, StyleCaptionSideValue, StyleCursorValue,
StyleDirectionValue, StyleEmptyCellsValue, StyleExclusionMarginValue,
StyleFilterVecValue, StyleFontFamilyVecValue, StyleFontSizeValue, StyleFontStyleValue,
StyleFontValue, StyleFontWeightValue, StyleHangingPunctuationValue,
StyleHyphenationLanguageValue, StyleHyphensValue, StyleInitialLetterValue,
StyleLetterSpacingValue, StyleLineClampValue, StyleLineHeightValue,
StyleListStylePositionValue, StyleListStyleTypeValue, StyleMixBlendModeValue,
StyleOpacityValue, StylePerspectiveOriginValue, StyleScrollbarColorValue,
StyleTabSizeValue, StyleTextAlignValue, StyleTextColorValue,
StyleTextCombineUprightValue, StyleTextDecorationValue, StyleTextIndentValue,
StyleTransformOriginValue, StyleTransformVecValue, StyleUserSelectValue,
StyleVerticalAlignValue, StyleVisibilityValue, StyleWhiteSpaceValue,
StyleWordSpacingValue, WidowsValue,
},
style::{StyleCursor, StyleTextColor, StyleTransformOrigin},
},
AzString,
};
use crate::{
dom::{NodeData, NodeId, TabIndex, TagId},
id::{NodeDataContainer, NodeDataContainerRef},
style::CascadeInfo,
styled_dom::{
NodeHierarchyItem, NodeHierarchyItemId, NodeHierarchyItemVec, ParentWithNodeDepth,
ParentWithNodeDepthVec, StyledNodeState, TagIdToNodeIdMapping,
},
};
use azul_css::dynamic_selector::{
CssPropertyWithConditions, CssPropertyWithConditionsVec, DynamicSelectorContext,
};
macro_rules! match_property_value {
($property:expr, $value:ident, $expr:expr) => {
match $property {
CssProperty::CaretColor($value) => $expr,
CssProperty::CaretAnimationDuration($value) => $expr,
CssProperty::SelectionBackgroundColor($value) => $expr,
CssProperty::SelectionColor($value) => $expr,
CssProperty::SelectionRadius($value) => $expr,
CssProperty::TextColor($value) => $expr,
CssProperty::FontSize($value) => $expr,
CssProperty::FontFamily($value) => $expr,
CssProperty::FontWeight($value) => $expr,
CssProperty::FontStyle($value) => $expr,
CssProperty::TextAlign($value) => $expr,
CssProperty::TextJustify($value) => $expr,
CssProperty::VerticalAlign($value) => $expr,
CssProperty::LetterSpacing($value) => $expr,
CssProperty::TextIndent($value) => $expr,
CssProperty::InitialLetter($value) => $expr,
CssProperty::LineClamp($value) => $expr,
CssProperty::HangingPunctuation($value) => $expr,
CssProperty::TextCombineUpright($value) => $expr,
CssProperty::ExclusionMargin($value) => $expr,
CssProperty::HyphenationLanguage($value) => $expr,
CssProperty::LineHeight($value) => $expr,
CssProperty::WordSpacing($value) => $expr,
CssProperty::TabSize($value) => $expr,
CssProperty::WhiteSpace($value) => $expr,
CssProperty::Hyphens($value) => $expr,
CssProperty::Direction($value) => $expr,
CssProperty::UserSelect($value) => $expr,
CssProperty::TextDecoration($value) => $expr,
CssProperty::Cursor($value) => $expr,
CssProperty::Display($value) => $expr,
CssProperty::Float($value) => $expr,
CssProperty::BoxSizing($value) => $expr,
CssProperty::Width($value) => $expr,
CssProperty::Height($value) => $expr,
CssProperty::MinWidth($value) => $expr,
CssProperty::MinHeight($value) => $expr,
CssProperty::MaxWidth($value) => $expr,
CssProperty::MaxHeight($value) => $expr,
CssProperty::Position($value) => $expr,
CssProperty::Top($value) => $expr,
CssProperty::Right($value) => $expr,
CssProperty::Left($value) => $expr,
CssProperty::Bottom($value) => $expr,
CssProperty::ZIndex($value) => $expr,
CssProperty::FlexWrap($value) => $expr,
CssProperty::FlexDirection($value) => $expr,
CssProperty::FlexGrow($value) => $expr,
CssProperty::FlexShrink($value) => $expr,
CssProperty::FlexBasis($value) => $expr,
CssProperty::JustifyContent($value) => $expr,
CssProperty::AlignItems($value) => $expr,
CssProperty::AlignContent($value) => $expr,
CssProperty::AlignSelf($value) => $expr,
CssProperty::JustifyItems($value) => $expr,
CssProperty::JustifySelf($value) => $expr,
CssProperty::BackgroundContent($value) => $expr,
CssProperty::BackgroundPosition($value) => $expr,
CssProperty::BackgroundSize($value) => $expr,
CssProperty::BackgroundRepeat($value) => $expr,
CssProperty::OverflowX($value) => $expr,
CssProperty::OverflowY($value) => $expr,
CssProperty::PaddingTop($value) => $expr,
CssProperty::PaddingLeft($value) => $expr,
CssProperty::PaddingRight($value) => $expr,
CssProperty::PaddingBottom($value) => $expr,
CssProperty::MarginTop($value) => $expr,
CssProperty::MarginLeft($value) => $expr,
CssProperty::MarginRight($value) => $expr,
CssProperty::MarginBottom($value) => $expr,
CssProperty::BorderTopLeftRadius($value) => $expr,
CssProperty::BorderTopRightRadius($value) => $expr,
CssProperty::BorderBottomLeftRadius($value) => $expr,
CssProperty::BorderBottomRightRadius($value) => $expr,
CssProperty::BorderTopColor($value) => $expr,
CssProperty::BorderRightColor($value) => $expr,
CssProperty::BorderLeftColor($value) => $expr,
CssProperty::BorderBottomColor($value) => $expr,
CssProperty::BorderTopStyle($value) => $expr,
CssProperty::BorderRightStyle($value) => $expr,
CssProperty::BorderLeftStyle($value) => $expr,
CssProperty::BorderBottomStyle($value) => $expr,
CssProperty::BorderTopWidth($value) => $expr,
CssProperty::BorderRightWidth($value) => $expr,
CssProperty::BorderLeftWidth($value) => $expr,
CssProperty::BorderBottomWidth($value) => $expr,
CssProperty::BoxShadow($value) => $expr,
CssProperty::Opacity($value) => $expr,
CssProperty::Transform($value) => $expr,
CssProperty::TransformOrigin($value) => $expr,
CssProperty::PerspectiveOrigin($value) => $expr,
CssProperty::BackfaceVisibility($value) => $expr,
CssProperty::MixBlendMode($value) => $expr,
CssProperty::Filter($value) => $expr,
CssProperty::Visibility($value) => $expr,
CssProperty::WritingMode($value) => $expr,
CssProperty::GridTemplateColumns($value) => $expr,
CssProperty::GridTemplateRows($value) => $expr,
CssProperty::GridAutoColumns($value) => $expr,
CssProperty::GridAutoRows($value) => $expr,
CssProperty::GridAutoFlow($value) => $expr,
CssProperty::GridColumn($value) => $expr,
CssProperty::GridRow($value) => $expr,
CssProperty::GridTemplateAreas($value) => $expr,
CssProperty::Gap($value) => $expr,
CssProperty::ColumnGap($value) => $expr,
CssProperty::RowGap($value) => $expr,
CssProperty::Clear($value) => $expr,
CssProperty::ScrollbarStyle($value) => $expr,
CssProperty::ScrollbarWidth($value) => $expr,
CssProperty::ScrollbarColor($value) => $expr,
CssProperty::ListStyleType($value) => $expr,
CssProperty::ListStylePosition($value) => $expr,
CssProperty::Font($value) => $expr,
CssProperty::ColumnCount($value) => $expr,
CssProperty::ColumnWidth($value) => $expr,
CssProperty::ColumnSpan($value) => $expr,
CssProperty::ColumnFill($value) => $expr,
CssProperty::ColumnRuleStyle($value) => $expr,
CssProperty::ColumnRuleWidth($value) => $expr,
CssProperty::ColumnRuleColor($value) => $expr,
CssProperty::FlowInto($value) => $expr,
CssProperty::FlowFrom($value) => $expr,
CssProperty::ShapeOutside($value) => $expr,
CssProperty::ShapeInside($value) => $expr,
CssProperty::ShapeImageThreshold($value) => $expr,
CssProperty::ShapeMargin($value) => $expr,
CssProperty::ClipPath($value) => $expr,
CssProperty::Content($value) => $expr,
CssProperty::CounterIncrement($value) => $expr,
CssProperty::CounterReset($value) => $expr,
CssProperty::StringSet($value) => $expr,
CssProperty::Orphans($value) => $expr,
CssProperty::Widows($value) => $expr,
CssProperty::PageBreakBefore($value) => $expr,
CssProperty::PageBreakAfter($value) => $expr,
CssProperty::PageBreakInside($value) => $expr,
CssProperty::BreakInside($value) => $expr,
CssProperty::BoxDecorationBreak($value) => $expr,
CssProperty::TableLayout($value) => $expr,
CssProperty::BorderCollapse($value) => $expr,
CssProperty::BorderSpacing($value) => $expr,
CssProperty::CaptionSide($value) => $expr,
CssProperty::EmptyCells($value) => $expr,
}
};
}
fn get_initial_value(property_type: CssPropertyType) -> Option<CssProperty> {
use azul_css::css::CssPropertyValue;
match property_type {
_ => None,
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct CssPropertyCache {
pub node_count: usize,
pub user_overridden_properties: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_normal_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_hover_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_active_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_focus_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_dragging_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub cascaded_drag_over_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_normal_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_hover_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_active_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_focus_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_dragging_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub css_drag_over_props: Vec<BTreeMap<CssPropertyType, CssProperty>>,
pub computed_values: Vec<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
pub dependency_chains: Vec<BTreeMap<CssPropertyType, CssDependencyChain>>,
pub resolved_cache: Vec<Vec<(CssPropertyType, CssProperty)>>,
pub compact_cache: Option<azul_css::compact_cache::CompactLayoutCache>,
}
impl CssPropertyCache {
#[must_use]
pub fn restyle(
&mut self,
css: &mut Css,
node_data: &NodeDataContainerRef<NodeData>,
node_hierarchy: &NodeHierarchyItemVec,
non_leaf_nodes: &ParentWithNodeDepthVec,
html_tree: &NodeDataContainerRef<CascadeInfo>,
) -> Vec<TagIdToNodeIdMapping> {
use azul_css::{
css::{CssDeclaration, CssPathPseudoSelector::*},
props::layout::LayoutDisplay,
};
let css_is_empty = css.is_empty();
if !css_is_empty {
css.sort_by_specificity();
macro_rules! filter_rules {($expected_pseudo_selector:expr, $node_id:expr) => {{
css
.rules() .filter(|rule_block| crate::style::rule_ends_with(&rule_block.path, $expected_pseudo_selector))
.filter(|rule_block| crate::style::matches_html_element(
&rule_block.path,
$node_id,
&node_hierarchy.as_container(),
&node_data,
&html_tree,
$expected_pseudo_selector
))
.flat_map(|matched_rule| {
matched_rule.declarations
.iter()
.filter_map(move |declaration| {
match declaration {
CssDeclaration::Static(s) => Some(s),
CssDeclaration::Dynamic(_d) => None, }
})
})
.map(|prop| prop.clone())
.collect::<Vec<CssProperty>>()
}};}
let css_normal_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(None, node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
let css_hover_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(Some(Hover), node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
let css_active_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(Some(Active), node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
let css_focus_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(Some(Focus), node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
let css_dragging_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(Some(Dragging), node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
let css_drag_over_rules: NodeDataContainer<(NodeId, Vec<CssProperty>)> = node_data
.transform_nodeid_multithreaded_optional(|node_id| {
let r = filter_rules!(Some(DragOver), node_id);
if r.is_empty() {
None
} else {
Some((node_id, r))
}
});
macro_rules! assign_css_rules {
($field:expr, $rules:expr) => {{
for entry in $field.iter_mut() { entry.clear(); }
for (n, map) in $rules.internal.into_iter() {
$field[n.index()] = map.into_iter()
.map(|prop| (prop.get_type(), prop))
.collect();
}
}};
}
assign_css_rules!(self.css_normal_props, css_normal_rules);
assign_css_rules!(self.css_hover_props, css_hover_rules);
assign_css_rules!(self.css_active_props, css_active_rules);
assign_css_rules!(self.css_focus_props, css_focus_rules);
assign_css_rules!(self.css_dragging_props, css_dragging_rules);
assign_css_rules!(self.css_drag_over_props, css_drag_over_rules);
}
for ParentWithNodeDepth { depth: _, node_id } in non_leaf_nodes.iter() {
let parent_id = match node_id.into_crate_internal() {
Some(s) => s,
None => continue,
};
macro_rules! inherit_props {
($from_inherit_map:expr, $to_inherit_map:expr) => {
let parent_map = &$from_inherit_map[parent_id.index()];
if !parent_map.is_empty() {
let parent_inherit_props: Vec<(CssPropertyType, CssProperty)> = parent_map
.iter()
.filter(|(css_prop_type, _)| css_prop_type.is_inheritable())
.map(|(css_prop_type, css_prop)| (*css_prop_type, css_prop.clone()))
.collect();
if !parent_inherit_props.is_empty() {
for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
let child_map = &mut $to_inherit_map[child_id.index()];
for (inherited_rule_type, inherited_rule_value) in parent_inherit_props.iter() {
let _ = child_map
.entry(*inherited_rule_type)
.or_insert_with(|| inherited_rule_value.clone());
}
}
}
}
};
}
macro_rules! inherit_inline_css_props {($filter_pseudo_state:expr, $to_inherit_map:expr) => {{
let parent_inheritable_css_props = &node_data[parent_id]
.css_props
.iter()
.filter(|css_prop| {
let conditions = css_prop.apply_if.as_slice();
if conditions.is_empty() {
$filter_pseudo_state == PseudoStateType::Normal
} else {
conditions.iter().all(|c| {
matches!(c, DynamicSelector::PseudoState(state) if *state == $filter_pseudo_state)
})
}
})
.map(|css_prop| &css_prop.property)
.filter(|css_prop| css_prop.get_type().is_inheritable())
.cloned()
.collect::<Vec<CssProperty>>();
if !parent_inheritable_css_props.is_empty() {
for child_id in parent_id.az_children(&node_hierarchy.as_container()) {
let child_map = &mut $to_inherit_map[child_id.index()];
for inherited_rule in parent_inheritable_css_props.iter() {
let _ = child_map
.entry(inherited_rule.get_type())
.or_insert_with(|| inherited_rule.clone());
}
}
}
}};}
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
inherit_inline_css_props!(PseudoStateType::Normal, self.cascaded_normal_props);
inherit_inline_css_props!(PseudoStateType::Hover, self.cascaded_hover_props);
inherit_inline_css_props!(PseudoStateType::Active, self.cascaded_active_props);
inherit_inline_css_props!(PseudoStateType::Focus, self.cascaded_focus_props);
inherit_inline_css_props!(PseudoStateType::Dragging, self.cascaded_dragging_props);
inherit_inline_css_props!(PseudoStateType::DragOver, self.cascaded_drag_over_props);
if !css_is_empty {
inherit_props!(self.css_normal_props, self.cascaded_normal_props);
inherit_props!(self.css_hover_props, self.cascaded_hover_props);
inherit_props!(self.css_active_props, self.cascaded_active_props);
inherit_props!(self.css_focus_props, self.cascaded_focus_props);
inherit_props!(self.css_dragging_props, self.cascaded_dragging_props);
inherit_props!(self.css_drag_over_props, self.cascaded_drag_over_props);
}
inherit_props!(self.cascaded_normal_props, self.cascaded_normal_props);
inherit_props!(self.cascaded_hover_props, self.cascaded_hover_props);
inherit_props!(self.cascaded_active_props, self.cascaded_active_props);
inherit_props!(self.cascaded_focus_props, self.cascaded_focus_props);
inherit_props!(self.cascaded_dragging_props, self.cascaded_dragging_props);
inherit_props!(self.cascaded_drag_over_props, self.cascaded_drag_over_props);
}
let default_node_state = StyledNodeState::default();
let node_data_container = &node_data.internal;
node_data
.internal
.iter()
.enumerate()
.filter_map(|(node_id, node_data)| {
let node_id = NodeId::new(node_id);
let should_auto_insert_tabindex = node_data
.get_callbacks()
.iter()
.any(|cb| cb.event.is_focus_callback());
let tab_index = match node_data.get_tab_index() {
Some(s) => Some(*s),
None => {
if should_auto_insert_tabindex {
Some(TabIndex::Auto)
} else {
None
}
}
};
let mut node_should_have_tag = false;
loop {
let display = self
.get_display(&node_data, &node_id, &default_node_state)
.and_then(|p| p.get_property_or_default())
.unwrap_or_default();
if display == LayoutDisplay::None {
node_should_have_tag = false;
break;
}
if node_data.has_context_menu() {
node_should_have_tag = true;
break;
}
if tab_index.is_some() {
node_should_have_tag = true;
break;
}
if node_data.get_context_menu().is_some() {
node_should_have_tag = true;
break;
}
let node_has_hover_props = {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
node_data.css_props.as_ref().iter().any(|p| {
p.apply_if.as_slice().iter().any(|c| {
matches!(c, DynamicSelector::PseudoState(PseudoStateType::Hover))
})
})
} || self.css_hover_props.get(node_id.index()).map_or(false, |m| !m.is_empty())
|| self.cascaded_hover_props.get(node_id.index()).map_or(false, |m| !m.is_empty());
if node_has_hover_props {
node_should_have_tag = true;
break;
}
let node_has_active_props = {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
node_data.css_props.as_ref().iter().any(|p| {
p.apply_if.as_slice().iter().any(|c| {
matches!(c, DynamicSelector::PseudoState(PseudoStateType::Active))
})
})
} || self.css_active_props.get(node_id.index()).map_or(false, |m| !m.is_empty())
|| self.cascaded_active_props.get(node_id.index()).map_or(false, |m| !m.is_empty());
if node_has_active_props {
node_should_have_tag = true;
break;
}
let node_has_focus_props = {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
node_data.css_props.as_ref().iter().any(|p| {
p.apply_if.as_slice().iter().any(|c| {
matches!(c, DynamicSelector::PseudoState(PseudoStateType::Focus))
})
})
} || self.css_focus_props.get(node_id.index()).map_or(false, |m| !m.is_empty())
|| self.cascaded_focus_props.get(node_id.index()).map_or(false, |m| !m.is_empty());
if node_has_focus_props {
node_should_have_tag = true;
break;
}
let node_has_dragging_props = {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
node_data.css_props.as_ref().iter().any(|p| {
p.apply_if.as_slice().iter().any(|c| {
matches!(c, DynamicSelector::PseudoState(PseudoStateType::Dragging))
})
})
} || self.css_dragging_props.get(node_id.index()).map_or(false, |m| !m.is_empty())
|| self.cascaded_dragging_props.get(node_id.index()).map_or(false, |m| !m.is_empty());
if node_has_dragging_props {
node_should_have_tag = true;
break;
}
let node_has_drag_over_props = {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
node_data.css_props.as_ref().iter().any(|p| {
p.apply_if.as_slice().iter().any(|c| {
matches!(c, DynamicSelector::PseudoState(PseudoStateType::DragOver))
})
})
} || self.css_drag_over_props.get(node_id.index()).map_or(false, |m| !m.is_empty())
|| self.cascaded_drag_over_props.get(node_id.index()).map_or(false, |m| !m.is_empty());
if node_has_drag_over_props {
node_should_have_tag = true;
break;
}
let node_only_window_callbacks = node_data.get_callbacks().is_empty()
|| node_data
.get_callbacks()
.iter()
.all(|cb| cb.event.is_window_callback());
if !node_only_window_callbacks {
node_should_have_tag = true;
break;
}
let node_has_non_default_cursor = self
.get_cursor(&node_data, &node_id, &default_node_state)
.is_some();
if node_has_non_default_cursor {
node_should_have_tag = true;
break;
}
let node_has_overflow_scroll = {
use azul_css::props::layout::LayoutOverflow;
let overflow_x = self
.get_overflow_x(&node_data, &node_id, &default_node_state)
.and_then(|p| p.get_property_or_default());
let overflow_y = self
.get_overflow_y(&node_data, &node_id, &default_node_state)
.and_then(|p| p.get_property_or_default());
let x_scrollable = matches!(
overflow_x,
Some(LayoutOverflow::Scroll | LayoutOverflow::Auto)
);
let y_scrollable = matches!(
overflow_y,
Some(LayoutOverflow::Scroll | LayoutOverflow::Auto)
);
x_scrollable || y_scrollable
};
if node_has_overflow_scroll {
node_should_have_tag = true;
break;
}
let node_has_selectable_text = {
use azul_css::props::style::StyleUserSelect;
use crate::dom::NodeType;
let has_text_children = {
let hier = node_hierarchy.as_container()[node_id];
let mut has_text = false;
if let Some(first_child) = hier.first_child_id(node_id) {
let mut child_id = Some(first_child);
while let Some(cid) = child_id {
let child_data = &node_data_container[cid.index()];
if matches!(child_data.get_node_type(), NodeType::Text(_)) {
has_text = true;
break;
}
child_id = node_hierarchy.as_container()[cid].next_sibling_id();
}
}
has_text
};
if has_text_children {
let user_select = self
.get_user_select(&node_data, &node_id, &default_node_state)
.and_then(|p| p.get_property().cloned())
.unwrap_or(StyleUserSelect::Auto);
!matches!(user_select, StyleUserSelect::None)
} else {
false
}
};
if node_has_selectable_text {
node_should_have_tag = true;
break;
}
break;
}
if !node_should_have_tag {
None
} else {
Some(TagIdToNodeIdMapping {
tag_id: TagId::from_crate_internal(TagId::unique()),
node_id: NodeHierarchyItemId::from_crate_internal(Some(node_id)),
tab_index: tab_index.into(),
})
}
})
.collect()
}
pub fn get_computed_css_style_string(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> String {
let mut s = String::new();
if let Some(p) = self.get_background_content(&node_data, node_id, node_state) {
s.push_str(&format!("background: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_background_position(&node_data, node_id, node_state) {
s.push_str(&format!("background-position: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_background_size(&node_data, node_id, node_state) {
s.push_str(&format!("background-size: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_background_repeat(&node_data, node_id, node_state) {
s.push_str(&format!("background-repeat: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_font_size(&node_data, node_id, node_state) {
s.push_str(&format!("font-size: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_font_family(&node_data, node_id, node_state) {
s.push_str(&format!("font-family: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_text_color(&node_data, node_id, node_state) {
s.push_str(&format!("color: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_text_align(&node_data, node_id, node_state) {
s.push_str(&format!("text-align: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_line_height(&node_data, node_id, node_state) {
s.push_str(&format!("line-height: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_letter_spacing(&node_data, node_id, node_state) {
s.push_str(&format!("letter-spacing: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_word_spacing(&node_data, node_id, node_state) {
s.push_str(&format!("word-spacing: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_tab_size(&node_data, node_id, node_state) {
s.push_str(&format!("tab-size: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_cursor(&node_data, node_id, node_state) {
s.push_str(&format!("cursor: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_box_shadow_left(&node_data, node_id, node_state) {
s.push_str(&format!(
"-azul-box-shadow-left: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_box_shadow_right(&node_data, node_id, node_state) {
s.push_str(&format!(
"-azul-box-shadow-right: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_box_shadow_top(&node_data, node_id, node_state) {
s.push_str(&format!("-azul-box-shadow-top: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_box_shadow_bottom(&node_data, node_id, node_state) {
s.push_str(&format!(
"-azul-box-shadow-bottom: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_border_top_color(&node_data, node_id, node_state) {
s.push_str(&format!("border-top-color: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_left_color(&node_data, node_id, node_state) {
s.push_str(&format!("border-left-color: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_right_color(&node_data, node_id, node_state) {
s.push_str(&format!("border-right-color: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_bottom_color(&node_data, node_id, node_state) {
s.push_str(&format!("border-bottom-color: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_top_style(&node_data, node_id, node_state) {
s.push_str(&format!("border-top-style: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_left_style(&node_data, node_id, node_state) {
s.push_str(&format!("border-left-style: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_right_style(&node_data, node_id, node_state) {
s.push_str(&format!("border-right-style: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_bottom_style(&node_data, node_id, node_state) {
s.push_str(&format!("border-bottom-style: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_top_left_radius(&node_data, node_id, node_state) {
s.push_str(&format!(
"border-top-left-radius: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_border_top_right_radius(&node_data, node_id, node_state) {
s.push_str(&format!(
"border-top-right-radius: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_border_bottom_left_radius(&node_data, node_id, node_state) {
s.push_str(&format!(
"border-bottom-left-radius: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_border_bottom_right_radius(&node_data, node_id, node_state) {
s.push_str(&format!(
"border-bottom-right-radius: {};",
p.get_css_value_fmt()
));
}
if let Some(p) = self.get_opacity(&node_data, node_id, node_state) {
s.push_str(&format!("opacity: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_transform(&node_data, node_id, node_state) {
s.push_str(&format!("transform: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_transform_origin(&node_data, node_id, node_state) {
s.push_str(&format!("transform-origin: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_perspective_origin(&node_data, node_id, node_state) {
s.push_str(&format!("perspective-origin: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_backface_visibility(&node_data, node_id, node_state) {
s.push_str(&format!("backface-visibility: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_hyphens(&node_data, node_id, node_state) {
s.push_str(&format!("hyphens: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_direction(&node_data, node_id, node_state) {
s.push_str(&format!("direction: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_white_space(&node_data, node_id, node_state) {
s.push_str(&format!("white-space: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_display(&node_data, node_id, node_state) {
s.push_str(&format!("display: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_float(&node_data, node_id, node_state) {
s.push_str(&format!("float: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_box_sizing(&node_data, node_id, node_state) {
s.push_str(&format!("box-sizing: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_width(&node_data, node_id, node_state) {
s.push_str(&format!("width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_height(&node_data, node_id, node_state) {
s.push_str(&format!("height: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_min_width(&node_data, node_id, node_state) {
s.push_str(&format!("min-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_min_height(&node_data, node_id, node_state) {
s.push_str(&format!("min-height: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_max_width(&node_data, node_id, node_state) {
s.push_str(&format!("max-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_max_height(&node_data, node_id, node_state) {
s.push_str(&format!("max-height: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_position(&node_data, node_id, node_state) {
s.push_str(&format!("position: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_top(&node_data, node_id, node_state) {
s.push_str(&format!("top: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_bottom(&node_data, node_id, node_state) {
s.push_str(&format!("bottom: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_right(&node_data, node_id, node_state) {
s.push_str(&format!("right: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_left(&node_data, node_id, node_state) {
s.push_str(&format!("left: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_padding_top(&node_data, node_id, node_state) {
s.push_str(&format!("padding-top: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_padding_bottom(&node_data, node_id, node_state) {
s.push_str(&format!("padding-bottom: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_padding_left(&node_data, node_id, node_state) {
s.push_str(&format!("padding-left: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_padding_right(&node_data, node_id, node_state) {
s.push_str(&format!("padding-right: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_margin_top(&node_data, node_id, node_state) {
s.push_str(&format!("margin-top: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_margin_bottom(&node_data, node_id, node_state) {
s.push_str(&format!("margin-bottom: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_margin_left(&node_data, node_id, node_state) {
s.push_str(&format!("margin-left: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_margin_right(&node_data, node_id, node_state) {
s.push_str(&format!("margin-right: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_top_width(&node_data, node_id, node_state) {
s.push_str(&format!("border-top-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_left_width(&node_data, node_id, node_state) {
s.push_str(&format!("border-left-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_right_width(&node_data, node_id, node_state) {
s.push_str(&format!("border-right-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_border_bottom_width(&node_data, node_id, node_state) {
s.push_str(&format!("border-bottom-width: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_overflow_x(&node_data, node_id, node_state) {
s.push_str(&format!("overflow-x: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_overflow_y(&node_data, node_id, node_state) {
s.push_str(&format!("overflow-y: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_flex_direction(&node_data, node_id, node_state) {
s.push_str(&format!("flex-direction: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_flex_wrap(&node_data, node_id, node_state) {
s.push_str(&format!("flex-wrap: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_flex_grow(&node_data, node_id, node_state) {
s.push_str(&format!("flex-grow: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_flex_shrink(&node_data, node_id, node_state) {
s.push_str(&format!("flex-shrink: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_justify_content(&node_data, node_id, node_state) {
s.push_str(&format!("justify-content: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_align_items(&node_data, node_id, node_state) {
s.push_str(&format!("align-items: {};", p.get_css_value_fmt()));
}
if let Some(p) = self.get_align_content(&node_data, node_id, node_state) {
s.push_str(&format!("align-content: {};", p.get_css_value_fmt()));
}
s
}
}
#[repr(C)]
#[derive(Debug, PartialEq, Clone)]
pub struct CssPropertyCachePtr {
pub ptr: Box<CssPropertyCache>,
pub run_destructor: bool,
}
impl CssPropertyCachePtr {
pub fn new(cache: CssPropertyCache) -> Self {
Self {
ptr: Box::new(cache),
run_destructor: true,
}
}
pub fn downcast_mut<'a>(&'a mut self) -> &'a mut CssPropertyCache {
&mut *self.ptr
}
}
impl Drop for CssPropertyCachePtr {
fn drop(&mut self) {
self.run_destructor = false;
}
}
impl CssPropertyCache {
pub fn empty(node_count: usize) -> Self {
Self {
node_count,
user_overridden_properties: vec![BTreeMap::new(); node_count],
cascaded_normal_props: vec![BTreeMap::new(); node_count],
cascaded_hover_props: vec![BTreeMap::new(); node_count],
cascaded_active_props: vec![BTreeMap::new(); node_count],
cascaded_focus_props: vec![BTreeMap::new(); node_count],
cascaded_dragging_props: vec![BTreeMap::new(); node_count],
cascaded_drag_over_props: vec![BTreeMap::new(); node_count],
css_normal_props: vec![BTreeMap::new(); node_count],
css_hover_props: vec![BTreeMap::new(); node_count],
css_active_props: vec![BTreeMap::new(); node_count],
css_focus_props: vec![BTreeMap::new(); node_count],
css_dragging_props: vec![BTreeMap::new(); node_count],
css_drag_over_props: vec![BTreeMap::new(); node_count],
computed_values: vec![BTreeMap::new(); node_count],
dependency_chains: vec![BTreeMap::new(); node_count],
resolved_cache: Vec::new(),
compact_cache: None,
}
}
pub fn append(&mut self, other: &mut Self) {
macro_rules! append_css_property_vec {
($field_name:ident) => {{
self.$field_name.extend(other.$field_name.drain(..));
}};
}
append_css_property_vec!(user_overridden_properties);
append_css_property_vec!(cascaded_normal_props);
append_css_property_vec!(cascaded_hover_props);
append_css_property_vec!(cascaded_active_props);
append_css_property_vec!(cascaded_focus_props);
append_css_property_vec!(cascaded_dragging_props);
append_css_property_vec!(cascaded_drag_over_props);
append_css_property_vec!(css_normal_props);
append_css_property_vec!(css_hover_props);
append_css_property_vec!(css_active_props);
append_css_property_vec!(css_focus_props);
append_css_property_vec!(css_dragging_props);
append_css_property_vec!(css_drag_over_props);
append_css_property_vec!(computed_values);
for mut chains_map in other.dependency_chains.drain(..) {
for (_prop_type, chain) in chains_map.iter_mut() {
for step in chain.steps.iter_mut() {
match step {
CssDependencyChainStep::Em { source_node, .. } => {
*source_node = NodeId::new(source_node.index() + self.node_count);
}
CssDependencyChainStep::Percent { source_node, .. } => {
*source_node = NodeId::new(source_node.index() + self.node_count);
}
_ => {}
}
}
}
self.dependency_chains.push(chains_map);
}
self.node_count += other.node_count;
self.resolved_cache.clear();
}
pub fn is_horizontal_overflow_visible(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_overflow_x(node_data, node_id, node_state)
.and_then(|p| p.get_property_or_default())
.unwrap_or_default()
.is_overflow_visible()
}
pub fn is_vertical_overflow_visible(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_overflow_y(node_data, node_id, node_state)
.and_then(|p| p.get_property_or_default())
.unwrap_or_default()
.is_overflow_visible()
}
pub fn is_horizontal_overflow_hidden(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_overflow_x(node_data, node_id, node_state)
.and_then(|p| p.get_property_or_default())
.unwrap_or_default()
.is_overflow_hidden()
}
pub fn is_vertical_overflow_hidden(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_overflow_y(node_data, node_id, node_state)
.and_then(|p| p.get_property_or_default())
.unwrap_or_default()
.is_overflow_hidden()
}
pub fn get_text_color_or_default(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> StyleTextColor {
use crate::ui_solver::DEFAULT_TEXT_COLOR;
self.get_text_color(node_data, node_id, node_state)
.and_then(|fs| fs.get_property().cloned())
.unwrap_or(DEFAULT_TEXT_COLOR)
}
pub fn get_font_id_or_default(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> StyleFontFamilyVec {
use crate::ui_solver::DEFAULT_FONT_ID;
let default_font_id = vec![StyleFontFamily::System(AzString::from_const_str(
DEFAULT_FONT_ID,
))]
.into();
let font_family_opt = self.get_font_family(node_data, node_id, node_state);
font_family_opt
.as_ref()
.and_then(|family| Some(family.get_property()?.clone()))
.unwrap_or(default_font_id)
}
pub fn get_font_size_or_default(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> StyleFontSize {
use crate::ui_solver::DEFAULT_FONT_SIZE;
self.get_font_size(node_data, node_id, node_state)
.and_then(|fs| fs.get_property().cloned())
.unwrap_or(DEFAULT_FONT_SIZE)
}
pub fn has_border(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_border_left_width(node_data, node_id, node_state)
.is_some()
|| self
.get_border_right_width(node_data, node_id, node_state)
.is_some()
|| self
.get_border_top_width(node_data, node_id, node_state)
.is_some()
|| self
.get_border_bottom_width(node_data, node_id, node_state)
.is_some()
}
pub fn has_box_shadow(
&self,
node_data: &NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> bool {
self.get_box_shadow_left(node_data, node_id, node_state)
.is_some()
|| self
.get_box_shadow_right(node_data, node_id, node_state)
.is_some()
|| self
.get_box_shadow_top(node_data, node_id, node_state)
.is_some()
|| self
.get_box_shadow_bottom(node_data, node_id, node_state)
.is_some()
}
pub fn get_property<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
css_property_type: &CssPropertyType,
) -> Option<&CssProperty> {
if let Some(node_props) = self.resolved_cache.get(node_id.index()) {
return node_props
.binary_search_by_key(css_property_type, |(t, _)| *t)
.ok()
.map(|i| &node_props[i].1);
}
self.get_property_slow(node_data, node_id, node_state, css_property_type)
}
fn get_property_slow<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
css_property_type: &CssPropertyType,
) -> Option<&CssProperty> {
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
if let Some(p) = self
.user_overridden_properties
.get(node_id.index())
.and_then(|n| n.get(css_property_type))
{
return Some(p);
}
fn matches_pseudo_state(
prop: &azul_css::dynamic_selector::CssPropertyWithConditions,
state: PseudoStateType,
) -> bool {
let conditions = prop.apply_if.as_slice();
if conditions.is_empty() {
state == PseudoStateType::Normal
} else {
conditions
.iter()
.all(|c| matches!(c, DynamicSelector::PseudoState(s) if *s == state))
}
}
if node_state.focused {
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::Focus)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_focus_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_focus_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
}
if node_state.active {
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::Active)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_active_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_active_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
}
if node_state.dragging {
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::Dragging)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_dragging_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_dragging_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
}
if node_state.drag_over {
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::DragOver)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_drag_over_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_drag_over_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
}
if node_state.hover {
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::Hover)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_hover_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_hover_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
}
if let Some(p) = node_data.css_props.as_ref().iter().find_map(|css_prop| {
if matches_pseudo_state(css_prop, PseudoStateType::Normal)
&& css_prop.property.get_type() == *css_property_type
{
Some(&css_prop.property)
} else {
None
}
}) {
return Some(p);
}
if let Some(p) = self
.css_normal_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if let Some(p) = self
.cascaded_normal_props
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(p);
}
if css_property_type.is_inheritable() {
if let Some(prop_with_origin) = self
.computed_values
.get(node_id.index())
.and_then(|map| map.get(css_property_type))
{
return Some(&prop_with_origin.property);
}
}
crate::ua_css::get_ua_property(&node_data.node_type, *css_property_type)
}
pub fn get_property_with_context<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
context: &DynamicSelectorContext,
css_property_type: &CssPropertyType,
) -> Option<&CssProperty> {
if let Some(p) = self
.user_overridden_properties
.get(node_id.index())
.and_then(|n| n.get(css_property_type))
{
return Some(p);
}
if let Some(prop_with_conditions) =
node_data.css_props.as_ref().iter().rev().find(|prop| {
prop.property.get_type() == *css_property_type && prop.matches(context)
})
{
return Some(&prop_with_conditions.property);
}
let legacy_state = StyledNodeState::from_pseudo_state_flags(&context.pseudo_state);
if let Some(p) = self.get_property(node_data, node_id, &legacy_state, css_property_type) {
return Some(p);
}
None
}
pub fn check_properties_changed(
node_data: &NodeData,
old_context: &DynamicSelectorContext,
new_context: &DynamicSelectorContext,
) -> bool {
for prop in node_data.css_props.as_ref().iter() {
let was_active = prop.matches(old_context);
let is_active = prop.matches(new_context);
if was_active != is_active {
return true;
}
}
false
}
pub fn check_layout_properties_changed(
node_data: &NodeData,
old_context: &DynamicSelectorContext,
new_context: &DynamicSelectorContext,
) -> bool {
for prop in node_data.css_props.as_ref().iter() {
if !prop.is_layout_affecting() {
continue;
}
let was_active = prop.matches(old_context);
let is_active = prop.matches(new_context);
if was_active != is_active {
return true;
}
}
false
}
pub fn get_background_content<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBackgroundContentVecValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BackgroundContent,
)
.and_then(|p| p.as_background_content())
}
pub fn get_hyphens<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleHyphensValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Hyphens)
.and_then(|p| p.as_hyphens())
}
pub fn get_direction<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleDirectionValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Direction)
.and_then(|p| p.as_direction())
}
pub fn get_white_space<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleWhiteSpaceValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::WhiteSpace)
.and_then(|p| p.as_white_space())
}
pub fn get_background_position<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBackgroundPositionVecValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BackgroundPosition,
)
.and_then(|p| p.as_background_position())
}
pub fn get_background_size<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBackgroundSizeVecValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BackgroundSize,
)
.and_then(|p| p.as_background_size())
}
pub fn get_background_repeat<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBackgroundRepeatVecValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BackgroundRepeat,
)
.and_then(|p| p.as_background_repeat())
}
pub fn get_font_size<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFontSizeValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FontSize)
.and_then(|p| p.as_font_size())
}
pub fn get_font_family<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFontFamilyVecValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FontFamily)
.and_then(|p| p.as_font_family())
}
pub fn get_font_weight<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFontWeightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FontWeight)
.and_then(|p| p.as_font_weight())
}
pub fn get_font_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFontStyleValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FontStyle)
.and_then(|p| p.as_font_style())
}
pub fn get_text_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTextColorValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::TextColor)
.and_then(|p| p.as_text_color())
}
pub fn get_text_indent<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTextIndentValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::TextIndent)
.and_then(|p| p.as_text_indent())
}
pub fn get_initial_letter<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleInitialLetterValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::InitialLetter,
)
.and_then(|p| p.as_initial_letter())
}
pub fn get_line_clamp<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleLineClampValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::LineClamp)
.and_then(|p| p.as_line_clamp())
}
pub fn get_hanging_punctuation<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleHangingPunctuationValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::HangingPunctuation,
)
.and_then(|p| p.as_hanging_punctuation())
}
pub fn get_text_combine_upright<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTextCombineUprightValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::TextCombineUpright,
)
.and_then(|p| p.as_text_combine_upright())
}
pub fn get_exclusion_margin<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleExclusionMarginValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ExclusionMargin,
)
.and_then(|p| p.as_exclusion_margin())
}
pub fn get_hyphenation_language<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleHyphenationLanguageValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::HyphenationLanguage,
)
.and_then(|p| p.as_hyphenation_language())
}
pub fn get_caret_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a CaretColorValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretColor)
.and_then(|p| p.as_caret_color())
}
pub fn get_caret_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a CaretWidthValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::CaretWidth)
.and_then(|p| p.as_caret_width())
}
pub fn get_caret_animation_duration<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a CaretAnimationDurationValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::CaretAnimationDuration,
)
.and_then(|p| p.as_caret_animation_duration())
}
pub fn get_selection_background_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a SelectionBackgroundColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::SelectionBackgroundColor,
)
.and_then(|p| p.as_selection_background_color())
}
pub fn get_selection_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a SelectionColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::SelectionColor,
)
.and_then(|p| p.as_selection_color())
}
pub fn get_selection_radius<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a SelectionRadiusValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::SelectionRadius,
)
.and_then(|p| p.as_selection_radius())
}
pub fn get_text_justify<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutTextJustifyValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::TextJustify,
)
.and_then(|p| p.as_text_justify())
}
pub fn get_z_index<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutZIndexValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::ZIndex)
.and_then(|p| p.as_z_index())
}
pub fn get_flex_basis<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFlexBasisValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexBasis)
.and_then(|p| p.as_flex_basis())
}
pub fn get_column_gap<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutColumnGapValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnGap)
.and_then(|p| p.as_column_gap())
}
pub fn get_row_gap<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutRowGapValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::RowGap)
.and_then(|p| p.as_row_gap())
}
pub fn get_grid_template_columns<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridTemplateColumnsValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::GridTemplateColumns,
)
.and_then(|p| p.as_grid_template_columns())
}
pub fn get_grid_template_rows<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridTemplateRowsValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::GridTemplateRows,
)
.and_then(|p| p.as_grid_template_rows())
}
pub fn get_grid_auto_columns<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridAutoColumnsValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::GridAutoColumns,
)
.and_then(|p| p.as_grid_auto_columns())
}
pub fn get_grid_auto_rows<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridAutoRowsValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::GridAutoRows,
)
.and_then(|p| p.as_grid_auto_rows())
}
pub fn get_grid_column<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridColumnValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::GridColumn)
.and_then(|p| p.as_grid_column())
}
pub fn get_grid_row<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridRowValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::GridRow)
.and_then(|p| p.as_grid_row())
}
pub fn get_grid_auto_flow<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGridAutoFlowValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::GridAutoFlow,
)
.and_then(|p| p.as_grid_auto_flow())
}
pub fn get_justify_self<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutJustifySelfValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::JustifySelf,
)
.and_then(|p| p.as_justify_self())
}
pub fn get_justify_items<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutJustifyItemsValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::JustifyItems,
)
.and_then(|p| p.as_justify_items())
}
pub fn get_gap<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGapValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Gap)
.and_then(|p| p.as_gap())
}
pub fn get_grid_gap<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutGapValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::GridGap)
.and_then(|p| p.as_grid_gap())
}
pub fn get_align_self<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutAlignSelfValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignSelf)
.and_then(|p| p.as_align_self())
}
pub fn get_font<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFontValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Font)
.and_then(|p| p.as_font())
}
pub fn get_writing_mode<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutWritingModeValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::WritingMode,
)
.and_then(|p| p.as_writing_mode())
}
pub fn get_clear<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutClearValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Clear)
.and_then(|p| p.as_clear())
}
pub fn get_shape_outside<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ShapeOutsideValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ShapeOutside,
)
.and_then(|p| p.as_shape_outside())
}
pub fn get_shape_inside<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ShapeInsideValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ShapeInside,
)
.and_then(|p| p.as_shape_inside())
}
pub fn get_clip_path<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ClipPathValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::ClipPath)
.and_then(|p| p.as_clip_path())
}
pub fn get_scrollbar_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ScrollbarStyleValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Scrollbar)
.and_then(|p| p.as_scrollbar())
}
pub fn get_scrollbar_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutScrollbarWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ScrollbarWidth,
)
.and_then(|p| p.as_scrollbar_width())
}
pub fn get_scrollbar_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleScrollbarColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ScrollbarColor,
)
.and_then(|p| p.as_scrollbar_color())
}
pub fn get_visibility<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleVisibilityValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Visibility)
.and_then(|p| p.as_visibility())
}
pub fn get_break_before<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a PageBreakValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BreakBefore,
)
.and_then(|p| p.as_break_before())
}
pub fn get_break_after<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a PageBreakValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::BreakAfter)
.and_then(|p| p.as_break_after())
}
pub fn get_break_inside<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a BreakInsideValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BreakInside,
)
.and_then(|p| p.as_break_inside())
}
pub fn get_orphans<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a OrphansValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Orphans)
.and_then(|p| p.as_orphans())
}
pub fn get_widows<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a WidowsValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Widows)
.and_then(|p| p.as_widows())
}
pub fn get_box_decoration_break<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a BoxDecorationBreakValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BoxDecorationBreak,
)
.and_then(|p| p.as_box_decoration_break())
}
pub fn get_column_count<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnCountValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ColumnCount,
)
.and_then(|p| p.as_column_count())
}
pub fn get_column_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ColumnWidth,
)
.and_then(|p| p.as_column_width())
}
pub fn get_column_span<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnSpanValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnSpan)
.and_then(|p| p.as_column_span())
}
pub fn get_column_fill<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnFillValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::ColumnFill)
.and_then(|p| p.as_column_fill())
}
pub fn get_column_rule_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnRuleWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ColumnRuleWidth,
)
.and_then(|p| p.as_column_rule_width())
}
pub fn get_column_rule_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnRuleStyleValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ColumnRuleStyle,
)
.and_then(|p| p.as_column_rule_style())
}
pub fn get_column_rule_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ColumnRuleColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ColumnRuleColor,
)
.and_then(|p| p.as_column_rule_color())
}
pub fn get_flow_into<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a FlowIntoValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowInto)
.and_then(|p| p.as_flow_into())
}
pub fn get_flow_from<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a FlowFromValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlowFrom)
.and_then(|p| p.as_flow_from())
}
pub fn get_shape_margin<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ShapeMarginValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ShapeMargin,
)
.and_then(|p| p.as_shape_margin())
}
pub fn get_shape_image_threshold<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ShapeImageThresholdValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ShapeImageThreshold,
)
.and_then(|p| p.as_shape_image_threshold())
}
pub fn get_content<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a ContentValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Content)
.and_then(|p| p.as_content())
}
pub fn get_counter_reset<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a CounterResetValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::CounterReset,
)
.and_then(|p| p.as_counter_reset())
}
pub fn get_counter_increment<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a CounterIncrementValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::CounterIncrement,
)
.and_then(|p| p.as_counter_increment())
}
pub fn get_string_set<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StringSetValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::StringSet)
.and_then(|p| p.as_string_set())
}
pub fn get_text_align<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTextAlignValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::TextAlign)
.and_then(|p| p.as_text_align())
}
pub fn get_user_select<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleUserSelectValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::UserSelect)
.and_then(|p| p.as_user_select())
}
pub fn get_text_decoration<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTextDecorationValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::TextDecoration,
)
.and_then(|p| p.as_text_decoration())
}
pub fn get_vertical_align<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleVerticalAlignValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::VerticalAlign,
)
.and_then(|p| p.as_vertical_align())
}
pub fn get_line_height<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleLineHeightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::LineHeight)
.and_then(|p| p.as_line_height())
}
pub fn get_letter_spacing<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleLetterSpacingValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::LetterSpacing,
)
.and_then(|p| p.as_letter_spacing())
}
pub fn get_word_spacing<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleWordSpacingValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::WordSpacing,
)
.and_then(|p| p.as_word_spacing())
}
pub fn get_tab_size<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTabSizeValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::TabSize)
.and_then(|p| p.as_tab_size())
}
pub fn get_cursor<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleCursorValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Cursor)
.and_then(|p| p.as_cursor())
}
pub fn get_box_shadow_left<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBoxShadowValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BoxShadowLeft,
)
.and_then(|p| p.as_box_shadow_left())
}
pub fn get_box_shadow_right<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBoxShadowValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BoxShadowRight,
)
.and_then(|p| p.as_box_shadow_right())
}
pub fn get_box_shadow_top<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBoxShadowValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BoxShadowTop,
)
.and_then(|p| p.as_box_shadow_top())
}
pub fn get_box_shadow_bottom<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBoxShadowValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BoxShadowBottom,
)
.and_then(|p| p.as_box_shadow_bottom())
}
pub fn get_border_top_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderTopColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderTopColor,
)
.and_then(|p| p.as_border_top_color())
}
pub fn get_border_left_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderLeftColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderLeftColor,
)
.and_then(|p| p.as_border_left_color())
}
pub fn get_border_right_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderRightColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderRightColor,
)
.and_then(|p| p.as_border_right_color())
}
pub fn get_border_bottom_color<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderBottomColorValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderBottomColor,
)
.and_then(|p| p.as_border_bottom_color())
}
pub fn get_border_top_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderTopStyleValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderTopStyle,
)
.and_then(|p| p.as_border_top_style())
}
pub fn get_border_left_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderLeftStyleValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderLeftStyle,
)
.and_then(|p| p.as_border_left_style())
}
pub fn get_border_right_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderRightStyleValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderRightStyle,
)
.and_then(|p| p.as_border_right_style())
}
pub fn get_border_bottom_style<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderBottomStyleValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderBottomStyle,
)
.and_then(|p| p.as_border_bottom_style())
}
pub fn get_border_top_left_radius<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderTopLeftRadiusValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderTopLeftRadius,
)
.and_then(|p| p.as_border_top_left_radius())
}
pub fn get_border_top_right_radius<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderTopRightRadiusValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderTopRightRadius,
)
.and_then(|p| p.as_border_top_right_radius())
}
pub fn get_border_bottom_left_radius<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderBottomLeftRadiusValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderBottomLeftRadius,
)
.and_then(|p| p.as_border_bottom_left_radius())
}
pub fn get_border_bottom_right_radius<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderBottomRightRadiusValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderBottomRightRadius,
)
.and_then(|p| p.as_border_bottom_right_radius())
}
pub fn get_opacity<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleOpacityValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Opacity)
.and_then(|p| p.as_opacity())
}
pub fn get_transform<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTransformVecValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Transform)
.and_then(|p| p.as_transform())
}
pub fn get_transform_origin<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleTransformOriginValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::TransformOrigin,
)
.and_then(|p| p.as_transform_origin())
}
pub fn get_perspective_origin<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StylePerspectiveOriginValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::PerspectiveOrigin,
)
.and_then(|p| p.as_perspective_origin())
}
pub fn get_backface_visibility<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBackfaceVisibilityValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BackfaceVisibility,
)
.and_then(|p| p.as_backface_visibility())
}
pub fn get_display<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutDisplayValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Display)
.and_then(|p| p.as_display())
}
pub fn get_float<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFloatValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Float)
.and_then(|p| p.as_float())
}
pub fn get_box_sizing<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBoxSizingValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::BoxSizing)
.and_then(|p| p.as_box_sizing())
}
pub fn get_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutWidthValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Width)
.and_then(|p| p.as_width())
}
pub fn get_height<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutHeightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Height)
.and_then(|p| p.as_height())
}
pub fn get_min_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMinWidthValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MinWidth)
.and_then(|p| p.as_min_width())
}
pub fn get_min_height<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMinHeightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MinHeight)
.and_then(|p| p.as_min_height())
}
pub fn get_max_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMaxWidthValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxWidth)
.and_then(|p| p.as_max_width())
}
pub fn get_max_height<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMaxHeightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MaxHeight)
.and_then(|p| p.as_max_height())
}
pub fn get_position<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutPositionValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Position)
.and_then(|p| p.as_position())
}
pub fn get_top<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutTopValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Top)
.and_then(|p| p.as_top())
}
pub fn get_bottom<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutInsetBottomValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Bottom)
.and_then(|p| p.as_bottom())
}
pub fn get_right<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutRightValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Right)
.and_then(|p| p.as_right())
}
pub fn get_left<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutLeftValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Left)
.and_then(|p| p.as_left())
}
pub fn get_padding_top<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutPaddingTopValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::PaddingTop)
.and_then(|p| p.as_padding_top())
}
pub fn get_padding_bottom<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutPaddingBottomValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::PaddingBottom,
)
.and_then(|p| p.as_padding_bottom())
}
pub fn get_padding_left<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutPaddingLeftValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::PaddingLeft,
)
.and_then(|p| p.as_padding_left())
}
pub fn get_padding_right<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutPaddingRightValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::PaddingRight,
)
.and_then(|p| p.as_padding_right())
}
pub fn get_margin_top<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMarginTopValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginTop)
.and_then(|p| p.as_margin_top())
}
pub fn get_margin_bottom<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMarginBottomValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::MarginBottom,
)
.and_then(|p| p.as_margin_bottom())
}
pub fn get_margin_left<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMarginLeftValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::MarginLeft)
.and_then(|p| p.as_margin_left())
}
pub fn get_margin_right<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutMarginRightValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::MarginRight,
)
.and_then(|p| p.as_margin_right())
}
pub fn get_border_top_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBorderTopWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderTopWidth,
)
.and_then(|p| p.as_border_top_width())
}
pub fn get_border_left_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBorderLeftWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderLeftWidth,
)
.and_then(|p| p.as_border_left_width())
}
pub fn get_border_right_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBorderRightWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderRightWidth,
)
.and_then(|p| p.as_border_right_width())
}
pub fn get_border_bottom_width<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBorderBottomWidthValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderBottomWidth,
)
.and_then(|p| p.as_border_bottom_width())
}
pub fn get_overflow_x<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutOverflowValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowX)
.and_then(|p| p.as_overflow_x())
}
pub fn get_overflow_y<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutOverflowValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::OverflowY)
.and_then(|p| p.as_overflow_y())
}
pub fn get_flex_direction<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFlexDirectionValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::FlexDirection,
)
.and_then(|p| p.as_flex_direction())
}
pub fn get_flex_wrap<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFlexWrapValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexWrap)
.and_then(|p| p.as_flex_wrap())
}
pub fn get_flex_grow<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFlexGrowValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexGrow)
.and_then(|p| p.as_flex_grow())
}
pub fn get_flex_shrink<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutFlexShrinkValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::FlexShrink)
.and_then(|p| p.as_flex_shrink())
}
pub fn get_justify_content<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutJustifyContentValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::JustifyContent,
)
.and_then(|p| p.as_justify_content())
}
pub fn get_align_items<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutAlignItemsValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::AlignItems)
.and_then(|p| p.as_align_items())
}
pub fn get_align_content<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutAlignContentValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::AlignContent,
)
.and_then(|p| p.as_align_content())
}
pub fn get_mix_blend_mode<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleMixBlendModeValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::MixBlendMode,
)
.and_then(|p| p.as_mix_blend_mode())
}
pub fn get_filter<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFilterVecValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::Filter)
.and_then(|p| p.as_filter())
}
pub fn get_backdrop_filter<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleFilterVecValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::BackdropFilter)
.and_then(|p| p.as_backdrop_filter())
}
pub fn get_text_shadow<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBoxShadowValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::TextShadow)
.and_then(|p| p.as_text_shadow())
}
pub fn get_list_style_type<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleListStyleTypeValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ListStyleType,
)
.and_then(|p| p.as_list_style_type())
}
pub fn get_list_style_position<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleListStylePositionValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::ListStylePosition,
)
.and_then(|p| p.as_list_style_position())
}
pub fn get_table_layout<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutTableLayoutValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::TableLayout,
)
.and_then(|p| p.as_table_layout())
}
pub fn get_border_collapse<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleBorderCollapseValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderCollapse,
)
.and_then(|p| p.as_border_collapse())
}
pub fn get_border_spacing<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a LayoutBorderSpacingValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::BorderSpacing,
)
.and_then(|p| p.as_border_spacing())
}
pub fn get_caption_side<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleCaptionSideValue> {
self.get_property(
node_data,
node_id,
node_state,
&CssPropertyType::CaptionSide,
)
.and_then(|p| p.as_caption_side())
}
pub fn get_empty_cells<'a>(
&'a self,
node_data: &'a NodeData,
node_id: &NodeId,
node_state: &StyledNodeState,
) -> Option<&'a StyleEmptyCellsValue> {
self.get_property(node_data, node_id, node_state, &CssPropertyType::EmptyCells)
.and_then(|p| p.as_empty_cells())
}
pub fn calc_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_width(node_data, node_id, styled_node_state)
.and_then(|w| match w.get_property()? {
LayoutWidth::Px(px) => Some(px.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
)),
_ => Some(0.0), })
.unwrap_or(0.0)
}
pub fn calc_min_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_min_width(node_data, node_id, styled_node_state)
.and_then(|w| {
Some(w.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_max_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> Option<f32> {
self.get_max_width(node_data, node_id, styled_node_state)
.and_then(|w| {
Some(w.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_height(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_height(node_data, node_id, styled_node_state)
.and_then(|h| match h.get_property()? {
LayoutHeight::Px(px) => Some(px.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
)),
_ => Some(0.0), })
.unwrap_or(0.0)
}
pub fn calc_min_height(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_min_height(node_data, node_id, styled_node_state)
.and_then(|h| {
Some(h.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_max_height(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> Option<f32> {
self.get_max_height(node_data, node_id, styled_node_state)
.and_then(|h| {
Some(h.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_left(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> Option<f32> {
self.get_left(node_data, node_id, styled_node_state)
.and_then(|l| {
Some(l.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_right(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> Option<f32> {
self.get_right(node_data, node_id, styled_node_state)
.and_then(|r| {
Some(r.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_top(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> Option<f32> {
self.get_top(node_data, node_id, styled_node_state)
.and_then(|t| {
Some(t.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_bottom(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> Option<f32> {
self.get_bottom(node_data, node_id, styled_node_state)
.and_then(|b| {
Some(b.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
}
pub fn calc_border_left_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_border_left_width(node_data, node_id, styled_node_state)
.and_then(|b| {
Some(b.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_border_right_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_border_right_width(node_data, node_id, styled_node_state)
.and_then(|b| {
Some(b.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_border_top_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_border_top_width(node_data, node_id, styled_node_state)
.and_then(|b| {
Some(b.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_border_bottom_width(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_border_bottom_width(node_data, node_id, styled_node_state)
.and_then(|b| {
Some(b.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_padding_left(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_padding_left(node_data, node_id, styled_node_state)
.and_then(|p| {
Some(p.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_padding_right(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_padding_right(node_data, node_id, styled_node_state)
.and_then(|p| {
Some(p.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_padding_top(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_padding_top(node_data, node_id, styled_node_state)
.and_then(|p| {
Some(p.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_padding_bottom(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_padding_bottom(node_data, node_id, styled_node_state)
.and_then(|p| {
Some(p.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_margin_left(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_margin_left(node_data, node_id, styled_node_state)
.and_then(|m| {
Some(m.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_margin_right(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_width: f32,
) -> f32 {
self.get_margin_right(node_data, node_id, styled_node_state)
.and_then(|m| {
Some(m.get_property()?.inner.to_pixels_internal(
reference_width,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_margin_top(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_margin_top(node_data, node_id, styled_node_state)
.and_then(|m| {
Some(m.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
pub fn calc_margin_bottom(
&self,
node_data: &NodeData,
node_id: &NodeId,
styled_node_state: &StyledNodeState,
reference_height: f32,
) -> f32 {
self.get_margin_bottom(node_data, node_id, styled_node_state)
.and_then(|m| {
Some(m.get_property()?.inner.to_pixels_internal(
reference_height,
azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
))
})
.unwrap_or(0.0)
}
fn resolve_cascade_keyword(
property: &CssProperty,
property_type: CssPropertyType,
_node_type: &crate::dom::NodeType,
parent_value: Option<&CssProperty>,
ua_value: Option<&'static CssProperty>,
) -> Option<CssProperty> {
if property_type.is_inheritable() {
return parent_value.cloned().or_else(|| ua_value.cloned());
} else {
return ua_value.cloned();
}
}
fn resolve_property_dependency(
target_property: &CssProperty,
reference_property: &CssProperty,
) -> Option<CssProperty> {
use azul_css::{
css::CssPropertyValue,
props::{
basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
layout::*,
style::{SelectionRadius, StyleLetterSpacing, StyleWordSpacing},
},
};
let get_pixel_value = |prop: &CssProperty| -> Option<PixelValue> {
match prop {
CssProperty::FontSize(val) => val.get_property().map(|v| v.inner),
CssProperty::LetterSpacing(val) => val.get_property().map(|v| v.inner),
CssProperty::WordSpacing(val) => val.get_property().map(|v| v.inner),
CssProperty::PaddingLeft(val) => val.get_property().map(|v| v.inner),
CssProperty::PaddingRight(val) => val.get_property().map(|v| v.inner),
CssProperty::PaddingTop(val) => val.get_property().map(|v| v.inner),
CssProperty::PaddingBottom(val) => val.get_property().map(|v| v.inner),
CssProperty::MarginLeft(val) => val.get_property().map(|v| v.inner),
CssProperty::MarginRight(val) => val.get_property().map(|v| v.inner),
CssProperty::MarginTop(val) => val.get_property().map(|v| v.inner),
CssProperty::MarginBottom(val) => val.get_property().map(|v| v.inner),
CssProperty::MinWidth(val) => val.get_property().map(|v| v.inner),
CssProperty::MinHeight(val) => val.get_property().map(|v| v.inner),
CssProperty::MaxWidth(val) => val.get_property().map(|v| v.inner),
CssProperty::MaxHeight(val) => val.get_property().map(|v| v.inner),
CssProperty::SelectionRadius(val) => val.get_property().map(|v| v.inner),
_ => None,
}
};
let target_pixel_value = get_pixel_value(target_property)?;
let reference_pixel_value = get_pixel_value(reference_property)?;
let reference_px = match reference_pixel_value.metric {
SizeMetric::Px => reference_pixel_value.number.get(),
SizeMetric::Pt => reference_pixel_value.number.get() * 1.333333,
SizeMetric::In => reference_pixel_value.number.get() * 96.0,
SizeMetric::Cm => reference_pixel_value.number.get() * 37.7952755906,
SizeMetric::Mm => reference_pixel_value.number.get() * 3.7795275591,
SizeMetric::Em => return None, SizeMetric::Rem => return None, SizeMetric::Percent => return None, SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
};
let resolved_px = match target_pixel_value.metric {
SizeMetric::Px => target_pixel_value.number.get(),
SizeMetric::Pt => target_pixel_value.number.get() * 1.333333,
SizeMetric::In => target_pixel_value.number.get() * 96.0,
SizeMetric::Cm => target_pixel_value.number.get() * 37.7952755906,
SizeMetric::Mm => target_pixel_value.number.get() * 3.7795275591,
SizeMetric::Em => target_pixel_value.number.get() * reference_px,
SizeMetric::Rem => target_pixel_value.number.get() * reference_px,
SizeMetric::Percent => target_pixel_value.number.get() / 100.0 * reference_px,
SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => return None,
};
let resolved_pixel_value = PixelValue::px(resolved_px);
match target_property {
CssProperty::FontSize(_) => Some(CssProperty::FontSize(CssPropertyValue::Exact(
StyleFontSize {
inner: resolved_pixel_value,
},
))),
CssProperty::LetterSpacing(_) => Some(CssProperty::LetterSpacing(
CssPropertyValue::Exact(StyleLetterSpacing {
inner: resolved_pixel_value,
}),
)),
CssProperty::WordSpacing(_) => Some(CssProperty::WordSpacing(CssPropertyValue::Exact(
StyleWordSpacing {
inner: resolved_pixel_value,
},
))),
CssProperty::PaddingLeft(_) => Some(CssProperty::PaddingLeft(CssPropertyValue::Exact(
LayoutPaddingLeft {
inner: resolved_pixel_value,
},
))),
CssProperty::PaddingRight(_) => Some(CssProperty::PaddingRight(
CssPropertyValue::Exact(LayoutPaddingRight {
inner: resolved_pixel_value,
}),
)),
CssProperty::PaddingTop(_) => Some(CssProperty::PaddingTop(CssPropertyValue::Exact(
LayoutPaddingTop {
inner: resolved_pixel_value,
},
))),
CssProperty::PaddingBottom(_) => Some(CssProperty::PaddingBottom(
CssPropertyValue::Exact(LayoutPaddingBottom {
inner: resolved_pixel_value,
}),
)),
CssProperty::MarginLeft(_) => Some(CssProperty::MarginLeft(CssPropertyValue::Exact(
LayoutMarginLeft {
inner: resolved_pixel_value,
},
))),
CssProperty::MarginRight(_) => Some(CssProperty::MarginRight(CssPropertyValue::Exact(
LayoutMarginRight {
inner: resolved_pixel_value,
},
))),
CssProperty::MarginTop(_) => Some(CssProperty::MarginTop(CssPropertyValue::Exact(
LayoutMarginTop {
inner: resolved_pixel_value,
},
))),
CssProperty::MarginBottom(_) => Some(CssProperty::MarginBottom(
CssPropertyValue::Exact(LayoutMarginBottom {
inner: resolved_pixel_value,
}),
)),
CssProperty::MinWidth(_) => Some(CssProperty::MinWidth(CssPropertyValue::Exact(
LayoutMinWidth {
inner: resolved_pixel_value,
},
))),
CssProperty::MinHeight(_) => Some(CssProperty::MinHeight(CssPropertyValue::Exact(
LayoutMinHeight {
inner: resolved_pixel_value,
},
))),
CssProperty::MaxWidth(_) => Some(CssProperty::MaxWidth(CssPropertyValue::Exact(
LayoutMaxWidth {
inner: resolved_pixel_value,
},
))),
CssProperty::MaxHeight(_) => Some(CssProperty::MaxHeight(CssPropertyValue::Exact(
LayoutMaxHeight {
inner: resolved_pixel_value,
},
))),
CssProperty::SelectionRadius(_) => Some(CssProperty::SelectionRadius(
CssPropertyValue::Exact(SelectionRadius {
inner: resolved_pixel_value,
}),
)),
_ => None,
}
}
fn build_dependency_chain(
&self,
node_id: NodeId,
parent_id: Option<NodeId>,
property: &CssProperty,
) -> Option<CssDependencyChain> {
use azul_css::props::basic::{length::SizeMetric, pixel::PixelValue};
let prop_type = property.get_type();
if prop_type != CssPropertyType::FontSize {
return None;
}
let pixel_value = match property {
CssProperty::FontSize(val) => val.get_property().map(|v| &v.inner)?,
_ => return None,
};
let number = pixel_value.number.get();
let source_node = parent_id?;
match pixel_value.metric {
SizeMetric::Px => Some(CssDependencyChain::absolute(prop_type, number)),
SizeMetric::Pt => {
Some(CssDependencyChain::absolute(prop_type, number * 1.333333))
}
SizeMetric::Em => Some(CssDependencyChain::em(prop_type, source_node, number)),
SizeMetric::Rem => {
Some(CssDependencyChain::rem(prop_type, number))
}
SizeMetric::Percent => Some(CssDependencyChain::percent(
prop_type,
source_node,
number / 100.0,
)),
SizeMetric::In => {
Some(CssDependencyChain::absolute(prop_type, number * 96.0))
}
SizeMetric::Cm => {
Some(CssDependencyChain::absolute(
prop_type,
number * 37.7952755906,
))
}
SizeMetric::Mm => {
Some(CssDependencyChain::absolute(
prop_type,
number * 3.7795275591,
))
}
SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
None
}
}
}
pub fn apply_ua_css(&mut self, node_data: &[NodeData]) {
use azul_css::props::property::CssPropertyType;
for (node_index, node) in node_data.iter().enumerate() {
let node_id = NodeId::new(node_index);
let node_type = &node.node_type;
let property_types = [
CssPropertyType::Display,
CssPropertyType::Width,
CssPropertyType::Height,
CssPropertyType::FontSize,
CssPropertyType::FontWeight,
CssPropertyType::FontFamily,
CssPropertyType::MarginTop,
CssPropertyType::MarginBottom,
CssPropertyType::MarginLeft,
CssPropertyType::MarginRight,
CssPropertyType::PaddingTop,
CssPropertyType::PaddingBottom,
CssPropertyType::PaddingLeft,
CssPropertyType::PaddingRight,
];
for prop_type in &property_types {
if let Some(ua_prop) = crate::ua_css::get_ua_property(node_type, *prop_type) {
let has_inline = node.css_props.iter().any(|p| {
let is_normal = p.apply_if.as_slice().is_empty();
is_normal && p.property.get_type() == *prop_type
});
let has_css = self
.css_normal_props
.get(node_id.index())
.map(|map| map.contains_key(prop_type))
.unwrap_or(false);
let has_cascaded = self
.cascaded_normal_props
.get(node_id.index())
.map(|map| map.contains_key(prop_type))
.unwrap_or(false);
if !has_inline && !has_css && !has_cascaded {
self.cascaded_normal_props[node_id.index()]
.entry(*prop_type)
.or_insert_with(|| ua_prop.clone());
}
}
}
}
}
pub fn compute_inherited_values(
&mut self,
node_hierarchy: &[NodeHierarchyItem],
node_data: &[NodeData],
) -> Vec<NodeId> {
node_hierarchy
.iter()
.enumerate()
.filter_map(|(node_index, hierarchy_item)| {
let node_id = NodeId::new(node_index);
let parent_id = hierarchy_item.parent_id();
let parent_computed =
parent_id.and_then(|pid| self.computed_values.get(pid.index()).cloned());
let mut ctx = InheritanceContext {
node_id,
parent_id,
computed_values: BTreeMap::new(),
dependency_chains: BTreeMap::new(),
};
if let Some(ref parent_values) = parent_computed {
self.inherit_from_parent(&mut ctx, parent_values);
}
self.apply_cascade_properties(
&mut ctx,
node_id,
&parent_computed,
node_data,
node_index,
);
let changed = self.store_if_changed(&ctx);
changed.then_some(node_id)
})
.collect()
}
fn inherit_from_parent(
&self,
ctx: &mut InheritanceContext,
parent_values: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
) {
for (prop_type, prop_with_origin) in
parent_values.iter().filter(|(pt, _)| pt.is_inheritable())
{
ctx.computed_values.insert(
*prop_type,
CssPropertyWithOrigin {
property: prop_with_origin.property.clone(),
origin: CssPropertyOrigin::Inherited,
},
);
if *prop_type == CssPropertyType::FontSize {
continue;
}
if let Some(chain) = ctx
.parent_id
.and_then(|pid| self.dependency_chains.get(pid.index()))
.and_then(|chains| chains.get(prop_type))
{
ctx.dependency_chains.insert(*prop_type, chain.clone());
}
}
}
fn apply_cascade_properties(
&self,
ctx: &mut InheritanceContext,
node_id: NodeId,
parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
node_data: &[NodeData],
node_index: usize,
) {
if let Some(cascaded_props) = self.cascaded_normal_props.get(node_id.index()).cloned() {
for (prop_type, prop) in cascaded_props.iter() {
if self.should_apply_cascaded(&ctx.computed_values, *prop_type, prop) {
self.process_property(ctx, prop, parent_computed);
}
}
}
if let Some(css_props) = self.css_normal_props.get(node_id.index()) {
for (_, prop) in css_props.iter() {
self.process_property(ctx, prop, parent_computed);
}
}
for inline_prop in node_data[node_index].css_props.iter() {
if inline_prop.apply_if.as_slice().is_empty() {
self.process_property(ctx, &inline_prop.property, parent_computed);
}
}
if let Some(user_props) = self.user_overridden_properties.get(node_id.index()) {
for (_, prop) in user_props.iter() {
self.process_property(ctx, prop, parent_computed);
}
}
}
fn should_apply_cascaded(
&self,
computed: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
prop_type: CssPropertyType,
prop: &CssProperty,
) -> bool {
if prop_type == CssPropertyType::FontSize {
if let Some(existing) = computed.get(&prop_type) {
if existing.origin == CssPropertyOrigin::Inherited
&& Self::has_relative_font_size_unit(prop)
{
return false;
}
}
}
match computed.get(&prop_type) {
None => true,
Some(existing) => existing.origin == CssPropertyOrigin::Inherited,
}
}
fn process_property(
&self,
ctx: &mut InheritanceContext,
prop: &CssProperty,
parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
) {
let prop_type = prop.get_type();
let resolved = if prop_type == CssPropertyType::FontSize {
self.resolve_font_size_property(prop, parent_computed)
} else {
self.resolve_other_property(prop, &ctx.computed_values)
};
ctx.computed_values.insert(
prop_type,
CssPropertyWithOrigin {
property: resolved.clone(),
origin: CssPropertyOrigin::Own,
},
);
if let Some(chain) = self.build_dependency_chain(ctx.node_id, ctx.parent_id, &resolved) {
ctx.dependency_chains.insert(prop_type, chain);
}
}
fn resolve_font_size_property(
&self,
prop: &CssProperty,
parent_computed: &Option<BTreeMap<CssPropertyType, CssPropertyWithOrigin>>,
) -> CssProperty {
const DEFAULT_FONT_SIZE_PX: f32 = 16.0;
let parent_font_size = parent_computed
.as_ref()
.and_then(|p| p.get(&CssPropertyType::FontSize));
match parent_font_size {
Some(pfs) => Self::resolve_property_dependency(prop, &pfs.property)
.unwrap_or_else(|| Self::resolve_font_size_to_pixels(prop, DEFAULT_FONT_SIZE_PX)),
None => Self::resolve_font_size_to_pixels(prop, DEFAULT_FONT_SIZE_PX),
}
}
fn resolve_other_property(
&self,
prop: &CssProperty,
computed: &BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
) -> CssProperty {
computed
.get(&CssPropertyType::FontSize)
.and_then(|fs| Self::resolve_property_dependency(prop, &fs.property))
.unwrap_or_else(|| prop.clone())
}
fn resolve_font_size_to_pixels(prop: &CssProperty, reference_px: f32) -> CssProperty {
use azul_css::{
css::CssPropertyValue,
props::basic::{font::StyleFontSize, length::SizeMetric, pixel::PixelValue},
};
const DEFAULT_FONT_SIZE_PX: f32 = 16.0;
let CssProperty::FontSize(css_val) = prop else {
return prop.clone();
};
let Some(font_size) = css_val.get_property() else {
return prop.clone();
};
let resolved_px = match font_size.inner.metric {
SizeMetric::Px => font_size.inner.number.get(),
SizeMetric::Pt => font_size.inner.number.get() * 1.333333,
SizeMetric::In => font_size.inner.number.get() * 96.0,
SizeMetric::Cm => font_size.inner.number.get() * 37.7952755906,
SizeMetric::Mm => font_size.inner.number.get() * 3.7795275591,
SizeMetric::Em => font_size.inner.number.get() * reference_px,
SizeMetric::Rem => font_size.inner.number.get() * DEFAULT_FONT_SIZE_PX,
SizeMetric::Percent => font_size.inner.number.get() / 100.0 * reference_px,
SizeMetric::Vw | SizeMetric::Vh | SizeMetric::Vmin | SizeMetric::Vmax => {
return prop.clone();
}
};
CssProperty::FontSize(CssPropertyValue::Exact(StyleFontSize {
inner: PixelValue::px(resolved_px),
}))
}
fn has_relative_font_size_unit(prop: &CssProperty) -> bool {
use azul_css::props::basic::length::SizeMetric;
let CssProperty::FontSize(css_val) = prop else {
return false;
};
css_val
.get_property()
.map(|fs| {
matches!(
fs.inner.metric,
SizeMetric::Em | SizeMetric::Rem | SizeMetric::Percent
)
})
.unwrap_or(false)
}
fn store_if_changed(&mut self, ctx: &InheritanceContext) -> bool {
let values_changed = self
.computed_values
.get(ctx.node_id.index())
.map(|old| old != &ctx.computed_values)
.unwrap_or(true);
let chains_changed = self
.dependency_chains
.get(ctx.node_id.index())
.map(|old| old != &ctx.dependency_chains)
.unwrap_or(!ctx.dependency_chains.is_empty());
let changed = values_changed || chains_changed;
self.computed_values[ctx.node_id.index()] = ctx.computed_values.clone();
if !ctx.dependency_chains.is_empty() {
self.dependency_chains[ctx.node_id.index()] = ctx.dependency_chains.clone();
}
changed
}
}
struct InheritanceContext {
node_id: NodeId,
parent_id: Option<NodeId>,
computed_values: BTreeMap<CssPropertyType, CssPropertyWithOrigin>,
dependency_chains: BTreeMap<CssPropertyType, CssDependencyChain>,
}
impl CssPropertyCache {
pub fn resolve_dependency_chain(
&self,
node_id: NodeId,
property_type: CssPropertyType,
root_font_size: f32,
) -> Option<f32> {
let chain = self.get_chain(node_id, property_type)?;
if let Some(cached) = chain.cached_pixels {
return Some(cached);
}
chain.steps.clone().iter().try_fold(None, |_, step| {
Some(self.resolve_step(step, property_type, root_font_size))
})?
}
fn get_chain(
&self,
node_id: NodeId,
property_type: CssPropertyType,
) -> Option<&CssDependencyChain> {
self.dependency_chains
.get(node_id.index())
.and_then(|chains| chains.get(&property_type))
}
fn resolve_step(
&self,
step: &CssDependencyChainStep,
property_type: CssPropertyType,
root_font_size: f32,
) -> Option<f32> {
match step {
CssDependencyChainStep::Absolute { pixels } => Some(*pixels),
CssDependencyChainStep::Percent {
source_node,
factor,
} => {
let source_val = self.get_cached_pixels(*source_node, property_type)?;
Some(source_val * factor)
}
CssDependencyChainStep::Em {
source_node,
factor,
} => {
let font_size = self.get_cached_pixels(*source_node, CssPropertyType::FontSize)?;
Some(font_size * factor)
}
CssDependencyChainStep::Rem { factor } => Some(root_font_size * factor),
}
}
fn get_cached_pixels(&self, node_id: NodeId, property_type: CssPropertyType) -> Option<f32> {
self.get_chain(node_id, property_type)
.and_then(|chain| chain.cached_pixels)
}
pub fn update_property_and_invalidate_dependents(
&mut self,
node_id: NodeId,
property: CssProperty,
node_hierarchy: &[NodeHierarchyItem],
_node_data: &[NodeData],
) -> Vec<NodeId> {
let prop_type = property.get_type();
self.update_computed_property(node_id, property.clone());
self.rebuild_dependency_chain(node_id, prop_type, &property, node_hierarchy);
let mut affected = self.invalidate_dependents(node_id);
affected.push(node_id);
affected
}
fn update_computed_property(&mut self, node_id: NodeId, property: CssProperty) {
self.computed_values[node_id.index()]
.insert(
property.get_type(),
CssPropertyWithOrigin {
property,
origin: CssPropertyOrigin::Own,
},
);
}
fn rebuild_dependency_chain(
&mut self,
node_id: NodeId,
prop_type: CssPropertyType,
property: &CssProperty,
node_hierarchy: &[NodeHierarchyItem],
) {
let parent_id = node_hierarchy
.get(node_id.index())
.and_then(|h| h.parent_id());
if let Some(chain) = self.build_dependency_chain(node_id, parent_id, property) {
self.dependency_chains[node_id.index()]
.insert(prop_type, chain);
}
}
fn invalidate_dependents(&mut self, node_id: NodeId) -> Vec<NodeId> {
self.dependency_chains
.iter_mut()
.enumerate()
.filter_map(|(dep_node_idx, chains)| {
let affected = chains.values_mut().any(|chain| {
if chain.depends_on(node_id) {
chain.cached_pixels = None;
true
} else {
false
}
});
affected.then_some(NodeId::new(dep_node_idx))
})
.collect()
}
const UA_PROPERTY_TYPES: &'static [CssPropertyType] = &[
CssPropertyType::Display,
CssPropertyType::Width,
CssPropertyType::Height,
CssPropertyType::FontSize,
CssPropertyType::FontWeight,
CssPropertyType::FontFamily,
CssPropertyType::MarginTop,
CssPropertyType::MarginBottom,
CssPropertyType::MarginLeft,
CssPropertyType::MarginRight,
CssPropertyType::PaddingTop,
CssPropertyType::PaddingBottom,
CssPropertyType::PaddingLeft,
CssPropertyType::PaddingRight,
CssPropertyType::BreakInside,
CssPropertyType::BreakBefore,
CssPropertyType::BreakAfter,
CssPropertyType::BorderCollapse,
CssPropertyType::BorderSpacing,
CssPropertyType::TextAlign,
CssPropertyType::VerticalAlign,
CssPropertyType::ListStyleType,
CssPropertyType::ListStylePosition,
];
pub fn build_resolved_cache(
&self,
node_data: &[NodeData],
styled_nodes: &[crate::styled_dom::StyledNode],
) -> Vec<Vec<(CssPropertyType, CssProperty)>> {
use alloc::collections::BTreeSet;
use azul_css::dynamic_selector::{DynamicSelector, PseudoStateType};
let node_count = node_data.len();
let mut resolved = Vec::with_capacity(node_count);
for node_index in 0..node_count {
let node_id = NodeId::new(node_index);
let nd = &node_data[node_index];
let node_state = &styled_nodes[node_index].styled_node_state;
let mut prop_types = BTreeSet::new();
if let Some(map) = self.user_overridden_properties.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
for css_prop in nd.css_props.iter() {
let conditions = css_prop.apply_if.as_slice();
let matches = if conditions.is_empty() {
true
} else {
conditions.iter().all(|c| match c {
DynamicSelector::PseudoState(PseudoStateType::Hover) => node_state.hover,
DynamicSelector::PseudoState(PseudoStateType::Active) => node_state.active,
DynamicSelector::PseudoState(PseudoStateType::Focus) => node_state.focused,
DynamicSelector::PseudoState(PseudoStateType::Dragging) => node_state.dragging,
DynamicSelector::PseudoState(PseudoStateType::DragOver) => node_state.drag_over,
_ => false,
})
};
if matches {
prop_types.insert(css_prop.property.get_type());
}
}
if let Some(map) = self.css_normal_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_normal_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.computed_values.get(node_id.index()) {
for pt in map.keys() {
if pt.is_inheritable() {
prop_types.insert(*pt);
}
}
}
if node_state.hover {
if let Some(map) = self.css_hover_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_hover_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.active {
if let Some(map) = self.css_active_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_active_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.focused {
if let Some(map) = self.css_focus_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_focus_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.dragging {
if let Some(map) = self.css_dragging_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_dragging_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.drag_over {
if let Some(map) = self.css_drag_over_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_drag_over_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
for pt in Self::UA_PROPERTY_TYPES {
if crate::ua_css::get_ua_property(&nd.node_type, *pt).is_some() {
prop_types.insert(*pt);
}
}
let mut props: Vec<(CssPropertyType, CssProperty)> =
Vec::with_capacity(prop_types.len());
for prop_type in &prop_types {
if let Some(prop) = self.get_property_slow(nd, &node_id, node_state, prop_type) {
props.push((*prop_type, prop.clone()));
}
}
resolved.push(props);
}
resolved
}
pub fn invalidate_resolved_node(
&mut self,
node_id: NodeId,
node_data: &NodeData,
styled_node: &crate::styled_dom::StyledNode,
) {
let idx = node_id.index();
if idx >= self.resolved_cache.len() {
return;
}
let node_state = &styled_node.styled_node_state;
let mut prop_types = alloc::collections::BTreeSet::new();
if let Some(map) = self.user_overridden_properties.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
for css_prop in node_data.css_props.iter() {
prop_types.insert(css_prop.property.get_type());
}
if let Some(map) = self.css_normal_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_normal_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.computed_values.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
for pt in Self::UA_PROPERTY_TYPES {
if crate::ua_css::get_ua_property(&node_data.node_type, *pt).is_some() {
prop_types.insert(*pt);
}
}
if node_state.hover {
if let Some(map) = self.css_hover_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_hover_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.active {
if let Some(map) = self.css_active_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_active_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
if node_state.focused {
if let Some(map) = self.css_focus_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
if let Some(map) = self.cascaded_focus_props.get(node_id.index()) {
prop_types.extend(map.keys().copied());
}
}
let mut props = Vec::with_capacity(prop_types.len());
for prop_type in &prop_types {
if let Some(prop) = self.get_property_slow(node_data, &node_id, node_state, prop_type) {
props.push((*prop_type, prop.clone()));
}
}
self.resolved_cache[idx] = props;
}
pub fn invalidate_resolved_cache(&mut self) {
self.resolved_cache.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dom::NodeType;
#[test]
fn test_ua_css_p_tag_properties() {
let cache = CssPropertyCache::empty(1);
let mut node_data = NodeData::create_node(NodeType::P);
let node_id = NodeId::new(0);
let node_state = StyledNodeState::default();
let display = cache.get_display(&node_data, &node_id, &node_state);
assert!(
display.is_some(),
"Expected <p> to have display property from UA CSS"
);
if let Some(d) = display {
if let Some(display_value) = d.get_property() {
assert_eq!(
*display_value,
LayoutDisplay::Block,
"Expected <p> to have display: block, got {:?}",
display_value
);
}
}
let width = cache.get_width(&node_data, &node_id, &node_state);
assert!(
width.is_none(),
"Expected <p> to NOT have explicit width (should be auto), but got {:?}",
width
);
let height = cache.get_height(&node_data, &node_id, &node_state);
println!("Height for <p> tag: {:?}", height);
assert!(
height.is_none(),
"Expected <p> to NOT have explicit height (should be auto), but got {:?}",
height
);
}
#[test]
fn test_ua_css_body_tag_properties() {
let cache = CssPropertyCache::empty(1);
let node_data = NodeData::create_node(NodeType::Body);
let node_id = NodeId::new(0);
let node_state = StyledNodeState::default();
let width = cache.get_width(&node_data, &node_id, &node_state);
assert!(
width.is_none(),
"Expected <body> to NOT have explicit width (should be auto), but got {:?}",
width
);
let height = cache.get_height(&node_data, &node_id, &node_state);
assert!(
height.is_none(),
"<body> should not have explicit height from UA CSS (should be auto)"
);
let margin_top = cache.get_margin_top(&node_data, &node_id, &node_state);
assert!(
margin_top.is_some(),
"Expected <body> to have margin-top from UA CSS"
);
}
}