use azul_core::{
dom::{NodeId, NodeType},
geom::LogicalSize,
id::NodeId as CoreNodeId,
styled_dom::{StyledDom, StyledNodeState},
};
use azul_css::{
css::CssPropertyValue,
props::{
basic::{
font::{StyleFontFamily, StyleFontFamilyVec, StyleFontWeight, StyleFontStyle},
pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
ColorU, PhysicalSize, PixelValue, PropertyContext, ResolutionContext,
},
layout::{
BoxDecorationBreak, BreakInside, LayoutBoxSizing, LayoutClear, LayoutDisplay,
LayoutFlexDirection, LayoutFlexWrap, LayoutFloat, LayoutHeight,
LayoutJustifyContent, LayoutAlignItems, LayoutAlignContent, LayoutOverflow,
LayoutPosition, LayoutWidth, LayoutWritingMode, Orphans, PageBreak, Widows,
grid::GridTemplateAreas,
},
property::{CssProperty, CssPropertyType,
LayoutFlexBasisValue, LayoutFlexDirectionValue, LayoutFlexWrapValue,
LayoutFlexGrowValue, LayoutFlexShrinkValue,
LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutAlignContentValue,
LayoutJustifyContentValue, LayoutJustifyItemsValue, LayoutJustifySelfValue,
LayoutGapValue,
LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
LayoutGridAutoColumnsValue, LayoutGridAutoRowsValue,
LayoutGridAutoFlowValue, LayoutGridColumnValue, LayoutGridRowValue,
},
style::{
border_radius::StyleBorderRadius,
lists::{StyleListStylePosition, StyleListStyleType},
StyleDirection, StyleTextAlign, StyleUserSelect, StyleVerticalAlign,
StyleVisibility, StyleWhiteSpace,
},
},
};
use crate::{
font_traits::{ParsedFontTrait, StyleProperties},
solver3::{
display_list::{BorderRadius, PhysicalSizeImport},
layout_tree::LayoutNode,
scrollbar::ScrollbarRequirements,
},
};
pub fn get_element_font_size(
styled_dom: &StyledDom,
dom_id: NodeId,
node_state: &StyledNodeState,
) -> f32 {
let node_data = &styled_dom.node_data.as_container()[dom_id];
let cache = &styled_dom.css_property_cache.ptr;
let cached_font_size = cache
.dependency_chains
.get(dom_id.index())
.and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
.and_then(|chain| chain.cached_pixels);
if let Some(cached) = cached_font_size {
return cached;
}
let parent_font_size = styled_dom
.node_hierarchy
.as_container()
.get(dom_id)
.and_then(|node| node.parent_id())
.and_then(|parent_id| {
cache
.dependency_chains
.get(parent_id.index())
.and_then(|chains| {
chains.get(&azul_css::props::property::CssPropertyType::FontSize)
})
.and_then(|chain| chain.cached_pixels)
})
.unwrap_or(DEFAULT_FONT_SIZE);
let root_font_size = {
let root_id = NodeId::new(0);
cache
.dependency_chains
.get(root_id.index())
.and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
.and_then(|chain| chain.cached_pixels)
.unwrap_or(DEFAULT_FONT_SIZE)
};
cache
.get_font_size(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| {
let context = ResolutionContext {
element_font_size: DEFAULT_FONT_SIZE, parent_font_size,
root_font_size,
containing_block_size: PhysicalSize::new(0.0, 0.0),
element_size: None,
viewport_size: PhysicalSize::new(0.0, 0.0), };
v.inner
.resolve_with_context(&context, PropertyContext::FontSize)
})
.unwrap_or(DEFAULT_FONT_SIZE)
}
pub fn get_parent_font_size(
styled_dom: &StyledDom,
dom_id: NodeId,
node_state: &StyledNodeState,
) -> f32 {
styled_dom
.node_hierarchy
.as_container()
.get(dom_id)
.and_then(|node| node.parent_id())
.map(|parent_id| get_element_font_size(styled_dom, parent_id, node_state))
.unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE)
}
pub fn get_root_font_size(styled_dom: &StyledDom, node_state: &StyledNodeState) -> f32 {
get_element_font_size(styled_dom, NodeId::new(0), node_state)
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum MultiValue<T> {
Auto,
Initial,
Inherit,
Exact(T),
}
impl<T> MultiValue<T> {
pub fn is_auto(&self) -> bool {
matches!(self, MultiValue::Auto)
}
pub fn is_exact(&self) -> bool {
matches!(self, MultiValue::Exact(_))
}
pub fn exact(self) -> Option<T> {
match self {
MultiValue::Exact(v) => Some(v),
_ => None,
}
}
pub fn unwrap_or(self, default: T) -> T {
match self {
MultiValue::Exact(v) => v,
_ => default,
}
}
pub fn unwrap_or_default(self) -> T
where
T: Default,
{
match self {
MultiValue::Exact(v) => v,
_ => T::default(),
}
}
pub fn map<U, F>(self, f: F) -> MultiValue<U>
where
F: FnOnce(T) -> U,
{
match self {
MultiValue::Exact(v) => MultiValue::Exact(f(v)),
MultiValue::Auto => MultiValue::Auto,
MultiValue::Initial => MultiValue::Initial,
MultiValue::Inherit => MultiValue::Inherit,
}
}
}
impl MultiValue<LayoutOverflow> {
pub fn is_clipped(&self) -> bool {
matches!(
self,
MultiValue::Exact(
LayoutOverflow::Hidden
| LayoutOverflow::Clip
| LayoutOverflow::Auto
| LayoutOverflow::Scroll
)
)
}
pub fn is_scroll(&self) -> bool {
matches!(
self,
MultiValue::Exact(LayoutOverflow::Scroll | LayoutOverflow::Auto)
)
}
pub fn is_auto_overflow(&self) -> bool {
matches!(self, MultiValue::Exact(LayoutOverflow::Auto))
}
pub fn is_hidden(&self) -> bool {
matches!(self, MultiValue::Exact(LayoutOverflow::Hidden))
}
pub fn is_hidden_or_clip(&self) -> bool {
matches!(
self,
MultiValue::Exact(LayoutOverflow::Hidden | LayoutOverflow::Clip)
)
}
pub fn is_scroll_explicit(&self) -> bool {
matches!(self, MultiValue::Exact(LayoutOverflow::Scroll))
}
pub fn is_visible_or_clip(&self) -> bool {
matches!(
self,
MultiValue::Exact(LayoutOverflow::Visible | LayoutOverflow::Clip)
)
}
}
impl MultiValue<LayoutPosition> {
pub fn is_absolute_or_fixed(&self) -> bool {
matches!(
self,
MultiValue::Exact(LayoutPosition::Absolute | LayoutPosition::Fixed)
)
}
}
impl MultiValue<LayoutFloat> {
pub fn is_none(&self) -> bool {
matches!(
self,
MultiValue::Auto
| MultiValue::Initial
| MultiValue::Inherit
| MultiValue::Exact(LayoutFloat::None)
)
}
}
impl<T: Default> Default for MultiValue<T> {
fn default() -> Self {
MultiValue::Auto
}
}
macro_rules! get_css_property_pixel {
($fn_name:ident, $cache_method:ident, $ua_property:expr, compact_i16 = $compact_method:ident) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<PixelValue> {
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let raw = cc.$compact_method(node_id.index());
if raw == azul_css::compact_cache::I16_AUTO {
return MultiValue::Auto;
}
if raw == azul_css::compact_cache::I16_INITIAL {
return MultiValue::Initial;
}
if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
return MultiValue::Exact(PixelValue::px(raw as f32 / 10.0));
}
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(ref val) = author_css {
if val.is_auto() {
return MultiValue::Auto;
}
if let Some(exact) = val.get_property().copied() {
return MultiValue::Exact(exact.inner);
}
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(inner) = ua_prop.get_pixel_inner() {
return MultiValue::Exact(inner);
}
}
MultiValue::Initial
}
};
($fn_name:ident, $cache_method:ident, $ua_property:expr) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<PixelValue> {
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(ref val) = author_css {
if val.is_auto() {
return MultiValue::Auto;
}
if let Some(exact) = val.get_property().copied() {
return MultiValue::Exact(exact.inner);
}
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(inner) = ua_prop.get_pixel_inner() {
return MultiValue::Exact(inner);
}
}
MultiValue::Initial
}
};
}
trait CssPropertyPixelInner {
fn get_pixel_inner(&self) -> Option<PixelValue>;
}
impl CssPropertyPixelInner for azul_css::props::property::CssProperty {
fn get_pixel_inner(&self) -> Option<PixelValue> {
match self {
CssProperty::Left(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::Right(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::Top(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::Bottom(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::MarginLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::MarginRight(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::MarginTop(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::MarginBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::PaddingLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::PaddingRight(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::PaddingTop(CssPropertyValue::Exact(v)) => Some(v.inner),
CssProperty::PaddingBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
_ => None,
}
}
}
macro_rules! get_css_property {
($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact = $compact_method:ident) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<$return_type> {
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
return MultiValue::Exact(cc.$compact_method(node_id.index()));
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
return MultiValue::Exact(val);
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
return MultiValue::Exact(val);
}
}
MultiValue::Auto
}
};
($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact_u32_dim = $compact_raw_method:ident, $px_variant:path, $auto_variant:path, $min_content_variant:path, $max_content_variant:path) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<$return_type> {
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let raw = cc.$compact_raw_method(node_id.index());
match raw {
azul_css::compact_cache::U32_AUTO => return MultiValue::Auto,
azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
azul_css::compact_cache::U32_MIN_CONTENT => return MultiValue::Exact($min_content_variant),
azul_css::compact_cache::U32_MAX_CONTENT => return MultiValue::Exact($max_content_variant),
azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
}
_ => {
if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
return MultiValue::Exact($px_variant(pv));
}
}
}
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
return MultiValue::Exact(val);
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
return MultiValue::Exact(val);
}
}
MultiValue::Auto
}
};
($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact_u32_struct = $compact_raw_method:ident) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<$return_type> {
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let raw = cc.$compact_raw_method(node_id.index());
match raw {
azul_css::compact_cache::U32_AUTO | azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
}
_ => {
if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
return MultiValue::Exact(
<$return_type as azul_css::props::PixelValueTaker>::from_pixel_value(pv)
);
}
}
}
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
return MultiValue::Exact(val);
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
return MultiValue::Exact(val);
}
}
MultiValue::Auto
}
};
($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> MultiValue<$return_type> {
let node_data = &styled_dom.node_data.as_container()[node_id];
let author_css = styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state);
if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
return MultiValue::Exact(val);
}
let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
if let Some(ua_prop) = ua_css {
if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
return MultiValue::Exact(val);
}
}
MultiValue::Auto
}
};
}
trait ExtractPropertyValue<T> {
fn extract(&self) -> Option<T>;
}
fn extract_property_value<T>(prop: &azul_css::props::property::CssProperty) -> Option<T>
where
azul_css::props::property::CssProperty: ExtractPropertyValue<T>,
{
prop.extract()
}
impl ExtractPropertyValue<LayoutWidth> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutWidth> {
match self {
Self::Width(CssPropertyValue::Exact(v)) => Some(v.clone()),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutHeight> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutHeight> {
match self {
Self::Height(CssPropertyValue::Exact(v)) => Some(v.clone()),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutMinWidth> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutMinWidth> {
match self {
Self::MinWidth(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutMinHeight> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutMinHeight> {
match self {
Self::MinHeight(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutMaxWidth> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutMaxWidth> {
match self {
Self::MaxWidth(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutMaxHeight> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutMaxHeight> {
match self {
Self::MaxHeight(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutDisplay> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutDisplay> {
match self {
Self::Display(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutWritingMode> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutWritingMode> {
match self {
Self::WritingMode(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutFlexWrap> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutFlexWrap> {
match self {
Self::FlexWrap(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutJustifyContent> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutJustifyContent> {
match self {
Self::JustifyContent(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleTextAlign> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleTextAlign> {
match self {
Self::TextAlign(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutFloat> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutFloat> {
match self {
Self::Float(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutClear> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutClear> {
match self {
Self::Clear(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutOverflow> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutOverflow> {
match self {
Self::OverflowX(CssPropertyValue::Exact(v)) => Some(*v),
Self::OverflowY(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutPosition> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutPosition> {
match self {
Self::Position(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutBoxSizing> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutBoxSizing> {
match self {
Self::BoxSizing(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<PixelValue> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<PixelValue> {
self.get_pixel_inner()
}
}
impl ExtractPropertyValue<LayoutFlexDirection> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutFlexDirection> {
match self {
Self::FlexDirection(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutAlignItems> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutAlignItems> {
match self {
Self::AlignItems(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutAlignContent> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<LayoutAlignContent> {
match self {
Self::AlignContent(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleFontWeight> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleFontWeight> {
match self {
Self::FontWeight(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleFontStyle> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleFontStyle> {
match self {
Self::FontStyle(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleVisibility> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleVisibility> {
match self {
Self::Visibility(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleWhiteSpace> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleWhiteSpace> {
match self {
Self::WhiteSpace(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleDirection> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleDirection> {
match self {
Self::Direction(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleVerticalAlign> for azul_css::props::property::CssProperty {
fn extract(&self) -> Option<StyleVerticalAlign> {
match self {
Self::VerticalAlign(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
get_css_property!(
get_writing_mode,
get_writing_mode,
LayoutWritingMode,
azul_css::props::property::CssPropertyType::WritingMode,
compact = get_writing_mode
);
get_css_property!(
get_css_width,
get_width,
LayoutWidth,
azul_css::props::property::CssPropertyType::Width,
compact_u32_dim = get_width_raw, LayoutWidth::Px, LayoutWidth::Auto, LayoutWidth::MinContent, LayoutWidth::MaxContent
);
get_css_property!(
get_css_height,
get_height,
LayoutHeight,
azul_css::props::property::CssPropertyType::Height,
compact_u32_dim = get_height_raw, LayoutHeight::Px, LayoutHeight::Auto, LayoutHeight::MinContent, LayoutHeight::MaxContent
);
get_css_property!(
get_wrap,
get_flex_wrap,
LayoutFlexWrap,
azul_css::props::property::CssPropertyType::FlexWrap,
compact = get_flex_wrap
);
get_css_property!(
get_justify_content,
get_justify_content,
LayoutJustifyContent,
azul_css::props::property::CssPropertyType::JustifyContent,
compact = get_justify_content
);
get_css_property!(
get_text_align,
get_text_align,
StyleTextAlign,
azul_css::props::property::CssPropertyType::TextAlign,
compact = get_text_align
);
get_css_property!(
get_float,
get_float,
LayoutFloat,
azul_css::props::property::CssPropertyType::Float,
compact = get_float
);
get_css_property!(
get_clear,
get_clear,
LayoutClear,
azul_css::props::property::CssPropertyType::Clear,
compact = get_clear
);
get_css_property!(
get_overflow_x,
get_overflow_x,
LayoutOverflow,
azul_css::props::property::CssPropertyType::OverflowX,
compact = get_overflow_x
);
get_css_property!(
get_overflow_y,
get_overflow_y,
LayoutOverflow,
azul_css::props::property::CssPropertyType::OverflowY,
compact = get_overflow_y
);
get_css_property!(
get_position,
get_position,
LayoutPosition,
azul_css::props::property::CssPropertyType::Position,
compact = get_position
);
get_css_property!(
get_css_box_sizing,
get_box_sizing,
LayoutBoxSizing,
azul_css::props::property::CssPropertyType::BoxSizing,
compact = get_box_sizing
);
get_css_property!(
get_flex_direction,
get_flex_direction,
LayoutFlexDirection,
azul_css::props::property::CssPropertyType::FlexDirection,
compact = get_flex_direction
);
get_css_property!(
get_align_items,
get_align_items,
LayoutAlignItems,
azul_css::props::property::CssPropertyType::AlignItems,
compact = get_align_items
);
get_css_property!(
get_align_content,
get_align_content,
LayoutAlignContent,
azul_css::props::property::CssPropertyType::AlignContent,
compact = get_align_content
);
get_css_property!(
get_font_weight_property,
get_font_weight,
StyleFontWeight,
azul_css::props::property::CssPropertyType::FontWeight,
compact = get_font_weight
);
get_css_property!(
get_font_style_property,
get_font_style,
StyleFontStyle,
azul_css::props::property::CssPropertyType::FontStyle,
compact = get_font_style
);
get_css_property!(
get_visibility,
get_visibility,
StyleVisibility,
azul_css::props::property::CssPropertyType::Visibility,
compact = get_visibility
);
get_css_property!(
get_white_space_property,
get_white_space,
StyleWhiteSpace,
azul_css::props::property::CssPropertyType::WhiteSpace,
compact = get_white_space
);
get_css_property!(
get_direction_property,
get_direction,
StyleDirection,
azul_css::props::property::CssPropertyType::Direction,
compact = get_direction
);
get_css_property!(
get_vertical_align_property,
get_vertical_align,
StyleVerticalAlign,
azul_css::props::property::CssPropertyType::VerticalAlign,
compact = get_vertical_align
);
pub fn get_style_border_radius(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> azul_css::props::style::border_radius::StyleBorderRadius {
let node_data = &styled_dom.node_data.as_container()[node_id];
let top_left = styled_dom
.css_property_cache
.ptr
.get_border_top_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property_or_default())
.map(|v| v.inner)
.unwrap_or_default();
let top_right = styled_dom
.css_property_cache
.ptr
.get_border_top_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property_or_default())
.map(|v| v.inner)
.unwrap_or_default();
let bottom_right = styled_dom
.css_property_cache
.ptr
.get_border_bottom_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property_or_default())
.map(|v| v.inner)
.unwrap_or_default();
let bottom_left = styled_dom
.css_property_cache
.ptr
.get_border_bottom_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property_or_default())
.map(|v| v.inner)
.unwrap_or_default();
StyleBorderRadius {
top_left,
top_right,
bottom_right,
bottom_left,
}
}
pub fn get_border_radius(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
element_size: PhysicalSizeImport,
viewport_size: LogicalSize,
) -> BorderRadius {
use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
let node_data = &styled_dom.node_data.as_container()[node_id];
let element_font_size = get_element_font_size(styled_dom, node_id, node_state);
let parent_font_size = styled_dom
.node_hierarchy
.as_container()
.get(node_id)
.and_then(|node| node.parent_id())
.map(|p| get_element_font_size(styled_dom, p, node_state))
.unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
let root_font_size = get_root_font_size(styled_dom, node_state);
let context = ResolutionContext {
element_font_size,
parent_font_size,
root_font_size,
containing_block_size: PhysicalSize::new(0.0, 0.0), element_size: Some(PhysicalSize::new(element_size.width, element_size.height)),
viewport_size: PhysicalSize::new(viewport_size.width, viewport_size.height),
};
let top_left = styled_dom
.css_property_cache
.ptr
.get_border_top_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.unwrap_or_default();
let top_right = styled_dom
.css_property_cache
.ptr
.get_border_top_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.unwrap_or_default();
let bottom_right = styled_dom
.css_property_cache
.ptr
.get_border_bottom_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.unwrap_or_default();
let bottom_left = styled_dom
.css_property_cache
.ptr
.get_border_bottom_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.unwrap_or_default();
BorderRadius {
top_left: top_left
.inner
.resolve_with_context(&context, PropertyContext::BorderRadius),
top_right: top_right
.inner
.resolve_with_context(&context, PropertyContext::BorderRadius),
bottom_right: bottom_right
.inner
.resolve_with_context(&context, PropertyContext::BorderRadius),
bottom_left: bottom_left
.inner
.resolve_with_context(&context, PropertyContext::BorderRadius),
}
}
pub fn get_z_index(styled_dom: &StyledDom, node_id: Option<NodeId>) -> i32 {
use azul_css::props::layout::position::LayoutZIndex;
let node_id = match node_id {
Some(id) => id,
None => return 0,
};
let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let raw = cc.get_z_index(node_id.index());
if raw == azul_css::compact_cache::I16_AUTO {
return 0;
}
if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
return raw as i32;
}
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom
.css_property_cache
.ptr
.get_z_index(node_data, &node_id, &node_state)
.and_then(|v| v.get_property())
.map(|z| match z {
LayoutZIndex::Auto => 0,
LayoutZIndex::Integer(i) => *i,
})
.unwrap_or(0)
}
pub fn get_background_color(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> ColorU {
let node_data = &styled_dom.node_data.as_container()[node_id];
let get_node_bg = |node_id: NodeId, node_data: &azul_core::dom::NodeData| {
styled_dom
.css_property_cache
.ptr
.get_background_content(node_data, &node_id, node_state)
.and_then(|bg| bg.get_property())
.and_then(|bg_vec| bg_vec.get(0).cloned())
.and_then(|first_bg| match &first_bg {
azul_css::props::style::StyleBackgroundContent::Color(color) => Some(color.clone()),
azul_css::props::style::StyleBackgroundContent::Image(_) => None, _ => None,
})
};
let own_bg = get_node_bg(node_id, node_data);
if !matches!(node_data.node_type, NodeType::Html) || own_bg.is_some() {
return own_bg.unwrap_or(ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
});
}
let first_child = styled_dom
.node_hierarchy
.as_container()
.get(node_id)
.and_then(|node| node.first_child_id(node_id));
let Some(first_child) = first_child else {
return ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
};
};
let first_child_data = &styled_dom.node_data.as_container()[first_child];
if !matches!(first_child_data.node_type, NodeType::Body) {
return ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
};
}
get_node_bg(first_child, first_child_data).unwrap_or(ColorU {
r: 0,
g: 0,
b: 0,
a: 0,
})
}
pub fn get_background_contents(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Vec<azul_css::props::style::StyleBackgroundContent> {
use azul_core::dom::NodeType;
use azul_css::props::style::StyleBackgroundContent;
let node_data = &styled_dom.node_data.as_container()[node_id];
let get_node_backgrounds =
|nid: NodeId, ndata: &azul_core::dom::NodeData| -> Vec<StyleBackgroundContent> {
styled_dom
.css_property_cache
.ptr
.get_background_content(ndata, &nid, node_state)
.and_then(|bg| bg.get_property())
.map(|bg_vec| bg_vec.iter().cloned().collect())
.unwrap_or_default()
};
let own_backgrounds = get_node_backgrounds(node_id, node_data);
if !matches!(node_data.node_type, NodeType::Html) || !own_backgrounds.is_empty() {
return own_backgrounds;
}
let first_child = styled_dom
.node_hierarchy
.as_container()
.get(node_id)
.and_then(|node| node.first_child_id(node_id));
let Some(first_child) = first_child else {
return own_backgrounds;
};
let first_child_data = &styled_dom.node_data.as_container()[first_child];
if !matches!(first_child_data.node_type, NodeType::Body) {
return own_backgrounds;
}
get_node_backgrounds(first_child, first_child_data)
}
pub struct BorderInfo {
pub widths: crate::solver3::display_list::StyleBorderWidths,
pub colors: crate::solver3::display_list::StyleBorderColors,
pub styles: crate::solver3::display_list::StyleBorderStyles,
}
pub fn get_border_info(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> BorderInfo {
use crate::solver3::display_list::{StyleBorderColors, StyleBorderStyles, StyleBorderWidths};
use azul_css::css::CssPropertyValue;
use azul_css::props::basic::color::ColorU;
use azul_css::props::style::border::{
BorderStyle, StyleBorderTopColor, StyleBorderRightColor,
StyleBorderBottomColor, StyleBorderLeftColor,
StyleBorderTopStyle, StyleBorderRightStyle,
StyleBorderBottomStyle, StyleBorderLeftStyle,
};
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let idx = node_id.index();
let node_data = &styled_dom.node_data.as_container()[node_id];
let widths = StyleBorderWidths {
top: styled_dom.css_property_cache.ptr
.get_border_top_width(node_data, &node_id, node_state).cloned(),
right: styled_dom.css_property_cache.ptr
.get_border_right_width(node_data, &node_id, node_state).cloned(),
bottom: styled_dom.css_property_cache.ptr
.get_border_bottom_width(node_data, &node_id, node_state).cloned(),
left: styled_dom.css_property_cache.ptr
.get_border_left_width(node_data, &node_id, node_state).cloned(),
};
let make_color = |raw: u32| -> Option<ColorU> {
if raw == 0 { None } else {
Some(ColorU {
r: ((raw >> 24) & 0xFF) as u8,
g: ((raw >> 16) & 0xFF) as u8,
b: ((raw >> 8) & 0xFF) as u8,
a: (raw & 0xFF) as u8,
})
}
};
let colors = StyleBorderColors {
top: make_color(cc.get_border_top_color_raw(idx))
.map(|c| CssPropertyValue::Exact(StyleBorderTopColor { inner: c })),
right: make_color(cc.get_border_right_color_raw(idx))
.map(|c| CssPropertyValue::Exact(StyleBorderRightColor { inner: c })),
bottom: make_color(cc.get_border_bottom_color_raw(idx))
.map(|c| CssPropertyValue::Exact(StyleBorderBottomColor { inner: c })),
left: make_color(cc.get_border_left_color_raw(idx))
.map(|c| CssPropertyValue::Exact(StyleBorderLeftColor { inner: c })),
};
let styles = StyleBorderStyles {
top: Some(CssPropertyValue::Exact(StyleBorderTopStyle {
inner: cc.get_border_top_style(idx),
})),
right: Some(CssPropertyValue::Exact(StyleBorderRightStyle {
inner: cc.get_border_right_style(idx),
})),
bottom: Some(CssPropertyValue::Exact(StyleBorderBottomStyle {
inner: cc.get_border_bottom_style(idx),
})),
left: Some(CssPropertyValue::Exact(StyleBorderLeftStyle {
inner: cc.get_border_left_style(idx),
})),
};
return BorderInfo { widths, colors, styles };
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
let widths = StyleBorderWidths {
top: styled_dom
.css_property_cache
.ptr
.get_border_top_width(node_data, &node_id, node_state)
.cloned(),
right: styled_dom
.css_property_cache
.ptr
.get_border_right_width(node_data, &node_id, node_state)
.cloned(),
bottom: styled_dom
.css_property_cache
.ptr
.get_border_bottom_width(node_data, &node_id, node_state)
.cloned(),
left: styled_dom
.css_property_cache
.ptr
.get_border_left_width(node_data, &node_id, node_state)
.cloned(),
};
let colors = StyleBorderColors {
top: styled_dom
.css_property_cache
.ptr
.get_border_top_color(node_data, &node_id, node_state)
.cloned(),
right: styled_dom
.css_property_cache
.ptr
.get_border_right_color(node_data, &node_id, node_state)
.cloned(),
bottom: styled_dom
.css_property_cache
.ptr
.get_border_bottom_color(node_data, &node_id, node_state)
.cloned(),
left: styled_dom
.css_property_cache
.ptr
.get_border_left_color(node_data, &node_id, node_state)
.cloned(),
};
let styles = StyleBorderStyles {
top: styled_dom
.css_property_cache
.ptr
.get_border_top_style(node_data, &node_id, node_state)
.cloned(),
right: styled_dom
.css_property_cache
.ptr
.get_border_right_style(node_data, &node_id, node_state)
.cloned(),
bottom: styled_dom
.css_property_cache
.ptr
.get_border_bottom_style(node_data, &node_id, node_state)
.cloned(),
left: styled_dom
.css_property_cache
.ptr
.get_border_left_style(node_data, &node_id, node_state)
.cloned(),
};
BorderInfo {
widths,
colors,
styles,
}
}
pub fn get_inline_border_info(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
border_info: &BorderInfo,
) -> Option<crate::text3::cache::InlineBorderInfo> {
use crate::text3::cache::InlineBorderInfo;
fn get_border_width_px(
width: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderTopWidth>,
>,
) -> f32 {
width
.as_ref()
.and_then(|v| v.get_property())
.map(|w| w.inner.number.get())
.unwrap_or(0.0)
}
fn get_border_width_px_right(
width: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderRightWidth>,
>,
) -> f32 {
width
.as_ref()
.and_then(|v| v.get_property())
.map(|w| w.inner.number.get())
.unwrap_or(0.0)
}
fn get_border_width_px_bottom(
width: &Option<
azul_css::css::CssPropertyValue<
azul_css::props::style::border::LayoutBorderBottomWidth,
>,
>,
) -> f32 {
width
.as_ref()
.and_then(|v| v.get_property())
.map(|w| w.inner.number.get())
.unwrap_or(0.0)
}
fn get_border_width_px_left(
width: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderLeftWidth>,
>,
) -> f32 {
width
.as_ref()
.and_then(|v| v.get_property())
.map(|w| w.inner.number.get())
.unwrap_or(0.0)
}
fn get_border_color_top(
color: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderTopColor>,
>,
) -> ColorU {
color
.as_ref()
.and_then(|v| v.get_property())
.map(|c| c.inner)
.unwrap_or(ColorU::BLACK)
}
fn get_border_color_right(
color: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderRightColor>,
>,
) -> ColorU {
color
.as_ref()
.and_then(|v| v.get_property())
.map(|c| c.inner)
.unwrap_or(ColorU::BLACK)
}
fn get_border_color_bottom(
color: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderBottomColor>,
>,
) -> ColorU {
color
.as_ref()
.and_then(|v| v.get_property())
.map(|c| c.inner)
.unwrap_or(ColorU::BLACK)
}
fn get_border_color_left(
color: &Option<
azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderLeftColor>,
>,
) -> ColorU {
color
.as_ref()
.and_then(|v| v.get_property())
.map(|c| c.inner)
.unwrap_or(ColorU::BLACK)
}
fn get_border_radius_px(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<f32> {
let node_data = &styled_dom.node_data.as_container()[node_id];
let top_left = styled_dom
.css_property_cache
.ptr
.get_border_top_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.map(|v| v.inner.number.get());
let top_right = styled_dom
.css_property_cache
.ptr
.get_border_top_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.map(|v| v.inner.number.get());
let bottom_left = styled_dom
.css_property_cache
.ptr
.get_border_bottom_left_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.map(|v| v.inner.number.get());
let bottom_right = styled_dom
.css_property_cache
.ptr
.get_border_bottom_right_radius(node_data, &node_id, node_state)
.and_then(|br| br.get_property().cloned())
.map(|v| v.inner.number.get());
let radii: Vec<f32> = [top_left, top_right, bottom_left, bottom_right]
.into_iter()
.filter_map(|r| r)
.collect();
if radii.is_empty() {
None
} else {
Some(radii.into_iter().fold(0.0f32, |a, b| a.max(b)))
}
}
let top = get_border_width_px(&border_info.widths.top);
let right = get_border_width_px_right(&border_info.widths.right);
let bottom = get_border_width_px_bottom(&border_info.widths.bottom);
let left = get_border_width_px_left(&border_info.widths.left);
fn resolve_padding(mv: MultiValue<PixelValue>) -> f32 {
match mv {
MultiValue::Exact(pv) => {
use azul_css::props::basic::SizeMetric;
match pv.metric {
SizeMetric::Px => pv.number.get(),
SizeMetric::Pt => pv.number.get() * 1.333333,
SizeMetric::Em | SizeMetric::Rem => pv.number.get() * 16.0,
_ => 0.0,
}
}
_ => 0.0,
}
}
let p_top = resolve_padding(get_css_padding_top(styled_dom, node_id, node_state));
let p_right = resolve_padding(get_css_padding_right(styled_dom, node_id, node_state));
let p_bottom = resolve_padding(get_css_padding_bottom(styled_dom, node_id, node_state));
let p_left = resolve_padding(get_css_padding_left(styled_dom, node_id, node_state));
let has_border = top > 0.0 || right > 0.0 || bottom > 0.0 || left > 0.0;
let has_padding = p_top > 0.0 || p_right > 0.0 || p_bottom > 0.0 || p_left > 0.0;
if !has_border && !has_padding {
return None;
}
Some(InlineBorderInfo {
top,
right,
bottom,
left,
top_color: get_border_color_top(&border_info.colors.top),
right_color: get_border_color_right(&border_info.colors.right),
bottom_color: get_border_color_bottom(&border_info.colors.bottom),
left_color: get_border_color_left(&border_info.colors.left),
radius: get_border_radius_px(styled_dom, node_id, node_state),
padding_top: p_top,
padding_right: p_right,
padding_bottom: p_bottom,
padding_left: p_left,
})
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SelectionStyle {
pub bg_color: ColorU,
pub text_color: Option<ColorU>,
pub radius: f32,
}
pub fn get_selection_style(
styled_dom: &StyledDom,
node_id: Option<NodeId>,
system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
) -> SelectionStyle {
let Some(node_id) = node_id else {
return SelectionStyle::default();
};
let node_data = &styled_dom.node_data.as_container()[node_id];
let node_state = &StyledNodeState::default();
let default_bg = system_style
.and_then(|ss| ss.colors.selection_background.as_option().copied())
.unwrap_or(ColorU {
r: 51,
g: 153,
b: 255, a: 128, });
let bg_color = styled_dom
.css_property_cache
.ptr
.get_selection_background_color(node_data, &node_id, node_state)
.and_then(|c| c.get_property().cloned())
.map(|c| c.inner)
.unwrap_or(default_bg);
let default_text = system_style
.and_then(|ss| ss.colors.selection_text.as_option().copied());
let text_color = styled_dom
.css_property_cache
.ptr
.get_selection_color(node_data, &node_id, node_state)
.and_then(|c| c.get_property().cloned())
.map(|c| c.inner)
.or(default_text);
let radius = styled_dom
.css_property_cache
.ptr
.get_selection_radius(node_data, &node_id, node_state)
.and_then(|r| r.get_property().cloned())
.map(|r| r.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(0.0);
SelectionStyle {
bg_color,
text_color,
radius,
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CaretStyle {
pub color: ColorU,
pub width: f32,
pub animation_duration: u32,
}
pub fn get_caret_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> CaretStyle {
let Some(node_id) = node_id else {
return CaretStyle::default();
};
let node_data = &styled_dom.node_data.as_container()[node_id];
let node_state = &StyledNodeState::default();
let color = styled_dom
.css_property_cache
.ptr
.get_caret_color(node_data, &node_id, node_state)
.and_then(|c| c.get_property().cloned())
.map(|c| c.inner)
.unwrap_or(ColorU {
r: 255,
g: 255,
b: 255,
a: 255, });
let width = styled_dom
.css_property_cache
.ptr
.get_caret_width(node_data, &node_id, node_state)
.and_then(|w| w.get_property().cloned())
.map(|w| w.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(2.0);
let animation_duration = styled_dom
.css_property_cache
.ptr
.get_caret_animation_duration(node_data, &node_id, node_state)
.and_then(|d| d.get_property().cloned())
.map(|d| d.inner.inner) .unwrap_or(500);
CaretStyle {
color,
width,
animation_duration,
}
}
pub fn get_scrollbar_info_from_layout(node: &LayoutNode) -> ScrollbarRequirements {
node.scrollbar_info
.clone()
.unwrap_or_default()
}
pub fn get_layout_scrollbar_width_px<T: crate::font_traits::ParsedFontTrait>(
ctx: &crate::solver3::LayoutContext<'_, T>,
dom_id: NodeId,
styled_node_state: &StyledNodeState,
) -> f32 {
use azul_css::props::style::scrollbar::LayoutScrollbarWidth;
if let Some(ref sys) = ctx.system_style {
use azul_css::system::ScrollbarVisibility;
match sys.scrollbar_preferences.visibility {
ScrollbarVisibility::WhenScrolling => return 0.0, ScrollbarVisibility::Always | ScrollbarVisibility::Automatic => {}
}
}
get_scrollbar_width_px(ctx.styled_dom, dom_id, styled_node_state)
}
get_css_property!(
get_display_property_internal,
get_display,
LayoutDisplay,
azul_css::props::property::CssPropertyType::Display,
compact = get_display
);
pub fn get_display_property(
styled_dom: &StyledDom,
dom_id: Option<NodeId>,
) -> MultiValue<LayoutDisplay> {
let Some(id) = dom_id else {
return MultiValue::Exact(LayoutDisplay::Inline);
};
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
get_display_property_internal(styled_dom, id, node_state)
}
pub fn get_vertical_align_for_node(
styled_dom: &StyledDom,
dom_id: NodeId,
) -> crate::text3::cache::VerticalAlign {
let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
let va = match get_vertical_align_property(styled_dom, dom_id, node_state) {
MultiValue::Exact(v) => v,
_ => StyleVerticalAlign::default(),
};
match va {
StyleVerticalAlign::Baseline => crate::text3::cache::VerticalAlign::Baseline,
StyleVerticalAlign::Top => crate::text3::cache::VerticalAlign::Top,
StyleVerticalAlign::Middle => crate::text3::cache::VerticalAlign::Middle,
StyleVerticalAlign::Bottom => crate::text3::cache::VerticalAlign::Bottom,
StyleVerticalAlign::Sub => crate::text3::cache::VerticalAlign::Sub,
StyleVerticalAlign::Superscript => crate::text3::cache::VerticalAlign::Super,
StyleVerticalAlign::TextTop => crate::text3::cache::VerticalAlign::TextTop,
StyleVerticalAlign::TextBottom => crate::text3::cache::VerticalAlign::TextBottom,
}
}
pub fn get_style_properties(
styled_dom: &StyledDom,
dom_id: NodeId,
system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
) -> StyleProperties {
use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
let node_data = &styled_dom.node_data.as_container()[dom_id];
let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
let cache = &styled_dom.css_property_cache.ptr;
use azul_css::props::basic::font::{StyleFontFamily, StyleFontFamilyVec};
let font_families = cache
.get_font_family(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or_else(|| {
StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
});
let parent_font_size = styled_dom
.node_hierarchy
.as_container()
.get(dom_id)
.and_then(|node| {
let parent_id = CoreNodeId::from_usize(node.parent)?;
cache
.get_font_size(
&styled_dom.node_data.as_container()[parent_id],
&parent_id,
&styled_dom.styled_nodes.as_container()[parent_id].styled_node_state,
)
.and_then(|v| v.get_property().cloned())
.map(|v| {
use azul_css::props::basic::pixel::DEFAULT_FONT_SIZE;
v.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE)
})
})
.unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
let root_font_size = get_root_font_size(styled_dom, node_state);
let font_size_context = ResolutionContext {
element_font_size: azul_css::props::basic::pixel::DEFAULT_FONT_SIZE,
parent_font_size,
root_font_size,
containing_block_size: PhysicalSize::new(0.0, 0.0),
element_size: None,
viewport_size: PhysicalSize::new(0.0, 0.0), };
let font_size = {
let mut fast_font_size = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
let raw = cc.get_font_size_raw(dom_id.index());
if raw != azul_css::compact_cache::U32_SENTINEL
&& raw != azul_css::compact_cache::U32_INHERIT
&& raw != azul_css::compact_cache::U32_INITIAL
{
if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
fast_font_size = Some(pv.resolve_with_context(
&font_size_context,
PropertyContext::FontSize,
));
}
}
}
}
fast_font_size.unwrap_or_else(|| {
cache
.get_font_size(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| {
v.inner
.resolve_with_context(&font_size_context, PropertyContext::FontSize)
})
.unwrap_or(parent_font_size)
})
};
let color_from_cache = {
let mut fast_color = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
let raw = cc.get_text_color_raw(dom_id.index());
if raw != 0 {
fast_color = Some(ColorU {
r: (raw >> 24) as u8,
g: (raw >> 16) as u8,
b: (raw >> 8) as u8,
a: raw as u8,
});
}
}
}
fast_color.or_else(|| {
cache
.get_text_color(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| v.inner)
})
};
let system_text_color = system_style
.and_then(|ss| ss.colors.text.as_option().copied())
.unwrap_or(ColorU::BLACK);
let color = color_from_cache.unwrap_or(system_text_color);
let line_height = {
let mut fast_lh = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
if let Some(normalized) = cc.get_line_height(dom_id.index()) {
fast_lh = Some(normalized / 100.0 * font_size);
}
}
}
fast_lh.unwrap_or_else(|| {
cache
.get_line_height(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| v.inner.normalized() * font_size)
.unwrap_or(font_size * 1.2)
})
};
use azul_css::props::layout::LayoutDisplay;
let display = cache
.get_display(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or(LayoutDisplay::Inline);
let (background_color, background_content, border) =
if matches!(display, LayoutDisplay::Inline | LayoutDisplay::InlineBlock) {
let bg = get_background_color(styled_dom, dom_id, node_state);
let bg_color = if bg.a > 0 { Some(bg) } else { None };
let bg_contents = get_background_contents(styled_dom, dom_id, node_state);
let border_info = get_border_info(styled_dom, dom_id, node_state);
let inline_border =
get_inline_border_info(styled_dom, dom_id, node_state, &border_info);
(bg_color, bg_contents, inline_border)
} else {
(None, Vec::new(), None)
};
let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
MultiValue::Exact(v) => v,
_ => StyleFontWeight::Normal,
};
let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
MultiValue::Exact(v) => v,
_ => StyleFontStyle::Normal,
};
let fc_weight = super::fc::convert_font_weight(font_weight);
let fc_style = super::fc::convert_font_style(font_style);
let font_stack = {
let font_ref = (0..font_families.len())
.find_map(|i| {
match font_families.get(i).unwrap() {
azul_css::props::basic::font::StyleFontFamily::Ref(r) => Some(r.clone()),
_ => None,
}
});
let platform = system_style.map(|ss| &ss.platform);
if let Some(font_ref) = font_ref {
FontStack::Ref(font_ref)
} else {
let mut stack = Vec::with_capacity(font_families.len() + 3);
for i in 0..font_families.len() {
let family = font_families.get(i).unwrap();
if let azul_css::props::basic::font::StyleFontFamily::SystemType(system_type) = family {
if let Some(platform) = platform {
let font_names = system_type.get_fallback_chain(platform);
let system_weight = if system_type.is_bold() {
rust_fontconfig::FcWeight::Bold
} else {
fc_weight
};
let system_style_val = if system_type.is_italic() {
crate::text3::cache::FontStyle::Italic
} else {
fc_style
};
for font_name in font_names {
stack.push(crate::text3::cache::FontSelector {
family: font_name.to_string(),
weight: system_weight,
style: system_style_val,
unicode_ranges: Vec::new(),
});
}
} else {
stack.push(crate::text3::cache::FontSelector {
family: "sans-serif".to_string(),
weight: fc_weight,
style: fc_style,
unicode_ranges: Vec::new(),
});
}
} else {
stack.push(crate::text3::cache::FontSelector {
family: family.as_string(),
weight: fc_weight,
style: fc_style,
unicode_ranges: Vec::new(),
});
}
}
let generic_fallbacks = ["sans-serif", "serif", "monospace"];
for fallback in &generic_fallbacks {
if !stack
.iter()
.any(|f| f.family.to_lowercase() == fallback.to_lowercase())
{
stack.push(crate::text3::cache::FontSelector {
family: fallback.to_string(),
weight: rust_fontconfig::FcWeight::Normal,
style: crate::text3::cache::FontStyle::Normal,
unicode_ranges: Vec::new(),
});
}
}
FontStack::Stack(stack)
}
};
let letter_spacing = {
let mut fast_ls = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
if let Some(px_val) = cc.get_letter_spacing(dom_id.index()) {
fast_ls = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
}
}
}
fast_ls.unwrap_or_else(|| {
cache
.get_letter_spacing(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| {
let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
crate::text3::cache::Spacing::Px(px_value.round() as i32)
})
.unwrap_or_default()
})
};
let word_spacing = {
let mut fast_ws = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
if let Some(px_val) = cc.get_word_spacing(dom_id.index()) {
fast_ws = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
}
}
}
fast_ws.unwrap_or_else(|| {
cache
.get_word_spacing(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| {
let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
crate::text3::cache::Spacing::Px(px_value.round() as i32)
})
.unwrap_or_default()
})
};
let text_decoration = cache
.get_text_decoration(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| crate::text3::cache::TextDecoration::from_css(v))
.unwrap_or_default();
let tab_size = {
let mut fast_tab = None;
if node_state.is_normal() {
if let Some(ref cc) = cache.compact_cache {
let raw = cc.get_tab_size_raw(dom_id.index());
if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
fast_tab = Some(raw as f32 / 10.0);
}
}
}
fast_tab.unwrap_or_else(|| {
cache
.get_tab_size(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|v| v.inner.number.get())
.unwrap_or(8.0)
})
};
let properties = StyleProperties {
font_stack,
font_size_px: font_size,
color,
background_color,
background_content,
border,
line_height,
letter_spacing,
word_spacing,
text_decoration,
tab_size,
..Default::default()
};
properties
}
pub fn get_list_style_type(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> StyleListStyleType {
let Some(id) = dom_id else {
return StyleListStyleType::default();
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_list_style_type(node_data, &id, node_state)
.and_then(|v| v.get_property().copied())
.unwrap_or_default()
}
pub fn get_list_style_position(
styled_dom: &StyledDom,
dom_id: Option<NodeId>,
) -> StyleListStylePosition {
let Some(id) = dom_id else {
return StyleListStylePosition::default();
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_list_style_position(node_data, &id, node_state)
.and_then(|v| v.get_property().copied())
.unwrap_or_default()
}
use azul_css::props::layout::{
LayoutInsetBottom, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight,
LayoutMarginTop, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth,
LayoutPaddingBottom, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutRight,
LayoutTop,
};
get_css_property_pixel!(
get_css_left,
get_left,
azul_css::props::property::CssPropertyType::Left,
compact_i16 = get_left
);
get_css_property_pixel!(
get_css_right,
get_right,
azul_css::props::property::CssPropertyType::Right,
compact_i16 = get_right
);
get_css_property_pixel!(
get_css_top,
get_top,
azul_css::props::property::CssPropertyType::Top,
compact_i16 = get_top
);
get_css_property_pixel!(
get_css_bottom,
get_bottom,
azul_css::props::property::CssPropertyType::Bottom,
compact_i16 = get_bottom
);
get_css_property_pixel!(
get_css_margin_left,
get_margin_left,
azul_css::props::property::CssPropertyType::MarginLeft,
compact_i16 = get_margin_left_raw
);
get_css_property_pixel!(
get_css_margin_right,
get_margin_right,
azul_css::props::property::CssPropertyType::MarginRight,
compact_i16 = get_margin_right_raw
);
get_css_property_pixel!(
get_css_margin_top,
get_margin_top,
azul_css::props::property::CssPropertyType::MarginTop,
compact_i16 = get_margin_top_raw
);
get_css_property_pixel!(
get_css_margin_bottom,
get_margin_bottom,
azul_css::props::property::CssPropertyType::MarginBottom,
compact_i16 = get_margin_bottom_raw
);
get_css_property_pixel!(
get_css_padding_left,
get_padding_left,
azul_css::props::property::CssPropertyType::PaddingLeft,
compact_i16 = get_padding_left_raw
);
get_css_property_pixel!(
get_css_padding_right,
get_padding_right,
azul_css::props::property::CssPropertyType::PaddingRight,
compact_i16 = get_padding_right_raw
);
get_css_property_pixel!(
get_css_padding_top,
get_padding_top,
azul_css::props::property::CssPropertyType::PaddingTop,
compact_i16 = get_padding_top_raw
);
get_css_property_pixel!(
get_css_padding_bottom,
get_padding_bottom,
azul_css::props::property::CssPropertyType::PaddingBottom,
compact_i16 = get_padding_bottom_raw
);
get_css_property!(
get_css_min_width,
get_min_width,
LayoutMinWidth,
azul_css::props::property::CssPropertyType::MinWidth,
compact_u32_struct = get_min_width_raw
);
get_css_property!(
get_css_min_height,
get_min_height,
LayoutMinHeight,
azul_css::props::property::CssPropertyType::MinHeight,
compact_u32_struct = get_min_height_raw
);
get_css_property!(
get_css_max_width,
get_max_width,
LayoutMaxWidth,
azul_css::props::property::CssPropertyType::MaxWidth,
compact_u32_struct = get_max_width_raw
);
get_css_property!(
get_css_max_height,
get_max_height,
LayoutMaxHeight,
azul_css::props::property::CssPropertyType::MaxHeight,
compact_u32_struct = get_max_height_raw
);
get_css_property_pixel!(
get_css_border_left_width,
get_border_left_width,
azul_css::props::property::CssPropertyType::BorderLeftWidth,
compact_i16 = get_border_left_width_raw
);
get_css_property_pixel!(
get_css_border_right_width,
get_border_right_width,
azul_css::props::property::CssPropertyType::BorderRightWidth,
compact_i16 = get_border_right_width_raw
);
get_css_property_pixel!(
get_css_border_top_width,
get_border_top_width,
azul_css::props::property::CssPropertyType::BorderTopWidth,
compact_i16 = get_border_top_width_raw
);
get_css_property_pixel!(
get_css_border_bottom_width,
get_border_bottom_width,
azul_css::props::property::CssPropertyType::BorderBottomWidth,
compact_i16 = get_border_bottom_width_raw
);
pub fn get_break_before(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
let Some(id) = dom_id else {
return PageBreak::Auto;
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_break_before(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or(PageBreak::Auto)
}
pub fn get_break_after(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
let Some(id) = dom_id else {
return PageBreak::Auto;
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_break_after(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or(PageBreak::Auto)
}
pub fn is_forced_page_break(page_break: PageBreak) -> bool {
matches!(
page_break,
PageBreak::Always
| PageBreak::Page
| PageBreak::Left
| PageBreak::Right
| PageBreak::Recto
| PageBreak::Verso
| PageBreak::All
)
}
pub fn get_break_inside(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> BreakInside {
let Some(id) = dom_id else {
return BreakInside::Auto;
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_break_inside(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or(BreakInside::Auto)
}
pub fn get_orphans(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
let Some(id) = dom_id else {
return 2; };
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_orphans(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|o| o.inner)
.unwrap_or(2)
}
pub fn get_widows(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
let Some(id) = dom_id else {
return 2; };
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_widows(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.map(|w| w.inner)
.unwrap_or(2)
}
pub fn get_box_decoration_break(
styled_dom: &StyledDom,
dom_id: Option<NodeId>,
) -> BoxDecorationBreak {
let Some(id) = dom_id else {
return BoxDecorationBreak::Slice;
};
let node_data = &styled_dom.node_data.as_container()[id];
let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
styled_dom
.css_property_cache
.ptr
.get_box_decoration_break(node_data, &id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or(BoxDecorationBreak::Slice)
}
pub fn is_avoid_page_break(page_break: &PageBreak) -> bool {
matches!(page_break, PageBreak::Avoid | PageBreak::AvoidPage)
}
pub fn is_avoid_break_inside(break_inside: &BreakInside) -> bool {
matches!(
break_inside,
BreakInside::Avoid | BreakInside::AvoidPage | BreakInside::AvoidColumn
)
}
use std::collections::HashMap;
use rust_fontconfig::{FcFontCache, FcWeight, FontFallbackChain, PatternMatch};
use crate::text3::cache::{FontChainKey, FontChainKeyOrRef, FontSelector, FontStack, FontStyle};
#[derive(Debug, Clone)]
pub struct CollectedFontStacks {
pub font_stacks: Vec<Vec<FontSelector>>,
pub hash_to_index: HashMap<u64, usize>,
pub font_refs: HashMap<usize, azul_css::props::basic::font::FontRef>,
}
#[derive(Debug, Clone)]
pub struct ResolvedFontChains {
pub chains: HashMap<FontChainKeyOrRef, FontFallbackChain>,
}
impl ResolvedFontChains {
pub fn get(&self, key: &FontChainKeyOrRef) -> Option<&FontFallbackChain> {
self.chains.get(key)
}
pub fn get_by_chain_key(&self, key: &FontChainKey) -> Option<&FontFallbackChain> {
self.chains.get(&FontChainKeyOrRef::Chain(key.clone()))
}
pub fn get_for_font_stack(&self, font_stack: &[FontSelector]) -> Option<&FontFallbackChain> {
let key = FontChainKeyOrRef::Chain(FontChainKey::from_selectors(font_stack));
self.chains.get(&key)
}
pub fn get_for_font_ref(&self, ptr: usize) -> Option<&FontFallbackChain> {
self.chains.get(&FontChainKeyOrRef::Ref(ptr))
}
pub fn into_inner(self) -> HashMap<FontChainKeyOrRef, FontFallbackChain> {
self.chains
}
pub fn into_fontconfig_chains(self) -> HashMap<FontChainKey, FontFallbackChain> {
self.chains
.into_iter()
.filter_map(|(key, chain)| {
match key {
FontChainKeyOrRef::Chain(chain_key) => Some((chain_key, chain)),
FontChainKeyOrRef::Ref(_) => None,
}
})
.collect()
}
pub fn len(&self) -> usize {
self.chains.len()
}
pub fn is_empty(&self) -> bool {
self.chains.is_empty()
}
pub fn font_refs_len(&self) -> usize {
self.chains.keys().filter(|k| k.is_ref()).count()
}
}
pub fn collect_font_stacks_from_styled_dom(
styled_dom: &StyledDom,
platform: &azul_css::system::Platform,
) -> CollectedFontStacks {
let mut font_stacks = Vec::new();
let mut hash_to_index: HashMap<u64, usize> = HashMap::new();
let mut seen_hashes = std::collections::HashSet::new();
let mut font_refs: HashMap<usize, azul_css::props::basic::font::FontRef> = HashMap::new();
let node_data_container = styled_dom.node_data.as_container();
let styled_nodes_container = styled_dom.styled_nodes.as_container();
let cache = &styled_dom.css_property_cache.ptr;
for (node_idx, node_data) in node_data_container.internal.iter().enumerate() {
if !matches!(node_data.node_type, NodeType::Text(_)) {
continue;
}
let dom_id = match NodeId::from_usize(node_idx) {
Some(id) => id,
None => continue,
};
let node_state = &styled_nodes_container[dom_id].styled_node_state;
let font_families = cache
.get_font_family(node_data, &dom_id, node_state)
.and_then(|v| v.get_property().cloned())
.unwrap_or_else(|| {
StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
});
if let Some(first_family) = font_families.get(0) {
if let StyleFontFamily::Ref(font_ref) = first_family {
let ptr = font_ref.parsed as usize;
if !font_refs.contains_key(&ptr) {
font_refs.insert(ptr, font_ref.clone());
}
continue;
}
}
let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
MultiValue::Exact(v) => v,
_ => StyleFontWeight::Normal,
};
let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
MultiValue::Exact(v) => v,
_ => StyleFontStyle::Normal,
};
let mut fc_weight = super::fc::convert_font_weight(font_weight);
let mut fc_style = super::fc::convert_font_style(font_style);
let mut font_stack = Vec::with_capacity(font_families.len() + 3);
for i in 0..font_families.len() {
let family = font_families.get(i).unwrap();
if matches!(family, StyleFontFamily::Ref(_)) {
continue;
}
if let StyleFontFamily::SystemType(system_type) = family {
let font_names = system_type.get_fallback_chain(platform);
let system_weight = if system_type.is_bold() {
FcWeight::Bold
} else {
fc_weight
};
let system_style = if system_type.is_italic() {
FontStyle::Italic
} else {
fc_style
};
for font_name in font_names {
font_stack.push(FontSelector {
family: font_name.to_string(),
weight: system_weight,
style: system_style,
unicode_ranges: Vec::new(),
});
}
} else {
font_stack.push(FontSelector {
family: family.as_string(),
weight: fc_weight,
style: fc_style,
unicode_ranges: Vec::new(),
});
}
}
let generic_fallbacks = ["sans-serif", "serif", "monospace"];
for fallback in &generic_fallbacks {
if !font_stack
.iter()
.any(|f| f.family.to_lowercase() == fallback.to_lowercase())
{
font_stack.push(FontSelector {
family: fallback.to_string(),
weight: FcWeight::Normal,
style: FontStyle::Normal,
unicode_ranges: Vec::new(),
});
}
}
if font_stack.is_empty() {
continue;
}
let key = FontChainKey::from_selectors(&font_stack);
let hash = {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
key.hash(&mut hasher);
hasher.finish()
};
if !seen_hashes.contains(&hash) {
seen_hashes.insert(hash);
let idx = font_stacks.len();
font_stacks.push(font_stack);
hash_to_index.insert(hash, idx);
}
}
CollectedFontStacks {
font_stacks,
hash_to_index,
font_refs,
}
}
pub fn resolve_font_chains(
collected: &CollectedFontStacks,
fc_cache: &FcFontCache,
) -> ResolvedFontChains {
let mut chains = HashMap::new();
for font_stack in &collected.font_stacks {
if font_stack.is_empty() {
continue;
}
let font_families: Vec<String> = font_stack
.iter()
.map(|s| s.family.clone())
.filter(|f| !f.is_empty())
.collect();
let font_families = if font_families.is_empty() {
vec!["sans-serif".to_string()]
} else {
font_families
};
let weight = font_stack[0].weight;
let is_italic = font_stack[0].style == FontStyle::Italic;
let is_oblique = font_stack[0].style == FontStyle::Oblique;
let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
font_families: font_families.clone(),
weight,
italic: is_italic,
oblique: is_oblique,
});
if chains.contains_key(&cache_key) {
continue;
}
let italic = if is_italic {
PatternMatch::True
} else {
PatternMatch::False
};
let oblique = if is_oblique {
PatternMatch::True
} else {
PatternMatch::False
};
let mut trace = Vec::new();
let chain =
fc_cache.resolve_font_chain(&font_families, weight, italic, oblique, &mut trace);
chains.insert(cache_key, chain);
}
for (ptr, _font_ref) in &collected.font_refs {
let cache_key = FontChainKeyOrRef::Ref(*ptr);
let _ = cache_key; }
ResolvedFontChains { chains }
}
pub fn collect_and_resolve_font_chains(
styled_dom: &StyledDom,
fc_cache: &FcFontCache,
platform: &azul_css::system::Platform,
) -> ResolvedFontChains {
let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
resolve_font_chains(&collected, fc_cache)
}
pub fn register_embedded_fonts_from_styled_dom<T: crate::font_traits::ParsedFontTrait>(
styled_dom: &StyledDom,
font_manager: &crate::text3::cache::FontManager<T>,
platform: &azul_css::system::Platform,
) {
let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
for (_ptr, font_ref) in &collected.font_refs {
font_manager.register_embedded_font(font_ref);
}
}
use std::collections::HashSet;
use rust_fontconfig::FontId;
pub fn collect_font_ids_from_chains(chains: &ResolvedFontChains) -> HashSet<FontId> {
let mut font_ids = HashSet::new();
for chain in chains.chains.values() {
for group in &chain.css_fallbacks {
for font in &group.fonts {
font_ids.insert(font.id);
}
}
for font in &chain.unicode_fallbacks {
font_ids.insert(font.id);
}
}
font_ids
}
pub fn compute_fonts_to_load(
required_fonts: &HashSet<FontId>,
already_loaded: &HashSet<FontId>,
) -> HashSet<FontId> {
required_fonts.difference(already_loaded).cloned().collect()
}
#[derive(Debug)]
pub struct FontLoadResult<T> {
pub loaded: HashMap<FontId, T>,
pub failed: Vec<(FontId, String)>,
}
pub fn load_fonts_from_disk<T, F>(
font_ids: &HashSet<FontId>,
fc_cache: &FcFontCache,
load_fn: F,
) -> FontLoadResult<T>
where
F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
{
let mut loaded = HashMap::new();
let mut failed = Vec::new();
for font_id in font_ids {
let font_bytes = match fc_cache.get_font_bytes(font_id) {
Some(bytes) => bytes,
None => {
failed.push((
*font_id,
format!("Could not get font bytes for {:?}", font_id),
));
continue;
}
};
let font_index = fc_cache
.get_font_by_id(font_id)
.and_then(|source| match source {
rust_fontconfig::FontSource::Disk(path) => Some(path.font_index),
rust_fontconfig::FontSource::Memory(font) => Some(font.font_index),
})
.unwrap_or(0) as usize;
match load_fn(&font_bytes, font_index) {
Ok(font) => {
loaded.insert(*font_id, font);
}
Err(e) => {
failed.push((
*font_id,
format!("Failed to parse font {:?}: {:?}", font_id, e),
));
}
}
}
FontLoadResult { loaded, failed }
}
pub fn resolve_and_load_fonts<T, F>(
styled_dom: &StyledDom,
fc_cache: &FcFontCache,
already_loaded: &HashSet<FontId>,
load_fn: F,
platform: &azul_css::system::Platform,
) -> (ResolvedFontChains, FontLoadResult<T>)
where
F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
{
let chains = collect_and_resolve_font_chains(styled_dom, fc_cache, platform);
let required_fonts = collect_font_ids_from_chains(&chains);
let fonts_to_load = compute_fonts_to_load(&required_fonts, already_loaded);
let load_result = load_fonts_from_disk(&fonts_to_load, fc_cache, load_fn);
(chains, load_result)
}
use azul_css::props::style::scrollbar::{
LayoutScrollbarWidth, ScrollbarColorCustom, ScrollbarInfo, StyleScrollbarColor,
SCROLLBAR_CLASSIC_LIGHT,
};
#[derive(Debug, Clone)]
pub struct ComputedScrollbarStyle {
pub width_mode: LayoutScrollbarWidth,
pub width_px: f32,
pub thumb_color: ColorU,
pub track_color: ColorU,
pub button_color: ColorU,
pub corner_color: ColorU,
pub clip_to_container_border: bool,
}
impl Default for ComputedScrollbarStyle {
fn default() -> Self {
Self {
width_mode: LayoutScrollbarWidth::Auto,
width_px: 16.0, thumb_color: ColorU::new(255, 0, 255, 255), track_color: ColorU::new(255, 165, 0, 255), button_color: ColorU::new(0, 255, 0, 255), corner_color: ColorU::new(0, 0, 255, 255), clip_to_container_border: false,
}
}
}
pub fn get_scrollbar_style(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> ComputedScrollbarStyle {
let node_data = &styled_dom.node_data.as_container()[node_id];
let mut result = ComputedScrollbarStyle::default();
if let Some(scrollbar_style) = styled_dom
.css_property_cache
.ptr
.get_scrollbar_style(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
{
result.width_px = match scrollbar_style.horizontal.width {
azul_css::props::layout::dimensions::LayoutWidth::Px(px) => {
px.to_pixels_internal(16.0, 16.0)
}
_ => 16.0,
};
result.thumb_color = extract_color_from_background(&scrollbar_style.horizontal.thumb);
result.track_color = extract_color_from_background(&scrollbar_style.horizontal.track);
result.button_color = extract_color_from_background(&scrollbar_style.horizontal.button);
result.corner_color = extract_color_from_background(&scrollbar_style.horizontal.corner);
result.clip_to_container_border = scrollbar_style.horizontal.clip_to_container_border;
}
if let Some(scrollbar_width) = styled_dom
.css_property_cache
.ptr
.get_scrollbar_width(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
{
result.width_mode = *scrollbar_width;
result.width_px = match scrollbar_width {
LayoutScrollbarWidth::Auto => 16.0,
LayoutScrollbarWidth::Thin => 8.0,
LayoutScrollbarWidth::None => 0.0,
};
}
if let Some(scrollbar_color) = styled_dom
.css_property_cache
.ptr
.get_scrollbar_color(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
{
match scrollbar_color {
StyleScrollbarColor::Auto => {
}
StyleScrollbarColor::Custom(custom) => {
result.thumb_color = custom.thumb;
result.track_color = custom.track;
}
}
}
result
}
fn extract_color_from_background(
bg: &azul_css::props::style::background::StyleBackgroundContent,
) -> ColorU {
use azul_css::props::style::background::StyleBackgroundContent;
match bg {
StyleBackgroundContent::Color(c) => *c,
_ => ColorU::TRANSPARENT,
}
}
pub fn should_clip_scrollbar_to_border(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> bool {
let style = get_scrollbar_style(styled_dom, node_id, node_state);
style.clip_to_container_border
}
pub fn get_scrollbar_width_px(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> f32 {
let style = get_scrollbar_style(styled_dom, node_id, node_state);
style.width_px
}
pub fn is_text_selectable(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> bool {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom
.css_property_cache
.ptr
.get_user_select(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.map(|us| *us != StyleUserSelect::None)
.unwrap_or(true) }
pub fn is_node_contenteditable(styled_dom: &StyledDom, node_id: NodeId) -> bool {
use azul_core::dom::AttributeType;
let node_data = &styled_dom.node_data.as_container()[node_id];
if node_data.is_contenteditable() {
return true;
}
node_data.attributes.as_ref().iter().any(|attr| {
matches!(attr, AttributeType::ContentEditable(true))
})
}
use azul_css::props::layout::text::LayoutTextJustify;
use azul_css::props::layout::table::{LayoutTableLayout, StyleBorderCollapse, StyleCaptionSide};
use azul_css::props::style::text::StyleHyphens;
use azul_css::props::style::effects::StyleCursor;
impl ExtractPropertyValue<LayoutTextJustify> for CssProperty {
fn extract(&self) -> Option<LayoutTextJustify> {
match self {
Self::TextJustify(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleHyphens> for CssProperty {
fn extract(&self) -> Option<StyleHyphens> {
match self {
Self::Hyphens(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<LayoutTableLayout> for CssProperty {
fn extract(&self) -> Option<LayoutTableLayout> {
match self {
Self::TableLayout(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleBorderCollapse> for CssProperty {
fn extract(&self) -> Option<StyleBorderCollapse> {
match self {
Self::BorderCollapse(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleCaptionSide> for CssProperty {
fn extract(&self) -> Option<StyleCaptionSide> {
match self {
Self::CaptionSide(CssPropertyValue::Exact(v)) => Some(*v),
_ => None,
}
}
}
impl ExtractPropertyValue<StyleCursor> for CssProperty {
fn extract(&self) -> Option<StyleCursor> {
match self {
Self::Cursor(CssPropertyValue::Exact(v)) => Some(v.clone()),
_ => None,
}
}
}
get_css_property!(
get_text_justify,
get_text_justify,
LayoutTextJustify,
CssPropertyType::TextJustify
);
get_css_property!(
get_hyphens,
get_hyphens,
StyleHyphens,
CssPropertyType::Hyphens
);
get_css_property!(
get_table_layout,
get_table_layout,
LayoutTableLayout,
CssPropertyType::TableLayout
);
get_css_property!(
get_border_collapse,
get_border_collapse,
StyleBorderCollapse,
CssPropertyType::BorderCollapse,
compact = get_border_collapse
);
get_css_property!(
get_caption_side,
get_caption_side,
StyleCaptionSide,
CssPropertyType::CaptionSide
);
get_css_property!(
get_cursor_property,
get_cursor,
StyleCursor,
CssPropertyType::Cursor
);
pub fn get_height_value(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<LayoutHeight> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_height(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_shape_inside(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::layout::shape::ShapeInside> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_shape_inside(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_shape_outside(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::layout::shape::ShapeOutside> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_shape_outside(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_line_height_value(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleLineHeight> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_line_height(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_text_indent_value(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleTextIndent> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_text_indent(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_column_count(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::layout::column::ColumnCount> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_column_count(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_column_gap_value(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::layout::spacing::LayoutColumnGap> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_column_gap(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_initial_letter(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleInitialLetter> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_initial_letter(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_line_clamp(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleLineClamp> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_line_clamp(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_hanging_punctuation(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleHangingPunctuation> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_hanging_punctuation(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_text_combine_upright(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::text::StyleTextCombineUpright> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_text_combine_upright(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_exclusion_margin(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> f32 {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_exclusion_margin(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.map(|v| v.inner.get() as f32)
.unwrap_or(0.0)
}
pub fn get_hyphenation_language(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::azul_exclusion::StyleHyphenationLanguage> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_hyphenation_language(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_border_spacing(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> azul_css::props::layout::table::LayoutBorderSpacing {
use azul_css::props::basic::pixel::PixelValue;
if node_state.is_normal() {
if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
let h_raw = cc.get_border_spacing_h_raw(node_id.index());
let v_raw = cc.get_border_spacing_v_raw(node_id.index());
if h_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
&& v_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
{
return azul_css::props::layout::table::LayoutBorderSpacing {
horizontal: PixelValue::px(h_raw as f32 / 10.0),
vertical: PixelValue::px(v_raw as f32 / 10.0),
};
}
}
}
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_border_spacing(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
.unwrap_or_default()
}
pub fn get_opacity(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> f32 {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_opacity(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.map(|v| v.inner.normalized())
.unwrap_or(1.0)
}
pub fn get_filter(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::filter::StyleFilterVec> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_filter(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_backdrop_filter(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::filter::StyleFilterVec> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_backdrop_filter(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_box_shadow_left(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_box_shadow_left(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_box_shadow_right(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_box_shadow_right(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_box_shadow_top(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_box_shadow_top(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_box_shadow_bottom(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_box_shadow_bottom(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_text_shadow(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_text_shadow(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_transform(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::transform::StyleTransformVec> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_transform(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_display_raw(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<LayoutDisplay> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_display(node_data, &node_id, node_state)
.and_then(|v| v.get_property().copied())
}
pub fn get_counter_reset(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::content::CounterReset> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_counter_reset(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn get_counter_increment(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<azul_css::props::style::content::CounterIncrement> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom.css_property_cache.ptr
.get_counter_increment(node_data, &node_id, node_state)
.and_then(|v| v.get_property())
.cloned()
}
pub fn is_node_contenteditable_inherited(styled_dom: &StyledDom, node_id: NodeId) -> bool {
use azul_core::dom::AttributeType;
let node_data_container = styled_dom.node_data.as_container();
let hierarchy = styled_dom.node_hierarchy.as_container();
let mut current_node_id = Some(node_id);
while let Some(nid) = current_node_id {
let node_data = &node_data_container[nid];
if node_data.is_contenteditable() {
return true;
}
for attr in node_data.attributes.as_ref().iter() {
if let AttributeType::ContentEditable(is_editable) = attr {
return *is_editable;
}
}
current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
}
false
}
pub fn find_contenteditable_ancestor(styled_dom: &StyledDom, node_id: NodeId) -> Option<NodeId> {
use azul_core::dom::AttributeType;
let node_data_container = styled_dom.node_data.as_container();
let hierarchy = styled_dom.node_hierarchy.as_container();
let mut current_node_id = Some(node_id);
while let Some(nid) = current_node_id {
let node_data = &node_data_container[nid];
if node_data.is_contenteditable() {
return Some(nid);
}
for attr in node_data.attributes.as_ref().iter() {
if let AttributeType::ContentEditable(is_editable) = attr {
if *is_editable {
return Some(nid);
} else {
return None;
}
}
}
current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
}
None
}
macro_rules! get_css_property_value {
($fn_name:ident, $cache_method:ident, $ret_type:ty) => {
pub fn $fn_name(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<$ret_type> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom
.css_property_cache
.ptr
.$cache_method(node_data, &node_id, node_state)
.cloned()
}
};
}
get_css_property_value!(get_flex_direction_prop, get_flex_direction, LayoutFlexDirectionValue);
get_css_property_value!(get_flex_wrap_prop, get_flex_wrap, LayoutFlexWrapValue);
get_css_property_value!(get_flex_grow_prop, get_flex_grow, LayoutFlexGrowValue);
get_css_property_value!(get_flex_shrink_prop, get_flex_shrink, LayoutFlexShrinkValue);
get_css_property_value!(get_flex_basis_prop, get_flex_basis, LayoutFlexBasisValue);
get_css_property_value!(get_align_items_prop, get_align_items, LayoutAlignItemsValue);
get_css_property_value!(get_align_self_prop, get_align_self, LayoutAlignSelfValue);
get_css_property_value!(get_align_content_prop, get_align_content, LayoutAlignContentValue);
get_css_property_value!(get_justify_content_prop, get_justify_content, LayoutJustifyContentValue);
get_css_property_value!(get_justify_items_prop, get_justify_items, LayoutJustifyItemsValue);
get_css_property_value!(get_justify_self_prop, get_justify_self, LayoutJustifySelfValue);
get_css_property_value!(get_gap_prop, get_gap, LayoutGapValue);
get_css_property_value!(get_grid_template_rows_prop, get_grid_template_rows, LayoutGridTemplateRowsValue);
get_css_property_value!(get_grid_template_columns_prop, get_grid_template_columns, LayoutGridTemplateColumnsValue);
get_css_property_value!(get_grid_auto_rows_prop, get_grid_auto_rows, LayoutGridAutoRowsValue);
get_css_property_value!(get_grid_auto_columns_prop, get_grid_auto_columns, LayoutGridAutoColumnsValue);
get_css_property_value!(get_grid_auto_flow_prop, get_grid_auto_flow, LayoutGridAutoFlowValue);
get_css_property_value!(get_grid_column_prop, get_grid_column, LayoutGridColumnValue);
get_css_property_value!(get_grid_row_prop, get_grid_row, LayoutGridRowValue);
pub fn get_grid_template_areas_prop(
styled_dom: &StyledDom,
node_id: NodeId,
node_state: &StyledNodeState,
) -> Option<GridTemplateAreas> {
let node_data = &styled_dom.node_data.as_container()[node_id];
styled_dom
.css_property_cache
.ptr
.get_property(node_data, &node_id, node_state, &CssPropertyType::GridTemplateAreas)
.and_then(|p| {
if let CssProperty::GridTemplateAreas(v) = p {
v.get_property().cloned()
} else {
None
}
})
}