1use azul_core::{
5 dom::{NodeId, NodeType},
6 geom::LogicalSize,
7 id::NodeId as CoreNodeId,
8 styled_dom::{StyledDom, StyledNodeState},
9};
10use azul_css::{
11 css::CssPropertyValue,
12 props::{
13 basic::{
14 font::{StyleFontFamily, StyleFontFamilyVec, StyleFontStyle, StyleFontWeight},
15 pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
16 ColorU, PhysicalSize, PixelValue, PropertyContext, ResolutionContext,
17 },
18 layout::{
19 grid::GridTemplateAreas, BoxDecorationBreak, BreakInside, LayoutAlignContent,
20 LayoutAlignItems, LayoutBoxSizing, LayoutClear, LayoutDisplay, LayoutFlexDirection,
21 LayoutFlexWrap, LayoutFloat, LayoutHeight, LayoutJustifyContent, LayoutOverflow,
22 LayoutPosition, LayoutWidth, LayoutWritingMode, Orphans, PageBreak,
23 StyleOverflowClipMargin, StyleScrollbarGutter, Widows,
24 },
25 property::{
26 CssProperty, CssPropertyType, LayoutAlignContentValue, LayoutAlignItemsValue,
27 LayoutAlignSelfValue, LayoutFlexBasisValue, LayoutFlexDirectionValue,
28 LayoutFlexGrowValue, LayoutFlexShrinkValue, LayoutFlexWrapValue, LayoutGapValue,
29 LayoutGridAutoColumnsValue, LayoutGridAutoFlowValue, LayoutGridAutoRowsValue,
30 LayoutGridColumnValue, LayoutGridRowValue, LayoutGridTemplateColumnsValue,
31 LayoutGridTemplateRowsValue, LayoutJustifyContentValue, LayoutJustifyItemsValue,
32 LayoutJustifySelfValue,
33 },
34 style::{
35 border_radius::StyleBorderRadius,
36 lists::{StyleListStylePosition, StyleListStyleType},
37 StyleAlignmentBaseline, StyleDirection, StyleDominantBaseline, StyleInitialLetterAlign,
38 StyleInitialLetterWrap, StyleTextAlign, StyleTextBoxEdge, StyleTextBoxTrim,
39 StyleUnicodeBidi, StyleUserSelect, StyleVerticalAlign, StyleVisibility,
40 StyleWhiteSpace,
41 },
42 },
43};
44
45use crate::{
46 font_traits::{ParsedFontTrait, StyleProperties},
47 solver3::{
48 display_list::{BorderRadius, PhysicalSizeImport},
49 layout_tree::LayoutNode,
50 scrollbar::ScrollbarRequirements,
51 },
52};
53
54const DEFAULT_EM_SIZE: f32 = 16.0;
55const DEFAULT_CARET_WIDTH_PX: f32 = 2.0;
56const DEFAULT_CARET_BLINK_MS: u32 = 500;
57const DEFAULT_TAB_SIZE: f32 = 8.0;
58const SCROLLBAR_WIDTH_THIN: f32 = 8.0;
59const SCROLLBAR_WIDTH_AUTO: f32 = 12.0;
60const SCROLLBAR_HOVER_EXPAND_PX: f32 = 4.0;
61const THUMB_HOVER_LIGHTEN: u8 = 30;
62const THUMB_HOVER_ALPHA_ADD: u8 = 40;
63const THUMB_ACTIVE_DARKEN: u8 = 15;
64
65#[must_use] pub fn get_element_font_size(
86 styled_dom: &StyledDom,
87 dom_id: NodeId,
88 node_state: &StyledNodeState,
89) -> f32 {
90 let _ = compute_all_font_sizes_px; resolve_font_size_slow(styled_dom, dom_id, node_state)
103}
104
105fn compute_all_font_sizes_px(styled_dom: &StyledDom) -> Vec<f32> {
123 use azul_css::props::{
124 basic::length::SizeMetric,
125 property::{CssProperty, CssPropertyType},
126 };
127
128 let n = styled_dom.node_data.len();
129 let mut sizes = alloc::vec![DEFAULT_FONT_SIZE; n];
130 if n == 0 {
131 return sizes;
132 }
133
134 let data_container = styled_dom.node_data.as_container();
135 let state_container = styled_dom.styled_nodes.as_container();
136 let hierarchy = styled_dom.node_hierarchy.as_container();
137 let cache = &styled_dom.css_property_cache.ptr;
138
139 for idx in 0..n {
140 let dom_id = NodeId::new(idx);
141
142 if let Some(vec) = cache.computed_values.get(idx) {
144 if let Ok(cv_idx) = vec.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k) {
145 if let CssProperty::FontSize(css_val) = &vec[cv_idx].1.property {
146 if let Some(fs) = css_val.get_property() {
147 if fs.inner.metric == SizeMetric::Px {
148 sizes[idx] = fs.inner.number.get();
149 continue;
150 }
151 }
152 }
153 }
154 }
155
156 let parent_font_size = hierarchy
158 .get(dom_id)
159 .and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id)
160 .map_or(DEFAULT_FONT_SIZE, |p| sizes[p.index()]);
161 let root_font_size = sizes[0];
162
163 let Some(node_data) = data_container.internal.get(idx) else {
164 sizes[idx] = DEFAULT_FONT_SIZE;
165 continue;
166 };
167 let Some(styled) = state_container.internal.get(idx) else {
168 sizes[idx] = DEFAULT_FONT_SIZE;
169 continue;
170 };
171 let node_state = &styled.styled_node_state;
172
173 let mut fast_fs: Option<f32> = None;
177 let mut compact_said_inherit = false;
178 if node_state.is_normal() {
179 if let Some(ref cc) = cache.compact_cache {
180 let raw = cc.get_font_size_raw(idx);
181 if raw == azul_css::compact_cache::U32_SENTINEL
182 || raw == azul_css::compact_cache::U32_INHERIT
183 || raw == azul_css::compact_cache::U32_INITIAL
184 {
185 compact_said_inherit = true;
186 } else if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
187 if pv.metric == SizeMetric::Px {
189 fast_fs = Some(pv.number.get());
190 } else {
191 let context = ResolutionContext {
193 element_font_size: DEFAULT_FONT_SIZE,
194 parent_font_size,
195 root_font_size,
196 containing_block_size: PhysicalSize::new(0.0, 0.0),
197 element_size: None,
198 viewport_size: PhysicalSize::new(0.0, 0.0),
199 };
200 fast_fs =
201 Some(pv.resolve_with_context(&context, PropertyContext::FontSize));
202 }
203 }
204 }
205 }
206 if let Some(fs) = fast_fs {
207 sizes[idx] = fs;
208 continue;
209 }
210 if compact_said_inherit {
211 sizes[idx] = parent_font_size;
212 continue;
213 }
214
215 let resolved = cache
216 .get_font_size(node_data, &dom_id, node_state)
217 .and_then(|v| v.get_property().copied())
218 .map(|v| {
219 let context = ResolutionContext {
220 element_font_size: DEFAULT_FONT_SIZE,
221 parent_font_size,
222 root_font_size,
223 containing_block_size: PhysicalSize::new(0.0, 0.0),
224 element_size: None,
225 viewport_size: PhysicalSize::new(0.0, 0.0),
226 };
227 v.inner
228 .resolve_with_context(&context, PropertyContext::FontSize)
229 });
230
231 sizes[idx] = resolved.unwrap_or(DEFAULT_FONT_SIZE);
233 }
234 sizes
235}
236
237fn resolve_font_size_slow(
242 styled_dom: &StyledDom,
243 dom_id: NodeId,
244 node_state: &StyledNodeState,
245) -> f32 {
246 let hierarchy = styled_dom.node_hierarchy.as_container();
257 let states = styled_dom.styled_nodes.as_container();
258 let root_id = NodeId::new(0);
259
260 let root_font_size = if dom_id == root_id {
263 DEFAULT_FONT_SIZE
264 } else {
265 let root_state = &states[root_id].styled_node_state;
266 resolve_font_size_one(
267 styled_dom,
268 root_id,
269 root_state,
270 DEFAULT_FONT_SIZE,
271 DEFAULT_FONT_SIZE,
272 )
273 };
274
275 let mut chain = Vec::new();
277 let mut cur = Some(dom_id);
278 while let Some(id) = cur {
279 chain.push(id);
280 cur = hierarchy
281 .get(id)
282 .and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id);
283 }
284
285 let mut parent_font_size = DEFAULT_FONT_SIZE;
288 let mut resolved = DEFAULT_FONT_SIZE;
289 for &id in chain.iter().rev() {
290 let this_state = if id == dom_id {
293 node_state
294 } else {
295 &states[id].styled_node_state
296 };
297 let this_root_fs = if id == root_id {
298 DEFAULT_FONT_SIZE
299 } else {
300 root_font_size
301 };
302 resolved =
303 resolve_font_size_one(styled_dom, id, this_state, parent_font_size, this_root_fs);
304 parent_font_size = resolved;
305 }
306 resolved
307}
308
309fn resolve_font_size_one(
314 styled_dom: &StyledDom,
315 dom_id: NodeId,
316 node_state: &StyledNodeState,
317 parent_font_size: f32,
318 root_font_size: f32,
319) -> f32 {
320 let node_data = &styled_dom.node_data.as_container()[dom_id];
321 let cache = &styled_dom.css_property_cache.ptr;
322
323 if let Some(vec) = cache.computed_values.get(dom_id.index()) {
324 if let Ok(idx) = vec.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k) {
325 if let CssProperty::FontSize(css_val) = &vec[idx].1.property {
326 if let Some(fs) = css_val.get_property() {
327 if fs.inner.metric == azul_css::props::basic::length::SizeMetric::Px {
328 return fs.inner.number.get();
329 }
330 }
331 }
332 }
333 }
334
335 cache
336 .get_font_size(node_data, &dom_id, node_state)
337 .and_then(|v| v.get_property().copied())
338 .map_or(DEFAULT_FONT_SIZE, |v| {
339 let context = ResolutionContext {
340 element_font_size: DEFAULT_FONT_SIZE,
341 parent_font_size,
342 root_font_size,
343 containing_block_size: PhysicalSize::new(0.0, 0.0),
344 element_size: None,
345 viewport_size: PhysicalSize::new(0.0, 0.0),
346 };
347 v.inner
348 .resolve_with_context(&context, PropertyContext::FontSize)
349 })
350}
351
352#[must_use] pub fn get_parent_font_size(
358 styled_dom: &StyledDom,
359 dom_id: NodeId,
360 _node_state: &StyledNodeState, ) -> f32 {
362 styled_dom
363 .node_hierarchy
364 .as_container()
365 .get(dom_id)
366 .and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id)
367 .map_or(DEFAULT_FONT_SIZE, |parent_id| {
368 let parent_state = &styled_dom.styled_nodes.as_container()[parent_id].styled_node_state;
369 get_element_font_size(styled_dom, parent_id, parent_state)
370 })
371}
372
373#[must_use] pub fn get_root_font_size(styled_dom: &StyledDom, _node_state: &StyledNodeState) -> f32 {
378 let root_id = NodeId::new(0);
379 let root_state = &styled_dom.styled_nodes.as_container()[root_id].styled_node_state;
380 get_element_font_size(styled_dom, root_id, root_state)
381}
382
383#[derive(Debug, Copy, Clone, PartialEq, Eq)]
386#[derive(Default)]
387pub enum MultiValue<T> {
388 #[default]
390 Auto,
391 Initial,
393 Inherit,
395 Exact(T),
397}
398
399impl<T> MultiValue<T> {
400 pub const fn is_auto(&self) -> bool {
402 matches!(self, Self::Auto)
403 }
404
405 pub const fn is_exact(&self) -> bool {
407 matches!(self, Self::Exact(_))
408 }
409
410 pub fn exact(self) -> Option<T> {
412 match self {
413 Self::Exact(v) => Some(v),
414 _ => None,
415 }
416 }
417
418 pub fn unwrap_or(self, default: T) -> T {
420 match self {
421 Self::Exact(v) => v,
422 _ => default,
423 }
424 }
425
426 pub fn unwrap_or_default(self) -> T
428 where
429 T: Default,
430 {
431 match self {
432 Self::Exact(v) => v,
433 _ => T::default(),
434 }
435 }
436
437 pub fn map<U, F>(self, f: F) -> MultiValue<U>
439 where
440 F: FnOnce(T) -> U,
441 {
442 match self {
443 Self::Exact(v) => MultiValue::Exact(f(v)),
444 Self::Auto => MultiValue::Auto,
445 Self::Initial => MultiValue::Initial,
446 Self::Inherit => MultiValue::Inherit,
447 }
448 }
449}
450
451impl MultiValue<LayoutOverflow> {
453 #[must_use] pub const fn is_clipped(&self) -> bool {
456 matches!(
457 self,
458 Self::Exact(
459 LayoutOverflow::Hidden
460 | LayoutOverflow::Clip
461 | LayoutOverflow::Auto
462 | LayoutOverflow::Scroll
463 )
464 )
465 }
466
467 #[must_use] pub const fn is_scroll(&self) -> bool {
468 matches!(
469 self,
470 Self::Exact(LayoutOverflow::Scroll | LayoutOverflow::Auto)
471 )
472 }
473
474 #[must_use] pub const fn is_auto_overflow(&self) -> bool {
475 matches!(self, Self::Exact(LayoutOverflow::Auto))
476 }
477
478 #[must_use] pub const fn is_hidden(&self) -> bool {
479 matches!(self, Self::Exact(LayoutOverflow::Hidden))
480 }
481
482 #[must_use] pub const fn is_hidden_or_clip(&self) -> bool {
483 matches!(
484 self,
485 Self::Exact(LayoutOverflow::Hidden | LayoutOverflow::Clip)
486 )
487 }
488
489 #[must_use] pub const fn is_scroll_explicit(&self) -> bool {
490 matches!(self, Self::Exact(LayoutOverflow::Scroll))
491 }
492
493 #[must_use] pub const fn is_clip(&self) -> bool {
494 matches!(self, Self::Exact(LayoutOverflow::Clip))
495 }
496
497 #[must_use] pub const fn is_visible_or_clip(&self) -> bool {
498 matches!(
499 self,
500 Self::Exact(LayoutOverflow::Visible | LayoutOverflow::Clip)
501 )
502 }
503
504 #[must_use] pub const fn resolve_computed(
509 &self,
510 other_axis: &Self,
511 ) -> Self {
512 match (self, other_axis) {
513 (Self::Exact(val), Self::Exact(other)) => {
514 Self::Exact(val.resolve_computed(*other))
515 }
516 _ => *self,
517 }
518 }
519}
520
521impl MultiValue<LayoutPosition> {
523 #[must_use] pub const fn is_absolute_or_fixed(&self) -> bool {
524 matches!(
525 self,
526 Self::Exact(LayoutPosition::Absolute | LayoutPosition::Fixed)
527 )
528 }
529}
530
531impl MultiValue<LayoutFloat> {
533 #[must_use] pub const fn is_none(&self) -> bool {
534 matches!(
535 self,
536 Self::Auto
537 | Self::Initial
538 | Self::Inherit
539 | Self::Exact(LayoutFloat::None)
540 )
541 }
542}
543
544
545macro_rules! get_css_property_pixel {
548 ($fn_name:ident, $cache_method:ident, $ua_property:expr, compact_i16 = $compact_method:ident) => {
550 #[must_use] pub fn $fn_name(
551 styled_dom: &StyledDom,
552 node_id: NodeId,
553 node_state: &StyledNodeState,
554 ) -> MultiValue<PixelValue> {
555 if node_state.is_normal() {
557 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
558 let raw = cc.$compact_method(node_id.index());
559 if raw == azul_css::compact_cache::I16_AUTO {
560 return MultiValue::Auto;
561 }
562 if raw == azul_css::compact_cache::I16_INITIAL {
563 return MultiValue::Initial;
564 }
565 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
566 return MultiValue::Exact(PixelValue::px(f32::from(raw) / 10.0));
568 }
569 }
571 }
572
573 let node_data = &styled_dom.node_data.as_container()[node_id];
574
575 let author_css = styled_dom
576 .css_property_cache
577 .ptr
578 .$cache_method(node_data, &node_id, node_state);
579
580 if let Some(ref val) = author_css {
581 if val.is_auto() {
582 return MultiValue::Auto;
583 }
584 if let Some(exact) = val.get_property().copied() {
585 return MultiValue::Exact(exact.inner);
586 }
587 }
588
589 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
590
591 if let Some(ua_prop) = ua_css {
592 if let Some(inner) = ua_prop.get_pixel_inner() {
593 return MultiValue::Exact(inner);
594 }
595 }
596
597 MultiValue::Initial
598 }
599 };
600}
601
602trait CssPropertyPixelInner {
604 fn get_pixel_inner(&self) -> Option<PixelValue>;
605}
606
607impl CssPropertyPixelInner for CssProperty {
608 fn get_pixel_inner(&self) -> Option<PixelValue> {
609 match self {
610 Self::Left(CssPropertyValue::Exact(v)) => Some(v.inner),
611 Self::Right(CssPropertyValue::Exact(v)) => Some(v.inner),
612 Self::Top(CssPropertyValue::Exact(v)) => Some(v.inner),
613 Self::Bottom(CssPropertyValue::Exact(v)) => Some(v.inner),
614 Self::MarginLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
615 Self::MarginRight(CssPropertyValue::Exact(v)) => Some(v.inner),
616 Self::MarginTop(CssPropertyValue::Exact(v)) => Some(v.inner),
617 Self::MarginBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
618 Self::PaddingLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
619 Self::PaddingRight(CssPropertyValue::Exact(v)) => Some(v.inner),
620 Self::PaddingTop(CssPropertyValue::Exact(v)) => Some(v.inner),
621 Self::PaddingBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
622 _ => None,
623 }
624 }
625}
626
627macro_rules! get_css_property {
629 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact = $compact_method:ident) => {
631 #[must_use] pub fn $fn_name(
632 styled_dom: &StyledDom,
633 node_id: NodeId,
634 node_state: &StyledNodeState,
635 ) -> MultiValue<$return_type> {
636 if node_state.is_normal() {
642 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
643 return MultiValue::Exact(cc.$compact_method(node_id.index()));
644 }
645 }
646
647 let node_data = &styled_dom.node_data.as_container()[node_id];
649
650 let author_css = styled_dom
652 .css_property_cache
653 .ptr
654 .$cache_method(node_data, &node_id, node_state);
655
656 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
657 return MultiValue::Exact(val);
658 }
659
660 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
662
663 if let Some(ua_prop) = ua_css {
664 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
665 return MultiValue::Exact(val);
666 }
667 }
668
669 MultiValue::Auto
671 }
672 };
673 ($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) => {
676 #[must_use] pub fn $fn_name(
677 styled_dom: &StyledDom,
678 node_id: NodeId,
679 node_state: &StyledNodeState,
680 ) -> MultiValue<$return_type> {
681 if node_state.is_normal() {
683 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
684 let raw = cc.$compact_raw_method(node_id.index());
685 match raw {
686 azul_css::compact_cache::U32_AUTO => return MultiValue::Auto,
687 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
688 azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
689 azul_css::compact_cache::U32_MIN_CONTENT => return MultiValue::Exact($min_content_variant),
690 azul_css::compact_cache::U32_MAX_CONTENT => return MultiValue::Exact($max_content_variant),
691 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
692 }
694 _ => {
695 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
697 return MultiValue::Exact($px_variant(pv));
698 }
699 }
701 }
702 }
703 }
704
705 let node_data = &styled_dom.node_data.as_container()[node_id];
707
708 let author_css = styled_dom
709 .css_property_cache
710 .ptr
711 .$cache_method(node_data, &node_id, node_state);
712
713 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
714 return MultiValue::Exact(val);
715 }
716
717 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
718
719 if let Some(ua_prop) = ua_css {
720 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
721 return MultiValue::Exact(val);
722 }
723 }
724
725 MultiValue::Auto
726 }
727 };
728 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact_u32_struct = $compact_raw_method:ident) => {
731 #[must_use] pub fn $fn_name(
732 styled_dom: &StyledDom,
733 node_id: NodeId,
734 node_state: &StyledNodeState,
735 ) -> MultiValue<$return_type> {
736 if node_state.is_normal() {
738 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
739 let raw = cc.$compact_raw_method(node_id.index());
740 match raw {
741 azul_css::compact_cache::U32_AUTO | azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
742 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
743 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
744 }
746 _ => {
747 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
748 return MultiValue::Exact(
749 <$return_type as azul_css::props::PixelValueTaker>::from_pixel_value(pv)
750 );
751 }
752 }
753 }
754 }
755 }
756
757 let node_data = &styled_dom.node_data.as_container()[node_id];
759
760 let author_css = styled_dom
761 .css_property_cache
762 .ptr
763 .$cache_method(node_data, &node_id, node_state);
764
765 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
766 return MultiValue::Exact(val);
767 }
768
769 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
770
771 if let Some(ua_prop) = ua_css {
772 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
773 return MultiValue::Exact(val);
774 }
775 }
776
777 MultiValue::Auto
778 }
779 };
780 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr) => {
782 #[must_use] pub fn $fn_name(
783 styled_dom: &StyledDom,
784 node_id: NodeId,
785 node_state: &StyledNodeState,
786 ) -> MultiValue<$return_type> {
787 let node_data = &styled_dom.node_data.as_container()[node_id];
788
789 let author_css = styled_dom
791 .css_property_cache
792 .ptr
793 .$cache_method(node_data, &node_id, node_state);
794
795 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
796 return MultiValue::Exact(val);
797 }
798
799 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
801
802 if let Some(ua_prop) = ua_css {
803 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
804 return MultiValue::Exact(val);
805 }
806 }
807
808 MultiValue::Auto
810 }
811 };
812}
813
814trait ExtractPropertyValue<T> {
816 fn extract(&self) -> Option<T>;
817}
818
819fn extract_property_value<T>(prop: &CssProperty) -> Option<T>
820where
821 CssProperty: ExtractPropertyValue<T>,
822{
823 prop.extract()
824}
825
826impl ExtractPropertyValue<LayoutWidth> for CssProperty {
829 fn extract(&self) -> Option<LayoutWidth> {
830 match self {
831 Self::Width(CssPropertyValue::Exact(v)) => Some(v.clone()),
832 _ => None,
833 }
834 }
835}
836
837impl ExtractPropertyValue<LayoutHeight> for CssProperty {
838 fn extract(&self) -> Option<LayoutHeight> {
839 match self {
840 Self::Height(CssPropertyValue::Exact(v)) => Some(v.clone()),
841 _ => None,
842 }
843 }
844}
845
846impl ExtractPropertyValue<LayoutMinWidth> for CssProperty {
847 fn extract(&self) -> Option<LayoutMinWidth> {
848 match self {
849 Self::MinWidth(CssPropertyValue::Exact(v)) => Some(*v),
850 _ => None,
851 }
852 }
853}
854
855impl ExtractPropertyValue<LayoutMinHeight> for CssProperty {
856 fn extract(&self) -> Option<LayoutMinHeight> {
857 match self {
858 Self::MinHeight(CssPropertyValue::Exact(v)) => Some(*v),
859 _ => None,
860 }
861 }
862}
863
864impl ExtractPropertyValue<LayoutMaxWidth> for CssProperty {
865 fn extract(&self) -> Option<LayoutMaxWidth> {
866 match self {
867 Self::MaxWidth(CssPropertyValue::Exact(v)) => Some(*v),
868 _ => None,
869 }
870 }
871}
872
873impl ExtractPropertyValue<LayoutMaxHeight> for CssProperty {
874 fn extract(&self) -> Option<LayoutMaxHeight> {
875 match self {
876 Self::MaxHeight(CssPropertyValue::Exact(v)) => Some(*v),
877 _ => None,
878 }
879 }
880}
881
882impl ExtractPropertyValue<LayoutDisplay> for CssProperty {
883 fn extract(&self) -> Option<LayoutDisplay> {
884 match self {
885 Self::Display(CssPropertyValue::Exact(v)) => Some(*v),
886 _ => None,
887 }
888 }
889}
890
891impl ExtractPropertyValue<LayoutWritingMode> for CssProperty {
892 fn extract(&self) -> Option<LayoutWritingMode> {
893 match self {
894 Self::WritingMode(CssPropertyValue::Exact(v)) => Some(*v),
895 _ => None,
896 }
897 }
898}
899
900impl ExtractPropertyValue<LayoutFlexWrap> for CssProperty {
901 fn extract(&self) -> Option<LayoutFlexWrap> {
902 match self {
903 Self::FlexWrap(CssPropertyValue::Exact(v)) => Some(*v),
904 _ => None,
905 }
906 }
907}
908
909impl ExtractPropertyValue<LayoutJustifyContent> for CssProperty {
910 fn extract(&self) -> Option<LayoutJustifyContent> {
911 match self {
912 Self::JustifyContent(CssPropertyValue::Exact(v)) => Some(*v),
913 _ => None,
914 }
915 }
916}
917
918impl ExtractPropertyValue<StyleTextAlign> for CssProperty {
919 fn extract(&self) -> Option<StyleTextAlign> {
920 match self {
921 Self::TextAlign(CssPropertyValue::Exact(v)) => Some(*v),
922 _ => None,
923 }
924 }
925}
926
927impl ExtractPropertyValue<LayoutFloat> for CssProperty {
928 fn extract(&self) -> Option<LayoutFloat> {
929 match self {
930 Self::Float(CssPropertyValue::Exact(v)) => Some(*v),
931 _ => None,
932 }
933 }
934}
935
936impl ExtractPropertyValue<LayoutClear> for CssProperty {
937 fn extract(&self) -> Option<LayoutClear> {
938 match self {
939 Self::Clear(CssPropertyValue::Exact(v)) => Some(*v),
940 _ => None,
941 }
942 }
943}
944
945impl ExtractPropertyValue<LayoutOverflow> for CssProperty {
946 fn extract(&self) -> Option<LayoutOverflow> {
947 match self {
948 Self::OverflowX(CssPropertyValue::Exact(v))
949 | Self::OverflowY(CssPropertyValue::Exact(v))
950 | Self::OverflowBlock(CssPropertyValue::Exact(v))
951 | Self::OverflowInline(CssPropertyValue::Exact(v)) => Some(*v),
952 _ => None,
953 }
954 }
955}
956
957impl ExtractPropertyValue<LayoutPosition> for CssProperty {
958 fn extract(&self) -> Option<LayoutPosition> {
959 match self {
960 Self::Position(CssPropertyValue::Exact(v)) => Some(*v),
961 _ => None,
962 }
963 }
964}
965
966impl ExtractPropertyValue<LayoutBoxSizing> for CssProperty {
967 fn extract(&self) -> Option<LayoutBoxSizing> {
968 match self {
969 Self::BoxSizing(CssPropertyValue::Exact(v)) => Some(*v),
970 _ => None,
971 }
972 }
973}
974
975impl ExtractPropertyValue<PixelValue> for CssProperty {
976 fn extract(&self) -> Option<PixelValue> {
977 self.get_pixel_inner()
978 }
979}
980
981impl ExtractPropertyValue<LayoutFlexDirection> for CssProperty {
982 fn extract(&self) -> Option<LayoutFlexDirection> {
983 match self {
984 Self::FlexDirection(CssPropertyValue::Exact(v)) => Some(*v),
985 _ => None,
986 }
987 }
988}
989
990impl ExtractPropertyValue<LayoutAlignItems> for CssProperty {
991 fn extract(&self) -> Option<LayoutAlignItems> {
992 match self {
993 Self::AlignItems(CssPropertyValue::Exact(v)) => Some(*v),
994 _ => None,
995 }
996 }
997}
998
999impl ExtractPropertyValue<LayoutAlignContent> for CssProperty {
1000 fn extract(&self) -> Option<LayoutAlignContent> {
1001 match self {
1002 Self::AlignContent(CssPropertyValue::Exact(v)) => Some(*v),
1003 _ => None,
1004 }
1005 }
1006}
1007
1008impl ExtractPropertyValue<StyleFontWeight> for CssProperty {
1009 fn extract(&self) -> Option<StyleFontWeight> {
1010 match self {
1011 Self::FontWeight(CssPropertyValue::Exact(v)) => Some(*v),
1012 _ => None,
1013 }
1014 }
1015}
1016
1017impl ExtractPropertyValue<StyleFontStyle> for CssProperty {
1018 fn extract(&self) -> Option<StyleFontStyle> {
1019 match self {
1020 Self::FontStyle(CssPropertyValue::Exact(v)) => Some(*v),
1021 _ => None,
1022 }
1023 }
1024}
1025
1026impl ExtractPropertyValue<StyleVisibility> for CssProperty {
1027 fn extract(&self) -> Option<StyleVisibility> {
1028 match self {
1029 Self::Visibility(CssPropertyValue::Exact(v)) => Some(*v),
1030 _ => None,
1031 }
1032 }
1033}
1034
1035impl ExtractPropertyValue<StyleWhiteSpace> for CssProperty {
1036 fn extract(&self) -> Option<StyleWhiteSpace> {
1037 match self {
1038 Self::WhiteSpace(CssPropertyValue::Exact(v)) => Some(*v),
1039 _ => None,
1040 }
1041 }
1042}
1043
1044impl ExtractPropertyValue<StyleDirection> for CssProperty {
1045 fn extract(&self) -> Option<StyleDirection> {
1046 match self {
1047 Self::Direction(CssPropertyValue::Exact(v)) => Some(*v),
1048 _ => None,
1049 }
1050 }
1051}
1052
1053impl ExtractPropertyValue<StyleUnicodeBidi> for CssProperty {
1054 fn extract(&self) -> Option<StyleUnicodeBidi> {
1055 match self {
1056 Self::UnicodeBidi(CssPropertyValue::Exact(v)) => Some(*v),
1057 _ => None,
1058 }
1059 }
1060}
1061
1062impl ExtractPropertyValue<StyleTextBoxTrim> for CssProperty {
1063 fn extract(&self) -> Option<StyleTextBoxTrim> {
1064 match self {
1065 Self::TextBoxTrim(CssPropertyValue::Exact(v)) => Some(*v),
1066 _ => None,
1067 }
1068 }
1069}
1070
1071impl ExtractPropertyValue<StyleTextBoxEdge> for CssProperty {
1072 fn extract(&self) -> Option<StyleTextBoxEdge> {
1073 match self {
1074 Self::TextBoxEdge(CssPropertyValue::Exact(v)) => Some(*v),
1075 _ => None,
1076 }
1077 }
1078}
1079
1080impl ExtractPropertyValue<StyleDominantBaseline> for CssProperty {
1081 fn extract(&self) -> Option<StyleDominantBaseline> {
1082 match self {
1083 Self::DominantBaseline(CssPropertyValue::Exact(v)) => Some(*v),
1084 _ => None,
1085 }
1086 }
1087}
1088
1089impl ExtractPropertyValue<StyleAlignmentBaseline> for CssProperty {
1090 fn extract(&self) -> Option<StyleAlignmentBaseline> {
1091 match self {
1092 Self::AlignmentBaseline(CssPropertyValue::Exact(v)) => Some(*v),
1093 _ => None,
1094 }
1095 }
1096}
1097
1098impl ExtractPropertyValue<StyleInitialLetterAlign> for CssProperty {
1099 fn extract(&self) -> Option<StyleInitialLetterAlign> {
1100 match self {
1101 Self::InitialLetterAlign(CssPropertyValue::Exact(v)) => Some(*v),
1102 _ => None,
1103 }
1104 }
1105}
1106
1107impl ExtractPropertyValue<StyleInitialLetterWrap> for CssProperty {
1108 fn extract(&self) -> Option<StyleInitialLetterWrap> {
1109 match self {
1110 Self::InitialLetterWrap(CssPropertyValue::Exact(v)) => Some(*v),
1111 _ => None,
1112 }
1113 }
1114}
1115
1116impl ExtractPropertyValue<StyleScrollbarGutter> for CssProperty {
1117 fn extract(&self) -> Option<StyleScrollbarGutter> {
1118 match self {
1119 Self::ScrollbarGutter(CssPropertyValue::Exact(v)) => Some(*v),
1120 _ => None,
1121 }
1122 }
1123}
1124
1125impl ExtractPropertyValue<StyleOverflowClipMargin> for CssProperty {
1126 fn extract(&self) -> Option<StyleOverflowClipMargin> {
1127 match self {
1128 Self::OverflowClipMargin(CssPropertyValue::Exact(v)) => Some(*v),
1129 _ => None,
1130 }
1131 }
1132}
1133
1134impl ExtractPropertyValue<StyleVerticalAlign> for CssProperty {
1135 fn extract(&self) -> Option<StyleVerticalAlign> {
1136 match self {
1137 Self::VerticalAlign(CssPropertyValue::Exact(v)) => Some(*v),
1138 _ => None,
1139 }
1140 }
1141}
1142
1143get_css_property!(
1144 get_writing_mode,
1145 get_writing_mode,
1146 LayoutWritingMode,
1147 CssPropertyType::WritingMode,
1148 compact = get_writing_mode
1149);
1150
1151get_css_property!(
1152 get_css_width,
1153 get_width,
1154 LayoutWidth,
1155 CssPropertyType::Width,
1156 compact_u32_dim = get_width_raw,
1157 LayoutWidth::Px,
1158 LayoutWidth::Auto,
1159 LayoutWidth::MinContent,
1160 LayoutWidth::MaxContent
1161);
1162
1163get_css_property!(
1164 get_css_height,
1165 get_height,
1166 LayoutHeight,
1167 CssPropertyType::Height,
1168 compact_u32_dim = get_height_raw,
1169 LayoutHeight::Px,
1170 LayoutHeight::Auto,
1171 LayoutHeight::MinContent,
1172 LayoutHeight::MaxContent
1173);
1174
1175get_css_property!(
1176 get_wrap,
1177 get_flex_wrap,
1178 LayoutFlexWrap,
1179 CssPropertyType::FlexWrap,
1180 compact = get_flex_wrap
1181);
1182
1183get_css_property!(
1184 get_justify_content,
1185 get_justify_content,
1186 LayoutJustifyContent,
1187 CssPropertyType::JustifyContent,
1188 compact = get_justify_content
1189);
1190
1191get_css_property!(
1192 get_text_align,
1193 get_text_align,
1194 StyleTextAlign,
1195 CssPropertyType::TextAlign,
1196 compact = get_text_align
1197);
1198
1199get_css_property!(
1200 get_float,
1201 get_float,
1202 LayoutFloat,
1203 CssPropertyType::Float,
1204 compact = get_float
1205);
1206
1207get_css_property!(
1208 get_clear,
1209 get_clear,
1210 LayoutClear,
1211 CssPropertyType::Clear,
1212 compact = get_clear
1213);
1214
1215get_css_property!(
1216 get_overflow_x,
1217 get_overflow_x,
1218 LayoutOverflow,
1219 CssPropertyType::OverflowX,
1220 compact = get_overflow_x
1221);
1222
1223get_css_property!(
1224 get_overflow_y,
1225 get_overflow_y,
1226 LayoutOverflow,
1227 CssPropertyType::OverflowY,
1228 compact = get_overflow_y
1229);
1230
1231get_css_property!(
1233 get_overflow_block,
1234 get_overflow_block,
1235 LayoutOverflow,
1236 CssPropertyType::OverflowBlock
1237);
1238
1239get_css_property!(
1240 get_overflow_inline,
1241 get_overflow_inline,
1242 LayoutOverflow,
1243 CssPropertyType::OverflowInline
1244);
1245
1246get_css_property!(
1247 get_position,
1248 get_position,
1249 LayoutPosition,
1250 CssPropertyType::Position,
1251 compact = get_position
1252);
1253
1254get_css_property!(
1255 get_css_box_sizing,
1256 get_box_sizing,
1257 LayoutBoxSizing,
1258 CssPropertyType::BoxSizing,
1259 compact = get_box_sizing
1260);
1261
1262get_css_property!(
1263 get_flex_direction,
1264 get_flex_direction,
1265 LayoutFlexDirection,
1266 CssPropertyType::FlexDirection,
1267 compact = get_flex_direction
1268);
1269
1270get_css_property!(
1271 get_align_items,
1272 get_align_items,
1273 LayoutAlignItems,
1274 CssPropertyType::AlignItems,
1275 compact = get_align_items
1276);
1277
1278get_css_property!(
1279 get_align_content,
1280 get_align_content,
1281 LayoutAlignContent,
1282 CssPropertyType::AlignContent,
1283 compact = get_align_content
1284);
1285
1286get_css_property!(
1287 get_font_weight_property,
1288 get_font_weight,
1289 StyleFontWeight,
1290 CssPropertyType::FontWeight,
1291 compact = get_font_weight
1292);
1293
1294get_css_property!(
1295 get_font_style_property,
1296 get_font_style,
1297 StyleFontStyle,
1298 CssPropertyType::FontStyle,
1299 compact = get_font_style
1300);
1301
1302get_css_property!(
1303 get_visibility,
1304 get_visibility,
1305 StyleVisibility,
1306 CssPropertyType::Visibility,
1307 compact = get_visibility
1308);
1309
1310get_css_property!(
1311 get_white_space_property,
1312 get_white_space,
1313 StyleWhiteSpace,
1314 CssPropertyType::WhiteSpace,
1315 compact = get_white_space
1316);
1317
1318get_css_property!(
1320 get_direction_property,
1321 get_direction,
1322 StyleDirection,
1323 CssPropertyType::Direction,
1324 compact = get_direction
1325);
1326
1327get_css_property!(
1331 get_unicode_bidi_property,
1332 get_unicode_bidi,
1333 StyleUnicodeBidi,
1334 CssPropertyType::UnicodeBidi
1335);
1336
1337get_css_property!(
1340 get_text_box_trim_property,
1341 get_text_box_trim,
1342 StyleTextBoxTrim,
1343 CssPropertyType::TextBoxTrim
1344);
1345
1346get_css_property!(
1347 get_text_box_edge_property,
1348 get_text_box_edge,
1349 StyleTextBoxEdge,
1350 CssPropertyType::TextBoxEdge
1351);
1352
1353get_css_property!(
1354 get_dominant_baseline_property,
1355 get_dominant_baseline,
1356 StyleDominantBaseline,
1357 CssPropertyType::DominantBaseline
1358);
1359
1360get_css_property!(
1361 get_alignment_baseline_property,
1362 get_alignment_baseline,
1363 StyleAlignmentBaseline,
1364 CssPropertyType::AlignmentBaseline
1365);
1366
1367get_css_property!(
1368 get_initial_letter_align_property,
1369 get_initial_letter_align,
1370 StyleInitialLetterAlign,
1371 CssPropertyType::InitialLetterAlign
1372);
1373
1374get_css_property!(
1375 get_initial_letter_wrap_property,
1376 get_initial_letter_wrap,
1377 StyleInitialLetterWrap,
1378 CssPropertyType::InitialLetterWrap
1379);
1380
1381#[allow(clippy::match_same_arms)] #[must_use] pub fn get_scrollbar_gutter_property(
1388 styled_dom: &StyledDom,
1389 node_id: NodeId,
1390 node_state: &StyledNodeState,
1391) -> MultiValue<StyleScrollbarGutter> {
1392 if node_state.is_normal() {
1394 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1395 let bits = cc.get_scrollbar_gutter_bits(node_id.index());
1396 let val = match bits {
1397 azul_css::compact_cache::SCROLLBAR_GUTTER_AUTO => StyleScrollbarGutter::Auto,
1398 azul_css::compact_cache::SCROLLBAR_GUTTER_STABLE => StyleScrollbarGutter::Stable,
1399 azul_css::compact_cache::SCROLLBAR_GUTTER_BOTH_EDGES => {
1400 StyleScrollbarGutter::StableBothEdges
1401 }
1402 _ => StyleScrollbarGutter::Auto,
1403 };
1404 return MultiValue::Exact(val);
1405 }
1406 }
1407
1408 let node_data = &styled_dom.node_data.as_container()[node_id];
1410 let author_css = styled_dom
1411 .css_property_cache
1412 .ptr
1413 .get_scrollbar_gutter(node_data, &node_id, node_state);
1414 if let Some(val) = author_css.and_then(|v| v.get_property().copied()) {
1415 return MultiValue::Exact(val);
1416 }
1417 MultiValue::Auto
1418}
1419
1420get_css_property!(
1421 get_overflow_clip_margin_property,
1422 get_overflow_clip_margin,
1423 StyleOverflowClipMargin,
1424 CssPropertyType::OverflowClipMargin
1425);
1426
1427get_css_property!(
1428 get_object_fit_property,
1429 get_object_fit,
1430 StyleObjectFit,
1431 CssPropertyType::ObjectFit
1432);
1433
1434#[must_use] pub fn get_text_orientation_property(
1440 styled_dom: &StyledDom,
1441 node_id: NodeId,
1442 node_state: &StyledNodeState,
1443) -> MultiValue<StyleTextOrientation> {
1444 if node_state.is_normal() {
1445 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1446 if !cc.has_text_orientation(node_id.index()) {
1447 return MultiValue::Auto;
1448 }
1449 }
1450 }
1451 let node_data = &styled_dom.node_data.as_container()[node_id];
1452 if let Some(val) = styled_dom
1453 .css_property_cache
1454 .ptr
1455 .get_text_orientation(node_data, &node_id, node_state)
1456 .and_then(|v| v.get_property().copied())
1457 {
1458 return MultiValue::Exact(val);
1459 }
1460 let ua = azul_core::ua_css::get_ua_property(
1461 &node_data.node_type,
1462 CssPropertyType::TextOrientation,
1463 );
1464 if let Some(ua_prop) = ua {
1465 if let Some(val) = extract_property_value::<StyleTextOrientation>(ua_prop) {
1466 return MultiValue::Exact(val);
1467 }
1468 }
1469 MultiValue::Auto
1470}
1471
1472get_css_property!(
1473 get_object_position_property,
1474 get_object_position,
1475 StyleObjectPosition,
1476 CssPropertyType::ObjectPosition
1477);
1478
1479get_css_property!(
1480 get_aspect_ratio_property,
1481 get_aspect_ratio,
1482 StyleAspectRatio,
1483 CssPropertyType::AspectRatio
1484);
1485
1486#[must_use] pub fn get_vertical_align_property(
1490 styled_dom: &StyledDom,
1491 node_id: NodeId,
1492 node_state: &StyledNodeState,
1493) -> MultiValue<StyleVerticalAlign> {
1494 let node_data = &styled_dom.node_data.as_container()[node_id];
1495
1496 let author_css = styled_dom
1497 .css_property_cache
1498 .ptr
1499 .get_vertical_align(node_data, &node_id, node_state);
1500
1501 if let Some(val) = author_css.and_then(|v| v.get_property().copied()) {
1502 return MultiValue::Exact(val);
1503 }
1504
1505 let ua_css = azul_core::ua_css::get_ua_property(
1506 &node_data.node_type,
1507 CssPropertyType::VerticalAlign,
1508 );
1509
1510 if let Some(ua_prop) = ua_css {
1511 if let Some(val) = extract_property_value::<StyleVerticalAlign>(ua_prop) {
1512 return MultiValue::Exact(val);
1513 }
1514 }
1515
1516 MultiValue::Auto
1517}
1518#[must_use] pub fn get_style_border_radius(
1522 styled_dom: &StyledDom,
1523 node_id: NodeId,
1524 node_state: &StyledNodeState,
1525) -> StyleBorderRadius {
1526 use azul_css::props::basic::pixel::PixelValue;
1527 if node_state.is_normal() {
1530 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1531 let idx = node_id.index();
1532 let decode = |raw: i16| -> PixelValue {
1533 if raw >= azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1534 PixelValue::px(0.0)
1535 } else {
1536 PixelValue::px(f32::from(raw) / 10.0)
1537 }
1538 };
1539 return StyleBorderRadius {
1540 top_left: decode(cc.get_border_top_left_radius_raw(idx)),
1541 top_right: decode(cc.get_border_top_right_radius_raw(idx)),
1542 bottom_right: decode(cc.get_border_bottom_right_radius_raw(idx)),
1543 bottom_left: decode(cc.get_border_bottom_left_radius_raw(idx)),
1544 };
1545 }
1546 }
1547 let node_data = &styled_dom.node_data.as_container()[node_id];
1548
1549 let top_left = styled_dom
1550 .css_property_cache
1551 .ptr
1552 .get_border_top_left_radius(node_data, &node_id, node_state)
1553 .and_then(|br| br.get_property_or_default())
1554 .map(|v| v.inner)
1555 .unwrap_or_default();
1556
1557 let top_right = styled_dom
1558 .css_property_cache
1559 .ptr
1560 .get_border_top_right_radius(node_data, &node_id, node_state)
1561 .and_then(|br| br.get_property_or_default())
1562 .map(|v| v.inner)
1563 .unwrap_or_default();
1564
1565 let bottom_right = styled_dom
1566 .css_property_cache
1567 .ptr
1568 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1569 .and_then(|br| br.get_property_or_default())
1570 .map(|v| v.inner)
1571 .unwrap_or_default();
1572
1573 let bottom_left = styled_dom
1574 .css_property_cache
1575 .ptr
1576 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1577 .and_then(|br| br.get_property_or_default())
1578 .map(|v| v.inner)
1579 .unwrap_or_default();
1580
1581 StyleBorderRadius {
1582 top_left,
1583 top_right,
1584 bottom_right,
1585 bottom_left,
1586 }
1587}
1588
1589#[must_use] pub fn get_border_radius(
1595 styled_dom: &StyledDom,
1596 node_id: NodeId,
1597 node_state: &StyledNodeState,
1598 element_size: PhysicalSizeImport,
1599 viewport_size: LogicalSize,
1600) -> BorderRadius {
1601 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
1602
1603 if node_state.is_normal() {
1607 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1608 let idx = node_id.index();
1609 let tl = cc.get_border_top_left_radius_raw(idx);
1610 let tr = cc.get_border_top_right_radius_raw(idx);
1611 let br = cc.get_border_bottom_right_radius_raw(idx);
1612 let bl = cc.get_border_bottom_left_radius_raw(idx);
1613 let thresh = azul_css::compact_cache::I16_SENTINEL_THRESHOLD;
1615 let decode = |raw: i16| -> f32 {
1616 if raw >= thresh {
1617 0.0
1618 } else {
1619 f32::from(raw) / 10.0
1620 }
1621 };
1622 return BorderRadius {
1623 top_left: decode(tl),
1624 top_right: decode(tr),
1625 bottom_right: decode(br),
1626 bottom_left: decode(bl),
1627 };
1628 }
1629 }
1630
1631 let node_data = &styled_dom.node_data.as_container()[node_id];
1632
1633 let element_font_size = get_element_font_size(styled_dom, node_id, node_state);
1635 let parent_font_size = styled_dom
1636 .node_hierarchy
1637 .as_container()
1638 .get(node_id)
1639 .and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id)
1640 .map_or(DEFAULT_FONT_SIZE, |p| get_element_font_size(styled_dom, p, node_state));
1641 let root_font_size = get_root_font_size(styled_dom, node_state);
1642
1643 let context = ResolutionContext {
1645 element_font_size,
1646 parent_font_size,
1647 root_font_size,
1648 containing_block_size: PhysicalSize::new(0.0, 0.0), element_size: Some(PhysicalSize::new(element_size.width, element_size.height)),
1650 viewport_size: PhysicalSize::new(viewport_size.width, viewport_size.height),
1651 };
1652
1653 let top_left = styled_dom
1654 .css_property_cache
1655 .ptr
1656 .get_border_top_left_radius(node_data, &node_id, node_state)
1657 .and_then(|br| br.get_property().copied())
1658 .unwrap_or_default();
1659
1660 let top_right = styled_dom
1661 .css_property_cache
1662 .ptr
1663 .get_border_top_right_radius(node_data, &node_id, node_state)
1664 .and_then(|br| br.get_property().copied())
1665 .unwrap_or_default();
1666
1667 let bottom_right = styled_dom
1668 .css_property_cache
1669 .ptr
1670 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1671 .and_then(|br| br.get_property().copied())
1672 .unwrap_or_default();
1673
1674 let bottom_left = styled_dom
1675 .css_property_cache
1676 .ptr
1677 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1678 .and_then(|br| br.get_property().copied())
1679 .unwrap_or_default();
1680
1681 BorderRadius {
1682 top_left: top_left
1683 .inner
1684 .resolve_with_context(&context, PropertyContext::BorderRadius),
1685 top_right: top_right
1686 .inner
1687 .resolve_with_context(&context, PropertyContext::BorderRadius),
1688 bottom_right: bottom_right
1689 .inner
1690 .resolve_with_context(&context, PropertyContext::BorderRadius),
1691 bottom_left: bottom_left
1692 .inner
1693 .resolve_with_context(&context, PropertyContext::BorderRadius),
1694 }
1695}
1696
1697#[must_use] pub fn get_z_index(styled_dom: &StyledDom, node_id: Option<NodeId>) -> i32 {
1705 use azul_css::props::layout::position::LayoutZIndex;
1706
1707 let Some(node_id) = node_id else {
1708 return 0;
1709 };
1710
1711 let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
1712
1713 if node_state.is_normal() {
1715 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1716 let raw = cc.get_z_index(node_id.index());
1717 if raw == azul_css::compact_cache::I16_AUTO {
1718 return 0;
1719 }
1720 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1721 return i32::from(raw);
1722 }
1723 }
1725 }
1726
1727 let node_data = &styled_dom.node_data.as_container()[node_id];
1729
1730 styled_dom
1731 .css_property_cache
1732 .ptr
1733 .get_z_index(node_data, &node_id, node_state)
1734 .and_then(|v| v.get_property())
1735 .map_or(0, |z| match z {
1736 LayoutZIndex::Auto => 0,
1737 LayoutZIndex::Integer(i) => *i,
1738 })
1739}
1740
1741#[must_use] pub fn is_z_index_auto(styled_dom: &StyledDom, node_id: Option<NodeId>) -> bool {
1746 use azul_css::props::layout::position::LayoutZIndex;
1747
1748 let Some(node_id) = node_id else {
1749 return true;
1750 };
1751
1752 let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
1753
1754 if node_state.is_normal() {
1756 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1757 let raw = cc.get_z_index(node_id.index());
1758 if raw == azul_css::compact_cache::I16_AUTO {
1759 return true;
1760 }
1761 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1762 return false; }
1764 }
1766 }
1767
1768 let node_data = &styled_dom.node_data.as_container()[node_id];
1770
1771 styled_dom
1772 .css_property_cache
1773 .ptr
1774 .get_z_index(node_data, &node_id, node_state)
1775 .and_then(|v| v.get_property())
1776 .is_none_or(|z| matches!(z, LayoutZIndex::Auto)) }
1778
1779#[allow(clippy::match_same_arms)] #[must_use] pub fn get_background_color(
1803 styled_dom: &StyledDom,
1804 node_id: NodeId,
1805 node_state: &StyledNodeState,
1806) -> ColorU {
1807 let node_data = &styled_dom.node_data.as_container()[node_id];
1808 let cache = &styled_dom.css_property_cache.ptr;
1809
1810 let get_node_bg = |nid: NodeId, ndata: &azul_core::dom::NodeData, state: &StyledNodeState| {
1815 if state.is_normal() {
1816 if let Some(ref cc) = cache.compact_cache {
1817 if !cc.has_background(nid.index()) {
1818 return None;
1819 }
1820 }
1821 }
1822 cache
1823 .get_background_content(ndata, &nid, state)
1824 .and_then(|bg| bg.get_property())
1825 .and_then(|bg_vec| bg_vec.get(0).cloned())
1826 .and_then(|first_bg| match &first_bg {
1827 azul_css::props::style::StyleBackgroundContent::Color(color) => Some(*color),
1828 azul_css::props::style::StyleBackgroundContent::Image(_) => None, _ => None,
1830 })
1831 };
1832
1833 let own_bg = get_node_bg(node_id, node_data, node_state);
1834
1835 if !matches!(node_data.node_type, NodeType::Html) || own_bg.is_some() {
1839 return own_bg.unwrap_or(ColorU {
1841 r: 0,
1842 g: 0,
1843 b: 0,
1844 a: 0,
1845 });
1846 }
1847
1848 let first_child = styled_dom
1850 .node_hierarchy
1851 .as_container()
1852 .get(node_id)
1853 .and_then(|node| node.first_child_id(node_id));
1854
1855 let Some(first_child) = first_child else {
1856 return ColorU {
1857 r: 0,
1858 g: 0,
1859 b: 0,
1860 a: 0,
1861 };
1862 };
1863
1864 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1865
1866 if !matches!(first_child_data.node_type, NodeType::Body) {
1868 return ColorU {
1869 r: 0,
1870 g: 0,
1871 b: 0,
1872 a: 0,
1873 };
1874 }
1875
1876 let first_child_state = &styled_dom.styled_nodes.as_container()[first_child].styled_node_state;
1878 get_node_bg(first_child, first_child_data, first_child_state).unwrap_or(ColorU {
1879 r: 0,
1880 g: 0,
1881 b: 0,
1882 a: 0,
1883 })
1884}
1885
1886#[must_use] pub fn get_background_contents(
1893 styled_dom: &StyledDom,
1894 node_id: NodeId,
1895 node_state: &StyledNodeState,
1896) -> Vec<azul_css::props::style::StyleBackgroundContent> {
1897 use azul_core::dom::NodeType;
1898 use azul_css::props::style::StyleBackgroundContent;
1899
1900 let node_data = &styled_dom.node_data.as_container()[node_id];
1901 let cache = &styled_dom.css_property_cache.ptr;
1902
1903 let get_node_backgrounds = |nid: NodeId,
1907 ndata: &azul_core::dom::NodeData,
1908 state: &StyledNodeState|
1909 -> Vec<StyleBackgroundContent> {
1910 if state.is_normal() {
1911 if let Some(ref cc) = cache.compact_cache {
1912 if !cc.has_background(nid.index()) {
1913 return Vec::new();
1914 }
1915 }
1916 }
1917 cache
1918 .get_background_content(ndata, &nid, state)
1919 .and_then(|bg| bg.get_property())
1920 .map(|bg_vec| bg_vec.iter().cloned().collect())
1921 .unwrap_or_default()
1922 };
1923
1924 let own_backgrounds = get_node_backgrounds(node_id, node_data, node_state);
1925
1926 if !matches!(node_data.node_type, NodeType::Html) || !own_backgrounds.is_empty() {
1929 return own_backgrounds;
1930 }
1931
1932 let first_child = styled_dom
1934 .node_hierarchy
1935 .as_container()
1936 .get(node_id)
1937 .and_then(|node| node.first_child_id(node_id));
1938
1939 let Some(first_child) = first_child else {
1940 return own_backgrounds;
1941 };
1942
1943 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1944
1945 if !matches!(first_child_data.node_type, NodeType::Body) {
1947 return own_backgrounds;
1948 }
1949
1950 let first_child_state = &styled_dom.styled_nodes.as_container()[first_child].styled_node_state;
1952 get_node_backgrounds(first_child, first_child_data, first_child_state)
1953}
1954
1955#[derive(Copy, Clone, Debug)]
1957pub struct BorderInfo {
1958 pub widths: crate::solver3::display_list::StyleBorderWidths,
1959 pub colors: crate::solver3::display_list::StyleBorderColors,
1960 pub styles: crate::solver3::display_list::StyleBorderStyles,
1961}
1962
1963#[allow(clippy::too_many_lines)] #[must_use] pub fn get_border_info(
1965 styled_dom: &StyledDom,
1966 node_id: NodeId,
1967 node_state: &StyledNodeState,
1968) -> BorderInfo {
1969 use crate::solver3::display_list::{StyleBorderColors, StyleBorderStyles, StyleBorderWidths};
1970 use azul_css::css::CssPropertyValue;
1971 use azul_css::props::basic::color::ColorU;
1972 use azul_css::props::basic::pixel::PixelValue;
1973 use azul_css::props::style::border::{
1974 BorderStyle, StyleBorderBottomColor, StyleBorderBottomStyle, StyleBorderLeftColor,
1975 StyleBorderLeftStyle, StyleBorderRightColor, StyleBorderRightStyle, StyleBorderTopColor,
1976 StyleBorderTopStyle,
1977 };
1978 use azul_css::props::style::{
1979 LayoutBorderBottomWidth, LayoutBorderLeftWidth, LayoutBorderRightWidth,
1980 LayoutBorderTopWidth,
1981 };
1982
1983 if node_state.is_normal() {
1985 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1986 let idx = node_id.index();
1987
1988 let make_width_px = |raw: i16| -> Option<PixelValue> {
1994 if raw == azul_css::compact_cache::I16_AUTO
1995 || raw == azul_css::compact_cache::I16_INITIAL
1996 || raw >= azul_css::compact_cache::I16_SENTINEL_THRESHOLD
1997 {
1998 None
1999 } else {
2000 Some(PixelValue::px(f32::from(raw) / 10.0))
2001 }
2002 };
2003 let widths = StyleBorderWidths {
2004 top: make_width_px(cc.get_border_top_width_raw(idx))
2005 .map(|px| CssPropertyValue::Exact(LayoutBorderTopWidth { inner: px })),
2006 right: make_width_px(cc.get_border_right_width_raw(idx))
2007 .map(|px| CssPropertyValue::Exact(LayoutBorderRightWidth { inner: px })),
2008 bottom: make_width_px(cc.get_border_bottom_width_raw(idx))
2009 .map(|px| CssPropertyValue::Exact(LayoutBorderBottomWidth { inner: px })),
2010 left: make_width_px(cc.get_border_left_width_raw(idx))
2011 .map(|px| CssPropertyValue::Exact(LayoutBorderLeftWidth { inner: px })),
2012 };
2013
2014 let make_color = |raw: u32| -> Option<ColorU> {
2016 if raw == 0 {
2017 None
2018 } else {
2019 Some(ColorU {
2020 r: ((raw >> 24) & 0xFF) as u8,
2021 g: ((raw >> 16) & 0xFF) as u8,
2022 b: ((raw >> 8) & 0xFF) as u8,
2023 a: (raw & 0xFF) as u8,
2024 })
2025 }
2026 };
2027
2028 let colors = StyleBorderColors {
2029 top: make_color(cc.get_border_top_color_raw(idx))
2030 .map(|c| CssPropertyValue::Exact(StyleBorderTopColor { inner: c })),
2031 right: make_color(cc.get_border_right_color_raw(idx))
2032 .map(|c| CssPropertyValue::Exact(StyleBorderRightColor { inner: c })),
2033 bottom: make_color(cc.get_border_bottom_color_raw(idx))
2034 .map(|c| CssPropertyValue::Exact(StyleBorderBottomColor { inner: c })),
2035 left: make_color(cc.get_border_left_color_raw(idx))
2036 .map(|c| CssPropertyValue::Exact(StyleBorderLeftColor { inner: c })),
2037 };
2038
2039 let styles = StyleBorderStyles {
2041 top: Some(CssPropertyValue::Exact(StyleBorderTopStyle {
2042 inner: cc.get_border_top_style(idx),
2043 })),
2044 right: Some(CssPropertyValue::Exact(StyleBorderRightStyle {
2045 inner: cc.get_border_right_style(idx),
2046 })),
2047 bottom: Some(CssPropertyValue::Exact(StyleBorderBottomStyle {
2048 inner: cc.get_border_bottom_style(idx),
2049 })),
2050 left: Some(CssPropertyValue::Exact(StyleBorderLeftStyle {
2051 inner: cc.get_border_left_style(idx),
2052 })),
2053 };
2054
2055 return BorderInfo {
2056 widths,
2057 colors,
2058 styles,
2059 };
2060 }
2061 }
2062
2063 let node_data = &styled_dom.node_data.as_container()[node_id];
2065
2066 let widths = StyleBorderWidths {
2068 top: styled_dom
2069 .css_property_cache
2070 .ptr
2071 .get_border_top_width(node_data, &node_id, node_state)
2072 .copied(),
2073 right: styled_dom
2074 .css_property_cache
2075 .ptr
2076 .get_border_right_width(node_data, &node_id, node_state)
2077 .copied(),
2078 bottom: styled_dom
2079 .css_property_cache
2080 .ptr
2081 .get_border_bottom_width(node_data, &node_id, node_state)
2082 .copied(),
2083 left: styled_dom
2084 .css_property_cache
2085 .ptr
2086 .get_border_left_width(node_data, &node_id, node_state)
2087 .copied(),
2088 };
2089
2090 let colors = StyleBorderColors {
2092 top: styled_dom
2093 .css_property_cache
2094 .ptr
2095 .get_border_top_color(node_data, &node_id, node_state)
2096 .copied(),
2097 right: styled_dom
2098 .css_property_cache
2099 .ptr
2100 .get_border_right_color(node_data, &node_id, node_state)
2101 .copied(),
2102 bottom: styled_dom
2103 .css_property_cache
2104 .ptr
2105 .get_border_bottom_color(node_data, &node_id, node_state)
2106 .copied(),
2107 left: styled_dom
2108 .css_property_cache
2109 .ptr
2110 .get_border_left_color(node_data, &node_id, node_state)
2111 .copied(),
2112 };
2113
2114 let styles = StyleBorderStyles {
2116 top: styled_dom
2117 .css_property_cache
2118 .ptr
2119 .get_border_top_style(node_data, &node_id, node_state)
2120 .copied(),
2121 right: styled_dom
2122 .css_property_cache
2123 .ptr
2124 .get_border_right_style(node_data, &node_id, node_state)
2125 .copied(),
2126 bottom: styled_dom
2127 .css_property_cache
2128 .ptr
2129 .get_border_bottom_style(node_data, &node_id, node_state)
2130 .copied(),
2131 left: styled_dom
2132 .css_property_cache
2133 .ptr
2134 .get_border_left_style(node_data, &node_id, node_state)
2135 .copied(),
2136 };
2137
2138 BorderInfo {
2139 widths,
2140 colors,
2141 styles,
2142 }
2143}
2144
2145#[allow(clippy::too_many_lines)] fn get_inline_border_info(
2151 styled_dom: &StyledDom,
2152 node_id: NodeId,
2153 node_state: &StyledNodeState,
2154 border_info: &BorderInfo,
2155 viewport: PhysicalSize,
2156) -> Option<crate::text3::cache::InlineBorderInfo> {
2157 use crate::text3::cache::InlineBorderInfo;
2158
2159 fn resolve_padding(
2162 mv: MultiValue<PixelValue>,
2163 viewport: PhysicalSize,
2164 ) -> f32 {
2165 match mv {
2166 MultiValue::Exact(pv) => super::calc::resolve_pixel_value_with_viewport(
2167 &pv,
2168 0.0,
2169 DEFAULT_FONT_SIZE,
2170 DEFAULT_FONT_SIZE,
2171 viewport.width,
2172 viewport.height,
2173 ),
2174 _ => 0.0,
2175 }
2176 }
2177
2178 macro_rules! border_width_px {
2179 ($field:expr) => {
2180 $field
2181 .as_ref()
2182 .and_then(|v| v.get_property())
2183 .map(|w| w.inner.number.get())
2184 .unwrap_or(0.0)
2185 };
2186 }
2187
2188 macro_rules! border_color {
2189 ($field:expr) => {
2190 $field
2191 .as_ref()
2192 .and_then(|v| v.get_property())
2193 .map(|c| c.inner)
2194 .unwrap_or(ColorU::BLACK)
2195 };
2196 }
2197
2198 fn get_border_radius_px(
2200 styled_dom: &StyledDom,
2201 node_id: NodeId,
2202 node_state: &StyledNodeState,
2203 ) -> Option<f32> {
2204 let node_data = &styled_dom.node_data.as_container()[node_id];
2205
2206 let top_left = styled_dom
2207 .css_property_cache
2208 .ptr
2209 .get_border_top_left_radius(node_data, &node_id, node_state)
2210 .and_then(|br| br.get_property().copied())
2211 .map(|v| v.inner.number.get());
2212
2213 let top_right = styled_dom
2214 .css_property_cache
2215 .ptr
2216 .get_border_top_right_radius(node_data, &node_id, node_state)
2217 .and_then(|br| br.get_property().copied())
2218 .map(|v| v.inner.number.get());
2219
2220 let bottom_left = styled_dom
2221 .css_property_cache
2222 .ptr
2223 .get_border_bottom_left_radius(node_data, &node_id, node_state)
2224 .and_then(|br| br.get_property().copied())
2225 .map(|v| v.inner.number.get());
2226
2227 let bottom_right = styled_dom
2228 .css_property_cache
2229 .ptr
2230 .get_border_bottom_right_radius(node_data, &node_id, node_state)
2231 .and_then(|br| br.get_property().copied())
2232 .map(|v| v.inner.number.get());
2233
2234 let radii: Vec<f32> = [top_left, top_right, bottom_left, bottom_right]
2236 .into_iter()
2237 .flatten()
2238 .collect();
2239
2240 if radii.is_empty() {
2241 None
2242 } else {
2243 Some(radii.into_iter().fold(0.0f32, f32::max))
2244 }
2245 }
2246
2247 let top = border_width_px!(&border_info.widths.top);
2248 let right = border_width_px!(&border_info.widths.right);
2249 let bottom = border_width_px!(&border_info.widths.bottom);
2250 let left = border_width_px!(&border_info.widths.left);
2251
2252 let p_top = resolve_padding(get_css_padding_top(styled_dom, node_id, node_state), viewport);
2253 let p_right = resolve_padding(get_css_padding_right(styled_dom, node_id, node_state), viewport);
2254 let p_bottom = resolve_padding(get_css_padding_bottom(styled_dom, node_id, node_state), viewport);
2255 let p_left = resolve_padding(get_css_padding_left(styled_dom, node_id, node_state), viewport);
2256
2257 let has_border = top > 0.0 || right > 0.0 || bottom > 0.0 || left > 0.0;
2259 let has_padding = p_top > 0.0 || p_right > 0.0 || p_bottom > 0.0 || p_left > 0.0;
2260 if !has_border && !has_padding {
2261 return None;
2262 }
2263
2264 let is_rtl = matches!(
2266 get_direction_property(styled_dom, node_id, node_state),
2267 MultiValue::Exact(StyleDirection::Rtl)
2268 );
2269
2270 Some(InlineBorderInfo {
2271 top,
2272 right,
2273 bottom,
2274 left,
2275 top_color: border_color!(&border_info.colors.top),
2276 right_color: border_color!(&border_info.colors.right),
2277 bottom_color: border_color!(&border_info.colors.bottom),
2278 left_color: border_color!(&border_info.colors.left),
2279 radius: get_border_radius_px(styled_dom, node_id, node_state),
2280 padding_top: p_top,
2281 padding_right: p_right,
2282 padding_bottom: p_bottom,
2283 padding_left: p_left,
2284 is_first_fragment: true,
2285 is_last_fragment: true,
2286 is_rtl,
2287 })
2288}
2289
2290#[derive(Debug, Clone, Copy, Default)]
2294pub struct SelectionStyle {
2295 pub bg_color: ColorU,
2297 pub text_color: Option<ColorU>,
2299 pub radius: f32,
2301}
2302
2303#[must_use] pub fn get_selection_style(
2305 styled_dom: &StyledDom,
2306 node_id: Option<NodeId>,
2307 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
2308) -> SelectionStyle {
2309 let Some(node_id) = node_id else {
2310 return SelectionStyle::default();
2311 };
2312
2313 let node_data = &styled_dom.node_data.as_container()[node_id];
2314 let node_state = &StyledNodeState::default();
2315
2316 let default_bg = system_style
2318 .and_then(|ss| ss.colors.selection_background.as_option().copied())
2319 .unwrap_or(ColorU {
2320 r: 51,
2321 g: 153,
2322 b: 255, a: 128, });
2325
2326 let bg_color = styled_dom
2327 .css_property_cache
2328 .ptr
2329 .get_selection_background_color(node_data, &node_id, node_state)
2330 .and_then(|c| c.get_property().copied())
2331 .map_or(default_bg, |c| c.inner);
2332
2333 let default_text = system_style.and_then(|ss| ss.colors.selection_text.as_option().copied());
2335
2336 let text_color = styled_dom
2337 .css_property_cache
2338 .ptr
2339 .get_selection_color(node_data, &node_id, node_state)
2340 .and_then(|c| c.get_property().copied())
2341 .map(|c| c.inner)
2342 .or(default_text);
2343
2344 let radius = styled_dom
2345 .css_property_cache
2346 .ptr
2347 .get_selection_radius(node_data, &node_id, node_state)
2348 .and_then(|r| r.get_property().copied())
2349 .map_or(0.0, |r| r.inner.to_pixels_internal(0.0, DEFAULT_EM_SIZE, DEFAULT_EM_SIZE));
2350
2351 SelectionStyle {
2352 bg_color,
2353 text_color,
2354 radius,
2355 }
2356}
2357
2358#[derive(Debug, Clone, Copy)]
2360pub struct CaretStyle {
2361 pub color: ColorU,
2363 pub width: f32,
2365 pub animation_duration: u32,
2367}
2368
2369impl Default for CaretStyle {
2370 fn default() -> Self {
2371 Self {
2372 color: ColorU::BLACK,
2373 width: DEFAULT_CARET_WIDTH_PX,
2374 animation_duration: DEFAULT_CARET_BLINK_MS,
2375 }
2376 }
2377}
2378
2379#[must_use] pub fn get_caret_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> CaretStyle {
2381 let Some(node_id) = node_id else {
2382 return CaretStyle::default();
2383 };
2384
2385 let node_data = &styled_dom.node_data.as_container()[node_id];
2386 let node_state = &StyledNodeState::default();
2387
2388 let color = styled_dom
2389 .css_property_cache
2390 .ptr
2391 .get_caret_color(node_data, &node_id, node_state)
2392 .and_then(|c| c.get_property().copied())
2393 .map_or_else(|| {
2399 styled_dom
2400 .css_property_cache
2401 .ptr
2402 .get_text_color_or_default(node_data, &node_id, node_state)
2403 .inner
2404 }, |c| c.inner);
2405
2406 let width = styled_dom
2407 .css_property_cache
2408 .ptr
2409 .get_caret_width(node_data, &node_id, node_state)
2410 .and_then(|w| w.get_property().copied())
2411 .map_or(DEFAULT_CARET_WIDTH_PX, |w| w.inner.to_pixels_internal(0.0, DEFAULT_EM_SIZE, DEFAULT_EM_SIZE));
2412
2413 let animation_duration = styled_dom
2414 .css_property_cache
2415 .ptr
2416 .get_caret_animation_duration(node_data, &node_id, node_state)
2417 .and_then(|d| d.get_property().copied())
2418 .map_or(DEFAULT_CARET_BLINK_MS, |d| d.inner.inner);
2419
2420 CaretStyle {
2421 color,
2422 width,
2423 animation_duration,
2424 }
2425}
2426
2427#[must_use] pub fn get_scrollbar_info_from_layout(node: &LayoutNode) -> ScrollbarRequirements {
2439 node.scrollbar_info.unwrap_or_default()
2440}
2441
2442pub fn get_layout_scrollbar_width_px<T: ParsedFontTrait>(
2457 ctx: &crate::solver3::LayoutContext<'_, T>,
2458 dom_id: NodeId,
2459 styled_node_state: &StyledNodeState,
2460) -> f32 {
2461 let style = get_scrollbar_style(
2466 ctx.styled_dom,
2467 dom_id,
2468 styled_node_state,
2469 ctx.system_style.as_deref(),
2470 );
2471 style.reserve_width_px
2472}
2473
2474get_css_property!(
2475 get_display_property_internal,
2476 get_display,
2477 LayoutDisplay,
2478 CssPropertyType::Display,
2479 compact = get_display
2480);
2481
2482#[must_use] pub fn get_display_property(
2483 styled_dom: &StyledDom,
2484 dom_id: Option<NodeId>,
2485) -> MultiValue<LayoutDisplay> {
2486 let Some(id) = dom_id else {
2487 return MultiValue::Exact(LayoutDisplay::Inline);
2488 };
2489 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2490 get_display_property_internal(styled_dom, id, node_state)
2491}
2492
2493#[allow(clippy::match_same_arms)] #[must_use] pub const fn blockify_display(raw_display: LayoutDisplay) -> LayoutDisplay {
2500 match raw_display {
2501 LayoutDisplay::Inline => LayoutDisplay::Block,
2503 LayoutDisplay::InlineBlock => LayoutDisplay::Block,
2506 LayoutDisplay::InlineFlex => LayoutDisplay::Flex,
2507 LayoutDisplay::InlineTable => LayoutDisplay::Table,
2508 LayoutDisplay::InlineGrid => LayoutDisplay::Grid,
2509 LayoutDisplay::TableRowGroup
2512 | LayoutDisplay::TableColumn
2513 | LayoutDisplay::TableColumnGroup
2514 | LayoutDisplay::TableHeaderGroup
2515 | LayoutDisplay::TableFooterGroup
2516 | LayoutDisplay::TableRow
2517 | LayoutDisplay::TableCell
2518 | LayoutDisplay::TableCaption => LayoutDisplay::Block,
2519 other => other,
2521 }
2522}
2523
2524#[allow(clippy::fn_params_excessive_bools)]
2536#[must_use] pub fn get_computed_display(
2537 raw_display: LayoutDisplay,
2538 is_absolute_or_fixed: bool,
2539 is_floated: bool,
2540 is_root: bool,
2541 is_flex_grid_child: bool,
2542) -> LayoutDisplay {
2543 if raw_display == LayoutDisplay::None {
2544 return LayoutDisplay::None;
2545 }
2546 if is_absolute_or_fixed || is_floated || is_root || is_flex_grid_child {
2548 blockify_display(raw_display)
2549 } else {
2550 raw_display
2551 }
2552}
2553
2554#[must_use] pub fn get_vertical_align_for_node(
2559 styled_dom: &StyledDom,
2560 dom_id: NodeId,
2561) -> crate::text3::cache::VerticalAlign {
2562 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
2563 let va = match get_vertical_align_property(styled_dom, dom_id, node_state) {
2564 MultiValue::Exact(v) => v,
2565 _ => StyleVerticalAlign::default(),
2566 };
2567 match va {
2568 StyleVerticalAlign::Baseline => crate::text3::cache::VerticalAlign::Baseline,
2569 StyleVerticalAlign::Top => crate::text3::cache::VerticalAlign::Top,
2570 StyleVerticalAlign::Middle => crate::text3::cache::VerticalAlign::Middle,
2571 StyleVerticalAlign::Bottom => crate::text3::cache::VerticalAlign::Bottom,
2572 StyleVerticalAlign::Sub => crate::text3::cache::VerticalAlign::Sub,
2573 StyleVerticalAlign::Superscript => crate::text3::cache::VerticalAlign::Super,
2574 StyleVerticalAlign::TextTop => crate::text3::cache::VerticalAlign::TextTop,
2575 StyleVerticalAlign::TextBottom => crate::text3::cache::VerticalAlign::TextBottom,
2576 StyleVerticalAlign::Percentage(p) => {
2578 let font_size = get_element_font_size(styled_dom, dom_id, node_state);
2579 let line_height = get_line_height_value(styled_dom, dom_id, node_state)
2580 .map_or(font_size * 1.2, |lh| lh.inner.normalized() * font_size);
2581 crate::text3::cache::VerticalAlign::Offset(p.normalized() * line_height)
2582 }
2583 StyleVerticalAlign::Length(l) => {
2585 let font_size = get_element_font_size(styled_dom, dom_id, node_state);
2586 let px = super::calc::resolve_pixel_value(&l, 0.0, font_size, font_size);
2594 crate::text3::cache::VerticalAlign::Offset(px)
2595 }
2596 }
2597}
2598
2599#[allow(clippy::cast_possible_truncation)] #[allow(clippy::too_many_lines, clippy::cognitive_complexity)] pub fn get_style_properties(
2605 styled_dom: &StyledDom,
2606 dom_id: NodeId,
2607 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
2608 viewport_size: PhysicalSize,
2609) -> StyleProperties {
2610 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
2611
2612 let node_data = &styled_dom.node_data.as_container()[dom_id];
2613 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
2614 let cache = &styled_dom.css_property_cache.ptr;
2615
2616 let font_families = if node_state.is_normal() {
2619 cache
2620 .compact_cache
2621 .as_ref()
2622 .and_then(|cc| {
2623 let fh = cc.tier2b_text[dom_id.index()].font_family_hash;
2624 if fh == 0 {
2625 return None;
2626 }
2627 cc.font_hash_to_families.get(&fh).cloned()
2628 })
2629 .unwrap_or_else(|| {
2630 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2631 })
2632 } else {
2633 cache
2634 .get_font_family(node_data, &dom_id, node_state)
2635 .and_then(|v| v.get_property().cloned())
2636 .unwrap_or_else(|| {
2637 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2638 })
2639 };
2640
2641 let parent_font_size = get_parent_font_size(styled_dom, dom_id, node_state);
2647
2648 let root_font_size = get_root_font_size(styled_dom, node_state);
2649
2650 let font_size_context = ResolutionContext {
2652 element_font_size: DEFAULT_FONT_SIZE, parent_font_size,
2654 root_font_size,
2655 containing_block_size: PhysicalSize::new(0.0, 0.0),
2656 element_size: None,
2657 viewport_size,
2658 };
2659
2660 let font_size = {
2664 let mut fast_font_size: Option<f32> = None;
2669 let mut compact_said_inherit = false;
2670 if node_state.is_normal() {
2671 if let Some(ref cc) = cache.compact_cache {
2672 let raw = cc.get_font_size_raw(dom_id.index());
2673 if raw == azul_css::compact_cache::U32_SENTINEL
2674 || raw == azul_css::compact_cache::U32_INHERIT
2675 || raw == azul_css::compact_cache::U32_INITIAL
2676 {
2677 compact_said_inherit = true;
2678 } else if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
2679 fast_font_size = Some(
2680 pv.resolve_with_context(&font_size_context, PropertyContext::FontSize),
2681 );
2682 }
2683 }
2684 }
2685 fast_font_size.unwrap_or_else(|| {
2686 if compact_said_inherit {
2687 parent_font_size
2688 } else {
2689 cache
2690 .get_font_size(node_data, &dom_id, node_state)
2691 .and_then(|v| v.get_property().copied())
2692 .map_or(parent_font_size, |v| {
2693 v.inner
2694 .resolve_with_context(&font_size_context, PropertyContext::FontSize)
2695 })
2696 }
2697 })
2698 };
2699
2700 let color_from_cache = {
2701 let mut fast_color = None;
2703 if node_state.is_normal() {
2704 if let Some(ref cc) = cache.compact_cache {
2705 let raw = cc.get_text_color_raw(dom_id.index());
2706 if raw != 0 {
2707 fast_color = Some(ColorU {
2709 r: (raw >> 24) as u8,
2710 g: (raw >> 16) as u8,
2711 b: (raw >> 8) as u8,
2712 a: raw as u8,
2713 });
2714 }
2715 }
2716 }
2717 fast_color.or_else(|| {
2718 cache
2719 .get_text_color(node_data, &dom_id, node_state)
2720 .and_then(|v| v.get_property().copied())
2721 .map(|v| v.inner)
2722 })
2723 };
2724
2725 let color = color_from_cache.unwrap_or(ColorU::BLACK);
2731
2732 let line_height = {
2734 let mut fast_lh = None;
2743 let mut sentinel_normal = false;
2744 if node_state.is_normal() {
2745 if let Some(ref cc) = cache.compact_cache {
2746 if let Some(normalized) = cc.get_line_height(dom_id.index()) {
2747 let n = normalized / 100.0;
2754 fast_lh = Some(crate::text3::cache::LineHeight::Px(
2755 if n < 0.0 { -n } else { n * font_size },
2756 ));
2757 } else {
2758 sentinel_normal = true;
2760 }
2761 }
2762 }
2763 if sentinel_normal {
2764 crate::text3::cache::LineHeight::Normal
2765 } else {
2766 fast_lh.unwrap_or_else(|| {
2767 cache
2768 .get_line_height(node_data, &dom_id, node_state)
2769 .and_then(|v| v.get_property().copied())
2770 .map_or(crate::text3::cache::LineHeight::Normal, |v| {
2771 let n = v.inner.normalized();
2774 crate::text3::cache::LineHeight::Px(if n < 0.0 { -n } else { n * font_size })
2775 })
2776 })
2777 }
2778 };
2779
2780 let display = match get_display_property(styled_dom, Some(dom_id)) {
2791 MultiValue::Exact(v) => v,
2792 _ => LayoutDisplay::Inline,
2793 };
2794
2795 let (background_color, background_content, border) =
2798 if matches!(display, LayoutDisplay::Inline | LayoutDisplay::InlineBlock) {
2799 let bg = get_background_color(styled_dom, dom_id, node_state);
2800 let bg_color = if bg.a > 0 { Some(bg) } else { None };
2801
2802 let bg_contents = get_background_contents(styled_dom, dom_id, node_state);
2804
2805 let border_info = get_border_info(styled_dom, dom_id, node_state);
2807 let inline_border =
2808 get_inline_border_info(styled_dom, dom_id, node_state, &border_info, viewport_size);
2809
2810 (bg_color, bg_contents, inline_border)
2811 } else {
2812 (None, Vec::new(), None)
2815 };
2816
2817 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
2819 MultiValue::Exact(v) => v,
2820 _ => StyleFontWeight::Normal,
2821 };
2822
2823 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
2825 MultiValue::Exact(v) => v,
2826 _ => StyleFontStyle::Normal,
2827 };
2828
2829 let fc_weight = super::fc::convert_font_weight(font_weight);
2831 let fc_style = super::fc::convert_font_style(font_style);
2832
2833 let font_stack = {
2836 let font_ref = (0..font_families.len()).find_map(|i| match font_families.get(i).unwrap() {
2837 StyleFontFamily::Ref(r) => Some(r.clone()),
2838 _ => None,
2839 });
2840
2841 font_ref.map_or_else(
2842 || {
2843 let platform = system_style.map(|ss| &ss.platform);
2848 FontStack::Stack(build_font_selector_stack(
2849 &font_families,
2850 platform,
2851 fc_weight,
2852 fc_style,
2853 ))
2854 },
2855 FontStack::Ref,
2856 )
2857 };
2858
2859 let letter_spacing = {
2861 let mut fast_ls = None;
2863 if node_state.is_normal() {
2864 if let Some(ref cc) = cache.compact_cache {
2865 if let Some(px_val) = cc.get_letter_spacing(dom_id.index()) {
2866 fast_ls = Some(crate::text3::cache::Spacing::PxF(px_val));
2867 }
2868 }
2869 }
2870 fast_ls.unwrap_or_else(|| {
2871 cache
2872 .get_letter_spacing(node_data, &dom_id, node_state)
2873 .and_then(|v| v.get_property().copied())
2874 .map(|v| {
2875 let px_value = v
2876 .inner
2877 .resolve_with_context(&font_size_context, PropertyContext::FontSize);
2878 crate::text3::cache::Spacing::PxF(px_value)
2879 })
2880 .unwrap_or_default()
2881 })
2882 };
2883
2884 let word_spacing = {
2886 let mut fast_ws = None;
2888 if node_state.is_normal() {
2889 if let Some(ref cc) = cache.compact_cache {
2890 if let Some(px_val) = cc.get_word_spacing(dom_id.index()) {
2891 fast_ws = Some(crate::text3::cache::Spacing::PxF(px_val));
2892 }
2893 }
2894 }
2895 fast_ws.unwrap_or_else(|| {
2896 cache
2897 .get_word_spacing(node_data, &dom_id, node_state)
2898 .and_then(|v| v.get_property().copied())
2899 .map(|v| {
2900 let px_value = v
2901 .inner
2902 .resolve_with_context(&font_size_context, PropertyContext::FontSize);
2903 crate::text3::cache::Spacing::PxF(px_value)
2904 })
2905 .unwrap_or_default()
2906 })
2907 };
2908
2909 let text_decoration = {
2916 let mut skip_walk = false;
2917 if node_state.is_normal() {
2918 if let Some(ref cc) = cache.compact_cache {
2919 if !cc.has_text_decoration(dom_id.index()) {
2920 skip_walk = true;
2921 }
2922 }
2923 }
2924 if skip_walk {
2925 crate::text3::cache::TextDecoration::default()
2926 } else {
2927 cache
2928 .get_text_decoration(node_data, &dom_id, node_state)
2929 .and_then(|v| v.get_property().copied())
2930 .map(crate::text3::cache::TextDecoration::from_css)
2931 .unwrap_or_default()
2932 }
2933 };
2934
2935 let tab_size = {
2947 let mut fast_tab = None;
2948 if node_state.is_normal() {
2949 if let Some(ref cc) = cache.compact_cache {
2950 let raw = cc.get_tab_size_raw(dom_id.index());
2951 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
2952 fast_tab = Some(f32::from(raw) / 10.0);
2953 } else {
2954 fast_tab = Some(8.0);
2956 }
2957 }
2958 }
2959 fast_tab.unwrap_or_else(|| {
2960 cache
2961 .get_tab_size(node_data, &dom_id, node_state)
2962 .and_then(|v| v.get_property().copied())
2963 .map_or(DEFAULT_TAB_SIZE, |v| v.inner.number.get())
2964 })
2965 };
2966
2967 let text_transform = cache
2971 .get_text_transform(node_data, &dom_id, node_state)
2972 .and_then(|v| v.get_property().copied())
2973 .map(|t| {
2974 use azul_css::props::style::text::StyleTextTransform as Css;
2975 use crate::text3::cache::TextTransform as T3;
2976 match t {
2977 Css::None => T3::None,
2978 Css::Uppercase => T3::Uppercase,
2979 Css::Lowercase => T3::Lowercase,
2980 Css::Capitalize => T3::Capitalize,
2981 Css::FullWidth => T3::FullWidth,
2982 }
2983 })
2984 .unwrap_or_default();
2985
2986 StyleProperties {
2987 font_stack,
2988 font_size_px: font_size,
2989 color,
2990 background_color,
2991 background_content,
2992 border,
2993 line_height,
2994 letter_spacing,
2995 word_spacing,
2996 text_decoration,
2997 tab_size,
2998 text_transform,
2999 ..Default::default()
3003 }
3004}
3005
3006#[must_use] pub fn get_list_style_type(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> StyleListStyleType {
3007 let Some(id) = dom_id else {
3008 return StyleListStyleType::default();
3009 };
3010 let node_data = &styled_dom.node_data.as_container()[id];
3011 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3012 styled_dom
3013 .css_property_cache
3014 .ptr
3015 .get_list_style_type(node_data, &id, node_state)
3016 .and_then(|v| v.get_property().copied())
3017 .unwrap_or_default()
3018}
3019
3020#[must_use] pub fn get_list_style_position(
3021 styled_dom: &StyledDom,
3022 dom_id: Option<NodeId>,
3023) -> StyleListStylePosition {
3024 let Some(id) = dom_id else {
3025 return StyleListStylePosition::default();
3026 };
3027 let node_data = &styled_dom.node_data.as_container()[id];
3028 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3029 styled_dom
3030 .css_property_cache
3031 .ptr
3032 .get_list_style_position(node_data, &id, node_state)
3033 .and_then(|v| v.get_property().copied())
3034 .unwrap_or_default()
3035}
3036
3037use azul_css::props::layout::{
3040 LayoutInsetBottom, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight,
3041 LayoutMarginTop, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth,
3042 LayoutPaddingBottom, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutRight,
3043 LayoutTop,
3044};
3045
3046get_css_property_pixel!(
3048 get_css_left,
3049 get_left,
3050 CssPropertyType::Left,
3051 compact_i16 = get_left
3052);
3053get_css_property_pixel!(
3054 get_css_right,
3055 get_right,
3056 CssPropertyType::Right,
3057 compact_i16 = get_right
3058);
3059get_css_property_pixel!(
3060 get_css_top,
3061 get_top,
3062 CssPropertyType::Top,
3063 compact_i16 = get_top
3064);
3065get_css_property_pixel!(
3066 get_css_bottom,
3067 get_bottom,
3068 CssPropertyType::Bottom,
3069 compact_i16 = get_bottom
3070);
3071
3072get_css_property_pixel!(
3074 get_css_margin_left,
3075 get_margin_left,
3076 CssPropertyType::MarginLeft,
3077 compact_i16 = get_margin_left_raw
3078);
3079get_css_property_pixel!(
3080 get_css_margin_right,
3081 get_margin_right,
3082 CssPropertyType::MarginRight,
3083 compact_i16 = get_margin_right_raw
3084);
3085get_css_property_pixel!(
3086 get_css_margin_top,
3087 get_margin_top,
3088 CssPropertyType::MarginTop,
3089 compact_i16 = get_margin_top_raw
3090);
3091get_css_property_pixel!(
3092 get_css_margin_bottom,
3093 get_margin_bottom,
3094 CssPropertyType::MarginBottom,
3095 compact_i16 = get_margin_bottom_raw
3096);
3097
3098get_css_property_pixel!(
3100 get_css_padding_left,
3101 get_padding_left,
3102 CssPropertyType::PaddingLeft,
3103 compact_i16 = get_padding_left_raw
3104);
3105get_css_property_pixel!(
3106 get_css_padding_right,
3107 get_padding_right,
3108 CssPropertyType::PaddingRight,
3109 compact_i16 = get_padding_right_raw
3110);
3111get_css_property_pixel!(
3112 get_css_padding_top,
3113 get_padding_top,
3114 CssPropertyType::PaddingTop,
3115 compact_i16 = get_padding_top_raw
3116);
3117get_css_property_pixel!(
3118 get_css_padding_bottom,
3119 get_padding_bottom,
3120 CssPropertyType::PaddingBottom,
3121 compact_i16 = get_padding_bottom_raw
3122);
3123
3124get_css_property!(
3126 get_css_min_width,
3127 get_min_width,
3128 LayoutMinWidth,
3129 CssPropertyType::MinWidth,
3130 compact_u32_struct = get_min_width_raw
3131);
3132
3133get_css_property!(
3134 get_css_min_height,
3135 get_min_height,
3136 LayoutMinHeight,
3137 CssPropertyType::MinHeight,
3138 compact_u32_struct = get_min_height_raw
3139);
3140
3141get_css_property!(
3142 get_css_max_width,
3143 get_max_width,
3144 LayoutMaxWidth,
3145 CssPropertyType::MaxWidth,
3146 compact_u32_struct = get_max_width_raw
3147);
3148
3149get_css_property!(
3150 get_css_max_height,
3151 get_max_height,
3152 LayoutMaxHeight,
3153 CssPropertyType::MaxHeight,
3154 compact_u32_struct = get_max_height_raw
3155);
3156
3157get_css_property_pixel!(
3159 get_css_border_left_width,
3160 get_border_left_width,
3161 CssPropertyType::BorderLeftWidth,
3162 compact_i16 = get_border_left_width_raw
3163);
3164get_css_property_pixel!(
3165 get_css_border_right_width,
3166 get_border_right_width,
3167 CssPropertyType::BorderRightWidth,
3168 compact_i16 = get_border_right_width_raw
3169);
3170get_css_property_pixel!(
3171 get_css_border_top_width,
3172 get_border_top_width,
3173 CssPropertyType::BorderTopWidth,
3174 compact_i16 = get_border_top_width_raw
3175);
3176get_css_property_pixel!(
3177 get_css_border_bottom_width,
3178 get_border_bottom_width,
3179 CssPropertyType::BorderBottomWidth,
3180 compact_i16 = get_border_bottom_width_raw
3181);
3182
3183#[must_use] pub fn get_break_before(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
3187 let Some(id) = dom_id else {
3188 return PageBreak::Auto;
3189 };
3190 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3191 if node_state.is_normal() {
3193 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
3194 if !cc.has_break(id.index()) {
3195 return PageBreak::Auto;
3196 }
3197 }
3198 }
3199 let node_data = &styled_dom.node_data.as_container()[id];
3200 styled_dom
3201 .css_property_cache
3202 .ptr
3203 .get_break_before(node_data, &id, node_state)
3204 .and_then(|v| v.get_property().copied())
3205 .unwrap_or(PageBreak::Auto)
3206}
3207
3208#[must_use] pub fn get_break_after(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
3210 let Some(id) = dom_id else {
3211 return PageBreak::Auto;
3212 };
3213 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3214 if node_state.is_normal() {
3215 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
3216 if !cc.has_break(id.index()) {
3217 return PageBreak::Auto;
3218 }
3219 }
3220 }
3221 let node_data = &styled_dom.node_data.as_container()[id];
3222 styled_dom
3223 .css_property_cache
3224 .ptr
3225 .get_break_after(node_data, &id, node_state)
3226 .and_then(|v| v.get_property().copied())
3227 .unwrap_or(PageBreak::Auto)
3228}
3229
3230#[must_use] pub const fn is_forced_page_break(page_break: PageBreak) -> bool {
3232 matches!(
3233 page_break,
3234 PageBreak::Always
3235 | PageBreak::Page
3236 | PageBreak::Left
3237 | PageBreak::Right
3238 | PageBreak::Recto
3239 | PageBreak::Verso
3240 | PageBreak::All
3241 )
3242}
3243
3244#[must_use] pub fn get_break_inside(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> BreakInside {
3246 let Some(id) = dom_id else {
3247 return BreakInside::Auto;
3248 };
3249 let node_data = &styled_dom.node_data.as_container()[id];
3250 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3251 styled_dom
3252 .css_property_cache
3253 .ptr
3254 .get_break_inside(node_data, &id, node_state)
3255 .and_then(|v| v.get_property().copied())
3256 .unwrap_or(BreakInside::Auto)
3257}
3258
3259#[must_use] pub fn get_orphans(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
3261 let Some(id) = dom_id else {
3262 return 2; };
3264 let node_data = &styled_dom.node_data.as_container()[id];
3265 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3266 styled_dom
3267 .css_property_cache
3268 .ptr
3269 .get_orphans(node_data, &id, node_state)
3270 .and_then(|v| v.get_property().copied())
3271 .map_or(2, |o| o.inner)
3272}
3273
3274#[must_use] pub fn get_widows(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
3276 let Some(id) = dom_id else {
3277 return 2; };
3279 let node_data = &styled_dom.node_data.as_container()[id];
3280 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3281 styled_dom
3282 .css_property_cache
3283 .ptr
3284 .get_widows(node_data, &id, node_state)
3285 .and_then(|v| v.get_property().copied())
3286 .map_or(2, |w| w.inner)
3287}
3288
3289#[must_use] pub fn get_box_decoration_break(
3291 styled_dom: &StyledDom,
3292 dom_id: Option<NodeId>,
3293) -> BoxDecorationBreak {
3294 let Some(id) = dom_id else {
3295 return BoxDecorationBreak::Slice;
3296 };
3297 let node_data = &styled_dom.node_data.as_container()[id];
3298 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3299 styled_dom
3300 .css_property_cache
3301 .ptr
3302 .get_box_decoration_break(node_data, &id, node_state)
3303 .and_then(|v| v.get_property().copied())
3304 .unwrap_or(BoxDecorationBreak::Slice)
3305}
3306
3307#[must_use] pub const fn is_avoid_page_break(page_break: &PageBreak) -> bool {
3311 matches!(page_break, PageBreak::Avoid | PageBreak::AvoidPage)
3312}
3313
3314#[must_use] pub const fn is_avoid_break_inside(break_inside: &BreakInside) -> bool {
3316 matches!(
3317 break_inside,
3318 BreakInside::Avoid | BreakInside::AvoidPage | BreakInside::AvoidColumn
3319 )
3320}
3321
3322use std::collections::HashMap;
3325
3326use rust_fontconfig::{
3327 FcFontCache, FcWeight, FontFallbackChain, PatternMatch, UnicodeRange,
3328 DEFAULT_UNICODE_FALLBACK_SCRIPTS,
3329};
3330
3331use crate::text3::cache::{FontChainKey, FontChainKeyOrRef, FontSelector, FontStack, FontStyle};
3332
3333#[allow(clippy::option_if_let_else)]
3349fn build_font_selector_stack(
3350 font_families: &StyleFontFamilyVec,
3351 platform: Option<&azul_css::system::Platform>,
3352 fc_weight: FcWeight,
3353 fc_style: FontStyle,
3354) -> Vec<FontSelector> {
3355 let mut stack = Vec::with_capacity(font_families.len() + 3);
3356
3357 for i in 0..font_families.len() {
3358 let family = font_families.get(i).unwrap();
3359 if matches!(family, StyleFontFamily::Ref(_)) {
3360 continue;
3361 }
3362 if let StyleFontFamily::SystemType(system_type) = family {
3363 let current;
3364 let platform = if let Some(p) = platform { p } else {
3365 current = azul_css::system::Platform::current();
3366 ¤t
3367 };
3368 let font_names = system_type.get_fallback_chain(platform);
3369 let system_weight = if system_type.is_bold() {
3370 FcWeight::Bold
3371 } else {
3372 fc_weight
3373 };
3374 let system_style = if system_type.is_italic() {
3375 FontStyle::Italic
3376 } else {
3377 fc_style
3378 };
3379 for font_name in font_names {
3380 stack.push(FontSelector {
3381 family: font_name.to_string(),
3382 weight: system_weight,
3383 style: system_style,
3384 unicode_ranges: Vec::new(),
3385 });
3386 }
3387 } else {
3388 stack.push(FontSelector {
3389 family: family.as_string(),
3390 weight: fc_weight,
3391 style: fc_style,
3392 unicode_ranges: Vec::new(),
3393 });
3394 }
3395 }
3396
3397 for fallback in &["sans-serif", "serif", "monospace"] {
3398 if !stack
3399 .iter()
3400 .any(|f| f.family.eq_ignore_ascii_case(fallback))
3401 {
3402 stack.push(FontSelector {
3403 family: (*fallback).to_string(),
3404 weight: FcWeight::Normal,
3405 style: FontStyle::Normal,
3406 unicode_ranges: Vec::new(),
3407 });
3408 }
3409 }
3410
3411 stack
3412}
3413
3414#[derive(Debug, Clone)]
3417pub struct CollectedFontStacks {
3418 pub font_stacks: Vec<Vec<FontSelector>>,
3420 pub hash_to_index: HashMap<u64, usize>,
3422 pub font_refs: HashMap<usize, azul_css::props::basic::font::FontRef>,
3425}
3426
3427#[derive(Debug, Clone, Default)]
3430pub struct ResolvedFontChains {
3431 pub chains: HashMap<FontChainKeyOrRef, FontFallbackChain>,
3435 pub unresolved_families: std::collections::BTreeSet<String>,
3446 pub last_resort_chains: usize,
3450}
3451
3452impl ResolvedFontChains {
3453 #[must_use] pub fn get(&self, key: &FontChainKeyOrRef) -> Option<&FontFallbackChain> {
3455 self.chains.get(key)
3456 }
3457
3458 #[must_use] pub fn get_by_chain_key(&self, key: &FontChainKey) -> Option<&FontFallbackChain> {
3460 self.chains.get(&FontChainKeyOrRef::Chain(key.clone()))
3461 }
3462
3463 #[must_use] pub fn get_for_font_stack(&self, font_stack: &[FontSelector]) -> Option<&FontFallbackChain> {
3465 let key = FontChainKeyOrRef::Chain(FontChainKey::from_selectors(font_stack));
3466 self.chains.get(&key)
3467 }
3468
3469 #[must_use] pub fn get_for_font_ref(&self, ptr: usize) -> Option<&FontFallbackChain> {
3471 self.chains.get(&FontChainKeyOrRef::Ref(ptr))
3472 }
3473
3474 #[must_use] pub fn into_inner(self) -> HashMap<FontChainKeyOrRef, FontFallbackChain> {
3478 self.chains
3479 }
3480
3481 #[must_use] pub fn into_fontconfig_chains(self) -> HashMap<FontChainKey, FontFallbackChain> {
3486 let mut out: HashMap<FontChainKey, FontFallbackChain> = HashMap::new();
3490 if self.chains.is_empty() {
3491 return out;
3492 }
3493 for (key, chain) in self.chains {
3494 if let FontChainKeyOrRef::Chain(chain_key) = key {
3495 out.insert(chain_key, chain);
3496 }
3497 }
3498 out
3499 }
3500
3501 #[must_use] pub fn len(&self) -> usize {
3503 self.chains.len()
3504 }
3505
3506 #[must_use] pub fn is_empty(&self) -> bool {
3508 self.chains.is_empty()
3509 }
3510
3511 #[must_use] pub fn font_refs_len(&self) -> usize {
3513 self.chains.keys().filter(|k| k.is_ref()).count()
3514 }
3515}
3516
3517#[allow(clippy::cast_possible_truncation)] #[allow(clippy::too_many_lines)] #[must_use] pub fn collect_font_stacks_from_styled_dom(
3531 styled_dom: &StyledDom,
3532 platform: &azul_css::system::Platform,
3533) -> CollectedFontStacks {
3534 use azul_css::compact_cache::{
3535 FONT_STYLE_MASK, FONT_STYLE_SHIFT, FONT_WEIGHT_MASK, FONT_WEIGHT_SHIFT,
3536 };
3537
3538 let mut font_stacks = Vec::new();
3539 let mut hash_to_index: HashMap<u64, usize> = HashMap::new();
3540 let mut font_refs: HashMap<usize, azul_css::props::basic::font::FontRef> = HashMap::new();
3541
3542 let node_data = styled_dom.node_data.as_container();
3543 let cache = &styled_dom.css_property_cache.ptr;
3544 let Some(compact) = cache.compact_cache.as_ref() else {
3545 return CollectedFontStacks {
3546 font_stacks,
3547 hash_to_index,
3548 font_refs,
3549 };
3550 };
3551
3552 let mut unique_font_keys: HashMap<(u64, u8, u8), usize> = HashMap::new();
3561 let node_count = node_data.internal.len();
3562
3563 if node_count > 1 {
3566 let p1 = (&raw const node_data.internal[1].node_type).cast::<u8>();
3567 let p0 = (&raw const node_data.internal[0].node_type).cast::<u8>();
3568 unsafe {
3569 crate::az_mark(0x606D0_u32, u32::from(core::ptr::read(p1)));
3570 crate::az_mark(0x606D4_u32, u32::from(core::ptr::read(p1.add(1))));
3571 crate::az_mark(0x606D8_u32, u32::from(core::ptr::read(p1.add(2))));
3572 crate::az_mark(0x606DC_u32, u32::from(core::ptr::read(p1.add(4))));
3573 crate::az_mark(0x606E0_u32, u32::from(core::ptr::read(p0)));
3574 }
3575 }
3576 for i in 0..node_count {
3577 let nt_disc = unsafe {
3584 core::ptr::read((&raw const node_data.internal[i].node_type).cast::<u8>())
3585 };
3586 let is_text = nt_disc == 177
3587 || matches!(node_data.internal[i].node_type, NodeType::Text(_));
3588 if !is_text {
3589 continue;
3590 }
3591 let fh = compact.tier2b_text[i].font_family_hash;
3592 let t1 = compact.tier1_enums[i];
3593 let weight_bits = ((t1 >> FONT_WEIGHT_SHIFT) & FONT_WEIGHT_MASK) as u8;
3594 let style_bits = ((t1 >> FONT_STYLE_SHIFT) & FONT_STYLE_MASK) as u8;
3595 let key = (fh, weight_bits, style_bits);
3596 unique_font_keys.entry(key).or_insert(i);
3597 }
3598
3599 {
3605 let mut raw_text = 0u32;
3606 for i in 0..node_count {
3607 let nt_ptr = (&raw const node_data.internal[i].node_type).cast::<u8>();
3609 let disc = unsafe { core::ptr::read_volatile(nt_ptr) };
3610 if disc != unsafe { core::ptr::read_volatile((&raw const node_data.internal[0].node_type).cast::<u8>()) } {
3612 raw_text += 1;
3613 }
3614 }
3615 unsafe {
3616 crate::az_mark(0x606C0_u32, (0x5E5E_0003_u32));
3617 crate::az_mark(0x606C4_u32, (node_count as u32));
3618 crate::az_mark(0x606C8_u32, (unique_font_keys.len() as u32));
3619 crate::az_mark(0x606CC_u32, (raw_text));
3620 }
3621 }
3622
3623 let styled_nodes = styled_dom.styled_nodes.as_container();
3626
3627 for (&(fh, _wb, _sb), &repr_idx) in &unique_font_keys {
3628 let Some(dom_id) = NodeId::from_usize(repr_idx) else {
3629 continue;
3630 };
3631 let node_state = &styled_nodes[dom_id].styled_node_state;
3632
3633 let font_families = compact
3637 .font_hash_to_families
3638 .get(&fh)
3639 .cloned()
3640 .unwrap_or_else(|| {
3641 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
3642 });
3643
3644 if let Some(StyleFontFamily::Ref(font_ref)) = font_families.get(0) {
3646 let ptr = font_ref.parsed as usize;
3647 font_refs.entry(ptr).or_insert_with(|| font_ref.clone());
3648 continue;
3649 }
3650
3651 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
3652 MultiValue::Exact(v) => v,
3653 _ => StyleFontWeight::Normal,
3654 };
3655 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
3656 MultiValue::Exact(v) => v,
3657 _ => StyleFontStyle::Normal,
3658 };
3659
3660 let fc_weight = super::fc::convert_font_weight(font_weight);
3661 let fc_style = super::fc::convert_font_style(font_style);
3662
3663 let font_stack =
3664 build_font_selector_stack(&font_families, Some(platform), fc_weight, fc_style);
3665
3666 if font_stack.is_empty() {
3667 continue;
3668 }
3669
3670 let key = FontChainKey::from_selectors(&font_stack);
3671 let hash = {
3672 use std::hash::{Hash, Hasher};
3673 let mut hasher = std::collections::hash_map::DefaultHasher::new();
3674 key.hash(&mut hasher);
3675 hasher.finish()
3676 };
3677
3678 hash_to_index.entry(hash).or_insert_with(|| {
3679 let idx = font_stacks.len();
3680 font_stacks.push(font_stack);
3681 idx
3682 });
3683 }
3684
3685 CollectedFontStacks {
3686 font_stacks,
3687 hash_to_index,
3688 font_refs,
3689 }
3690}
3691
3692#[must_use] pub fn collect_used_codepoints(styled_dom: &StyledDom) -> std::collections::BTreeSet<u32> {
3715 let mut out = std::collections::BTreeSet::new();
3716 let node_data = styled_dom.node_data.as_container();
3717 for node in node_data.internal {
3718 let NodeType::Text(s) = &node.node_type else {
3719 continue;
3720 };
3721 for c in s.as_str().chars() {
3722 let cp = c as u32;
3723 if cp >= 0x80 {
3724 out.insert(cp);
3725 }
3726 }
3727 }
3728 out
3729}
3730
3731#[must_use] pub fn collect_used_codepoints_all(styled_dom: &StyledDom) -> std::collections::BTreeSet<char> {
3745 let mut out = std::collections::BTreeSet::new();
3746 let node_data = styled_dom.node_data.as_container();
3747 for node in node_data.internal {
3748 let NodeType::Text(s) = &node.node_type else {
3749 continue;
3750 };
3751 for c in s.as_str().chars() {
3752 out.insert(c);
3753 }
3754 }
3755 out
3756}
3757
3758pub fn prune_chain_to_used_chars(
3780 chain: &mut FontFallbackChain,
3781 used_chars: &std::collections::BTreeSet<u32>,
3782) {
3783 fn fm_covers(fm: &rust_fontconfig::FontMatch, cp: u32) -> bool {
3784 fm.unicode_ranges
3785 .iter()
3786 .any(|r| cp >= r.start && cp <= r.end)
3787 }
3788
3789 for group in &mut chain.css_fallbacks {
3790 if group.fonts.is_empty() {
3791 continue;
3792 }
3793 let mut needed: Vec<u32> = used_chars.iter().copied().collect();
3796 needed.retain(|&cp| !fm_covers(&group.fonts[0], cp));
3797 let mut keep = 1;
3798 for fm in group.fonts.iter().skip(1) {
3799 if needed.is_empty() {
3800 break;
3801 }
3802 keep += 1;
3803 needed.retain(|&cp| !fm_covers(fm, cp));
3804 }
3805 group.fonts.truncate(keep);
3806 }
3807
3808 chain
3809 .unicode_fallbacks
3810 .retain(|fm| used_chars.iter().any(|&cp| fm_covers(fm, cp)));
3811}
3812
3813#[must_use] pub fn scripts_present_in_styled_dom(styled_dom: &StyledDom) -> Vec<UnicodeRange> {
3828 let scripts = DEFAULT_UNICODE_FALLBACK_SCRIPTS;
3829 let mut seen = vec![false; scripts.len()];
3830 let mut hits = 0usize;
3831 let node_data = styled_dom.node_data.as_container();
3832 'outer: for node in node_data.internal {
3833 let text: &str = match &node.node_type {
3834 NodeType::Text(s) => s.as_str(),
3835 _ => continue,
3836 };
3837 for c in text.chars() {
3838 let cp = c as u32;
3839 if cp < 0x0400 {
3843 continue;
3844 }
3845 for (idx, r) in scripts.iter().enumerate() {
3846 if !seen[idx] && cp >= r.start && cp <= r.end {
3847 seen[idx] = true;
3848 hits += 1;
3849 if hits == scripts.len() {
3850 break 'outer;
3851 }
3852 break;
3853 }
3854 }
3855 }
3856 }
3857 scripts
3858 .iter()
3859 .enumerate()
3860 .filter_map(|(i, r)| if seen[i] { Some(*r) } else { None })
3861 .collect()
3862}
3863
3864#[must_use] pub fn resolve_font_chains(
3879 collected: &CollectedFontStacks,
3880 fc_cache: &FcFontCache,
3881 scripts_hint: Option<&[UnicodeRange]>,
3882) -> ResolvedFontChains {
3883 resolve_font_chains_with_registry(collected, fc_cache, None, scripts_hint, &HashMap::new())
3884}
3885
3886fn split_memory_matches(
3898 font_families: &[String],
3899 memory_families: &HashMap<String, rust_fontconfig::FontMatch>,
3900) -> (Vec<rust_fontconfig::CssFallbackGroup>, Vec<String>) {
3901 let mut groups = Vec::new();
3902 let mut disk = Vec::new();
3903 for family in font_families {
3904 let norm = rust_fontconfig::utils::normalize_family_name(family);
3905 if let Some(m) = memory_families.get(&norm) {
3906 groups.push(rust_fontconfig::CssFallbackGroup {
3907 css_name: family.clone(),
3908 fonts: vec![m.clone()],
3909 });
3910 } else {
3911 disk.push(family.clone());
3912 }
3913 }
3914 (groups, disk)
3915}
3916
3917#[must_use] pub fn resolve_font_chains_with_registry(
3932 collected: &CollectedFontStacks,
3933 fc_cache: &FcFontCache,
3934 registry: Option<&rust_fontconfig::registry::FcFontRegistry>,
3935 scripts_hint: Option<&[UnicodeRange]>,
3936 memory_families: &HashMap<String, rust_fontconfig::FontMatch>,
3937) -> ResolvedFontChains {
3938 let mut chains = HashMap::new();
3939 let mut unresolved: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
3940
3941 for font_stack in &collected.font_stacks {
3943 if font_stack.is_empty() {
3944 continue;
3945 }
3946
3947 let canonical_key = FontChainKey::from_selectors(font_stack);
3952 let font_families = canonical_key.font_families.clone();
3953
3954 let weight = font_stack[0].weight;
3955 let is_italic = font_stack[0].style == FontStyle::Italic;
3956 let is_oblique = font_stack[0].style == FontStyle::Oblique;
3957
3958 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
3959 font_families: font_families.clone(),
3960 weight,
3961 italic: is_italic,
3962 oblique: is_oblique,
3963 });
3964
3965 if chains.contains_key(&cache_key) {
3967 continue;
3968 }
3969
3970 let italic = if is_italic {
3975 PatternMatch::True
3976 } else {
3977 PatternMatch::False
3978 };
3979 let oblique = if is_oblique {
3980 PatternMatch::True
3981 } else {
3982 PatternMatch::False
3983 };
3984
3985 let (mem_groups, disk_families) = split_memory_matches(&font_families, memory_families);
3989
3990 let mut chain = if disk_families.is_empty() {
3993 FontFallbackChain {
3994 css_fallbacks: Vec::new(),
3995 unicode_fallbacks: Vec::new(),
3996 original_stack: font_families.clone(),
3997 }
3998 } else {
3999 registry.map_or_else(
4000 || {
4001 let mut trace = Vec::new();
4002 fc_cache.resolve_font_chain_with_scripts(
4003 &disk_families,
4004 weight,
4005 italic,
4006 oblique,
4007 scripts_hint,
4008 &mut trace,
4009 )
4010 },
4011 |reg| {
4012 reg.request_and_resolve_with_scripts(
4013 &disk_families,
4014 weight,
4015 italic,
4016 oblique,
4017 scripts_hint,
4018 )
4019 },
4020 )
4021 };
4022 if !mem_groups.is_empty() {
4023 let mut merged = mem_groups;
4024 merged.extend(chain.css_fallbacks.drain(..));
4025 chain.css_fallbacks = merged;
4026 }
4027
4028 for family in &font_families {
4031 let matched = chain
4032 .css_fallbacks
4033 .iter()
4034 .any(|g| g.css_name.eq_ignore_ascii_case(family) && !g.fonts.is_empty());
4035 if !matched && !is_generic_family(family) {
4036 unresolved.insert(family.clone());
4037 }
4038 }
4039
4040 let total_fonts = chain.css_fallbacks.iter().map(|g| g.fonts.len()).sum::<usize>()
4047 + chain.unicode_fallbacks.len();
4048 if total_fonts == 0 {
4049 if let Some((_pattern, id)) = fc_cache.list().first() {
4050 chain.unicode_fallbacks.push(rust_fontconfig::FontMatch {
4053 id: *id,
4054 unicode_ranges: Vec::new(),
4055 fallbacks: Vec::new(),
4056 });
4057 }
4058 }
4059
4060 chains.insert(cache_key, chain);
4061 }
4062
4063 let out = ResolvedFontChains {
4068 chains,
4069 unresolved_families: unresolved,
4070 last_resort_chains: 0,
4071 };
4072 report_unresolved_families(&out);
4073 out
4074}
4075
4076fn ensure_chains_nonempty(resolved: &mut ResolvedFontChains, fc_cache: &FcFontCache) {
4087 let fallback_id = match fc_cache.list().first() {
4088 Some((_pattern, id)) => *id,
4089 None => return,
4090 };
4091 let keys: Vec<FontChainKeyOrRef> = resolved.chains.keys().cloned().collect();
4092 let mut rebuilt: HashMap<FontChainKeyOrRef, FontFallbackChain> =
4093 HashMap::new();
4094 let mut last_resort = 0usize;
4095 for key in keys {
4096 if let Some(mut chain) = resolved.chains.remove(&key) {
4097 let total = chain.css_fallbacks.iter().map(|g| g.fonts.len()).sum::<usize>()
4098 + chain.unicode_fallbacks.len();
4099 if total == 0 {
4100 last_resort += 1;
4106 if let FontChainKeyOrRef::Chain(k) = &key {
4107 eprintln!(
4108 "[azul][font] LAST-RESORT fallback for font stack {:?}: nothing in \
4109 the stack matched, rendering in an arbitrary system font.",
4110 k.font_families
4111 );
4112 }
4113 chain.unicode_fallbacks.push(rust_fontconfig::FontMatch {
4114 id: fallback_id,
4115 unicode_ranges: Vec::new(),
4116 fallbacks: Vec::new(),
4117 });
4118 }
4119 rebuilt.insert(key, chain);
4120 }
4121 }
4122 resolved.chains = rebuilt;
4123 resolved.last_resort_chains = last_resort;
4124}
4125
4126pub fn collect_and_resolve_font_chains_with_registration<T: ParsedFontTrait>(
4140 styled_dom: &StyledDom,
4141 fc_cache: &FcFontCache,
4142 font_manager: &crate::text3::cache::FontManager<T>,
4143 platform: &azul_css::system::Platform,
4144) -> ResolvedFontChains {
4145 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
4146
4147 for font_ref in collected.font_refs.values() {
4149 font_manager.register_embedded_font(font_ref);
4150 }
4151
4152 if let Some(registry) = font_manager.registry.as_deref() {
4165 let used_chars = collect_used_codepoints_all(styled_dom);
4166 if !used_chars.is_empty() {
4167 let mut fast = resolve_font_chains_fast(
4168 &collected,
4169 registry,
4170 &used_chars,
4171 &font_manager.memory_families,
4172 );
4173 ensure_chains_nonempty(&mut fast, fc_cache);
4174 return fast;
4175 }
4176 }
4177
4178 let scripts = scripts_present_in_styled_dom(styled_dom);
4182 let mut resolved = resolve_font_chains_with_registry(
4183 &collected,
4184 fc_cache,
4185 font_manager.registry.as_deref(),
4186 Some(&scripts),
4187 &font_manager.memory_families,
4188 );
4189
4190 let used_chars = collect_used_codepoints(styled_dom);
4191 for chain in resolved.chains.values_mut() {
4192 prune_chain_to_used_chars(chain, &used_chars);
4193 }
4194 ensure_chains_nonempty(&mut resolved, fc_cache);
4202 resolved
4203}
4204
4205pub fn resolve_font_chains_fast(
4215 collected: &CollectedFontStacks,
4216 registry: &rust_fontconfig::registry::FcFontRegistry,
4217 codepoints: &std::collections::BTreeSet<char>,
4218 memory_families: &HashMap<String, rust_fontconfig::FontMatch>,
4219) -> ResolvedFontChains {
4220 use rust_fontconfig::PatternMatch;
4221
4222 static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
4223 let dbg = *DBG.get_or_init(|| std::env::var_os("AZ_FAST_RESOLVE_DEBUG").is_some());
4224
4225 let mut chains: HashMap<FontChainKeyOrRef, FontFallbackChain> = HashMap::new();
4226 let mut unresolved: std::collections::BTreeSet<String> = std::collections::BTreeSet::new();
4227
4228 for font_stack in &collected.font_stacks {
4229 if font_stack.is_empty() {
4230 continue;
4231 }
4232
4233 let canonical_key = FontChainKey::from_selectors(font_stack);
4237 let font_families = canonical_key.font_families.clone();
4238
4239 let weight = font_stack[0].weight;
4240 let is_italic = font_stack[0].style == FontStyle::Italic;
4241 let is_oblique = font_stack[0].style == FontStyle::Oblique;
4242
4243 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
4244 font_families: font_families.clone(),
4245 weight,
4246 italic: is_italic,
4247 oblique: is_oblique,
4248 });
4249
4250 if chains.contains_key(&cache_key) {
4251 continue;
4252 }
4253
4254 let italic_match = if is_italic {
4255 PatternMatch::True
4256 } else {
4257 PatternMatch::False
4258 };
4259
4260 let (mut css_fallbacks, disk_families) =
4271 split_memory_matches(&font_families, memory_families);
4272
4273 let request = vec![(disk_families.clone(), codepoints.clone())];
4274 let mut chains_out = if disk_families.is_empty() {
4275 Vec::new()
4276 } else {
4277 registry.request_fonts_fast(&request, weight, italic_match)
4278 };
4279 if dbg {
4280 let total_fonts: usize = chains_out
4281 .iter()
4282 .map(|c| c.css_fallbacks.iter().map(|g| g.fonts.len()).sum::<usize>())
4283 .sum();
4284 eprintln!(
4285 "[FAST] stack {:?} w={:?} i={:?} → {} groups, {} faces",
4286 font_families,
4287 weight,
4288 italic_match,
4289 chains_out
4290 .first()
4291 .map_or(0, |c| c.css_fallbacks.len()),
4292 total_fonts,
4293 );
4294 }
4295 let mut chain = chains_out.pop().unwrap_or(FontFallbackChain {
4298 css_fallbacks: Vec::new(),
4299 unicode_fallbacks: Vec::new(),
4300 original_stack: font_families.clone(),
4301 });
4302 if !css_fallbacks.is_empty() {
4303 css_fallbacks.extend(chain.css_fallbacks.drain(..));
4304 chain.css_fallbacks = css_fallbacks;
4305 }
4306
4307 for family in &font_families {
4311 let matched = chain
4312 .css_fallbacks
4313 .iter()
4314 .any(|g| g.css_name.eq_ignore_ascii_case(family) && !g.fonts.is_empty());
4315 if !matched && !is_generic_family(family) {
4316 unresolved.insert(family.clone());
4317 }
4318 }
4319
4320 chains.insert(cache_key, chain);
4321 }
4322
4323 let out = ResolvedFontChains {
4324 chains,
4325 unresolved_families: unresolved,
4326 last_resort_chains: 0,
4327 };
4328 report_unresolved_families(&out);
4329 out
4330}
4331
4332fn is_generic_family(family: &str) -> bool {
4336 matches!(
4337 family.to_ascii_lowercase().as_str(),
4338 "serif"
4339 | "sans-serif"
4340 | "monospace"
4341 | "cursive"
4342 | "fantasy"
4343 | "system-ui"
4344 | "ui-serif"
4345 | "ui-sans-serif"
4346 | "ui-monospace"
4347 | "ui-rounded"
4348 | "emoji"
4349 | "math"
4350 | "fangsong"
4351 )
4352}
4353
4354fn report_unresolved_families(resolved: &ResolvedFontChains) {
4362 use std::sync::{Mutex, OnceLock};
4363 if resolved.unresolved_families.is_empty() {
4364 return;
4365 }
4366 static SEEN: OnceLock<Mutex<std::collections::BTreeSet<String>>> = OnceLock::new();
4367 let seen = SEEN.get_or_init(|| Mutex::new(std::collections::BTreeSet::new()));
4368 let Ok(mut seen) = seen.lock() else { return };
4369 for family in &resolved.unresolved_families {
4370 if seen.insert(family.clone()) {
4371 eprintln!(
4372 "[azul][font] UNRESOLVED font-family {family:?}: no font file and no \
4373 registered in-memory font matches this family. Text that asks for it \
4374 renders in a FALLBACK font. Register it with \
4375 FontManager::register_named_font(), or install it."
4376 );
4377 }
4378 }
4379}
4380
4381#[must_use] pub fn collect_and_resolve_font_chains(
4385 styled_dom: &StyledDom,
4386 fc_cache: &FcFontCache,
4387 platform: &azul_css::system::Platform,
4388) -> ResolvedFontChains {
4389 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
4390 resolve_font_chains(&collected, fc_cache, None)
4391}
4392
4393pub fn register_embedded_fonts_from_styled_dom<T: ParsedFontTrait>(
4395 styled_dom: &StyledDom,
4396 font_manager: &crate::text3::cache::FontManager<T>,
4397 platform: &azul_css::system::Platform,
4398) {
4399 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
4400 for font_ref in collected.font_refs.values() {
4401 font_manager.register_embedded_font(font_ref);
4402 }
4403}
4404
4405use std::collections::HashSet;
4408
4409use rust_fontconfig::FontId;
4410
4411#[must_use] pub fn collect_font_ids_from_chains(chains: &ResolvedFontChains) -> HashSet<FontId> {
4416 let mut font_ids = HashSet::new();
4417
4418 if chains.chains.is_empty() {
4422 return font_ids;
4423 }
4424
4425 for chain in chains.chains.values() {
4426 for group in &chain.css_fallbacks {
4428 for font in &group.fonts {
4429 font_ids.insert(font.id);
4430 }
4431 }
4432
4433 for font in &chain.unicode_fallbacks {
4435 font_ids.insert(font.id);
4436 }
4437 }
4438
4439 font_ids
4440}
4441
4442#[allow(clippy::implicit_hasher)] #[must_use] pub fn compute_fonts_to_load(
4452 required_fonts: &HashSet<FontId>,
4453 already_loaded: &HashSet<FontId>,
4454) -> HashSet<FontId> {
4455 if required_fonts.is_empty() {
4458 return HashSet::new();
4459 }
4460 required_fonts.difference(already_loaded).copied().collect()
4461}
4462
4463#[derive(Debug)]
4465pub struct FontLoadResult<T> {
4466 pub loaded: HashMap<FontId, T>,
4468 pub failed: Vec<(FontId, String)>,
4470}
4471
4472#[allow(clippy::implicit_hasher)] pub fn load_fonts_from_disk<T, F>(
4487 font_ids: &HashSet<FontId>,
4488 fc_cache: &FcFontCache,
4489 load_fn: F,
4490) -> FontLoadResult<T>
4491where
4492 F: Fn(
4497 std::sync::Arc<rust_fontconfig::FontBytes>,
4498 usize,
4499 ) -> Result<T, crate::text3::cache::LayoutError>,
4500{
4501 let mut loaded = HashMap::new();
4502 let mut failed = Vec::new();
4503
4504 for font_id in font_ids {
4505 let Some(font_bytes) = fc_cache.get_font_bytes(font_id) else {
4509 failed.push((
4510 *font_id,
4511 format!("Could not get font bytes for {font_id:?}"),
4512 ));
4513 continue;
4514 };
4515
4516 let font_index = fc_cache
4518 .get_font_by_id(font_id)
4519 .map_or(0, |source| match source {
4520 rust_fontconfig::OwnedFontSource::Disk(path) => path.font_index,
4521 rust_fontconfig::OwnedFontSource::Memory(font) => font.font_index,
4522 });
4523
4524 match load_fn(font_bytes, font_index) {
4526 Ok(font) => {
4527 loaded.insert(*font_id, font);
4528 }
4529 Err(e) => {
4530 failed.push((
4531 *font_id,
4532 format!("Failed to parse font {font_id:?}: {e:?}"),
4533 ));
4534 }
4535 }
4536 }
4537
4538 FontLoadResult { loaded, failed }
4539}
4540
4541#[allow(clippy::implicit_hasher)] pub fn resolve_and_load_fonts<T, F>(
4561 styled_dom: &StyledDom,
4562 fc_cache: &FcFontCache,
4563 already_loaded: &HashSet<FontId>,
4564 load_fn: F,
4565 platform: &azul_css::system::Platform,
4566) -> (ResolvedFontChains, FontLoadResult<T>)
4567where
4568 F: Fn(
4569 std::sync::Arc<rust_fontconfig::FontBytes>,
4570 usize,
4571 ) -> Result<T, crate::text3::cache::LayoutError>,
4572{
4573 let chains = collect_and_resolve_font_chains(styled_dom, fc_cache, platform);
4575
4576 let required_fonts = collect_font_ids_from_chains(&chains);
4578
4579 let fonts_to_load = compute_fonts_to_load(&required_fonts, already_loaded);
4581
4582 let load_result = load_fonts_from_disk(&fonts_to_load, fc_cache, load_fn);
4584
4585 (chains, load_result)
4586}
4587
4588use azul_css::props::style::scrollbar::{
4593 LayoutScrollbarWidth, ScrollbarVisibilityMode, StyleScrollbarColor,
4594};
4595
4596#[derive(Copy, Debug, Clone)]
4613pub struct ComputedScrollbarStyle {
4614 pub width_mode: LayoutScrollbarWidth,
4616 pub visual_width_px: f32,
4619 pub reserve_width_px: f32,
4622 pub thumb_color: ColorU,
4624 pub track_color: ColorU,
4626 pub button_color: ColorU,
4628 pub corner_color: ColorU,
4630 pub clip_to_container_border: bool,
4632 pub fade_delay_ms: u32,
4634 pub fade_duration_ms: u32,
4636 pub visibility: ScrollbarVisibilityMode,
4638 pub show_scroll_buttons: bool,
4641 pub scroll_button_size_px: f32,
4644 pub show_corner_rect: bool,
4646 pub thumb_color_hover: Option<ColorU>,
4648 pub thumb_color_active: Option<ColorU>,
4650 pub track_color_hover: Option<ColorU>,
4652 pub visual_width_px_hover: Option<f32>,
4654 pub visual_width_px_active: Option<f32>,
4656}
4657
4658impl Default for ComputedScrollbarStyle {
4659 fn default() -> Self {
4660 let ctx = azul_css::dynamic_selector::DynamicSelectorContext::default();
4663 let ua = azul_core::ua_css::evaluate_ua_scrollbar_css(&ctx);
4664 Self::from_ua_resolved(&ua)
4665 }
4666}
4667
4668impl ComputedScrollbarStyle {
4669 fn from_ua_resolved(ua: &azul_core::ua_css::ResolvedUaScrollbar) -> Self {
4673 let width_mode = ua.width;
4674 let visibility = ua.visibility;
4675 let fade_delay_ms = ua.fade_delay.ms;
4676 let fade_duration_ms = ua.fade_duration.ms;
4677
4678 let visual_width_px = match width_mode {
4679 LayoutScrollbarWidth::Thin => SCROLLBAR_WIDTH_THIN,
4680 LayoutScrollbarWidth::Auto => SCROLLBAR_WIDTH_AUTO,
4681 LayoutScrollbarWidth::None => 0.0,
4682 };
4683
4684 let is_overlay = visibility == ScrollbarVisibilityMode::WhenScrolling;
4686 let reserve_width_px = if is_overlay { 0.0 } else { visual_width_px };
4687 let show_scroll_buttons = !is_overlay;
4688 let scroll_button_size_px = if is_overlay { 0.0 } else { visual_width_px };
4689 let show_corner_rect = !is_overlay;
4690
4691 let (thumb_color, track_color) = match ua.color {
4692 StyleScrollbarColor::Custom(c) => (c.thumb, c.track),
4693 StyleScrollbarColor::Auto => (ColorU::TRANSPARENT, ColorU::TRANSPARENT),
4694 };
4695
4696 let thumb_hover = ColorU {
4700 r: thumb_color.r.saturating_add(THUMB_HOVER_LIGHTEN),
4701 g: thumb_color.g.saturating_add(THUMB_HOVER_LIGHTEN),
4702 b: thumb_color.b.saturating_add(THUMB_HOVER_LIGHTEN),
4703 a: thumb_color.a.saturating_add(THUMB_HOVER_ALPHA_ADD),
4704 };
4705 let thumb_active = ColorU {
4706 r: thumb_color.r.saturating_sub(THUMB_ACTIVE_DARKEN),
4707 g: thumb_color.g.saturating_sub(THUMB_ACTIVE_DARKEN),
4708 b: thumb_color.b.saturating_sub(THUMB_ACTIVE_DARKEN),
4709 a: 255,
4710 };
4711 let track_hover = ColorU {
4712 r: track_color.r,
4713 g: track_color.g,
4714 b: track_color.b,
4715 a: track_color.a.saturating_add(THUMB_HOVER_ALPHA_ADD),
4716 };
4717 let hover_width = visual_width_px + SCROLLBAR_HOVER_EXPAND_PX;
4718 let active_width = visual_width_px + SCROLLBAR_HOVER_EXPAND_PX;
4719
4720 Self {
4721 width_mode,
4722 visual_width_px,
4723 reserve_width_px,
4724 thumb_color,
4725 track_color,
4726 button_color: ColorU::TRANSPARENT,
4727 corner_color: ColorU::TRANSPARENT,
4728 clip_to_container_border: is_overlay,
4729 fade_delay_ms,
4730 fade_duration_ms,
4731 visibility,
4732 show_scroll_buttons,
4733 scroll_button_size_px,
4734 show_corner_rect,
4735 thumb_color_hover: Some(thumb_hover),
4736 thumb_color_active: Some(thumb_active),
4737 track_color_hover: Some(track_hover),
4738 visual_width_px_hover: Some(hover_width),
4739 visual_width_px_active: Some(active_width),
4740 }
4741 }
4742}
4743
4744#[allow(clippy::too_many_lines)] #[must_use] pub fn get_scrollbar_style(
4760 styled_dom: &StyledDom,
4761 node_id: NodeId,
4762 node_state: &StyledNodeState,
4763 system_style: Option<&azul_css::system::SystemStyle>,
4764) -> ComputedScrollbarStyle {
4765 let node_data = &styled_dom.node_data.as_container()[node_id];
4766
4767 let ctx = system_style.map_or_else(
4769 azul_css::dynamic_selector::DynamicSelectorContext::default,
4770 azul_css::dynamic_selector::DynamicSelectorContext::from_system_style,
4771 );
4772 let ua = azul_core::ua_css::evaluate_ua_scrollbar_css(&ctx);
4773 let result = ComputedScrollbarStyle::from_ua_resolved(&ua);
4774
4775 if node_state.is_normal() {
4777 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
4778 if !cc.has_scrollbar_css(node_id.index()) {
4779 return result;
4780 }
4781 }
4782 }
4783 let mut result = result;
4784
4785 if let Some(track) = styled_dom
4787 .css_property_cache
4788 .ptr
4789 .get_scrollbar_track(node_data, &node_id, node_state)
4790 .and_then(|v| v.get_property())
4791 {
4792 result.track_color = extract_color_from_background(track);
4793 }
4794 if let Some(thumb) = styled_dom
4795 .css_property_cache
4796 .ptr
4797 .get_scrollbar_thumb(node_data, &node_id, node_state)
4798 .and_then(|v| v.get_property())
4799 {
4800 result.thumb_color = extract_color_from_background(thumb);
4801 }
4802 if let Some(button) = styled_dom
4803 .css_property_cache
4804 .ptr
4805 .get_scrollbar_button(node_data, &node_id, node_state)
4806 .and_then(|v| v.get_property())
4807 {
4808 result.button_color = extract_color_from_background(button);
4809 }
4810 if let Some(corner) = styled_dom
4811 .css_property_cache
4812 .ptr
4813 .get_scrollbar_corner(node_data, &node_id, node_state)
4814 .and_then(|v| v.get_property())
4815 {
4816 result.corner_color = extract_color_from_background(corner);
4817 }
4818
4819 if let Some(scrollbar_width) = styled_dom
4821 .css_property_cache
4822 .ptr
4823 .get_scrollbar_width(node_data, &node_id, node_state)
4824 .and_then(|v| v.get_property())
4825 {
4826 result.width_mode = *scrollbar_width;
4827 let w = match scrollbar_width {
4828 LayoutScrollbarWidth::Auto => SCROLLBAR_WIDTH_AUTO,
4829 LayoutScrollbarWidth::Thin => SCROLLBAR_WIDTH_THIN,
4830 LayoutScrollbarWidth::None => 0.0,
4831 };
4832 result.visual_width_px = w;
4833 if result.visibility != ScrollbarVisibilityMode::WhenScrolling {
4834 result.reserve_width_px = w;
4835 }
4836 }
4837
4838 if let Some(scrollbar_color) = styled_dom
4840 .css_property_cache
4841 .ptr
4842 .get_scrollbar_color(node_data, &node_id, node_state)
4843 .and_then(|v| v.get_property())
4844 {
4845 match scrollbar_color {
4846 StyleScrollbarColor::Auto => { }
4847 StyleScrollbarColor::Custom(custom) => {
4848 result.thumb_color = custom.thumb;
4849 result.track_color = custom.track;
4850 }
4851 }
4852 }
4853
4854 if let Some(vis) = styled_dom
4856 .css_property_cache
4857 .ptr
4858 .get_scrollbar_visibility(node_data, &node_id, node_state)
4859 .and_then(|v| v.get_property())
4860 {
4861 result.visibility = *vis;
4862 result.clip_to_container_border = *vis == ScrollbarVisibilityMode::WhenScrolling;
4863 let is_overlay = *vis == ScrollbarVisibilityMode::WhenScrolling;
4865 if is_overlay {
4866 result.reserve_width_px = 0.0;
4867 result.show_scroll_buttons = false;
4868 result.scroll_button_size_px = 0.0;
4869 result.show_corner_rect = false;
4870 } else {
4871 result.reserve_width_px = result.visual_width_px;
4872 }
4873 }
4874
4875 if let Some(delay) = styled_dom
4877 .css_property_cache
4878 .ptr
4879 .get_scrollbar_fade_delay(node_data, &node_id, node_state)
4880 .and_then(|v| v.get_property())
4881 {
4882 result.fade_delay_ms = delay.ms;
4883 }
4884
4885 if let Some(dur) = styled_dom
4887 .css_property_cache
4888 .ptr
4889 .get_scrollbar_fade_duration(node_data, &node_id, node_state)
4890 .and_then(|v| v.get_property())
4891 {
4892 result.fade_duration_ms = dur.ms;
4893 }
4894
4895 result
4896}
4897
4898pub fn get_scrollbar_style_cached<T: ParsedFontTrait>(
4911 ctx: &crate::solver3::LayoutContext<'_, T>,
4912 node_id: NodeId,
4913 node_state: &StyledNodeState,
4914) -> ComputedScrollbarStyle {
4915 if let Some(s) = ctx.scrollbar_style_cache.borrow().get(&node_id) {
4916 return *s;
4917 }
4918 let style = get_scrollbar_style(
4919 ctx.styled_dom,
4920 node_id,
4921 node_state,
4922 ctx.system_style.as_deref(),
4923 );
4924 ctx.scrollbar_style_cache
4925 .borrow_mut()
4926 .insert(node_id, style);
4927 style
4928}
4929
4930const fn extract_color_from_background(
4932 bg: &azul_css::props::style::background::StyleBackgroundContent,
4933) -> ColorU {
4934 use azul_css::props::style::background::StyleBackgroundContent;
4935 match bg {
4936 StyleBackgroundContent::Color(c) => *c,
4937 _ => ColorU::TRANSPARENT,
4938 }
4939}
4940
4941#[must_use] pub fn should_clip_scrollbar_to_border(
4943 styled_dom: &StyledDom,
4944 node_id: NodeId,
4945 node_state: &StyledNodeState,
4946) -> bool {
4947 let style = get_scrollbar_style(styled_dom, node_id, node_state, None);
4948 style.clip_to_container_border
4949}
4950
4951#[must_use] pub fn get_scrollbar_width_px(
4953 styled_dom: &StyledDom,
4954 node_id: NodeId,
4955 node_state: &StyledNodeState,
4956) -> f32 {
4957 let style = get_scrollbar_style(styled_dom, node_id, node_state, None);
4958 style.visual_width_px
4959}
4960
4961#[must_use] pub fn is_text_selectable(
4966 styled_dom: &StyledDom,
4967 node_id: NodeId,
4968 node_state: &StyledNodeState,
4969) -> bool {
4970 let node_data = &styled_dom.node_data.as_container()[node_id];
4971
4972 styled_dom
4973 .css_property_cache
4974 .ptr
4975 .get_user_select(node_data, &node_id, node_state)
4976 .and_then(|v| v.get_property())
4977 .is_none_or(|us| *us != StyleUserSelect::None) }
4979
4980#[must_use] pub fn is_node_contenteditable(styled_dom: &StyledDom, node_id: NodeId) -> bool {
4988 use azul_core::dom::AttributeType;
4989
4990 let node_data = &styled_dom.node_data.as_container()[node_id];
4991
4992 if node_data.is_contenteditable() {
4994 return true;
4995 }
4996
4997 node_data
5000 .attributes()
5001 .as_ref()
5002 .iter()
5003 .any(|attr| matches!(attr, AttributeType::ContentEditable(true)))
5004}
5005use azul_css::props::layout::table::{
5010 LayoutTableLayout, StyleBorderCollapse, StyleCaptionSide, StyleEmptyCells,
5011};
5012use azul_css::props::layout::text::LayoutTextJustify;
5013use azul_css::props::style::effects::StyleAspectRatio;
5014use azul_css::props::style::effects::StyleCursor;
5015use azul_css::props::style::effects::StyleObjectFit;
5016use azul_css::props::style::effects::StyleObjectPosition;
5017use azul_css::props::style::effects::StyleTextOrientation;
5018use azul_css::props::style::text::StyleHyphens;
5019use azul_css::props::style::text::StyleLineBreak;
5020use azul_css::props::style::text::StyleOverflowWrap;
5021use azul_css::props::style::text::StyleTextAlignLast;
5022use azul_css::props::style::text::StyleWordBreak;
5023
5024impl ExtractPropertyValue<LayoutTextJustify> for CssProperty {
5025 fn extract(&self) -> Option<LayoutTextJustify> {
5026 match self {
5027 Self::TextJustify(CssPropertyValue::Exact(v)) => Some(*v),
5028 _ => None,
5029 }
5030 }
5031}
5032
5033impl ExtractPropertyValue<StyleHyphens> for CssProperty {
5034 fn extract(&self) -> Option<StyleHyphens> {
5035 match self {
5036 Self::Hyphens(CssPropertyValue::Exact(v)) => Some(*v),
5037 _ => None,
5038 }
5039 }
5040}
5041
5042impl ExtractPropertyValue<StyleWordBreak> for CssProperty {
5043 fn extract(&self) -> Option<StyleWordBreak> {
5044 match self {
5045 Self::WordBreak(CssPropertyValue::Exact(v)) => Some(*v),
5046 _ => None,
5047 }
5048 }
5049}
5050
5051impl ExtractPropertyValue<StyleOverflowWrap> for CssProperty {
5052 fn extract(&self) -> Option<StyleOverflowWrap> {
5053 match self {
5054 Self::OverflowWrap(CssPropertyValue::Exact(v)) => Some(*v),
5055 _ => None,
5056 }
5057 }
5058}
5059
5060impl ExtractPropertyValue<StyleLineBreak> for CssProperty {
5061 fn extract(&self) -> Option<StyleLineBreak> {
5062 match self {
5063 Self::LineBreak(CssPropertyValue::Exact(v)) => Some(*v),
5064 _ => None,
5065 }
5066 }
5067}
5068
5069impl ExtractPropertyValue<StyleTextAlignLast> for CssProperty {
5070 fn extract(&self) -> Option<StyleTextAlignLast> {
5071 match self {
5072 Self::TextAlignLast(CssPropertyValue::Exact(v)) => Some(*v),
5073 _ => None,
5074 }
5075 }
5076}
5077
5078impl ExtractPropertyValue<StyleObjectFit> for CssProperty {
5079 fn extract(&self) -> Option<StyleObjectFit> {
5080 match self {
5081 Self::ObjectFit(CssPropertyValue::Exact(v)) => Some(*v),
5082 _ => None,
5083 }
5084 }
5085}
5086
5087impl ExtractPropertyValue<StyleTextOrientation> for CssProperty {
5088 fn extract(&self) -> Option<StyleTextOrientation> {
5089 match self {
5090 Self::TextOrientation(CssPropertyValue::Exact(v)) => Some(*v),
5091 _ => None,
5092 }
5093 }
5094}
5095
5096impl ExtractPropertyValue<StyleObjectPosition> for CssProperty {
5097 fn extract(&self) -> Option<StyleObjectPosition> {
5098 match self {
5099 Self::ObjectPosition(CssPropertyValue::Exact(v)) => Some(*v),
5100 _ => None,
5101 }
5102 }
5103}
5104
5105impl ExtractPropertyValue<StyleAspectRatio> for CssProperty {
5106 fn extract(&self) -> Option<StyleAspectRatio> {
5107 match self {
5108 Self::AspectRatio(CssPropertyValue::Exact(v)) => Some(*v),
5109 _ => None,
5110 }
5111 }
5112}
5113
5114impl ExtractPropertyValue<LayoutTableLayout> for CssProperty {
5115 fn extract(&self) -> Option<LayoutTableLayout> {
5116 match self {
5117 Self::TableLayout(CssPropertyValue::Exact(v)) => Some(*v),
5118 _ => None,
5119 }
5120 }
5121}
5122
5123impl ExtractPropertyValue<StyleBorderCollapse> for CssProperty {
5124 fn extract(&self) -> Option<StyleBorderCollapse> {
5125 match self {
5126 Self::BorderCollapse(CssPropertyValue::Exact(v)) => Some(*v),
5127 _ => None,
5128 }
5129 }
5130}
5131
5132impl ExtractPropertyValue<StyleCaptionSide> for CssProperty {
5133 fn extract(&self) -> Option<StyleCaptionSide> {
5134 match self {
5135 Self::CaptionSide(CssPropertyValue::Exact(v)) => Some(*v),
5136 _ => None,
5137 }
5138 }
5139}
5140
5141impl ExtractPropertyValue<StyleEmptyCells> for CssProperty {
5142 fn extract(&self) -> Option<StyleEmptyCells> {
5143 match self {
5144 Self::EmptyCells(CssPropertyValue::Exact(v)) => Some(*v),
5145 _ => None,
5146 }
5147 }
5148}
5149
5150impl ExtractPropertyValue<StyleCursor> for CssProperty {
5151 fn extract(&self) -> Option<StyleCursor> {
5152 match self {
5153 Self::Cursor(CssPropertyValue::Exact(v)) => Some(*v),
5154 _ => None,
5155 }
5156 }
5157}
5158
5159get_css_property!(
5164 get_text_justify,
5165 get_text_justify,
5166 LayoutTextJustify,
5167 CssPropertyType::TextJustify
5168);
5169
5170get_css_property!(
5171 get_hyphens,
5172 get_hyphens,
5173 StyleHyphens,
5174 CssPropertyType::Hyphens
5175);
5176
5177get_css_property!(
5178 get_word_break,
5179 get_word_break,
5180 StyleWordBreak,
5181 CssPropertyType::WordBreak
5182);
5183
5184get_css_property!(
5185 get_overflow_wrap,
5186 get_overflow_wrap,
5187 StyleOverflowWrap,
5188 CssPropertyType::OverflowWrap
5189);
5190
5191get_css_property!(
5192 get_line_break,
5193 get_line_break,
5194 StyleLineBreak,
5195 CssPropertyType::LineBreak
5196);
5197
5198get_css_property!(
5199 get_text_align_last,
5200 get_text_align_last,
5201 StyleTextAlignLast,
5202 CssPropertyType::TextAlignLast
5203);
5204
5205get_css_property!(
5206 get_table_layout,
5207 get_table_layout,
5208 LayoutTableLayout,
5209 CssPropertyType::TableLayout
5210);
5211
5212get_css_property!(
5213 get_border_collapse,
5214 get_border_collapse,
5215 StyleBorderCollapse,
5216 CssPropertyType::BorderCollapse,
5217 compact = get_border_collapse
5218);
5219
5220get_css_property!(
5221 get_caption_side,
5222 get_caption_side,
5223 StyleCaptionSide,
5224 CssPropertyType::CaptionSide
5225);
5226
5227get_css_property!(
5228 get_empty_cells,
5229 get_empty_cells,
5230 StyleEmptyCells,
5231 CssPropertyType::EmptyCells
5232);
5233
5234get_css_property!(
5235 get_cursor_property,
5236 get_cursor,
5237 StyleCursor,
5238 CssPropertyType::Cursor
5239);
5240
5241#[must_use] pub fn get_height_value(
5247 styled_dom: &StyledDom,
5248 node_id: NodeId,
5249 node_state: &StyledNodeState,
5250) -> Option<LayoutHeight> {
5251 let node_data = &styled_dom.node_data.as_container()[node_id];
5252 styled_dom
5253 .css_property_cache
5254 .ptr
5255 .get_height(node_data, &node_id, node_state)
5256 .and_then(|v| v.get_property())
5257 .cloned()
5258}
5259
5260#[must_use] pub fn get_shape_inside(
5262 styled_dom: &StyledDom,
5263 node_id: NodeId,
5264 node_state: &StyledNodeState,
5265) -> Option<azul_css::props::layout::shape::ShapeInside> {
5266 let node_data = &styled_dom.node_data.as_container()[node_id];
5267 styled_dom
5268 .css_property_cache
5269 .ptr
5270 .get_shape_inside(node_data, &node_id, node_state)
5271 .and_then(|v| v.get_property())
5272 .cloned()
5273}
5274
5275#[must_use] pub fn get_shape_outside(
5277 styled_dom: &StyledDom,
5278 node_id: NodeId,
5279 node_state: &StyledNodeState,
5280) -> Option<azul_css::props::layout::shape::ShapeOutside> {
5281 let node_data = &styled_dom.node_data.as_container()[node_id];
5282 styled_dom
5283 .css_property_cache
5284 .ptr
5285 .get_shape_outside(node_data, &node_id, node_state)
5286 .and_then(|v| v.get_property())
5287 .cloned()
5288}
5289
5290#[must_use] pub fn get_line_height_value(
5292 styled_dom: &StyledDom,
5293 node_id: NodeId,
5294 node_state: &StyledNodeState,
5295) -> Option<azul_css::props::style::text::StyleLineHeight> {
5296 let node_data = &styled_dom.node_data.as_container()[node_id];
5297 styled_dom
5298 .css_property_cache
5299 .ptr
5300 .get_line_height(node_data, &node_id, node_state)
5301 .and_then(|v| v.get_property())
5302 .copied()
5303}
5304
5305#[must_use] pub fn get_text_indent_value(
5307 styled_dom: &StyledDom,
5308 node_id: NodeId,
5309 node_state: &StyledNodeState,
5310) -> Option<azul_css::props::style::text::StyleTextIndent> {
5311 let node_data = &styled_dom.node_data.as_container()[node_id];
5312 styled_dom
5313 .css_property_cache
5314 .ptr
5315 .get_text_indent(node_data, &node_id, node_state)
5316 .and_then(|v| v.get_property())
5317 .copied()
5318}
5319
5320#[must_use] pub fn get_column_count(
5322 styled_dom: &StyledDom,
5323 node_id: NodeId,
5324 node_state: &StyledNodeState,
5325) -> Option<azul_css::props::layout::column::ColumnCount> {
5326 let node_data = &styled_dom.node_data.as_container()[node_id];
5327 styled_dom
5328 .css_property_cache
5329 .ptr
5330 .get_column_count(node_data, &node_id, node_state)
5331 .and_then(|v| v.get_property())
5332 .copied()
5333}
5334
5335#[must_use] pub fn get_initial_letter(
5337 styled_dom: &StyledDom,
5338 node_id: NodeId,
5339 node_state: &StyledNodeState,
5340) -> Option<azul_css::props::style::text::StyleInitialLetter> {
5341 let node_data = &styled_dom.node_data.as_container()[node_id];
5342 styled_dom
5343 .css_property_cache
5344 .ptr
5345 .get_initial_letter(node_data, &node_id, node_state)
5346 .and_then(|v| v.get_property())
5347 .copied()
5348}
5349
5350#[must_use] pub fn get_line_clamp(
5352 styled_dom: &StyledDom,
5353 node_id: NodeId,
5354 node_state: &StyledNodeState,
5355) -> Option<azul_css::props::style::text::StyleLineClamp> {
5356 let node_data = &styled_dom.node_data.as_container()[node_id];
5357 styled_dom
5358 .css_property_cache
5359 .ptr
5360 .get_line_clamp(node_data, &node_id, node_state)
5361 .and_then(|v| v.get_property())
5362 .copied()
5363}
5364
5365#[must_use] pub fn get_hanging_punctuation(
5367 styled_dom: &StyledDom,
5368 node_id: NodeId,
5369 node_state: &StyledNodeState,
5370) -> Option<azul_css::props::style::text::StyleHangingPunctuation> {
5371 let node_data = &styled_dom.node_data.as_container()[node_id];
5372 styled_dom
5373 .css_property_cache
5374 .ptr
5375 .get_hanging_punctuation(node_data, &node_id, node_state)
5376 .and_then(|v| v.get_property())
5377 .copied()
5378}
5379
5380#[must_use] pub fn get_text_combine_upright(
5382 styled_dom: &StyledDom,
5383 node_id: NodeId,
5384 node_state: &StyledNodeState,
5385) -> Option<azul_css::props::style::text::StyleTextCombineUpright> {
5386 let node_data = &styled_dom.node_data.as_container()[node_id];
5387 styled_dom
5388 .css_property_cache
5389 .ptr
5390 .get_text_combine_upright(node_data, &node_id, node_state)
5391 .and_then(|v| v.get_property())
5392 .copied()
5393}
5394
5395#[must_use] pub fn get_exclusion_margin(
5397 styled_dom: &StyledDom,
5398 node_id: NodeId,
5399 node_state: &StyledNodeState,
5400) -> f32 {
5401 let node_data = &styled_dom.node_data.as_container()[node_id];
5402 styled_dom
5403 .css_property_cache
5404 .ptr
5405 .get_exclusion_margin(node_data, &node_id, node_state)
5406 .and_then(|v| v.get_property())
5407 .map_or(0.0, |v| v.inner.get())
5408}
5409
5410#[must_use] pub fn get_hyphenation_language(
5412 styled_dom: &StyledDom,
5413 node_id: NodeId,
5414 node_state: &StyledNodeState,
5415) -> Option<azul_css::props::style::exclusion::StyleHyphenationLanguage> {
5416 let node_data = &styled_dom.node_data.as_container()[node_id];
5417 styled_dom
5418 .css_property_cache
5419 .ptr
5420 .get_hyphenation_language(node_data, &node_id, node_state)
5421 .and_then(|v| v.get_property())
5422 .cloned()
5423}
5424
5425#[must_use] pub fn get_border_spacing(
5427 styled_dom: &StyledDom,
5428 node_id: NodeId,
5429 node_state: &StyledNodeState,
5430) -> azul_css::props::layout::table::LayoutBorderSpacing {
5431 use azul_css::props::basic::pixel::PixelValue;
5432
5433 if node_state.is_normal() {
5435 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5436 let h_raw = cc.get_border_spacing_h_raw(node_id.index());
5437 let v_raw = cc.get_border_spacing_v_raw(node_id.index());
5438 if h_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
5441 && v_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
5442 {
5443 return azul_css::props::layout::table::LayoutBorderSpacing {
5444 horizontal: PixelValue::px(f32::from(h_raw) / 10.0),
5445 vertical: PixelValue::px(f32::from(v_raw) / 10.0),
5446 };
5447 }
5448 }
5449 }
5450
5451 let node_data = &styled_dom.node_data.as_container()[node_id];
5453 styled_dom
5454 .css_property_cache
5455 .ptr
5456 .get_border_spacing(node_data, &node_id, node_state)
5457 .and_then(|v| v.get_property())
5458 .copied()
5459 .unwrap_or_default()
5460}
5461
5462#[must_use] pub fn get_opacity(styled_dom: &StyledDom, node_id: NodeId, node_state: &StyledNodeState) -> f32 {
5468 if node_state.is_normal() {
5470 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5471 let raw = cc.get_opacity_raw(node_id.index());
5472 if raw == azul_css::compact_cache::OPACITY_SENTINEL {
5473 return 1.0;
5474 }
5475 return f32::from(raw) / 254.0;
5476 }
5477 }
5478 let node_data = &styled_dom.node_data.as_container()[node_id];
5480 styled_dom
5481 .css_property_cache
5482 .ptr
5483 .get_opacity(node_data, &node_id, node_state)
5484 .and_then(|v| v.get_property())
5485 .map_or(1.0, |v| v.inner.normalized())
5486}
5487
5488#[must_use] pub fn get_filter(
5490 styled_dom: &StyledDom,
5491 node_id: NodeId,
5492 node_state: &StyledNodeState,
5493) -> Option<azul_css::props::style::filter::StyleFilterVec> {
5494 if node_state.is_normal() {
5495 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5496 if !cc.has_filter(node_id.index()) {
5497 return None;
5498 }
5499 }
5500 }
5501 let node_data = &styled_dom.node_data.as_container()[node_id];
5502 styled_dom
5503 .css_property_cache
5504 .ptr
5505 .get_filter(node_data, &node_id, node_state)
5506 .and_then(|v| v.get_property())
5507 .cloned()
5508}
5509
5510#[must_use] pub fn get_backdrop_filter(
5512 styled_dom: &StyledDom,
5513 node_id: NodeId,
5514 node_state: &StyledNodeState,
5515) -> Option<azul_css::props::style::filter::StyleFilterVec> {
5516 if node_state.is_normal() {
5517 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5518 if !cc.has_backdrop_filter(node_id.index()) {
5519 return None;
5520 }
5521 }
5522 }
5523 let node_data = &styled_dom.node_data.as_container()[node_id];
5524 styled_dom
5525 .css_property_cache
5526 .ptr
5527 .get_backdrop_filter(node_data, &node_id, node_state)
5528 .and_then(|v| v.get_property())
5529 .cloned()
5530}
5531
5532#[inline]
5535fn box_shadow_fast_bail(
5536 styled_dom: &StyledDom,
5537 node_id: NodeId,
5538 node_state: &StyledNodeState,
5539) -> bool {
5540 if !node_state.is_normal() {
5541 return false;
5542 }
5543 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5544 return !cc.has_box_shadow(node_id.index());
5545 }
5546 false
5547}
5548
5549#[must_use] pub fn get_box_shadow_left(
5551 styled_dom: &StyledDom,
5552 node_id: NodeId,
5553 node_state: &StyledNodeState,
5554) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5555 if box_shadow_fast_bail(styled_dom, node_id, node_state) {
5556 return None;
5557 }
5558 let node_data = &styled_dom.node_data.as_container()[node_id];
5559 styled_dom
5560 .css_property_cache
5561 .ptr
5562 .get_box_shadow_left(node_data, &node_id, node_state)
5563 .and_then(|v| v.get_property())
5564 .map(|v| (**v))
5565}
5566
5567#[must_use] pub fn get_box_shadow_right(
5569 styled_dom: &StyledDom,
5570 node_id: NodeId,
5571 node_state: &StyledNodeState,
5572) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5573 if box_shadow_fast_bail(styled_dom, node_id, node_state) {
5574 return None;
5575 }
5576 let node_data = &styled_dom.node_data.as_container()[node_id];
5577 styled_dom
5578 .css_property_cache
5579 .ptr
5580 .get_box_shadow_right(node_data, &node_id, node_state)
5581 .and_then(|v| v.get_property())
5582 .map(|v| (**v))
5583}
5584
5585#[must_use] pub fn get_box_shadow_top(
5587 styled_dom: &StyledDom,
5588 node_id: NodeId,
5589 node_state: &StyledNodeState,
5590) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5591 if box_shadow_fast_bail(styled_dom, node_id, node_state) {
5592 return None;
5593 }
5594 let node_data = &styled_dom.node_data.as_container()[node_id];
5595 styled_dom
5596 .css_property_cache
5597 .ptr
5598 .get_box_shadow_top(node_data, &node_id, node_state)
5599 .and_then(|v| v.get_property())
5600 .map(|v| (**v))
5601}
5602
5603#[must_use] pub fn get_box_shadow_bottom(
5605 styled_dom: &StyledDom,
5606 node_id: NodeId,
5607 node_state: &StyledNodeState,
5608) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5609 if box_shadow_fast_bail(styled_dom, node_id, node_state) {
5610 return None;
5611 }
5612 let node_data = &styled_dom.node_data.as_container()[node_id];
5613 styled_dom
5614 .css_property_cache
5615 .ptr
5616 .get_box_shadow_bottom(node_data, &node_id, node_state)
5617 .and_then(|v| v.get_property())
5618 .map(|v| (**v))
5619}
5620
5621#[must_use] pub fn get_text_shadow(
5623 styled_dom: &StyledDom,
5624 node_id: NodeId,
5625 node_state: &StyledNodeState,
5626) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5627 if node_state.is_normal() {
5628 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5629 if !cc.has_text_shadow(node_id.index()) {
5630 return None;
5631 }
5632 }
5633 }
5634 let node_data = &styled_dom.node_data.as_container()[node_id];
5635 styled_dom
5636 .css_property_cache
5637 .ptr
5638 .get_text_shadow(node_data, &node_id, node_state)
5639 .and_then(|v| v.get_property())
5640 .map(|v| (**v))
5641}
5642
5643#[must_use] pub fn get_transform(
5650 styled_dom: &StyledDom,
5651 node_id: NodeId,
5652 node_state: &StyledNodeState,
5653) -> Option<azul_css::props::style::transform::StyleTransformVec> {
5654 if node_state.is_normal() {
5656 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5657 if !cc.has_transform(node_id.index()) {
5658 return None;
5659 }
5660 }
5662 }
5663 let node_data = &styled_dom.node_data.as_container()[node_id];
5664 styled_dom
5665 .css_property_cache
5666 .ptr
5667 .get_transform(node_data, &node_id, node_state)
5668 .and_then(|v| v.get_property())
5669 .cloned()
5670}
5671
5672#[must_use] pub fn get_counter_reset(
5674 styled_dom: &StyledDom,
5675 node_id: NodeId,
5676 node_state: &StyledNodeState,
5677) -> Option<azul_css::props::style::content::CounterReset> {
5678 let node_data = &styled_dom.node_data.as_container()[node_id];
5679 styled_dom
5680 .css_property_cache
5681 .ptr
5682 .get_counter_reset(node_data, &node_id, node_state)
5683 .and_then(|v| v.get_property())
5684 .cloned()
5685}
5686
5687#[must_use] pub fn get_counter_increment(
5689 styled_dom: &StyledDom,
5690 node_id: NodeId,
5691 node_state: &StyledNodeState,
5692) -> Option<azul_css::props::style::content::CounterIncrement> {
5693 let node_data = &styled_dom.node_data.as_container()[node_id];
5694 styled_dom
5695 .css_property_cache
5696 .ptr
5697 .get_counter_increment(node_data, &node_id, node_state)
5698 .and_then(|v| v.get_property())
5699 .cloned()
5700}
5701
5702#[must_use] pub fn is_node_contenteditable_inherited(styled_dom: &StyledDom, node_id: NodeId) -> bool {
5728 use azul_core::dom::AttributeType;
5729
5730 let node_data_container = styled_dom.node_data.as_container();
5731 let hierarchy = styled_dom.node_hierarchy.as_container();
5732
5733 let mut current_node_id = Some(node_id);
5734
5735 while let Some(nid) = current_node_id {
5736 let node_data = &node_data_container[nid];
5737
5738 if node_data.is_contenteditable() {
5741 return true;
5742 }
5743
5744 for attr in node_data.attributes().as_ref() {
5747 if let AttributeType::ContentEditable(is_editable) = attr {
5748 return *is_editable;
5751 }
5752 }
5753
5754 current_node_id = hierarchy.get(nid).and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id);
5756 }
5757
5758 false
5760}
5761
5762#[must_use] pub fn find_contenteditable_ancestor(styled_dom: &StyledDom, node_id: NodeId) -> Option<NodeId> {
5772 use azul_core::dom::AttributeType;
5773
5774 let node_data_container = styled_dom.node_data.as_container();
5775 let hierarchy = styled_dom.node_hierarchy.as_container();
5776
5777 let mut current_node_id = Some(node_id);
5778
5779 while let Some(nid) = current_node_id {
5780 let node_data = &node_data_container[nid];
5781
5782 if node_data.is_contenteditable() {
5784 return Some(nid);
5785 }
5786
5787 for attr in node_data.attributes().as_ref() {
5789 if let AttributeType::ContentEditable(is_editable) = attr {
5790 if *is_editable {
5791 return Some(nid);
5792 }
5793 return None;
5795 }
5796 }
5797
5798 current_node_id = hierarchy.get(nid).and_then(azul_core::styled_dom::NodeHierarchyItem::parent_id);
5800 }
5801
5802 None
5803}
5804
5805macro_rules! get_css_property_value {
5813 ($fn_name:ident, $cache_method:ident, $ret_type:ty) => {
5814 #[must_use] pub fn $fn_name(
5815 styled_dom: &StyledDom,
5816 node_id: NodeId,
5817 node_state: &StyledNodeState,
5818 ) -> Option<$ret_type> {
5819 let node_data = &styled_dom.node_data.as_container()[node_id];
5820 styled_dom
5821 .css_property_cache
5822 .ptr
5823 .$cache_method(node_data, &node_id, node_state)
5824 .cloned()
5825 }
5826 };
5827}
5828
5829get_css_property_value!(
5831 get_flex_direction_prop,
5832 get_flex_direction,
5833 LayoutFlexDirectionValue
5834);
5835get_css_property_value!(get_flex_wrap_prop, get_flex_wrap, LayoutFlexWrapValue);
5836get_css_property_value!(get_flex_grow_prop, get_flex_grow, LayoutFlexGrowValue);
5837get_css_property_value!(get_flex_shrink_prop, get_flex_shrink, LayoutFlexShrinkValue);
5838get_css_property_value!(get_flex_basis_prop, get_flex_basis, LayoutFlexBasisValue);
5839
5840get_css_property_value!(get_align_items_prop, get_align_items, LayoutAlignItemsValue);
5842get_css_property_value!(get_align_self_prop, get_align_self, LayoutAlignSelfValue);
5843get_css_property_value!(
5844 get_align_content_prop,
5845 get_align_content,
5846 LayoutAlignContentValue
5847);
5848get_css_property_value!(
5849 get_justify_content_prop,
5850 get_justify_content,
5851 LayoutJustifyContentValue
5852);
5853get_css_property_value!(
5854 get_justify_items_prop,
5855 get_justify_items,
5856 LayoutJustifyItemsValue
5857);
5858get_css_property_value!(
5859 get_justify_self_prop,
5860 get_justify_self,
5861 LayoutJustifySelfValue
5862);
5863
5864get_css_property_value!(get_gap_prop, get_gap, LayoutGapValue);
5866
5867get_css_property_value!(
5869 get_grid_template_rows_prop,
5870 get_grid_template_rows,
5871 LayoutGridTemplateRowsValue
5872);
5873get_css_property_value!(
5874 get_grid_template_columns_prop,
5875 get_grid_template_columns,
5876 LayoutGridTemplateColumnsValue
5877);
5878get_css_property_value!(
5879 get_grid_auto_rows_prop,
5880 get_grid_auto_rows,
5881 LayoutGridAutoRowsValue
5882);
5883get_css_property_value!(
5884 get_grid_auto_columns_prop,
5885 get_grid_auto_columns,
5886 LayoutGridAutoColumnsValue
5887);
5888get_css_property_value!(
5889 get_grid_auto_flow_prop,
5890 get_grid_auto_flow,
5891 LayoutGridAutoFlowValue
5892);
5893get_css_property_value!(get_grid_column_prop, get_grid_column, LayoutGridColumnValue);
5894get_css_property_value!(get_grid_row_prop, get_grid_row, LayoutGridRowValue);
5895
5896#[must_use] pub fn get_grid_template_areas_prop(
5901 styled_dom: &StyledDom,
5902 node_id: NodeId,
5903 node_state: &StyledNodeState,
5904) -> Option<GridTemplateAreas> {
5905 let node_data = &styled_dom.node_data.as_container()[node_id];
5906 styled_dom
5907 .css_property_cache
5908 .ptr
5909 .get_property(
5910 node_data,
5911 &node_id,
5912 node_state,
5913 &CssPropertyType::GridTemplateAreas,
5914 )
5915 .and_then(|p| {
5916 if let CssProperty::GridTemplateAreas(v) = p {
5917 v.get_property().cloned()
5918 } else {
5919 None
5920 }
5921 })
5922}
5923
5924#[must_use] pub fn get_clip_path(
5930 styled_dom: &StyledDom,
5931 node_id: NodeId,
5932 node_state: &StyledNodeState,
5933) -> Option<azul_css::props::layout::shape::ClipPath> {
5934 if node_state.is_normal() {
5936 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5937 if !cc.has_clip_path(node_id.index()) {
5938 return None;
5939 }
5940 }
5941 }
5942 let node_data = &styled_dom.node_data.as_container()[node_id];
5943 styled_dom
5944 .css_property_cache
5945 .ptr
5946 .get_clip_path(node_data, &node_id, node_state)
5947 .and_then(|v| v.get_property())
5948 .cloned()
5949}