1use azul_core::{
7 dom::{NodeId, NodeType},
8 geom::LogicalSize,
9 id::NodeId as CoreNodeId,
10 styled_dom::{StyledDom, StyledNodeState},
11};
12use azul_css::{
13 css::CssPropertyValue,
14 props::{
15 basic::{
16 font::{StyleFontFamily, StyleFontFamilyVec, StyleFontWeight, StyleFontStyle},
17 pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
18 ColorU, PhysicalSize, PixelValue, PropertyContext, ResolutionContext,
19 },
20 layout::{
21 BoxDecorationBreak, BreakInside, LayoutBoxSizing, LayoutClear, LayoutDisplay,
22 LayoutFlexDirection, LayoutFlexWrap, LayoutFloat, LayoutHeight,
23 LayoutJustifyContent, LayoutAlignItems, LayoutAlignContent, LayoutOverflow,
24 LayoutPosition, LayoutWidth, LayoutWritingMode, Orphans, PageBreak, Widows,
25 grid::GridTemplateAreas,
26 },
27 property::{CssProperty, CssPropertyType,
28 LayoutFlexBasisValue, LayoutFlexDirectionValue, LayoutFlexWrapValue,
29 LayoutFlexGrowValue, LayoutFlexShrinkValue,
30 LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutAlignContentValue,
31 LayoutJustifyContentValue, LayoutJustifyItemsValue, LayoutJustifySelfValue,
32 LayoutGapValue,
33 LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
34 LayoutGridAutoColumnsValue, LayoutGridAutoRowsValue,
35 LayoutGridAutoFlowValue, LayoutGridColumnValue, LayoutGridRowValue,
36 },
37 style::{
38 border_radius::StyleBorderRadius,
39 lists::{StyleListStylePosition, StyleListStyleType},
40 StyleDirection, StyleTextAlign, StyleUserSelect, StyleVerticalAlign,
41 StyleVisibility, StyleWhiteSpace,
42 },
43 },
44};
45
46use crate::{
47 font_traits::{ParsedFontTrait, StyleProperties},
48 solver3::{
49 display_list::{BorderRadius, PhysicalSizeImport},
50 layout_tree::LayoutNode,
51 scrollbar::ScrollbarRequirements,
52 },
53};
54
55pub fn get_element_font_size(
59 styled_dom: &StyledDom,
60 dom_id: NodeId,
61 node_state: &StyledNodeState,
62) -> f32 {
63 let node_data = &styled_dom.node_data.as_container()[dom_id];
64 let cache = &styled_dom.css_property_cache.ptr;
65
66 let cached_font_size = cache
68 .dependency_chains
69 .get(dom_id.index())
70 .and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
71 .and_then(|chain| chain.cached_pixels);
72
73 if let Some(cached) = cached_font_size {
74 return cached;
75 }
76
77 let parent_font_size = styled_dom
79 .node_hierarchy
80 .as_container()
81 .get(dom_id)
82 .and_then(|node| node.parent_id())
83 .and_then(|parent_id| {
84 cache
86 .dependency_chains
87 .get(parent_id.index())
88 .and_then(|chains| {
89 chains.get(&azul_css::props::property::CssPropertyType::FontSize)
90 })
91 .and_then(|chain| chain.cached_pixels)
92 })
93 .unwrap_or(DEFAULT_FONT_SIZE);
94
95 let root_font_size = {
97 let root_id = NodeId::new(0);
98 cache
99 .dependency_chains
100 .get(root_id.index())
101 .and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
102 .and_then(|chain| chain.cached_pixels)
103 .unwrap_or(DEFAULT_FONT_SIZE)
104 };
105
106 cache
108 .get_font_size(node_data, &dom_id, node_state)
109 .and_then(|v| v.get_property().cloned())
110 .map(|v| {
111 let context = ResolutionContext {
112 element_font_size: DEFAULT_FONT_SIZE, parent_font_size,
114 root_font_size,
115 containing_block_size: PhysicalSize::new(0.0, 0.0),
116 element_size: None,
117 viewport_size: PhysicalSize::new(0.0, 0.0), };
119
120 v.inner
121 .resolve_with_context(&context, PropertyContext::FontSize)
122 })
123 .unwrap_or(DEFAULT_FONT_SIZE)
124}
125
126pub fn get_parent_font_size(
128 styled_dom: &StyledDom,
129 dom_id: NodeId,
130 node_state: &StyledNodeState,
131) -> f32 {
132 styled_dom
133 .node_hierarchy
134 .as_container()
135 .get(dom_id)
136 .and_then(|node| node.parent_id())
137 .map(|parent_id| get_element_font_size(styled_dom, parent_id, node_state))
138 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE)
139}
140
141pub fn get_root_font_size(styled_dom: &StyledDom, node_state: &StyledNodeState) -> f32 {
143 get_element_font_size(styled_dom, NodeId::new(0), node_state)
145}
146
147#[derive(Debug, Copy, Clone, PartialEq)]
150pub enum MultiValue<T> {
151 Auto,
153 Initial,
155 Inherit,
157 Exact(T),
159}
160
161impl<T> MultiValue<T> {
162 pub fn is_auto(&self) -> bool {
164 matches!(self, MultiValue::Auto)
165 }
166
167 pub fn is_exact(&self) -> bool {
169 matches!(self, MultiValue::Exact(_))
170 }
171
172 pub fn exact(self) -> Option<T> {
174 match self {
175 MultiValue::Exact(v) => Some(v),
176 _ => None,
177 }
178 }
179
180 pub fn unwrap_or(self, default: T) -> T {
182 match self {
183 MultiValue::Exact(v) => v,
184 _ => default,
185 }
186 }
187
188 pub fn unwrap_or_default(self) -> T
190 where
191 T: Default,
192 {
193 match self {
194 MultiValue::Exact(v) => v,
195 _ => T::default(),
196 }
197 }
198
199 pub fn map<U, F>(self, f: F) -> MultiValue<U>
201 where
202 F: FnOnce(T) -> U,
203 {
204 match self {
205 MultiValue::Exact(v) => MultiValue::Exact(f(v)),
206 MultiValue::Auto => MultiValue::Auto,
207 MultiValue::Initial => MultiValue::Initial,
208 MultiValue::Inherit => MultiValue::Inherit,
209 }
210 }
211}
212
213impl MultiValue<LayoutOverflow> {
215 pub fn is_clipped(&self) -> bool {
218 matches!(
219 self,
220 MultiValue::Exact(
221 LayoutOverflow::Hidden
222 | LayoutOverflow::Clip
223 | LayoutOverflow::Auto
224 | LayoutOverflow::Scroll
225 )
226 )
227 }
228
229 pub fn is_scroll(&self) -> bool {
230 matches!(
231 self,
232 MultiValue::Exact(LayoutOverflow::Scroll | LayoutOverflow::Auto)
233 )
234 }
235
236 pub fn is_auto_overflow(&self) -> bool {
237 matches!(self, MultiValue::Exact(LayoutOverflow::Auto))
238 }
239
240 pub fn is_hidden(&self) -> bool {
241 matches!(self, MultiValue::Exact(LayoutOverflow::Hidden))
242 }
243
244 pub fn is_hidden_or_clip(&self) -> bool {
245 matches!(
246 self,
247 MultiValue::Exact(LayoutOverflow::Hidden | LayoutOverflow::Clip)
248 )
249 }
250
251 pub fn is_scroll_explicit(&self) -> bool {
252 matches!(self, MultiValue::Exact(LayoutOverflow::Scroll))
253 }
254
255 pub fn is_visible_or_clip(&self) -> bool {
256 matches!(
257 self,
258 MultiValue::Exact(LayoutOverflow::Visible | LayoutOverflow::Clip)
259 )
260 }
261}
262
263impl MultiValue<LayoutPosition> {
265 pub fn is_absolute_or_fixed(&self) -> bool {
266 matches!(
267 self,
268 MultiValue::Exact(LayoutPosition::Absolute | LayoutPosition::Fixed)
269 )
270 }
271}
272
273impl MultiValue<LayoutFloat> {
275 pub fn is_none(&self) -> bool {
276 matches!(
277 self,
278 MultiValue::Auto
279 | MultiValue::Initial
280 | MultiValue::Inherit
281 | MultiValue::Exact(LayoutFloat::None)
282 )
283 }
284}
285
286impl<T: Default> Default for MultiValue<T> {
287 fn default() -> Self {
288 MultiValue::Auto
289 }
290}
291
292macro_rules! get_css_property_pixel {
295 ($fn_name:ident, $cache_method:ident, $ua_property:expr, compact_i16 = $compact_method:ident) => {
297 pub fn $fn_name(
298 styled_dom: &StyledDom,
299 node_id: NodeId,
300 node_state: &StyledNodeState,
301 ) -> MultiValue<PixelValue> {
302 if node_state.is_normal() {
304 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
305 let raw = cc.$compact_method(node_id.index());
306 if raw == azul_css::compact_cache::I16_AUTO {
307 return MultiValue::Auto;
308 }
309 if raw == azul_css::compact_cache::I16_INITIAL {
310 return MultiValue::Initial;
311 }
312 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
313 return MultiValue::Exact(PixelValue::px(raw as f32 / 10.0));
315 }
316 }
318 }
319
320 let node_data = &styled_dom.node_data.as_container()[node_id];
321
322 let author_css = styled_dom
323 .css_property_cache
324 .ptr
325 .$cache_method(node_data, &node_id, node_state);
326
327 if let Some(ref val) = author_css {
328 if val.is_auto() {
329 return MultiValue::Auto;
330 }
331 if let Some(exact) = val.get_property().copied() {
332 return MultiValue::Exact(exact.inner);
333 }
334 }
335
336 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
337
338 if let Some(ua_prop) = ua_css {
339 if let Some(inner) = ua_prop.get_pixel_inner() {
340 return MultiValue::Exact(inner);
341 }
342 }
343
344 MultiValue::Initial
345 }
346 };
347 ($fn_name:ident, $cache_method:ident, $ua_property:expr) => {
349 pub fn $fn_name(
350 styled_dom: &StyledDom,
351 node_id: NodeId,
352 node_state: &StyledNodeState,
353 ) -> MultiValue<PixelValue> {
354 let node_data = &styled_dom.node_data.as_container()[node_id];
355
356 let author_css = styled_dom
358 .css_property_cache
359 .ptr
360 .$cache_method(node_data, &node_id, node_state);
361
362 if let Some(ref val) = author_css {
366 if val.is_auto() {
367 return MultiValue::Auto;
368 }
369 if let Some(exact) = val.get_property().copied() {
370 return MultiValue::Exact(exact.inner);
371 }
372 }
374
375 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
377
378 if let Some(ua_prop) = ua_css {
379 if let Some(inner) = ua_prop.get_pixel_inner() {
380 return MultiValue::Exact(inner);
381 }
382 }
383
384 MultiValue::Initial
389 }
390 };
391}
392
393trait CssPropertyPixelInner {
395 fn get_pixel_inner(&self) -> Option<PixelValue>;
396}
397
398impl CssPropertyPixelInner for azul_css::props::property::CssProperty {
399 fn get_pixel_inner(&self) -> Option<PixelValue> {
400 match self {
401 CssProperty::Left(CssPropertyValue::Exact(v)) => Some(v.inner),
402 CssProperty::Right(CssPropertyValue::Exact(v)) => Some(v.inner),
403 CssProperty::Top(CssPropertyValue::Exact(v)) => Some(v.inner),
404 CssProperty::Bottom(CssPropertyValue::Exact(v)) => Some(v.inner),
405 CssProperty::MarginLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
406 CssProperty::MarginRight(CssPropertyValue::Exact(v)) => Some(v.inner),
407 CssProperty::MarginTop(CssPropertyValue::Exact(v)) => Some(v.inner),
408 CssProperty::MarginBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
409 CssProperty::PaddingLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
410 CssProperty::PaddingRight(CssPropertyValue::Exact(v)) => Some(v.inner),
411 CssProperty::PaddingTop(CssPropertyValue::Exact(v)) => Some(v.inner),
412 CssProperty::PaddingBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
413 _ => None,
414 }
415 }
416}
417
418macro_rules! get_css_property {
420 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact = $compact_method:ident) => {
422 pub fn $fn_name(
423 styled_dom: &StyledDom,
424 node_id: NodeId,
425 node_state: &StyledNodeState,
426 ) -> MultiValue<$return_type> {
427 if node_state.is_normal() {
429 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
430 return MultiValue::Exact(cc.$compact_method(node_id.index()));
431 }
432 }
433
434 let node_data = &styled_dom.node_data.as_container()[node_id];
436
437 let author_css = styled_dom
439 .css_property_cache
440 .ptr
441 .$cache_method(node_data, &node_id, node_state);
442
443 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
444 return MultiValue::Exact(val);
445 }
446
447 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
449
450 if let Some(ua_prop) = ua_css {
451 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
452 return MultiValue::Exact(val);
453 }
454 }
455
456 MultiValue::Auto
458 }
459 };
460 ($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) => {
463 pub fn $fn_name(
464 styled_dom: &StyledDom,
465 node_id: NodeId,
466 node_state: &StyledNodeState,
467 ) -> MultiValue<$return_type> {
468 if node_state.is_normal() {
470 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
471 let raw = cc.$compact_raw_method(node_id.index());
472 match raw {
473 azul_css::compact_cache::U32_AUTO => return MultiValue::Auto,
474 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
475 azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
476 azul_css::compact_cache::U32_MIN_CONTENT => return MultiValue::Exact($min_content_variant),
477 azul_css::compact_cache::U32_MAX_CONTENT => return MultiValue::Exact($max_content_variant),
478 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
479 }
481 _ => {
482 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
484 return MultiValue::Exact($px_variant(pv));
485 }
486 }
488 }
489 }
490 }
491
492 let node_data = &styled_dom.node_data.as_container()[node_id];
494
495 let author_css = styled_dom
496 .css_property_cache
497 .ptr
498 .$cache_method(node_data, &node_id, node_state);
499
500 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
501 return MultiValue::Exact(val);
502 }
503
504 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
505
506 if let Some(ua_prop) = ua_css {
507 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
508 return MultiValue::Exact(val);
509 }
510 }
511
512 MultiValue::Auto
513 }
514 };
515 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact_u32_struct = $compact_raw_method:ident) => {
518 pub fn $fn_name(
519 styled_dom: &StyledDom,
520 node_id: NodeId,
521 node_state: &StyledNodeState,
522 ) -> MultiValue<$return_type> {
523 if node_state.is_normal() {
525 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
526 let raw = cc.$compact_raw_method(node_id.index());
527 match raw {
528 azul_css::compact_cache::U32_AUTO | azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
529 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
530 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
531 }
533 _ => {
534 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
535 return MultiValue::Exact(
536 <$return_type as azul_css::props::PixelValueTaker>::from_pixel_value(pv)
537 );
538 }
539 }
540 }
541 }
542 }
543
544 let node_data = &styled_dom.node_data.as_container()[node_id];
546
547 let author_css = styled_dom
548 .css_property_cache
549 .ptr
550 .$cache_method(node_data, &node_id, node_state);
551
552 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
553 return MultiValue::Exact(val);
554 }
555
556 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
557
558 if let Some(ua_prop) = ua_css {
559 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
560 return MultiValue::Exact(val);
561 }
562 }
563
564 MultiValue::Auto
565 }
566 };
567 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr) => {
569 pub fn $fn_name(
570 styled_dom: &StyledDom,
571 node_id: NodeId,
572 node_state: &StyledNodeState,
573 ) -> MultiValue<$return_type> {
574 let node_data = &styled_dom.node_data.as_container()[node_id];
575
576 let author_css = styled_dom
578 .css_property_cache
579 .ptr
580 .$cache_method(node_data, &node_id, node_state);
581
582 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
583 return MultiValue::Exact(val);
584 }
585
586 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
588
589 if let Some(ua_prop) = ua_css {
590 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
591 return MultiValue::Exact(val);
592 }
593 }
594
595 MultiValue::Auto
597 }
598 };
599}
600
601trait ExtractPropertyValue<T> {
603 fn extract(&self) -> Option<T>;
604}
605
606fn extract_property_value<T>(prop: &azul_css::props::property::CssProperty) -> Option<T>
607where
608 azul_css::props::property::CssProperty: ExtractPropertyValue<T>,
609{
610 prop.extract()
611}
612
613impl ExtractPropertyValue<LayoutWidth> for azul_css::props::property::CssProperty {
616 fn extract(&self) -> Option<LayoutWidth> {
617 match self {
618 Self::Width(CssPropertyValue::Exact(v)) => Some(v.clone()),
619 _ => None,
620 }
621 }
622}
623
624impl ExtractPropertyValue<LayoutHeight> for azul_css::props::property::CssProperty {
625 fn extract(&self) -> Option<LayoutHeight> {
626 match self {
627 Self::Height(CssPropertyValue::Exact(v)) => Some(v.clone()),
628 _ => None,
629 }
630 }
631}
632
633impl ExtractPropertyValue<LayoutMinWidth> for azul_css::props::property::CssProperty {
634 fn extract(&self) -> Option<LayoutMinWidth> {
635 match self {
636 Self::MinWidth(CssPropertyValue::Exact(v)) => Some(*v),
637 _ => None,
638 }
639 }
640}
641
642impl ExtractPropertyValue<LayoutMinHeight> for azul_css::props::property::CssProperty {
643 fn extract(&self) -> Option<LayoutMinHeight> {
644 match self {
645 Self::MinHeight(CssPropertyValue::Exact(v)) => Some(*v),
646 _ => None,
647 }
648 }
649}
650
651impl ExtractPropertyValue<LayoutMaxWidth> for azul_css::props::property::CssProperty {
652 fn extract(&self) -> Option<LayoutMaxWidth> {
653 match self {
654 Self::MaxWidth(CssPropertyValue::Exact(v)) => Some(*v),
655 _ => None,
656 }
657 }
658}
659
660impl ExtractPropertyValue<LayoutMaxHeight> for azul_css::props::property::CssProperty {
661 fn extract(&self) -> Option<LayoutMaxHeight> {
662 match self {
663 Self::MaxHeight(CssPropertyValue::Exact(v)) => Some(*v),
664 _ => None,
665 }
666 }
667}
668
669impl ExtractPropertyValue<LayoutDisplay> for azul_css::props::property::CssProperty {
670 fn extract(&self) -> Option<LayoutDisplay> {
671 match self {
672 Self::Display(CssPropertyValue::Exact(v)) => Some(*v),
673 _ => None,
674 }
675 }
676}
677
678impl ExtractPropertyValue<LayoutWritingMode> for azul_css::props::property::CssProperty {
679 fn extract(&self) -> Option<LayoutWritingMode> {
680 match self {
681 Self::WritingMode(CssPropertyValue::Exact(v)) => Some(*v),
682 _ => None,
683 }
684 }
685}
686
687impl ExtractPropertyValue<LayoutFlexWrap> for azul_css::props::property::CssProperty {
688 fn extract(&self) -> Option<LayoutFlexWrap> {
689 match self {
690 Self::FlexWrap(CssPropertyValue::Exact(v)) => Some(*v),
691 _ => None,
692 }
693 }
694}
695
696impl ExtractPropertyValue<LayoutJustifyContent> for azul_css::props::property::CssProperty {
697 fn extract(&self) -> Option<LayoutJustifyContent> {
698 match self {
699 Self::JustifyContent(CssPropertyValue::Exact(v)) => Some(*v),
700 _ => None,
701 }
702 }
703}
704
705impl ExtractPropertyValue<StyleTextAlign> for azul_css::props::property::CssProperty {
706 fn extract(&self) -> Option<StyleTextAlign> {
707 match self {
708 Self::TextAlign(CssPropertyValue::Exact(v)) => Some(*v),
709 _ => None,
710 }
711 }
712}
713
714impl ExtractPropertyValue<LayoutFloat> for azul_css::props::property::CssProperty {
715 fn extract(&self) -> Option<LayoutFloat> {
716 match self {
717 Self::Float(CssPropertyValue::Exact(v)) => Some(*v),
718 _ => None,
719 }
720 }
721}
722
723impl ExtractPropertyValue<LayoutClear> for azul_css::props::property::CssProperty {
724 fn extract(&self) -> Option<LayoutClear> {
725 match self {
726 Self::Clear(CssPropertyValue::Exact(v)) => Some(*v),
727 _ => None,
728 }
729 }
730}
731
732impl ExtractPropertyValue<LayoutOverflow> for azul_css::props::property::CssProperty {
733 fn extract(&self) -> Option<LayoutOverflow> {
734 match self {
735 Self::OverflowX(CssPropertyValue::Exact(v)) => Some(*v),
736 Self::OverflowY(CssPropertyValue::Exact(v)) => Some(*v),
737 _ => None,
738 }
739 }
740}
741
742impl ExtractPropertyValue<LayoutPosition> for azul_css::props::property::CssProperty {
743 fn extract(&self) -> Option<LayoutPosition> {
744 match self {
745 Self::Position(CssPropertyValue::Exact(v)) => Some(*v),
746 _ => None,
747 }
748 }
749}
750
751impl ExtractPropertyValue<LayoutBoxSizing> for azul_css::props::property::CssProperty {
752 fn extract(&self) -> Option<LayoutBoxSizing> {
753 match self {
754 Self::BoxSizing(CssPropertyValue::Exact(v)) => Some(*v),
755 _ => None,
756 }
757 }
758}
759
760impl ExtractPropertyValue<PixelValue> for azul_css::props::property::CssProperty {
761 fn extract(&self) -> Option<PixelValue> {
762 self.get_pixel_inner()
763 }
764}
765
766impl ExtractPropertyValue<LayoutFlexDirection> for azul_css::props::property::CssProperty {
767 fn extract(&self) -> Option<LayoutFlexDirection> {
768 match self {
769 Self::FlexDirection(CssPropertyValue::Exact(v)) => Some(*v),
770 _ => None,
771 }
772 }
773}
774
775impl ExtractPropertyValue<LayoutAlignItems> for azul_css::props::property::CssProperty {
776 fn extract(&self) -> Option<LayoutAlignItems> {
777 match self {
778 Self::AlignItems(CssPropertyValue::Exact(v)) => Some(*v),
779 _ => None,
780 }
781 }
782}
783
784impl ExtractPropertyValue<LayoutAlignContent> for azul_css::props::property::CssProperty {
785 fn extract(&self) -> Option<LayoutAlignContent> {
786 match self {
787 Self::AlignContent(CssPropertyValue::Exact(v)) => Some(*v),
788 _ => None,
789 }
790 }
791}
792
793impl ExtractPropertyValue<StyleFontWeight> for azul_css::props::property::CssProperty {
794 fn extract(&self) -> Option<StyleFontWeight> {
795 match self {
796 Self::FontWeight(CssPropertyValue::Exact(v)) => Some(*v),
797 _ => None,
798 }
799 }
800}
801
802impl ExtractPropertyValue<StyleFontStyle> for azul_css::props::property::CssProperty {
803 fn extract(&self) -> Option<StyleFontStyle> {
804 match self {
805 Self::FontStyle(CssPropertyValue::Exact(v)) => Some(*v),
806 _ => None,
807 }
808 }
809}
810
811impl ExtractPropertyValue<StyleVisibility> for azul_css::props::property::CssProperty {
812 fn extract(&self) -> Option<StyleVisibility> {
813 match self {
814 Self::Visibility(CssPropertyValue::Exact(v)) => Some(*v),
815 _ => None,
816 }
817 }
818}
819
820impl ExtractPropertyValue<StyleWhiteSpace> for azul_css::props::property::CssProperty {
821 fn extract(&self) -> Option<StyleWhiteSpace> {
822 match self {
823 Self::WhiteSpace(CssPropertyValue::Exact(v)) => Some(*v),
824 _ => None,
825 }
826 }
827}
828
829impl ExtractPropertyValue<StyleDirection> for azul_css::props::property::CssProperty {
830 fn extract(&self) -> Option<StyleDirection> {
831 match self {
832 Self::Direction(CssPropertyValue::Exact(v)) => Some(*v),
833 _ => None,
834 }
835 }
836}
837
838impl ExtractPropertyValue<StyleVerticalAlign> for azul_css::props::property::CssProperty {
839 fn extract(&self) -> Option<StyleVerticalAlign> {
840 match self {
841 Self::VerticalAlign(CssPropertyValue::Exact(v)) => Some(*v),
842 _ => None,
843 }
844 }
845}
846
847get_css_property!(
848 get_writing_mode,
849 get_writing_mode,
850 LayoutWritingMode,
851 azul_css::props::property::CssPropertyType::WritingMode,
852 compact = get_writing_mode
853);
854
855get_css_property!(
856 get_css_width,
857 get_width,
858 LayoutWidth,
859 azul_css::props::property::CssPropertyType::Width,
860 compact_u32_dim = get_width_raw, LayoutWidth::Px, LayoutWidth::Auto, LayoutWidth::MinContent, LayoutWidth::MaxContent
861);
862
863get_css_property!(
864 get_css_height,
865 get_height,
866 LayoutHeight,
867 azul_css::props::property::CssPropertyType::Height,
868 compact_u32_dim = get_height_raw, LayoutHeight::Px, LayoutHeight::Auto, LayoutHeight::MinContent, LayoutHeight::MaxContent
869);
870
871get_css_property!(
872 get_wrap,
873 get_flex_wrap,
874 LayoutFlexWrap,
875 azul_css::props::property::CssPropertyType::FlexWrap,
876 compact = get_flex_wrap
877);
878
879get_css_property!(
880 get_justify_content,
881 get_justify_content,
882 LayoutJustifyContent,
883 azul_css::props::property::CssPropertyType::JustifyContent,
884 compact = get_justify_content
885);
886
887get_css_property!(
888 get_text_align,
889 get_text_align,
890 StyleTextAlign,
891 azul_css::props::property::CssPropertyType::TextAlign,
892 compact = get_text_align
893);
894
895get_css_property!(
896 get_float,
897 get_float,
898 LayoutFloat,
899 azul_css::props::property::CssPropertyType::Float,
900 compact = get_float
901);
902
903get_css_property!(
904 get_clear,
905 get_clear,
906 LayoutClear,
907 azul_css::props::property::CssPropertyType::Clear,
908 compact = get_clear
909);
910
911get_css_property!(
912 get_overflow_x,
913 get_overflow_x,
914 LayoutOverflow,
915 azul_css::props::property::CssPropertyType::OverflowX,
916 compact = get_overflow_x
917);
918
919get_css_property!(
920 get_overflow_y,
921 get_overflow_y,
922 LayoutOverflow,
923 azul_css::props::property::CssPropertyType::OverflowY,
924 compact = get_overflow_y
925);
926
927get_css_property!(
928 get_position,
929 get_position,
930 LayoutPosition,
931 azul_css::props::property::CssPropertyType::Position,
932 compact = get_position
933);
934
935get_css_property!(
936 get_css_box_sizing,
937 get_box_sizing,
938 LayoutBoxSizing,
939 azul_css::props::property::CssPropertyType::BoxSizing,
940 compact = get_box_sizing
941);
942
943get_css_property!(
944 get_flex_direction,
945 get_flex_direction,
946 LayoutFlexDirection,
947 azul_css::props::property::CssPropertyType::FlexDirection,
948 compact = get_flex_direction
949);
950
951get_css_property!(
952 get_align_items,
953 get_align_items,
954 LayoutAlignItems,
955 azul_css::props::property::CssPropertyType::AlignItems,
956 compact = get_align_items
957);
958
959get_css_property!(
960 get_align_content,
961 get_align_content,
962 LayoutAlignContent,
963 azul_css::props::property::CssPropertyType::AlignContent,
964 compact = get_align_content
965);
966
967get_css_property!(
968 get_font_weight_property,
969 get_font_weight,
970 StyleFontWeight,
971 azul_css::props::property::CssPropertyType::FontWeight,
972 compact = get_font_weight
973);
974
975get_css_property!(
976 get_font_style_property,
977 get_font_style,
978 StyleFontStyle,
979 azul_css::props::property::CssPropertyType::FontStyle,
980 compact = get_font_style
981);
982
983get_css_property!(
984 get_visibility,
985 get_visibility,
986 StyleVisibility,
987 azul_css::props::property::CssPropertyType::Visibility,
988 compact = get_visibility
989);
990
991get_css_property!(
992 get_white_space_property,
993 get_white_space,
994 StyleWhiteSpace,
995 azul_css::props::property::CssPropertyType::WhiteSpace,
996 compact = get_white_space
997);
998
999get_css_property!(
1000 get_direction_property,
1001 get_direction,
1002 StyleDirection,
1003 azul_css::props::property::CssPropertyType::Direction,
1004 compact = get_direction
1005);
1006
1007get_css_property!(
1008 get_vertical_align_property,
1009 get_vertical_align,
1010 StyleVerticalAlign,
1011 azul_css::props::property::CssPropertyType::VerticalAlign,
1012 compact = get_vertical_align
1013);
1014pub fn get_style_border_radius(
1018 styled_dom: &StyledDom,
1019 node_id: NodeId,
1020 node_state: &StyledNodeState,
1021) -> azul_css::props::style::border_radius::StyleBorderRadius {
1022 let node_data = &styled_dom.node_data.as_container()[node_id];
1023
1024 let top_left = styled_dom
1025 .css_property_cache
1026 .ptr
1027 .get_border_top_left_radius(node_data, &node_id, node_state)
1028 .and_then(|br| br.get_property_or_default())
1029 .map(|v| v.inner)
1030 .unwrap_or_default();
1031
1032 let top_right = styled_dom
1033 .css_property_cache
1034 .ptr
1035 .get_border_top_right_radius(node_data, &node_id, node_state)
1036 .and_then(|br| br.get_property_or_default())
1037 .map(|v| v.inner)
1038 .unwrap_or_default();
1039
1040 let bottom_right = styled_dom
1041 .css_property_cache
1042 .ptr
1043 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1044 .and_then(|br| br.get_property_or_default())
1045 .map(|v| v.inner)
1046 .unwrap_or_default();
1047
1048 let bottom_left = styled_dom
1049 .css_property_cache
1050 .ptr
1051 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1052 .and_then(|br| br.get_property_or_default())
1053 .map(|v| v.inner)
1054 .unwrap_or_default();
1055
1056 StyleBorderRadius {
1057 top_left,
1058 top_right,
1059 bottom_right,
1060 bottom_left,
1061 }
1062}
1063
1064pub fn get_border_radius(
1070 styled_dom: &StyledDom,
1071 node_id: NodeId,
1072 node_state: &StyledNodeState,
1073 element_size: PhysicalSizeImport,
1074 viewport_size: LogicalSize,
1075) -> BorderRadius {
1076 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
1077
1078 let node_data = &styled_dom.node_data.as_container()[node_id];
1079
1080 let element_font_size = get_element_font_size(styled_dom, node_id, node_state);
1082 let parent_font_size = styled_dom
1083 .node_hierarchy
1084 .as_container()
1085 .get(node_id)
1086 .and_then(|node| node.parent_id())
1087 .map(|p| get_element_font_size(styled_dom, p, node_state))
1088 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
1089 let root_font_size = get_root_font_size(styled_dom, node_state);
1090
1091 let context = ResolutionContext {
1093 element_font_size,
1094 parent_font_size,
1095 root_font_size,
1096 containing_block_size: PhysicalSize::new(0.0, 0.0), element_size: Some(PhysicalSize::new(element_size.width, element_size.height)),
1098 viewport_size: PhysicalSize::new(viewport_size.width, viewport_size.height),
1099 };
1100
1101 let top_left = styled_dom
1102 .css_property_cache
1103 .ptr
1104 .get_border_top_left_radius(node_data, &node_id, node_state)
1105 .and_then(|br| br.get_property().cloned())
1106 .unwrap_or_default();
1107
1108 let top_right = styled_dom
1109 .css_property_cache
1110 .ptr
1111 .get_border_top_right_radius(node_data, &node_id, node_state)
1112 .and_then(|br| br.get_property().cloned())
1113 .unwrap_or_default();
1114
1115 let bottom_right = styled_dom
1116 .css_property_cache
1117 .ptr
1118 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1119 .and_then(|br| br.get_property().cloned())
1120 .unwrap_or_default();
1121
1122 let bottom_left = styled_dom
1123 .css_property_cache
1124 .ptr
1125 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1126 .and_then(|br| br.get_property().cloned())
1127 .unwrap_or_default();
1128
1129 BorderRadius {
1130 top_left: top_left
1131 .inner
1132 .resolve_with_context(&context, PropertyContext::BorderRadius),
1133 top_right: top_right
1134 .inner
1135 .resolve_with_context(&context, PropertyContext::BorderRadius),
1136 bottom_right: bottom_right
1137 .inner
1138 .resolve_with_context(&context, PropertyContext::BorderRadius),
1139 bottom_left: bottom_left
1140 .inner
1141 .resolve_with_context(&context, PropertyContext::BorderRadius),
1142 }
1143}
1144
1145pub fn get_z_index(styled_dom: &StyledDom, node_id: Option<NodeId>) -> i32 {
1151 use azul_css::props::layout::position::LayoutZIndex;
1152
1153 let node_id = match node_id {
1154 Some(id) => id,
1155 None => return 0,
1156 };
1157
1158 let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
1159
1160 if node_state.is_normal() {
1162 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1163 let raw = cc.get_z_index(node_id.index());
1164 if raw == azul_css::compact_cache::I16_AUTO {
1165 return 0;
1166 }
1167 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1168 return raw as i32;
1169 }
1170 }
1172 }
1173
1174 let node_data = &styled_dom.node_data.as_container()[node_id];
1176
1177 styled_dom
1178 .css_property_cache
1179 .ptr
1180 .get_z_index(node_data, &node_id, &node_state)
1181 .and_then(|v| v.get_property())
1182 .map(|z| match z {
1183 LayoutZIndex::Auto => 0,
1184 LayoutZIndex::Integer(i) => *i,
1185 })
1186 .unwrap_or(0)
1187}
1188
1189pub fn get_background_color(
1212 styled_dom: &StyledDom,
1213 node_id: NodeId,
1214 node_state: &StyledNodeState,
1215) -> ColorU {
1216 let node_data = &styled_dom.node_data.as_container()[node_id];
1217
1218 let get_node_bg = |node_id: NodeId, node_data: &azul_core::dom::NodeData| {
1220 styled_dom
1221 .css_property_cache
1222 .ptr
1223 .get_background_content(node_data, &node_id, node_state)
1224 .and_then(|bg| bg.get_property())
1225 .and_then(|bg_vec| bg_vec.get(0).cloned())
1226 .and_then(|first_bg| match &first_bg {
1227 azul_css::props::style::StyleBackgroundContent::Color(color) => Some(color.clone()),
1228 azul_css::props::style::StyleBackgroundContent::Image(_) => None, _ => None,
1230 })
1231 };
1232
1233 let own_bg = get_node_bg(node_id, node_data);
1234
1235 if !matches!(node_data.node_type, NodeType::Html) || own_bg.is_some() {
1239 return own_bg.unwrap_or(ColorU {
1241 r: 0,
1242 g: 0,
1243 b: 0,
1244 a: 0,
1245 });
1246 }
1247
1248 let first_child = styled_dom
1250 .node_hierarchy
1251 .as_container()
1252 .get(node_id)
1253 .and_then(|node| node.first_child_id(node_id));
1254
1255 let Some(first_child) = first_child else {
1256 return ColorU {
1257 r: 0,
1258 g: 0,
1259 b: 0,
1260 a: 0,
1261 };
1262 };
1263
1264 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1265
1266 if !matches!(first_child_data.node_type, NodeType::Body) {
1268 return ColorU {
1269 r: 0,
1270 g: 0,
1271 b: 0,
1272 a: 0,
1273 };
1274 }
1275
1276 get_node_bg(first_child, first_child_data).unwrap_or(ColorU {
1278 r: 0,
1279 g: 0,
1280 b: 0,
1281 a: 0,
1282 })
1283}
1284
1285pub fn get_background_contents(
1292 styled_dom: &StyledDom,
1293 node_id: NodeId,
1294 node_state: &StyledNodeState,
1295) -> Vec<azul_css::props::style::StyleBackgroundContent> {
1296 use azul_core::dom::NodeType;
1297 use azul_css::props::style::StyleBackgroundContent;
1298
1299 let node_data = &styled_dom.node_data.as_container()[node_id];
1300
1301 let get_node_backgrounds =
1303 |nid: NodeId, ndata: &azul_core::dom::NodeData| -> Vec<StyleBackgroundContent> {
1304 styled_dom
1305 .css_property_cache
1306 .ptr
1307 .get_background_content(ndata, &nid, node_state)
1308 .and_then(|bg| bg.get_property())
1309 .map(|bg_vec| bg_vec.iter().cloned().collect())
1310 .unwrap_or_default()
1311 };
1312
1313 let own_backgrounds = get_node_backgrounds(node_id, node_data);
1314
1315 if !matches!(node_data.node_type, NodeType::Html) || !own_backgrounds.is_empty() {
1318 return own_backgrounds;
1319 }
1320
1321 let first_child = styled_dom
1323 .node_hierarchy
1324 .as_container()
1325 .get(node_id)
1326 .and_then(|node| node.first_child_id(node_id));
1327
1328 let Some(first_child) = first_child else {
1329 return own_backgrounds;
1330 };
1331
1332 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1333
1334 if !matches!(first_child_data.node_type, NodeType::Body) {
1336 return own_backgrounds;
1337 }
1338
1339 get_node_backgrounds(first_child, first_child_data)
1341}
1342
1343pub struct BorderInfo {
1345 pub widths: crate::solver3::display_list::StyleBorderWidths,
1346 pub colors: crate::solver3::display_list::StyleBorderColors,
1347 pub styles: crate::solver3::display_list::StyleBorderStyles,
1348}
1349
1350pub fn get_border_info(
1351 styled_dom: &StyledDom,
1352 node_id: NodeId,
1353 node_state: &StyledNodeState,
1354) -> BorderInfo {
1355 use crate::solver3::display_list::{StyleBorderColors, StyleBorderStyles, StyleBorderWidths};
1356 use azul_css::css::CssPropertyValue;
1357 use azul_css::props::basic::color::ColorU;
1358 use azul_css::props::style::border::{
1359 BorderStyle, StyleBorderTopColor, StyleBorderRightColor,
1360 StyleBorderBottomColor, StyleBorderLeftColor,
1361 StyleBorderTopStyle, StyleBorderRightStyle,
1362 StyleBorderBottomStyle, StyleBorderLeftStyle,
1363 };
1364
1365 if node_state.is_normal() {
1367 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1368 let idx = node_id.index();
1369
1370 let node_data = &styled_dom.node_data.as_container()[node_id];
1372 let widths = StyleBorderWidths {
1373 top: styled_dom.css_property_cache.ptr
1374 .get_border_top_width(node_data, &node_id, node_state).cloned(),
1375 right: styled_dom.css_property_cache.ptr
1376 .get_border_right_width(node_data, &node_id, node_state).cloned(),
1377 bottom: styled_dom.css_property_cache.ptr
1378 .get_border_bottom_width(node_data, &node_id, node_state).cloned(),
1379 left: styled_dom.css_property_cache.ptr
1380 .get_border_left_width(node_data, &node_id, node_state).cloned(),
1381 };
1382
1383 let make_color = |raw: u32| -> Option<ColorU> {
1385 if raw == 0 { None } else {
1386 Some(ColorU {
1387 r: ((raw >> 24) & 0xFF) as u8,
1388 g: ((raw >> 16) & 0xFF) as u8,
1389 b: ((raw >> 8) & 0xFF) as u8,
1390 a: (raw & 0xFF) as u8,
1391 })
1392 }
1393 };
1394
1395 let colors = StyleBorderColors {
1396 top: make_color(cc.get_border_top_color_raw(idx))
1397 .map(|c| CssPropertyValue::Exact(StyleBorderTopColor { inner: c })),
1398 right: make_color(cc.get_border_right_color_raw(idx))
1399 .map(|c| CssPropertyValue::Exact(StyleBorderRightColor { inner: c })),
1400 bottom: make_color(cc.get_border_bottom_color_raw(idx))
1401 .map(|c| CssPropertyValue::Exact(StyleBorderBottomColor { inner: c })),
1402 left: make_color(cc.get_border_left_color_raw(idx))
1403 .map(|c| CssPropertyValue::Exact(StyleBorderLeftColor { inner: c })),
1404 };
1405
1406 let styles = StyleBorderStyles {
1408 top: Some(CssPropertyValue::Exact(StyleBorderTopStyle {
1409 inner: cc.get_border_top_style(idx),
1410 })),
1411 right: Some(CssPropertyValue::Exact(StyleBorderRightStyle {
1412 inner: cc.get_border_right_style(idx),
1413 })),
1414 bottom: Some(CssPropertyValue::Exact(StyleBorderBottomStyle {
1415 inner: cc.get_border_bottom_style(idx),
1416 })),
1417 left: Some(CssPropertyValue::Exact(StyleBorderLeftStyle {
1418 inner: cc.get_border_left_style(idx),
1419 })),
1420 };
1421
1422 return BorderInfo { widths, colors, styles };
1423 }
1424 }
1425
1426 let node_data = &styled_dom.node_data.as_container()[node_id];
1428
1429 let widths = StyleBorderWidths {
1431 top: styled_dom
1432 .css_property_cache
1433 .ptr
1434 .get_border_top_width(node_data, &node_id, node_state)
1435 .cloned(),
1436 right: styled_dom
1437 .css_property_cache
1438 .ptr
1439 .get_border_right_width(node_data, &node_id, node_state)
1440 .cloned(),
1441 bottom: styled_dom
1442 .css_property_cache
1443 .ptr
1444 .get_border_bottom_width(node_data, &node_id, node_state)
1445 .cloned(),
1446 left: styled_dom
1447 .css_property_cache
1448 .ptr
1449 .get_border_left_width(node_data, &node_id, node_state)
1450 .cloned(),
1451 };
1452
1453 let colors = StyleBorderColors {
1455 top: styled_dom
1456 .css_property_cache
1457 .ptr
1458 .get_border_top_color(node_data, &node_id, node_state)
1459 .cloned(),
1460 right: styled_dom
1461 .css_property_cache
1462 .ptr
1463 .get_border_right_color(node_data, &node_id, node_state)
1464 .cloned(),
1465 bottom: styled_dom
1466 .css_property_cache
1467 .ptr
1468 .get_border_bottom_color(node_data, &node_id, node_state)
1469 .cloned(),
1470 left: styled_dom
1471 .css_property_cache
1472 .ptr
1473 .get_border_left_color(node_data, &node_id, node_state)
1474 .cloned(),
1475 };
1476
1477 let styles = StyleBorderStyles {
1479 top: styled_dom
1480 .css_property_cache
1481 .ptr
1482 .get_border_top_style(node_data, &node_id, node_state)
1483 .cloned(),
1484 right: styled_dom
1485 .css_property_cache
1486 .ptr
1487 .get_border_right_style(node_data, &node_id, node_state)
1488 .cloned(),
1489 bottom: styled_dom
1490 .css_property_cache
1491 .ptr
1492 .get_border_bottom_style(node_data, &node_id, node_state)
1493 .cloned(),
1494 left: styled_dom
1495 .css_property_cache
1496 .ptr
1497 .get_border_left_style(node_data, &node_id, node_state)
1498 .cloned(),
1499 };
1500
1501 BorderInfo {
1502 widths,
1503 colors,
1504 styles,
1505 }
1506}
1507
1508pub fn get_inline_border_info(
1513 styled_dom: &StyledDom,
1514 node_id: NodeId,
1515 node_state: &StyledNodeState,
1516 border_info: &BorderInfo,
1517) -> Option<crate::text3::cache::InlineBorderInfo> {
1518 use crate::text3::cache::InlineBorderInfo;
1519
1520 fn get_border_width_px(
1522 width: &Option<
1523 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderTopWidth>,
1524 >,
1525 ) -> f32 {
1526 width
1527 .as_ref()
1528 .and_then(|v| v.get_property())
1529 .map(|w| w.inner.number.get())
1530 .unwrap_or(0.0)
1531 }
1532
1533 fn get_border_width_px_right(
1534 width: &Option<
1535 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderRightWidth>,
1536 >,
1537 ) -> f32 {
1538 width
1539 .as_ref()
1540 .and_then(|v| v.get_property())
1541 .map(|w| w.inner.number.get())
1542 .unwrap_or(0.0)
1543 }
1544
1545 fn get_border_width_px_bottom(
1546 width: &Option<
1547 azul_css::css::CssPropertyValue<
1548 azul_css::props::style::border::LayoutBorderBottomWidth,
1549 >,
1550 >,
1551 ) -> f32 {
1552 width
1553 .as_ref()
1554 .and_then(|v| v.get_property())
1555 .map(|w| w.inner.number.get())
1556 .unwrap_or(0.0)
1557 }
1558
1559 fn get_border_width_px_left(
1560 width: &Option<
1561 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderLeftWidth>,
1562 >,
1563 ) -> f32 {
1564 width
1565 .as_ref()
1566 .and_then(|v| v.get_property())
1567 .map(|w| w.inner.number.get())
1568 .unwrap_or(0.0)
1569 }
1570
1571 fn get_border_color_top(
1573 color: &Option<
1574 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderTopColor>,
1575 >,
1576 ) -> ColorU {
1577 color
1578 .as_ref()
1579 .and_then(|v| v.get_property())
1580 .map(|c| c.inner)
1581 .unwrap_or(ColorU::BLACK)
1582 }
1583
1584 fn get_border_color_right(
1585 color: &Option<
1586 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderRightColor>,
1587 >,
1588 ) -> ColorU {
1589 color
1590 .as_ref()
1591 .and_then(|v| v.get_property())
1592 .map(|c| c.inner)
1593 .unwrap_or(ColorU::BLACK)
1594 }
1595
1596 fn get_border_color_bottom(
1597 color: &Option<
1598 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderBottomColor>,
1599 >,
1600 ) -> ColorU {
1601 color
1602 .as_ref()
1603 .and_then(|v| v.get_property())
1604 .map(|c| c.inner)
1605 .unwrap_or(ColorU::BLACK)
1606 }
1607
1608 fn get_border_color_left(
1609 color: &Option<
1610 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderLeftColor>,
1611 >,
1612 ) -> ColorU {
1613 color
1614 .as_ref()
1615 .and_then(|v| v.get_property())
1616 .map(|c| c.inner)
1617 .unwrap_or(ColorU::BLACK)
1618 }
1619
1620 fn get_border_radius_px(
1622 styled_dom: &StyledDom,
1623 node_id: NodeId,
1624 node_state: &StyledNodeState,
1625 ) -> Option<f32> {
1626 let node_data = &styled_dom.node_data.as_container()[node_id];
1627
1628 let top_left = styled_dom
1629 .css_property_cache
1630 .ptr
1631 .get_border_top_left_radius(node_data, &node_id, node_state)
1632 .and_then(|br| br.get_property().cloned())
1633 .map(|v| v.inner.number.get());
1634
1635 let top_right = styled_dom
1636 .css_property_cache
1637 .ptr
1638 .get_border_top_right_radius(node_data, &node_id, node_state)
1639 .and_then(|br| br.get_property().cloned())
1640 .map(|v| v.inner.number.get());
1641
1642 let bottom_left = styled_dom
1643 .css_property_cache
1644 .ptr
1645 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1646 .and_then(|br| br.get_property().cloned())
1647 .map(|v| v.inner.number.get());
1648
1649 let bottom_right = styled_dom
1650 .css_property_cache
1651 .ptr
1652 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1653 .and_then(|br| br.get_property().cloned())
1654 .map(|v| v.inner.number.get());
1655
1656 let radii: Vec<f32> = [top_left, top_right, bottom_left, bottom_right]
1658 .into_iter()
1659 .filter_map(|r| r)
1660 .collect();
1661
1662 if radii.is_empty() {
1663 None
1664 } else {
1665 Some(radii.into_iter().fold(0.0f32, |a, b| a.max(b)))
1666 }
1667 }
1668
1669 let top = get_border_width_px(&border_info.widths.top);
1670 let right = get_border_width_px_right(&border_info.widths.right);
1671 let bottom = get_border_width_px_bottom(&border_info.widths.bottom);
1672 let left = get_border_width_px_left(&border_info.widths.left);
1673
1674 fn resolve_padding(mv: MultiValue<PixelValue>) -> f32 {
1676 match mv {
1677 MultiValue::Exact(pv) => {
1678 use azul_css::props::basic::SizeMetric;
1679 match pv.metric {
1680 SizeMetric::Px => pv.number.get(),
1681 SizeMetric::Pt => pv.number.get() * 1.333333,
1682 SizeMetric::Em | SizeMetric::Rem => pv.number.get() * 16.0,
1683 _ => 0.0,
1684 }
1685 }
1686 _ => 0.0,
1687 }
1688 }
1689
1690 let p_top = resolve_padding(get_css_padding_top(styled_dom, node_id, node_state));
1691 let p_right = resolve_padding(get_css_padding_right(styled_dom, node_id, node_state));
1692 let p_bottom = resolve_padding(get_css_padding_bottom(styled_dom, node_id, node_state));
1693 let p_left = resolve_padding(get_css_padding_left(styled_dom, node_id, node_state));
1694
1695 let has_border = top > 0.0 || right > 0.0 || bottom > 0.0 || left > 0.0;
1697 let has_padding = p_top > 0.0 || p_right > 0.0 || p_bottom > 0.0 || p_left > 0.0;
1698 if !has_border && !has_padding {
1699 return None;
1700 }
1701
1702 Some(InlineBorderInfo {
1703 top,
1704 right,
1705 bottom,
1706 left,
1707 top_color: get_border_color_top(&border_info.colors.top),
1708 right_color: get_border_color_right(&border_info.colors.right),
1709 bottom_color: get_border_color_bottom(&border_info.colors.bottom),
1710 left_color: get_border_color_left(&border_info.colors.left),
1711 radius: get_border_radius_px(styled_dom, node_id, node_state),
1712 padding_top: p_top,
1713 padding_right: p_right,
1714 padding_bottom: p_bottom,
1715 padding_left: p_left,
1716 })
1717}
1718
1719#[derive(Debug, Clone, Copy, Default)]
1723pub struct SelectionStyle {
1724 pub bg_color: ColorU,
1726 pub text_color: Option<ColorU>,
1728 pub radius: f32,
1730}
1731
1732pub fn get_selection_style(
1734 styled_dom: &StyledDom,
1735 node_id: Option<NodeId>,
1736 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
1737) -> SelectionStyle {
1738 let Some(node_id) = node_id else {
1739 return SelectionStyle::default();
1740 };
1741
1742 let node_data = &styled_dom.node_data.as_container()[node_id];
1743 let node_state = &StyledNodeState::default();
1744
1745 let default_bg = system_style
1747 .and_then(|ss| ss.colors.selection_background.as_option().copied())
1748 .unwrap_or(ColorU {
1749 r: 51,
1750 g: 153,
1751 b: 255, a: 128, });
1754
1755 let bg_color = styled_dom
1756 .css_property_cache
1757 .ptr
1758 .get_selection_background_color(node_data, &node_id, node_state)
1759 .and_then(|c| c.get_property().cloned())
1760 .map(|c| c.inner)
1761 .unwrap_or(default_bg);
1762
1763 let default_text = system_style
1765 .and_then(|ss| ss.colors.selection_text.as_option().copied());
1766
1767 let text_color = styled_dom
1768 .css_property_cache
1769 .ptr
1770 .get_selection_color(node_data, &node_id, node_state)
1771 .and_then(|c| c.get_property().cloned())
1772 .map(|c| c.inner)
1773 .or(default_text);
1774
1775 let radius = styled_dom
1776 .css_property_cache
1777 .ptr
1778 .get_selection_radius(node_data, &node_id, node_state)
1779 .and_then(|r| r.get_property().cloned())
1780 .map(|r| r.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(0.0);
1782
1783 SelectionStyle {
1784 bg_color,
1785 text_color,
1786 radius,
1787 }
1788}
1789
1790#[derive(Debug, Clone, Copy, Default)]
1792pub struct CaretStyle {
1793 pub color: ColorU,
1794 pub width: f32,
1795 pub animation_duration: u32,
1796}
1797
1798pub fn get_caret_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> CaretStyle {
1800 let Some(node_id) = node_id else {
1801 return CaretStyle::default();
1802 };
1803
1804 let node_data = &styled_dom.node_data.as_container()[node_id];
1805 let node_state = &StyledNodeState::default();
1806
1807 let color = styled_dom
1808 .css_property_cache
1809 .ptr
1810 .get_caret_color(node_data, &node_id, node_state)
1811 .and_then(|c| c.get_property().cloned())
1812 .map(|c| c.inner)
1813 .unwrap_or(ColorU {
1814 r: 255,
1815 g: 255,
1816 b: 255,
1817 a: 255, });
1819
1820 let width = styled_dom
1821 .css_property_cache
1822 .ptr
1823 .get_caret_width(node_data, &node_id, node_state)
1824 .and_then(|w| w.get_property().cloned())
1825 .map(|w| w.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(2.0); let animation_duration = styled_dom
1829 .css_property_cache
1830 .ptr
1831 .get_caret_animation_duration(node_data, &node_id, node_state)
1832 .and_then(|d| d.get_property().cloned())
1833 .map(|d| d.inner.inner) .unwrap_or(500); CaretStyle {
1837 color,
1838 width,
1839 animation_duration,
1840 }
1841}
1842
1843pub fn get_scrollbar_info_from_layout(node: &LayoutNode) -> ScrollbarRequirements {
1855 node.scrollbar_info
1856 .clone()
1857 .unwrap_or_default()
1858}
1859
1860pub fn get_layout_scrollbar_width_px<T: crate::font_traits::ParsedFontTrait>(
1874 ctx: &crate::solver3::LayoutContext<'_, T>,
1875 dom_id: NodeId,
1876 styled_node_state: &StyledNodeState,
1877) -> f32 {
1878 use azul_css::props::style::scrollbar::LayoutScrollbarWidth;
1879
1880 if let Some(ref sys) = ctx.system_style {
1882 use azul_css::system::ScrollbarVisibility;
1883 match sys.scrollbar_preferences.visibility {
1884 ScrollbarVisibility::WhenScrolling => return 0.0, ScrollbarVisibility::Always | ScrollbarVisibility::Automatic => {}
1886 }
1887 }
1888
1889 get_scrollbar_width_px(ctx.styled_dom, dom_id, styled_node_state)
1891}
1892
1893get_css_property!(
1894 get_display_property_internal,
1895 get_display,
1896 LayoutDisplay,
1897 azul_css::props::property::CssPropertyType::Display,
1898 compact = get_display
1899);
1900
1901pub fn get_display_property(
1902 styled_dom: &StyledDom,
1903 dom_id: Option<NodeId>,
1904) -> MultiValue<LayoutDisplay> {
1905 let Some(id) = dom_id else {
1906 return MultiValue::Exact(LayoutDisplay::Inline);
1907 };
1908 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1909 get_display_property_internal(styled_dom, id, node_state)
1910}
1911
1912pub fn get_vertical_align_for_node(
1915 styled_dom: &StyledDom,
1916 dom_id: NodeId,
1917) -> crate::text3::cache::VerticalAlign {
1918 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
1919 let va = match get_vertical_align_property(styled_dom, dom_id, node_state) {
1920 MultiValue::Exact(v) => v,
1921 _ => StyleVerticalAlign::default(),
1922 };
1923 match va {
1924 StyleVerticalAlign::Baseline => crate::text3::cache::VerticalAlign::Baseline,
1925 StyleVerticalAlign::Top => crate::text3::cache::VerticalAlign::Top,
1926 StyleVerticalAlign::Middle => crate::text3::cache::VerticalAlign::Middle,
1927 StyleVerticalAlign::Bottom => crate::text3::cache::VerticalAlign::Bottom,
1928 StyleVerticalAlign::Sub => crate::text3::cache::VerticalAlign::Sub,
1929 StyleVerticalAlign::Superscript => crate::text3::cache::VerticalAlign::Super,
1930 StyleVerticalAlign::TextTop => crate::text3::cache::VerticalAlign::TextTop,
1931 StyleVerticalAlign::TextBottom => crate::text3::cache::VerticalAlign::TextBottom,
1932 }
1933}
1934
1935pub fn get_style_properties(
1936 styled_dom: &StyledDom,
1937 dom_id: NodeId,
1938 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
1939) -> StyleProperties {
1940 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
1941
1942 let node_data = &styled_dom.node_data.as_container()[dom_id];
1943 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
1944 let cache = &styled_dom.css_property_cache.ptr;
1945
1946 use azul_css::props::basic::font::{StyleFontFamily, StyleFontFamilyVec};
1948
1949 let font_families = cache
1950 .get_font_family(node_data, &dom_id, node_state)
1951 .and_then(|v| v.get_property().cloned())
1952 .unwrap_or_else(|| {
1953 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
1955 });
1956
1957 let parent_font_size = styled_dom
1959 .node_hierarchy
1960 .as_container()
1961 .get(dom_id)
1962 .and_then(|node| {
1963 let parent_id = CoreNodeId::from_usize(node.parent)?;
1964 cache
1966 .get_font_size(
1967 &styled_dom.node_data.as_container()[parent_id],
1968 &parent_id,
1969 &styled_dom.styled_nodes.as_container()[parent_id].styled_node_state,
1970 )
1971 .and_then(|v| v.get_property().cloned())
1972 .map(|v| {
1973 use azul_css::props::basic::pixel::DEFAULT_FONT_SIZE;
1975 v.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE)
1976 })
1977 })
1978 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
1979
1980 let root_font_size = get_root_font_size(styled_dom, node_state);
1981
1982 let font_size_context = ResolutionContext {
1984 element_font_size: azul_css::props::basic::pixel::DEFAULT_FONT_SIZE, parent_font_size,
1986 root_font_size,
1987 containing_block_size: PhysicalSize::new(0.0, 0.0),
1988 element_size: None,
1989 viewport_size: PhysicalSize::new(0.0, 0.0), };
1991
1992 let font_size = {
1996 let mut fast_font_size = None;
1998 if node_state.is_normal() {
1999 if let Some(ref cc) = cache.compact_cache {
2000 let raw = cc.get_font_size_raw(dom_id.index());
2001 if raw != azul_css::compact_cache::U32_SENTINEL
2002 && raw != azul_css::compact_cache::U32_INHERIT
2003 && raw != azul_css::compact_cache::U32_INITIAL
2004 {
2005 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
2006 fast_font_size = Some(pv.resolve_with_context(
2007 &font_size_context,
2008 PropertyContext::FontSize,
2009 ));
2010 }
2011 }
2012 }
2013 }
2014 fast_font_size.unwrap_or_else(|| {
2015 cache
2016 .get_font_size(node_data, &dom_id, node_state)
2017 .and_then(|v| v.get_property().cloned())
2018 .map(|v| {
2019 v.inner
2020 .resolve_with_context(&font_size_context, PropertyContext::FontSize)
2021 })
2022 .unwrap_or(parent_font_size)
2023 })
2024 };
2025
2026 let color_from_cache = {
2027 let mut fast_color = None;
2029 if node_state.is_normal() {
2030 if let Some(ref cc) = cache.compact_cache {
2031 let raw = cc.get_text_color_raw(dom_id.index());
2032 if raw != 0 {
2033 fast_color = Some(ColorU {
2035 r: (raw >> 24) as u8,
2036 g: (raw >> 16) as u8,
2037 b: (raw >> 8) as u8,
2038 a: raw as u8,
2039 });
2040 }
2041 }
2042 }
2043 fast_color.or_else(|| {
2044 cache
2045 .get_text_color(node_data, &dom_id, node_state)
2046 .and_then(|v| v.get_property().cloned())
2047 .map(|v| v.inner)
2048 })
2049 };
2050
2051 let system_text_color = system_style
2053 .and_then(|ss| ss.colors.text.as_option().copied())
2054 .unwrap_or(ColorU::BLACK); let color = color_from_cache.unwrap_or(system_text_color);
2057
2058 let line_height = {
2059 let mut fast_lh = None;
2061 if node_state.is_normal() {
2062 if let Some(ref cc) = cache.compact_cache {
2063 if let Some(normalized) = cc.get_line_height(dom_id.index()) {
2064 fast_lh = Some(normalized / 100.0 * font_size);
2072 }
2073 }
2074 }
2075 fast_lh.unwrap_or_else(|| {
2076 cache
2077 .get_line_height(node_data, &dom_id, node_state)
2078 .and_then(|v| v.get_property().cloned())
2079 .map(|v| v.inner.normalized() * font_size)
2080 .unwrap_or(font_size * 1.2)
2081 })
2082 };
2083
2084 use azul_css::props::layout::LayoutDisplay;
2090 let display = cache
2091 .get_display(node_data, &dom_id, node_state)
2092 .and_then(|v| v.get_property().cloned())
2093 .unwrap_or(LayoutDisplay::Inline);
2094
2095 let (background_color, background_content, border) =
2098 if matches!(display, LayoutDisplay::Inline | LayoutDisplay::InlineBlock) {
2099 let bg = get_background_color(styled_dom, dom_id, node_state);
2100 let bg_color = if bg.a > 0 { Some(bg) } else { None };
2101
2102 let bg_contents = get_background_contents(styled_dom, dom_id, node_state);
2104
2105 let border_info = get_border_info(styled_dom, dom_id, node_state);
2107 let inline_border =
2108 get_inline_border_info(styled_dom, dom_id, node_state, &border_info);
2109
2110 (bg_color, bg_contents, inline_border)
2111 } else {
2112 (None, Vec::new(), None)
2115 };
2116
2117 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
2119 MultiValue::Exact(v) => v,
2120 _ => StyleFontWeight::Normal,
2121 };
2122
2123 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
2125 MultiValue::Exact(v) => v,
2126 _ => StyleFontStyle::Normal,
2127 };
2128
2129 let fc_weight = super::fc::convert_font_weight(font_weight);
2131 let fc_style = super::fc::convert_font_style(font_style);
2132
2133 let font_stack = {
2136 let font_ref = (0..font_families.len())
2138 .find_map(|i| {
2139 match font_families.get(i).unwrap() {
2140 azul_css::props::basic::font::StyleFontFamily::Ref(r) => Some(r.clone()),
2141 _ => None,
2142 }
2143 });
2144
2145 let platform = system_style.map(|ss| &ss.platform);
2147
2148 if let Some(font_ref) = font_ref {
2149 FontStack::Ref(font_ref)
2151 } else {
2152 let mut stack = Vec::with_capacity(font_families.len() + 3);
2154
2155 for i in 0..font_families.len() {
2156 let family = font_families.get(i).unwrap();
2157
2158 if let azul_css::props::basic::font::StyleFontFamily::SystemType(system_type) = family {
2161 if let Some(platform) = platform {
2162 let font_names = system_type.get_fallback_chain(platform);
2163 let system_weight = if system_type.is_bold() {
2164 rust_fontconfig::FcWeight::Bold
2165 } else {
2166 fc_weight
2167 };
2168 let system_style_val = if system_type.is_italic() {
2169 crate::text3::cache::FontStyle::Italic
2170 } else {
2171 fc_style
2172 };
2173 for font_name in font_names {
2174 stack.push(crate::text3::cache::FontSelector {
2175 family: font_name.to_string(),
2176 weight: system_weight,
2177 style: system_style_val,
2178 unicode_ranges: Vec::new(),
2179 });
2180 }
2181 } else {
2182 stack.push(crate::text3::cache::FontSelector {
2184 family: "sans-serif".to_string(),
2185 weight: fc_weight,
2186 style: fc_style,
2187 unicode_ranges: Vec::new(),
2188 });
2189 }
2190 } else {
2191 stack.push(crate::text3::cache::FontSelector {
2192 family: family.as_string(),
2193 weight: fc_weight,
2194 style: fc_style,
2195 unicode_ranges: Vec::new(),
2196 });
2197 }
2198 }
2199
2200 let generic_fallbacks = ["sans-serif", "serif", "monospace"];
2202 for fallback in &generic_fallbacks {
2203 if !stack
2204 .iter()
2205 .any(|f| f.family.to_lowercase() == fallback.to_lowercase())
2206 {
2207 stack.push(crate::text3::cache::FontSelector {
2208 family: fallback.to_string(),
2209 weight: rust_fontconfig::FcWeight::Normal,
2210 style: crate::text3::cache::FontStyle::Normal,
2211 unicode_ranges: Vec::new(),
2212 });
2213 }
2214 }
2215
2216 FontStack::Stack(stack)
2217 }
2218 };
2219
2220 let letter_spacing = {
2222 let mut fast_ls = None;
2224 if node_state.is_normal() {
2225 if let Some(ref cc) = cache.compact_cache {
2226 if let Some(px_val) = cc.get_letter_spacing(dom_id.index()) {
2227 fast_ls = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
2228 }
2229 }
2230 }
2231 fast_ls.unwrap_or_else(|| {
2232 cache
2233 .get_letter_spacing(node_data, &dom_id, node_state)
2234 .and_then(|v| v.get_property().cloned())
2235 .map(|v| {
2236 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
2237 crate::text3::cache::Spacing::Px(px_value.round() as i32)
2238 })
2239 .unwrap_or_default()
2240 })
2241 };
2242
2243 let word_spacing = {
2245 let mut fast_ws = None;
2247 if node_state.is_normal() {
2248 if let Some(ref cc) = cache.compact_cache {
2249 if let Some(px_val) = cc.get_word_spacing(dom_id.index()) {
2250 fast_ws = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
2251 }
2252 }
2253 }
2254 fast_ws.unwrap_or_else(|| {
2255 cache
2256 .get_word_spacing(node_data, &dom_id, node_state)
2257 .and_then(|v| v.get_property().cloned())
2258 .map(|v| {
2259 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
2260 crate::text3::cache::Spacing::Px(px_value.round() as i32)
2261 })
2262 .unwrap_or_default()
2263 })
2264 };
2265
2266 let text_decoration = cache
2268 .get_text_decoration(node_data, &dom_id, node_state)
2269 .and_then(|v| v.get_property().cloned())
2270 .map(|v| crate::text3::cache::TextDecoration::from_css(v))
2271 .unwrap_or_default();
2272
2273 let tab_size = {
2275 let mut fast_tab = None;
2277 if node_state.is_normal() {
2278 if let Some(ref cc) = cache.compact_cache {
2279 let raw = cc.get_tab_size_raw(dom_id.index());
2280 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
2281 fast_tab = Some(raw as f32 / 10.0);
2282 }
2283 }
2284 }
2285 fast_tab.unwrap_or_else(|| {
2286 cache
2287 .get_tab_size(node_data, &dom_id, node_state)
2288 .and_then(|v| v.get_property().cloned())
2289 .map(|v| v.inner.number.get())
2290 .unwrap_or(8.0)
2291 })
2292 };
2293
2294 let properties = StyleProperties {
2295 font_stack,
2296 font_size_px: font_size,
2297 color,
2298 background_color,
2299 background_content,
2300 border,
2301 line_height,
2302 letter_spacing,
2303 word_spacing,
2304 text_decoration,
2305 tab_size,
2306 ..Default::default()
2310 };
2311
2312 properties
2313}
2314
2315pub fn get_list_style_type(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> StyleListStyleType {
2316 let Some(id) = dom_id else {
2317 return StyleListStyleType::default();
2318 };
2319 let node_data = &styled_dom.node_data.as_container()[id];
2320 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2321 styled_dom
2322 .css_property_cache
2323 .ptr
2324 .get_list_style_type(node_data, &id, node_state)
2325 .and_then(|v| v.get_property().copied())
2326 .unwrap_or_default()
2327}
2328
2329pub fn get_list_style_position(
2330 styled_dom: &StyledDom,
2331 dom_id: Option<NodeId>,
2332) -> StyleListStylePosition {
2333 let Some(id) = dom_id else {
2334 return StyleListStylePosition::default();
2335 };
2336 let node_data = &styled_dom.node_data.as_container()[id];
2337 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2338 styled_dom
2339 .css_property_cache
2340 .ptr
2341 .get_list_style_position(node_data, &id, node_state)
2342 .and_then(|v| v.get_property().copied())
2343 .unwrap_or_default()
2344}
2345
2346use azul_css::props::layout::{
2349 LayoutInsetBottom, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight,
2350 LayoutMarginTop, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth,
2351 LayoutPaddingBottom, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutRight,
2352 LayoutTop,
2353};
2354
2355get_css_property_pixel!(
2357 get_css_left,
2358 get_left,
2359 azul_css::props::property::CssPropertyType::Left,
2360 compact_i16 = get_left
2361);
2362get_css_property_pixel!(
2363 get_css_right,
2364 get_right,
2365 azul_css::props::property::CssPropertyType::Right,
2366 compact_i16 = get_right
2367);
2368get_css_property_pixel!(
2369 get_css_top,
2370 get_top,
2371 azul_css::props::property::CssPropertyType::Top,
2372 compact_i16 = get_top
2373);
2374get_css_property_pixel!(
2375 get_css_bottom,
2376 get_bottom,
2377 azul_css::props::property::CssPropertyType::Bottom,
2378 compact_i16 = get_bottom
2379);
2380
2381get_css_property_pixel!(
2383 get_css_margin_left,
2384 get_margin_left,
2385 azul_css::props::property::CssPropertyType::MarginLeft,
2386 compact_i16 = get_margin_left_raw
2387);
2388get_css_property_pixel!(
2389 get_css_margin_right,
2390 get_margin_right,
2391 azul_css::props::property::CssPropertyType::MarginRight,
2392 compact_i16 = get_margin_right_raw
2393);
2394get_css_property_pixel!(
2395 get_css_margin_top,
2396 get_margin_top,
2397 azul_css::props::property::CssPropertyType::MarginTop,
2398 compact_i16 = get_margin_top_raw
2399);
2400get_css_property_pixel!(
2401 get_css_margin_bottom,
2402 get_margin_bottom,
2403 azul_css::props::property::CssPropertyType::MarginBottom,
2404 compact_i16 = get_margin_bottom_raw
2405);
2406
2407get_css_property_pixel!(
2409 get_css_padding_left,
2410 get_padding_left,
2411 azul_css::props::property::CssPropertyType::PaddingLeft,
2412 compact_i16 = get_padding_left_raw
2413);
2414get_css_property_pixel!(
2415 get_css_padding_right,
2416 get_padding_right,
2417 azul_css::props::property::CssPropertyType::PaddingRight,
2418 compact_i16 = get_padding_right_raw
2419);
2420get_css_property_pixel!(
2421 get_css_padding_top,
2422 get_padding_top,
2423 azul_css::props::property::CssPropertyType::PaddingTop,
2424 compact_i16 = get_padding_top_raw
2425);
2426get_css_property_pixel!(
2427 get_css_padding_bottom,
2428 get_padding_bottom,
2429 azul_css::props::property::CssPropertyType::PaddingBottom,
2430 compact_i16 = get_padding_bottom_raw
2431);
2432
2433get_css_property!(
2435 get_css_min_width,
2436 get_min_width,
2437 LayoutMinWidth,
2438 azul_css::props::property::CssPropertyType::MinWidth,
2439 compact_u32_struct = get_min_width_raw
2440);
2441
2442get_css_property!(
2443 get_css_min_height,
2444 get_min_height,
2445 LayoutMinHeight,
2446 azul_css::props::property::CssPropertyType::MinHeight,
2447 compact_u32_struct = get_min_height_raw
2448);
2449
2450get_css_property!(
2451 get_css_max_width,
2452 get_max_width,
2453 LayoutMaxWidth,
2454 azul_css::props::property::CssPropertyType::MaxWidth,
2455 compact_u32_struct = get_max_width_raw
2456);
2457
2458get_css_property!(
2459 get_css_max_height,
2460 get_max_height,
2461 LayoutMaxHeight,
2462 azul_css::props::property::CssPropertyType::MaxHeight,
2463 compact_u32_struct = get_max_height_raw
2464);
2465
2466get_css_property_pixel!(
2468 get_css_border_left_width,
2469 get_border_left_width,
2470 azul_css::props::property::CssPropertyType::BorderLeftWidth,
2471 compact_i16 = get_border_left_width_raw
2472);
2473get_css_property_pixel!(
2474 get_css_border_right_width,
2475 get_border_right_width,
2476 azul_css::props::property::CssPropertyType::BorderRightWidth,
2477 compact_i16 = get_border_right_width_raw
2478);
2479get_css_property_pixel!(
2480 get_css_border_top_width,
2481 get_border_top_width,
2482 azul_css::props::property::CssPropertyType::BorderTopWidth,
2483 compact_i16 = get_border_top_width_raw
2484);
2485get_css_property_pixel!(
2486 get_css_border_bottom_width,
2487 get_border_bottom_width,
2488 azul_css::props::property::CssPropertyType::BorderBottomWidth,
2489 compact_i16 = get_border_bottom_width_raw
2490);
2491
2492pub fn get_break_before(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
2496 let Some(id) = dom_id else {
2497 return PageBreak::Auto;
2498 };
2499 let node_data = &styled_dom.node_data.as_container()[id];
2500 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2501 styled_dom
2502 .css_property_cache
2503 .ptr
2504 .get_break_before(node_data, &id, node_state)
2505 .and_then(|v| v.get_property().cloned())
2506 .unwrap_or(PageBreak::Auto)
2507}
2508
2509pub fn get_break_after(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
2511 let Some(id) = dom_id else {
2512 return PageBreak::Auto;
2513 };
2514 let node_data = &styled_dom.node_data.as_container()[id];
2515 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2516 styled_dom
2517 .css_property_cache
2518 .ptr
2519 .get_break_after(node_data, &id, node_state)
2520 .and_then(|v| v.get_property().cloned())
2521 .unwrap_or(PageBreak::Auto)
2522}
2523
2524pub fn is_forced_page_break(page_break: PageBreak) -> bool {
2526 matches!(
2527 page_break,
2528 PageBreak::Always
2529 | PageBreak::Page
2530 | PageBreak::Left
2531 | PageBreak::Right
2532 | PageBreak::Recto
2533 | PageBreak::Verso
2534 | PageBreak::All
2535 )
2536}
2537
2538pub fn get_break_inside(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> BreakInside {
2540 let Some(id) = dom_id else {
2541 return BreakInside::Auto;
2542 };
2543 let node_data = &styled_dom.node_data.as_container()[id];
2544 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2545 styled_dom
2546 .css_property_cache
2547 .ptr
2548 .get_break_inside(node_data, &id, node_state)
2549 .and_then(|v| v.get_property().cloned())
2550 .unwrap_or(BreakInside::Auto)
2551}
2552
2553pub fn get_orphans(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
2555 let Some(id) = dom_id else {
2556 return 2; };
2558 let node_data = &styled_dom.node_data.as_container()[id];
2559 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2560 styled_dom
2561 .css_property_cache
2562 .ptr
2563 .get_orphans(node_data, &id, node_state)
2564 .and_then(|v| v.get_property().cloned())
2565 .map(|o| o.inner)
2566 .unwrap_or(2)
2567}
2568
2569pub fn get_widows(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
2571 let Some(id) = dom_id else {
2572 return 2; };
2574 let node_data = &styled_dom.node_data.as_container()[id];
2575 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2576 styled_dom
2577 .css_property_cache
2578 .ptr
2579 .get_widows(node_data, &id, node_state)
2580 .and_then(|v| v.get_property().cloned())
2581 .map(|w| w.inner)
2582 .unwrap_or(2)
2583}
2584
2585pub fn get_box_decoration_break(
2587 styled_dom: &StyledDom,
2588 dom_id: Option<NodeId>,
2589) -> BoxDecorationBreak {
2590 let Some(id) = dom_id else {
2591 return BoxDecorationBreak::Slice;
2592 };
2593 let node_data = &styled_dom.node_data.as_container()[id];
2594 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2595 styled_dom
2596 .css_property_cache
2597 .ptr
2598 .get_box_decoration_break(node_data, &id, node_state)
2599 .and_then(|v| v.get_property().cloned())
2600 .unwrap_or(BoxDecorationBreak::Slice)
2601}
2602
2603pub fn is_avoid_page_break(page_break: &PageBreak) -> bool {
2607 matches!(page_break, PageBreak::Avoid | PageBreak::AvoidPage)
2608}
2609
2610pub fn is_avoid_break_inside(break_inside: &BreakInside) -> bool {
2612 matches!(
2613 break_inside,
2614 BreakInside::Avoid | BreakInside::AvoidPage | BreakInside::AvoidColumn
2615 )
2616}
2617
2618use std::collections::HashMap;
2621
2622use rust_fontconfig::{FcFontCache, FcWeight, FontFallbackChain, PatternMatch};
2623
2624use crate::text3::cache::{FontChainKey, FontChainKeyOrRef, FontSelector, FontStack, FontStyle};
2625
2626#[derive(Debug, Clone)]
2629pub struct CollectedFontStacks {
2630 pub font_stacks: Vec<Vec<FontSelector>>,
2632 pub hash_to_index: HashMap<u64, usize>,
2634 pub font_refs: HashMap<usize, azul_css::props::basic::font::FontRef>,
2637}
2638
2639#[derive(Debug, Clone)]
2642pub struct ResolvedFontChains {
2643 pub chains: HashMap<FontChainKeyOrRef, FontFallbackChain>,
2647}
2648
2649impl ResolvedFontChains {
2650 pub fn get(&self, key: &FontChainKeyOrRef) -> Option<&FontFallbackChain> {
2652 self.chains.get(key)
2653 }
2654
2655 pub fn get_by_chain_key(&self, key: &FontChainKey) -> Option<&FontFallbackChain> {
2657 self.chains.get(&FontChainKeyOrRef::Chain(key.clone()))
2658 }
2659
2660 pub fn get_for_font_stack(&self, font_stack: &[FontSelector]) -> Option<&FontFallbackChain> {
2662 let key = FontChainKeyOrRef::Chain(FontChainKey::from_selectors(font_stack));
2663 self.chains.get(&key)
2664 }
2665
2666 pub fn get_for_font_ref(&self, ptr: usize) -> Option<&FontFallbackChain> {
2668 self.chains.get(&FontChainKeyOrRef::Ref(ptr))
2669 }
2670
2671 pub fn into_inner(self) -> HashMap<FontChainKeyOrRef, FontFallbackChain> {
2675 self.chains
2676 }
2677
2678 pub fn into_fontconfig_chains(self) -> HashMap<FontChainKey, FontFallbackChain> {
2683 self.chains
2684 .into_iter()
2685 .filter_map(|(key, chain)| {
2686 match key {
2687 FontChainKeyOrRef::Chain(chain_key) => Some((chain_key, chain)),
2688 FontChainKeyOrRef::Ref(_) => None,
2689 }
2690 })
2691 .collect()
2692 }
2693
2694 pub fn len(&self) -> usize {
2696 self.chains.len()
2697 }
2698
2699 pub fn is_empty(&self) -> bool {
2701 self.chains.is_empty()
2702 }
2703
2704 pub fn font_refs_len(&self) -> usize {
2706 self.chains.keys().filter(|k| k.is_ref()).count()
2707 }
2708}
2709
2710pub fn collect_font_stacks_from_styled_dom(
2722 styled_dom: &StyledDom,
2723 platform: &azul_css::system::Platform,
2724) -> CollectedFontStacks {
2725 let mut font_stacks = Vec::new();
2726 let mut hash_to_index: HashMap<u64, usize> = HashMap::new();
2727 let mut seen_hashes = std::collections::HashSet::new();
2728 let mut font_refs: HashMap<usize, azul_css::props::basic::font::FontRef> = HashMap::new();
2729
2730 let node_data_container = styled_dom.node_data.as_container();
2731 let styled_nodes_container = styled_dom.styled_nodes.as_container();
2732 let cache = &styled_dom.css_property_cache.ptr;
2733
2734 for (node_idx, node_data) in node_data_container.internal.iter().enumerate() {
2736 if !matches!(node_data.node_type, NodeType::Text(_)) {
2738 continue;
2739 }
2740
2741 let dom_id = match NodeId::from_usize(node_idx) {
2742 Some(id) => id,
2743 None => continue,
2744 };
2745
2746 let node_state = &styled_nodes_container[dom_id].styled_node_state;
2747
2748 let font_families = cache
2750 .get_font_family(node_data, &dom_id, node_state)
2751 .and_then(|v| v.get_property().cloned())
2752 .unwrap_or_else(|| {
2753 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2754 });
2755
2756 if let Some(first_family) = font_families.get(0) {
2759 if let StyleFontFamily::Ref(font_ref) = first_family {
2760 let ptr = font_ref.parsed as usize;
2761 if !font_refs.contains_key(&ptr) {
2762 font_refs.insert(ptr, font_ref.clone());
2763 }
2764 continue;
2766 }
2767 }
2768
2769 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
2771 MultiValue::Exact(v) => v,
2772 _ => StyleFontWeight::Normal,
2773 };
2774
2775 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
2776 MultiValue::Exact(v) => v,
2777 _ => StyleFontStyle::Normal,
2778 };
2779
2780 let mut fc_weight = super::fc::convert_font_weight(font_weight);
2782 let mut fc_style = super::fc::convert_font_style(font_style);
2783
2784 let mut font_stack = Vec::with_capacity(font_families.len() + 3);
2786
2787 for i in 0..font_families.len() {
2788 let family = font_families.get(i).unwrap();
2789 if matches!(family, StyleFontFamily::Ref(_)) {
2791 continue;
2792 }
2793
2794 if let StyleFontFamily::SystemType(system_type) = family {
2797 let font_names = system_type.get_fallback_chain(platform);
2799
2800 let system_weight = if system_type.is_bold() {
2802 FcWeight::Bold
2803 } else {
2804 fc_weight
2805 };
2806 let system_style = if system_type.is_italic() {
2807 FontStyle::Italic
2808 } else {
2809 fc_style
2810 };
2811
2812 for font_name in font_names {
2814 font_stack.push(FontSelector {
2815 family: font_name.to_string(),
2816 weight: system_weight,
2817 style: system_style,
2818 unicode_ranges: Vec::new(),
2819 });
2820 }
2821 } else {
2822 font_stack.push(FontSelector {
2823 family: family.as_string(),
2824 weight: fc_weight,
2825 style: fc_style,
2826 unicode_ranges: Vec::new(),
2827 });
2828 }
2829 }
2830
2831 let generic_fallbacks = ["sans-serif", "serif", "monospace"];
2833 for fallback in &generic_fallbacks {
2834 if !font_stack
2835 .iter()
2836 .any(|f| f.family.to_lowercase() == fallback.to_lowercase())
2837 {
2838 font_stack.push(FontSelector {
2839 family: fallback.to_string(),
2840 weight: FcWeight::Normal,
2841 style: FontStyle::Normal,
2842 unicode_ranges: Vec::new(),
2843 });
2844 }
2845 }
2846
2847 if font_stack.is_empty() {
2849 continue;
2850 }
2851
2852 let key = FontChainKey::from_selectors(&font_stack);
2854 let hash = {
2855 use std::hash::{Hash, Hasher};
2856 let mut hasher = std::collections::hash_map::DefaultHasher::new();
2857 key.hash(&mut hasher);
2858 hasher.finish()
2859 };
2860
2861 if !seen_hashes.contains(&hash) {
2863 seen_hashes.insert(hash);
2864 let idx = font_stacks.len();
2865 font_stacks.push(font_stack);
2866 hash_to_index.insert(hash, idx);
2867 }
2868 }
2869
2870 CollectedFontStacks {
2871 font_stacks,
2872 hash_to_index,
2873 font_refs,
2874 }
2875}
2876
2877pub fn resolve_font_chains(
2889 collected: &CollectedFontStacks,
2890 fc_cache: &FcFontCache,
2891) -> ResolvedFontChains {
2892 let mut chains = HashMap::new();
2893
2894 for font_stack in &collected.font_stacks {
2896 if font_stack.is_empty() {
2897 continue;
2898 }
2899
2900 let font_families: Vec<String> = font_stack
2902 .iter()
2903 .map(|s| s.family.clone())
2904 .filter(|f| !f.is_empty())
2905 .collect();
2906
2907 let font_families = if font_families.is_empty() {
2908 vec!["sans-serif".to_string()]
2909 } else {
2910 font_families
2911 };
2912
2913 let weight = font_stack[0].weight;
2914 let is_italic = font_stack[0].style == FontStyle::Italic;
2915 let is_oblique = font_stack[0].style == FontStyle::Oblique;
2916
2917 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
2918 font_families: font_families.clone(),
2919 weight,
2920 italic: is_italic,
2921 oblique: is_oblique,
2922 });
2923
2924 if chains.contains_key(&cache_key) {
2926 continue;
2927 }
2928
2929 let italic = if is_italic {
2934 PatternMatch::True
2935 } else {
2936 PatternMatch::False
2937 };
2938 let oblique = if is_oblique {
2939 PatternMatch::True
2940 } else {
2941 PatternMatch::False
2942 };
2943
2944 let mut trace = Vec::new();
2945 let chain =
2946 fc_cache.resolve_font_chain(&font_families, weight, italic, oblique, &mut trace);
2947
2948 chains.insert(cache_key, chain);
2949 }
2950
2951 for (ptr, _font_ref) in &collected.font_refs {
2957 let cache_key = FontChainKeyOrRef::Ref(*ptr);
2958
2959 let _ = cache_key; }
2964
2965 ResolvedFontChains { chains }
2966}
2967
2968pub fn collect_and_resolve_font_chains(
2978 styled_dom: &StyledDom,
2979 fc_cache: &FcFontCache,
2980 platform: &azul_css::system::Platform,
2981) -> ResolvedFontChains {
2982 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
2983 resolve_font_chains(&collected, fc_cache)
2984}
2985
2986pub fn register_embedded_fonts_from_styled_dom<T: crate::font_traits::ParsedFontTrait>(
2991 styled_dom: &StyledDom,
2992 font_manager: &crate::text3::cache::FontManager<T>,
2993 platform: &azul_css::system::Platform,
2994) {
2995 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
2996 for (_ptr, font_ref) in &collected.font_refs {
2997 font_manager.register_embedded_font(font_ref);
2998 }
2999}
3000
3001use std::collections::HashSet;
3004
3005use rust_fontconfig::FontId;
3006
3007pub fn collect_font_ids_from_chains(chains: &ResolvedFontChains) -> HashSet<FontId> {
3012 let mut font_ids = HashSet::new();
3013
3014 for chain in chains.chains.values() {
3015 for group in &chain.css_fallbacks {
3017 for font in &group.fonts {
3018 font_ids.insert(font.id);
3019 }
3020 }
3021
3022 for font in &chain.unicode_fallbacks {
3024 font_ids.insert(font.id);
3025 }
3026 }
3027
3028 font_ids
3029}
3030
3031pub fn compute_fonts_to_load(
3040 required_fonts: &HashSet<FontId>,
3041 already_loaded: &HashSet<FontId>,
3042) -> HashSet<FontId> {
3043 required_fonts.difference(already_loaded).cloned().collect()
3044}
3045
3046#[derive(Debug)]
3048pub struct FontLoadResult<T> {
3049 pub loaded: HashMap<FontId, T>,
3051 pub failed: Vec<(FontId, String)>,
3053}
3054
3055pub fn load_fonts_from_disk<T, F>(
3069 font_ids: &HashSet<FontId>,
3070 fc_cache: &FcFontCache,
3071 load_fn: F,
3072) -> FontLoadResult<T>
3073where
3074 F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
3075{
3076 let mut loaded = HashMap::new();
3077 let mut failed = Vec::new();
3078
3079 for font_id in font_ids {
3080 let font_bytes = match fc_cache.get_font_bytes(font_id) {
3082 Some(bytes) => bytes,
3083 None => {
3084 failed.push((
3085 *font_id,
3086 format!("Could not get font bytes for {:?}", font_id),
3087 ));
3088 continue;
3089 }
3090 };
3091
3092 let font_index = fc_cache
3094 .get_font_by_id(font_id)
3095 .and_then(|source| match source {
3096 rust_fontconfig::FontSource::Disk(path) => Some(path.font_index),
3097 rust_fontconfig::FontSource::Memory(font) => Some(font.font_index),
3098 })
3099 .unwrap_or(0) as usize;
3100
3101 match load_fn(&font_bytes, font_index) {
3103 Ok(font) => {
3104 loaded.insert(*font_id, font);
3105 }
3106 Err(e) => {
3107 failed.push((
3108 *font_id,
3109 format!("Failed to parse font {:?}: {:?}", font_id, e),
3110 ));
3111 }
3112 }
3113 }
3114
3115 FontLoadResult { loaded, failed }
3116}
3117
3118pub fn resolve_and_load_fonts<T, F>(
3137 styled_dom: &StyledDom,
3138 fc_cache: &FcFontCache,
3139 already_loaded: &HashSet<FontId>,
3140 load_fn: F,
3141 platform: &azul_css::system::Platform,
3142) -> (ResolvedFontChains, FontLoadResult<T>)
3143where
3144 F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
3145{
3146 let chains = collect_and_resolve_font_chains(styled_dom, fc_cache, platform);
3148
3149 let required_fonts = collect_font_ids_from_chains(&chains);
3151
3152 let fonts_to_load = compute_fonts_to_load(&required_fonts, already_loaded);
3154
3155 let load_result = load_fonts_from_disk(&fonts_to_load, fc_cache, load_fn);
3157
3158 (chains, load_result)
3159}
3160
3161use azul_css::props::style::scrollbar::{
3166 LayoutScrollbarWidth, ScrollbarColorCustom, ScrollbarInfo, StyleScrollbarColor,
3167 SCROLLBAR_CLASSIC_LIGHT,
3168};
3169
3170#[derive(Debug, Clone)]
3172pub struct ComputedScrollbarStyle {
3173 pub width_mode: LayoutScrollbarWidth,
3175 pub width_px: f32,
3177 pub thumb_color: ColorU,
3179 pub track_color: ColorU,
3181 pub button_color: ColorU,
3183 pub corner_color: ColorU,
3185 pub clip_to_container_border: bool,
3187}
3188
3189impl Default for ComputedScrollbarStyle {
3190 fn default() -> Self {
3191 Self {
3192 width_mode: LayoutScrollbarWidth::Auto,
3193 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,
3200 }
3201 }
3202}
3203
3204pub fn get_scrollbar_style(
3211 styled_dom: &StyledDom,
3212 node_id: NodeId,
3213 node_state: &StyledNodeState,
3214) -> ComputedScrollbarStyle {
3215 let node_data = &styled_dom.node_data.as_container()[node_id];
3216
3217 let mut result = ComputedScrollbarStyle::default();
3219
3220 if let Some(scrollbar_style) = styled_dom
3222 .css_property_cache
3223 .ptr
3224 .get_scrollbar_style(node_data, &node_id, node_state)
3225 .and_then(|v| v.get_property())
3226 {
3227 result.width_px = match scrollbar_style.horizontal.width {
3229 azul_css::props::layout::dimensions::LayoutWidth::Px(px) => {
3230 px.to_pixels_internal(16.0, 16.0)
3232 }
3233 _ => 16.0,
3234 };
3235 result.thumb_color = extract_color_from_background(&scrollbar_style.horizontal.thumb);
3236 result.track_color = extract_color_from_background(&scrollbar_style.horizontal.track);
3237 result.button_color = extract_color_from_background(&scrollbar_style.horizontal.button);
3238 result.corner_color = extract_color_from_background(&scrollbar_style.horizontal.corner);
3239 result.clip_to_container_border = scrollbar_style.horizontal.clip_to_container_border;
3240 }
3241
3242 if let Some(scrollbar_width) = styled_dom
3244 .css_property_cache
3245 .ptr
3246 .get_scrollbar_width(node_data, &node_id, node_state)
3247 .and_then(|v| v.get_property())
3248 {
3249 result.width_mode = *scrollbar_width;
3250 result.width_px = match scrollbar_width {
3251 LayoutScrollbarWidth::Auto => 16.0,
3252 LayoutScrollbarWidth::Thin => 8.0,
3253 LayoutScrollbarWidth::None => 0.0,
3254 };
3255 }
3256
3257 if let Some(scrollbar_color) = styled_dom
3259 .css_property_cache
3260 .ptr
3261 .get_scrollbar_color(node_data, &node_id, node_state)
3262 .and_then(|v| v.get_property())
3263 {
3264 match scrollbar_color {
3265 StyleScrollbarColor::Auto => {
3266 }
3268 StyleScrollbarColor::Custom(custom) => {
3269 result.thumb_color = custom.thumb;
3270 result.track_color = custom.track;
3271 }
3272 }
3273 }
3274
3275 result
3276}
3277
3278fn extract_color_from_background(
3280 bg: &azul_css::props::style::background::StyleBackgroundContent,
3281) -> ColorU {
3282 use azul_css::props::style::background::StyleBackgroundContent;
3283 match bg {
3284 StyleBackgroundContent::Color(c) => *c,
3285 _ => ColorU::TRANSPARENT,
3286 }
3287}
3288
3289pub fn should_clip_scrollbar_to_border(
3291 styled_dom: &StyledDom,
3292 node_id: NodeId,
3293 node_state: &StyledNodeState,
3294) -> bool {
3295 let style = get_scrollbar_style(styled_dom, node_id, node_state);
3296 style.clip_to_container_border
3297}
3298
3299pub fn get_scrollbar_width_px(
3301 styled_dom: &StyledDom,
3302 node_id: NodeId,
3303 node_state: &StyledNodeState,
3304) -> f32 {
3305 let style = get_scrollbar_style(styled_dom, node_id, node_state);
3306 style.width_px
3307}
3308
3309pub fn is_text_selectable(
3314 styled_dom: &StyledDom,
3315 node_id: NodeId,
3316 node_state: &StyledNodeState,
3317) -> bool {
3318 let node_data = &styled_dom.node_data.as_container()[node_id];
3319
3320 styled_dom
3321 .css_property_cache
3322 .ptr
3323 .get_user_select(node_data, &node_id, node_state)
3324 .and_then(|v| v.get_property())
3325 .map(|us| *us != StyleUserSelect::None)
3326 .unwrap_or(true) }
3328
3329pub fn is_node_contenteditable(styled_dom: &StyledDom, node_id: NodeId) -> bool {
3337 use azul_core::dom::AttributeType;
3338
3339 let node_data = &styled_dom.node_data.as_container()[node_id];
3340
3341 if node_data.is_contenteditable() {
3343 return true;
3344 }
3345
3346 node_data.attributes.as_ref().iter().any(|attr| {
3349 matches!(attr, AttributeType::ContentEditable(true))
3350 })
3351}
3352use azul_css::props::layout::text::LayoutTextJustify;
3357use azul_css::props::layout::table::{LayoutTableLayout, StyleBorderCollapse, StyleCaptionSide};
3358use azul_css::props::style::text::StyleHyphens;
3359use azul_css::props::style::effects::StyleCursor;
3360
3361impl ExtractPropertyValue<LayoutTextJustify> for CssProperty {
3362 fn extract(&self) -> Option<LayoutTextJustify> {
3363 match self {
3364 Self::TextJustify(CssPropertyValue::Exact(v)) => Some(*v),
3365 _ => None,
3366 }
3367 }
3368}
3369
3370impl ExtractPropertyValue<StyleHyphens> for CssProperty {
3371 fn extract(&self) -> Option<StyleHyphens> {
3372 match self {
3373 Self::Hyphens(CssPropertyValue::Exact(v)) => Some(*v),
3374 _ => None,
3375 }
3376 }
3377}
3378
3379impl ExtractPropertyValue<LayoutTableLayout> for CssProperty {
3380 fn extract(&self) -> Option<LayoutTableLayout> {
3381 match self {
3382 Self::TableLayout(CssPropertyValue::Exact(v)) => Some(*v),
3383 _ => None,
3384 }
3385 }
3386}
3387
3388impl ExtractPropertyValue<StyleBorderCollapse> for CssProperty {
3389 fn extract(&self) -> Option<StyleBorderCollapse> {
3390 match self {
3391 Self::BorderCollapse(CssPropertyValue::Exact(v)) => Some(*v),
3392 _ => None,
3393 }
3394 }
3395}
3396
3397impl ExtractPropertyValue<StyleCaptionSide> for CssProperty {
3398 fn extract(&self) -> Option<StyleCaptionSide> {
3399 match self {
3400 Self::CaptionSide(CssPropertyValue::Exact(v)) => Some(*v),
3401 _ => None,
3402 }
3403 }
3404}
3405
3406impl ExtractPropertyValue<StyleCursor> for CssProperty {
3407 fn extract(&self) -> Option<StyleCursor> {
3408 match self {
3409 Self::Cursor(CssPropertyValue::Exact(v)) => Some(v.clone()),
3410 _ => None,
3411 }
3412 }
3413}
3414
3415get_css_property!(
3420 get_text_justify,
3421 get_text_justify,
3422 LayoutTextJustify,
3423 CssPropertyType::TextJustify
3424);
3425
3426get_css_property!(
3427 get_hyphens,
3428 get_hyphens,
3429 StyleHyphens,
3430 CssPropertyType::Hyphens
3431);
3432
3433get_css_property!(
3434 get_table_layout,
3435 get_table_layout,
3436 LayoutTableLayout,
3437 CssPropertyType::TableLayout
3438);
3439
3440get_css_property!(
3441 get_border_collapse,
3442 get_border_collapse,
3443 StyleBorderCollapse,
3444 CssPropertyType::BorderCollapse,
3445 compact = get_border_collapse
3446);
3447
3448get_css_property!(
3449 get_caption_side,
3450 get_caption_side,
3451 StyleCaptionSide,
3452 CssPropertyType::CaptionSide
3453);
3454
3455get_css_property!(
3456 get_cursor_property,
3457 get_cursor,
3458 StyleCursor,
3459 CssPropertyType::Cursor
3460);
3461
3462pub fn get_height_value(
3468 styled_dom: &StyledDom,
3469 node_id: NodeId,
3470 node_state: &StyledNodeState,
3471) -> Option<LayoutHeight> {
3472 let node_data = &styled_dom.node_data.as_container()[node_id];
3473 styled_dom.css_property_cache.ptr
3474 .get_height(node_data, &node_id, node_state)
3475 .and_then(|v| v.get_property())
3476 .cloned()
3477}
3478
3479pub fn get_shape_inside(
3481 styled_dom: &StyledDom,
3482 node_id: NodeId,
3483 node_state: &StyledNodeState,
3484) -> Option<azul_css::props::layout::shape::ShapeInside> {
3485 let node_data = &styled_dom.node_data.as_container()[node_id];
3486 styled_dom.css_property_cache.ptr
3487 .get_shape_inside(node_data, &node_id, node_state)
3488 .and_then(|v| v.get_property())
3489 .cloned()
3490}
3491
3492pub fn get_shape_outside(
3494 styled_dom: &StyledDom,
3495 node_id: NodeId,
3496 node_state: &StyledNodeState,
3497) -> Option<azul_css::props::layout::shape::ShapeOutside> {
3498 let node_data = &styled_dom.node_data.as_container()[node_id];
3499 styled_dom.css_property_cache.ptr
3500 .get_shape_outside(node_data, &node_id, node_state)
3501 .and_then(|v| v.get_property())
3502 .cloned()
3503}
3504
3505pub fn get_line_height_value(
3507 styled_dom: &StyledDom,
3508 node_id: NodeId,
3509 node_state: &StyledNodeState,
3510) -> Option<azul_css::props::style::text::StyleLineHeight> {
3511 let node_data = &styled_dom.node_data.as_container()[node_id];
3512 styled_dom.css_property_cache.ptr
3513 .get_line_height(node_data, &node_id, node_state)
3514 .and_then(|v| v.get_property())
3515 .cloned()
3516}
3517
3518pub fn get_text_indent_value(
3520 styled_dom: &StyledDom,
3521 node_id: NodeId,
3522 node_state: &StyledNodeState,
3523) -> Option<azul_css::props::style::text::StyleTextIndent> {
3524 let node_data = &styled_dom.node_data.as_container()[node_id];
3525 styled_dom.css_property_cache.ptr
3526 .get_text_indent(node_data, &node_id, node_state)
3527 .and_then(|v| v.get_property())
3528 .cloned()
3529}
3530
3531pub fn get_column_count(
3533 styled_dom: &StyledDom,
3534 node_id: NodeId,
3535 node_state: &StyledNodeState,
3536) -> Option<azul_css::props::layout::column::ColumnCount> {
3537 let node_data = &styled_dom.node_data.as_container()[node_id];
3538 styled_dom.css_property_cache.ptr
3539 .get_column_count(node_data, &node_id, node_state)
3540 .and_then(|v| v.get_property())
3541 .cloned()
3542}
3543
3544pub fn get_column_gap_value(
3546 styled_dom: &StyledDom,
3547 node_id: NodeId,
3548 node_state: &StyledNodeState,
3549) -> Option<azul_css::props::layout::spacing::LayoutColumnGap> {
3550 let node_data = &styled_dom.node_data.as_container()[node_id];
3551 styled_dom.css_property_cache.ptr
3552 .get_column_gap(node_data, &node_id, node_state)
3553 .and_then(|v| v.get_property())
3554 .cloned()
3555}
3556
3557pub fn get_initial_letter(
3559 styled_dom: &StyledDom,
3560 node_id: NodeId,
3561 node_state: &StyledNodeState,
3562) -> Option<azul_css::props::style::text::StyleInitialLetter> {
3563 let node_data = &styled_dom.node_data.as_container()[node_id];
3564 styled_dom.css_property_cache.ptr
3565 .get_initial_letter(node_data, &node_id, node_state)
3566 .and_then(|v| v.get_property())
3567 .cloned()
3568}
3569
3570pub fn get_line_clamp(
3572 styled_dom: &StyledDom,
3573 node_id: NodeId,
3574 node_state: &StyledNodeState,
3575) -> Option<azul_css::props::style::text::StyleLineClamp> {
3576 let node_data = &styled_dom.node_data.as_container()[node_id];
3577 styled_dom.css_property_cache.ptr
3578 .get_line_clamp(node_data, &node_id, node_state)
3579 .and_then(|v| v.get_property())
3580 .cloned()
3581}
3582
3583pub fn get_hanging_punctuation(
3585 styled_dom: &StyledDom,
3586 node_id: NodeId,
3587 node_state: &StyledNodeState,
3588) -> Option<azul_css::props::style::text::StyleHangingPunctuation> {
3589 let node_data = &styled_dom.node_data.as_container()[node_id];
3590 styled_dom.css_property_cache.ptr
3591 .get_hanging_punctuation(node_data, &node_id, node_state)
3592 .and_then(|v| v.get_property())
3593 .cloned()
3594}
3595
3596pub fn get_text_combine_upright(
3598 styled_dom: &StyledDom,
3599 node_id: NodeId,
3600 node_state: &StyledNodeState,
3601) -> Option<azul_css::props::style::text::StyleTextCombineUpright> {
3602 let node_data = &styled_dom.node_data.as_container()[node_id];
3603 styled_dom.css_property_cache.ptr
3604 .get_text_combine_upright(node_data, &node_id, node_state)
3605 .and_then(|v| v.get_property())
3606 .cloned()
3607}
3608
3609pub fn get_exclusion_margin(
3611 styled_dom: &StyledDom,
3612 node_id: NodeId,
3613 node_state: &StyledNodeState,
3614) -> f32 {
3615 let node_data = &styled_dom.node_data.as_container()[node_id];
3616 styled_dom.css_property_cache.ptr
3617 .get_exclusion_margin(node_data, &node_id, node_state)
3618 .and_then(|v| v.get_property())
3619 .map(|v| v.inner.get() as f32)
3620 .unwrap_or(0.0)
3621}
3622
3623pub fn get_hyphenation_language(
3625 styled_dom: &StyledDom,
3626 node_id: NodeId,
3627 node_state: &StyledNodeState,
3628) -> Option<azul_css::props::style::azul_exclusion::StyleHyphenationLanguage> {
3629 let node_data = &styled_dom.node_data.as_container()[node_id];
3630 styled_dom.css_property_cache.ptr
3631 .get_hyphenation_language(node_data, &node_id, node_state)
3632 .and_then(|v| v.get_property())
3633 .cloned()
3634}
3635
3636pub fn get_border_spacing(
3638 styled_dom: &StyledDom,
3639 node_id: NodeId,
3640 node_state: &StyledNodeState,
3641) -> azul_css::props::layout::table::LayoutBorderSpacing {
3642 use azul_css::props::basic::pixel::PixelValue;
3643
3644 if node_state.is_normal() {
3646 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
3647 let h_raw = cc.get_border_spacing_h_raw(node_id.index());
3648 let v_raw = cc.get_border_spacing_v_raw(node_id.index());
3649 if h_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
3652 && v_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
3653 {
3654 return azul_css::props::layout::table::LayoutBorderSpacing {
3655 horizontal: PixelValue::px(h_raw as f32 / 10.0),
3656 vertical: PixelValue::px(v_raw as f32 / 10.0),
3657 };
3658 }
3659 }
3660 }
3661
3662 let node_data = &styled_dom.node_data.as_container()[node_id];
3664 styled_dom.css_property_cache.ptr
3665 .get_border_spacing(node_data, &node_id, node_state)
3666 .and_then(|v| v.get_property())
3667 .cloned()
3668 .unwrap_or_default()
3669}
3670
3671pub fn get_opacity(
3673 styled_dom: &StyledDom,
3674 node_id: NodeId,
3675 node_state: &StyledNodeState,
3676) -> f32 {
3677 let node_data = &styled_dom.node_data.as_container()[node_id];
3678 styled_dom.css_property_cache.ptr
3679 .get_opacity(node_data, &node_id, node_state)
3680 .and_then(|v| v.get_property())
3681 .map(|v| v.inner.normalized())
3682 .unwrap_or(1.0)
3683}
3684
3685pub fn get_filter(
3687 styled_dom: &StyledDom,
3688 node_id: NodeId,
3689 node_state: &StyledNodeState,
3690) -> Option<azul_css::props::style::filter::StyleFilterVec> {
3691 let node_data = &styled_dom.node_data.as_container()[node_id];
3692 styled_dom.css_property_cache.ptr
3693 .get_filter(node_data, &node_id, node_state)
3694 .and_then(|v| v.get_property())
3695 .cloned()
3696}
3697
3698pub fn get_backdrop_filter(
3700 styled_dom: &StyledDom,
3701 node_id: NodeId,
3702 node_state: &StyledNodeState,
3703) -> Option<azul_css::props::style::filter::StyleFilterVec> {
3704 let node_data = &styled_dom.node_data.as_container()[node_id];
3705 styled_dom.css_property_cache.ptr
3706 .get_backdrop_filter(node_data, &node_id, node_state)
3707 .and_then(|v| v.get_property())
3708 .cloned()
3709}
3710
3711pub fn get_box_shadow_left(
3713 styled_dom: &StyledDom,
3714 node_id: NodeId,
3715 node_state: &StyledNodeState,
3716) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
3717 let node_data = &styled_dom.node_data.as_container()[node_id];
3718 styled_dom.css_property_cache.ptr
3719 .get_box_shadow_left(node_data, &node_id, node_state)
3720 .and_then(|v| v.get_property())
3721 .cloned()
3722}
3723
3724pub fn get_box_shadow_right(
3726 styled_dom: &StyledDom,
3727 node_id: NodeId,
3728 node_state: &StyledNodeState,
3729) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
3730 let node_data = &styled_dom.node_data.as_container()[node_id];
3731 styled_dom.css_property_cache.ptr
3732 .get_box_shadow_right(node_data, &node_id, node_state)
3733 .and_then(|v| v.get_property())
3734 .cloned()
3735}
3736
3737pub fn get_box_shadow_top(
3739 styled_dom: &StyledDom,
3740 node_id: NodeId,
3741 node_state: &StyledNodeState,
3742) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
3743 let node_data = &styled_dom.node_data.as_container()[node_id];
3744 styled_dom.css_property_cache.ptr
3745 .get_box_shadow_top(node_data, &node_id, node_state)
3746 .and_then(|v| v.get_property())
3747 .cloned()
3748}
3749
3750pub fn get_box_shadow_bottom(
3752 styled_dom: &StyledDom,
3753 node_id: NodeId,
3754 node_state: &StyledNodeState,
3755) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
3756 let node_data = &styled_dom.node_data.as_container()[node_id];
3757 styled_dom.css_property_cache.ptr
3758 .get_box_shadow_bottom(node_data, &node_id, node_state)
3759 .and_then(|v| v.get_property())
3760 .cloned()
3761}
3762
3763pub fn get_text_shadow(
3765 styled_dom: &StyledDom,
3766 node_id: NodeId,
3767 node_state: &StyledNodeState,
3768) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
3769 let node_data = &styled_dom.node_data.as_container()[node_id];
3770 styled_dom.css_property_cache.ptr
3771 .get_text_shadow(node_data, &node_id, node_state)
3772 .and_then(|v| v.get_property())
3773 .cloned()
3774}
3775
3776pub fn get_transform(
3778 styled_dom: &StyledDom,
3779 node_id: NodeId,
3780 node_state: &StyledNodeState,
3781) -> Option<azul_css::props::style::transform::StyleTransformVec> {
3782 let node_data = &styled_dom.node_data.as_container()[node_id];
3783 styled_dom.css_property_cache.ptr
3784 .get_transform(node_data, &node_id, node_state)
3785 .and_then(|v| v.get_property())
3786 .cloned()
3787}
3788
3789pub fn get_display_raw(
3791 styled_dom: &StyledDom,
3792 node_id: NodeId,
3793 node_state: &StyledNodeState,
3794) -> Option<LayoutDisplay> {
3795 let node_data = &styled_dom.node_data.as_container()[node_id];
3796 styled_dom.css_property_cache.ptr
3797 .get_display(node_data, &node_id, node_state)
3798 .and_then(|v| v.get_property().copied())
3799}
3800
3801pub fn get_counter_reset(
3803 styled_dom: &StyledDom,
3804 node_id: NodeId,
3805 node_state: &StyledNodeState,
3806) -> Option<azul_css::props::style::content::CounterReset> {
3807 let node_data = &styled_dom.node_data.as_container()[node_id];
3808 styled_dom.css_property_cache.ptr
3809 .get_counter_reset(node_data, &node_id, node_state)
3810 .and_then(|v| v.get_property())
3811 .cloned()
3812}
3813
3814pub fn get_counter_increment(
3816 styled_dom: &StyledDom,
3817 node_id: NodeId,
3818 node_state: &StyledNodeState,
3819) -> Option<azul_css::props::style::content::CounterIncrement> {
3820 let node_data = &styled_dom.node_data.as_container()[node_id];
3821 styled_dom.css_property_cache.ptr
3822 .get_counter_increment(node_data, &node_id, node_state)
3823 .and_then(|v| v.get_property())
3824 .cloned()
3825}
3826
3827pub fn is_node_contenteditable_inherited(styled_dom: &StyledDom, node_id: NodeId) -> bool {
3853 use azul_core::dom::AttributeType;
3854
3855 let node_data_container = styled_dom.node_data.as_container();
3856 let hierarchy = styled_dom.node_hierarchy.as_container();
3857
3858 let mut current_node_id = Some(node_id);
3859
3860 while let Some(nid) = current_node_id {
3861 let node_data = &node_data_container[nid];
3862
3863 if node_data.is_contenteditable() {
3866 return true;
3867 }
3868
3869 for attr in node_data.attributes.as_ref().iter() {
3872 if let AttributeType::ContentEditable(is_editable) = attr {
3873 return *is_editable;
3876 }
3877 }
3878
3879 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
3881 }
3882
3883 false
3885}
3886
3887pub fn find_contenteditable_ancestor(styled_dom: &StyledDom, node_id: NodeId) -> Option<NodeId> {
3897 use azul_core::dom::AttributeType;
3898
3899 let node_data_container = styled_dom.node_data.as_container();
3900 let hierarchy = styled_dom.node_hierarchy.as_container();
3901
3902 let mut current_node_id = Some(node_id);
3903
3904 while let Some(nid) = current_node_id {
3905 let node_data = &node_data_container[nid];
3906
3907 if node_data.is_contenteditable() {
3909 return Some(nid);
3910 }
3911
3912 for attr in node_data.attributes.as_ref().iter() {
3914 if let AttributeType::ContentEditable(is_editable) = attr {
3915 if *is_editable {
3916 return Some(nid);
3917 } else {
3918 return None;
3920 }
3921 }
3922 }
3923
3924 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
3926 }
3927
3928 None
3929}
3930
3931macro_rules! get_css_property_value {
3939 ($fn_name:ident, $cache_method:ident, $ret_type:ty) => {
3940 pub fn $fn_name(
3941 styled_dom: &StyledDom,
3942 node_id: NodeId,
3943 node_state: &StyledNodeState,
3944 ) -> Option<$ret_type> {
3945 let node_data = &styled_dom.node_data.as_container()[node_id];
3946 styled_dom
3947 .css_property_cache
3948 .ptr
3949 .$cache_method(node_data, &node_id, node_state)
3950 .cloned()
3951 }
3952 };
3953}
3954
3955get_css_property_value!(get_flex_direction_prop, get_flex_direction, LayoutFlexDirectionValue);
3957get_css_property_value!(get_flex_wrap_prop, get_flex_wrap, LayoutFlexWrapValue);
3958get_css_property_value!(get_flex_grow_prop, get_flex_grow, LayoutFlexGrowValue);
3959get_css_property_value!(get_flex_shrink_prop, get_flex_shrink, LayoutFlexShrinkValue);
3960get_css_property_value!(get_flex_basis_prop, get_flex_basis, LayoutFlexBasisValue);
3961
3962get_css_property_value!(get_align_items_prop, get_align_items, LayoutAlignItemsValue);
3964get_css_property_value!(get_align_self_prop, get_align_self, LayoutAlignSelfValue);
3965get_css_property_value!(get_align_content_prop, get_align_content, LayoutAlignContentValue);
3966get_css_property_value!(get_justify_content_prop, get_justify_content, LayoutJustifyContentValue);
3967get_css_property_value!(get_justify_items_prop, get_justify_items, LayoutJustifyItemsValue);
3968get_css_property_value!(get_justify_self_prop, get_justify_self, LayoutJustifySelfValue);
3969
3970get_css_property_value!(get_gap_prop, get_gap, LayoutGapValue);
3972
3973get_css_property_value!(get_grid_template_rows_prop, get_grid_template_rows, LayoutGridTemplateRowsValue);
3975get_css_property_value!(get_grid_template_columns_prop, get_grid_template_columns, LayoutGridTemplateColumnsValue);
3976get_css_property_value!(get_grid_auto_rows_prop, get_grid_auto_rows, LayoutGridAutoRowsValue);
3977get_css_property_value!(get_grid_auto_columns_prop, get_grid_auto_columns, LayoutGridAutoColumnsValue);
3978get_css_property_value!(get_grid_auto_flow_prop, get_grid_auto_flow, LayoutGridAutoFlowValue);
3979get_css_property_value!(get_grid_column_prop, get_grid_column, LayoutGridColumnValue);
3980get_css_property_value!(get_grid_row_prop, get_grid_row, LayoutGridRowValue);
3981
3982pub fn get_grid_template_areas_prop(
3986 styled_dom: &StyledDom,
3987 node_id: NodeId,
3988 node_state: &StyledNodeState,
3989) -> Option<GridTemplateAreas> {
3990 let node_data = &styled_dom.node_data.as_container()[node_id];
3991 styled_dom
3992 .css_property_cache
3993 .ptr
3994 .get_property(node_data, &node_id, node_state, &CssPropertyType::GridTemplateAreas)
3995 .and_then(|p| {
3996 if let CssProperty::GridTemplateAreas(v) = p {
3997 v.get_property().cloned()
3998 } else {
3999 None
4000 }
4001 })
4002}