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, StyleFontWeight, StyleFontStyle},
15 pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
16 ColorU, PhysicalSize, PixelValue, PropertyContext, ResolutionContext,
17 },
18 layout::{
19 BoxDecorationBreak, BreakInside, LayoutBoxSizing, LayoutClear, LayoutDisplay,
20 LayoutFlexDirection, LayoutFlexWrap, LayoutFloat, LayoutHeight,
21 LayoutJustifyContent, LayoutAlignItems, LayoutAlignContent, LayoutOverflow,
22 LayoutPosition, LayoutWidth, LayoutWritingMode, Orphans, PageBreak, Widows,
23 StyleScrollbarGutter, StyleOverflowClipMargin,
24 grid::GridTemplateAreas,
25 },
26 property::{CssProperty, CssPropertyType,
27 LayoutFlexBasisValue, LayoutFlexDirectionValue, LayoutFlexWrapValue,
28 LayoutFlexGrowValue, LayoutFlexShrinkValue,
29 LayoutAlignItemsValue, LayoutAlignSelfValue, LayoutAlignContentValue,
30 LayoutJustifyContentValue, LayoutJustifyItemsValue, LayoutJustifySelfValue,
31 LayoutGapValue,
32 LayoutGridTemplateColumnsValue, LayoutGridTemplateRowsValue,
33 LayoutGridAutoColumnsValue, LayoutGridAutoRowsValue,
34 LayoutGridAutoFlowValue, LayoutGridColumnValue, LayoutGridRowValue,
35 },
36 style::{
37 border_radius::StyleBorderRadius,
38 lists::{StyleListStylePosition, StyleListStyleType},
39 StyleDirection, StyleTextAlign, StyleUserSelect, StyleVerticalAlign,
40 StyleVisibility, StyleWhiteSpace,
41 StyleUnicodeBidi, StyleTextBoxTrim, StyleTextBoxEdge,
42 StyleDominantBaseline, StyleAlignmentBaseline,
43 StyleInitialLetterAlign, StyleInitialLetterWrap,
44 },
45 },
46};
47
48use crate::{
49 font_traits::{ParsedFontTrait, StyleProperties},
50 solver3::{
51 display_list::{BorderRadius, PhysicalSizeImport},
52 layout_tree::LayoutNode,
53 scrollbar::ScrollbarRequirements,
54 },
55};
56
57pub fn get_element_font_size(
78 styled_dom: &StyledDom,
79 dom_id: NodeId,
80 node_state: &StyledNodeState,
81) -> f32 {
82 let _ = compute_all_font_sizes_px; resolve_font_size_slow(styled_dom, dom_id, node_state)
95}
96
97fn compute_all_font_sizes_px(styled_dom: &StyledDom) -> alloc::vec::Vec<f32> {
115 use azul_css::props::{
116 basic::length::SizeMetric,
117 property::{CssProperty, CssPropertyType},
118 };
119
120 let n = styled_dom.node_data.len();
121 let mut sizes = alloc::vec![DEFAULT_FONT_SIZE; n];
122 if n == 0 {
123 return sizes;
124 }
125
126 let data_container = styled_dom.node_data.as_container();
127 let state_container = styled_dom.styled_nodes.as_container();
128 let hierarchy = styled_dom.node_hierarchy.as_container();
129 let cache = &styled_dom.css_property_cache.ptr;
130
131 for idx in 0..n {
132 let dom_id = NodeId::new(idx);
133
134 if let Some(vec) = cache.computed_values.get(idx) {
136 if let Ok(cv_idx) =
137 vec.binary_search_by_key(&CssPropertyType::FontSize, |(k, _)| *k)
138 {
139 if let CssProperty::FontSize(css_val) = &vec[cv_idx].1.property {
140 if let Some(fs) = css_val.get_property() {
141 if fs.inner.metric == SizeMetric::Px {
142 sizes[idx] = fs.inner.number.get();
143 continue;
144 }
145 }
146 }
147 }
148 }
149
150 let parent_font_size = hierarchy
152 .get(dom_id)
153 .and_then(|node| node.parent_id())
154 .map(|p| sizes[p.index()])
155 .unwrap_or(DEFAULT_FONT_SIZE);
156 let root_font_size = sizes[0];
157
158 let Some(node_data) = data_container.internal.get(idx) else {
159 sizes[idx] = DEFAULT_FONT_SIZE;
160 continue;
161 };
162 let Some(styled) = state_container.internal.get(idx) else {
163 sizes[idx] = DEFAULT_FONT_SIZE;
164 continue;
165 };
166 let node_state = &styled.styled_node_state;
167
168 let mut fast_fs: Option<f32> = None;
172 let mut compact_said_inherit = false;
173 if node_state.is_normal() {
174 if let Some(ref cc) = cache.compact_cache {
175 let raw = cc.get_font_size_raw(idx);
176 if raw == azul_css::compact_cache::U32_SENTINEL
177 || raw == azul_css::compact_cache::U32_INHERIT
178 || raw == azul_css::compact_cache::U32_INITIAL
179 {
180 compact_said_inherit = true;
181 } else if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
182 if pv.metric == SizeMetric::Px {
184 fast_fs = Some(pv.number.get());
185 } else {
186 let context = ResolutionContext {
188 element_font_size: DEFAULT_FONT_SIZE,
189 parent_font_size,
190 root_font_size,
191 containing_block_size: PhysicalSize::new(0.0, 0.0),
192 element_size: None,
193 viewport_size: PhysicalSize::new(0.0, 0.0),
194 };
195 fast_fs = Some(pv.resolve_with_context(&context, PropertyContext::FontSize));
196 }
197 }
198 }
199 }
200 if let Some(fs) = fast_fs {
201 sizes[idx] = fs;
202 continue;
203 }
204 if compact_said_inherit {
205 sizes[idx] = parent_font_size;
206 continue;
207 }
208
209 let resolved = cache
210 .get_font_size(node_data, &dom_id, node_state)
211 .and_then(|v| v.get_property().cloned())
212 .map(|v| {
213 let context = ResolutionContext {
214 element_font_size: DEFAULT_FONT_SIZE,
215 parent_font_size,
216 root_font_size,
217 containing_block_size: PhysicalSize::new(0.0, 0.0),
218 element_size: None,
219 viewport_size: PhysicalSize::new(0.0, 0.0),
220 };
221 v.inner
222 .resolve_with_context(&context, PropertyContext::FontSize)
223 });
224
225 sizes[idx] = resolved.unwrap_or(DEFAULT_FONT_SIZE);
227 }
228 sizes
229}
230
231fn resolve_font_size_slow(
236 styled_dom: &StyledDom,
237 dom_id: NodeId,
238 node_state: &StyledNodeState,
239) -> f32 {
240 let node_data = &styled_dom.node_data.as_container()[dom_id];
241 let cache = &styled_dom.css_property_cache.ptr;
242
243 if let Some(vec) = cache.computed_values.get(dom_id.index()) {
244 if let Ok(idx) = vec.binary_search_by_key(
245 &azul_css::props::property::CssPropertyType::FontSize,
246 |(k, _)| *k,
247 ) {
248 if let azul_css::props::property::CssProperty::FontSize(css_val) = &vec[idx].1.property {
249 if let Some(fs) = css_val.get_property() {
250 if fs.inner.metric == azul_css::props::basic::length::SizeMetric::Px {
251 return fs.inner.number.get();
252 }
253 }
254 }
255 }
256 }
257
258 let parent_font_size = styled_dom
259 .node_hierarchy
260 .as_container()
261 .get(dom_id)
262 .and_then(|node| node.parent_id())
263 .map(|parent_id| resolve_font_size_slow(styled_dom, parent_id, node_state))
264 .unwrap_or(DEFAULT_FONT_SIZE);
265
266 let root_font_size = if dom_id == NodeId::new(0) {
267 DEFAULT_FONT_SIZE
268 } else {
269 resolve_font_size_slow(styled_dom, NodeId::new(0), node_state)
270 };
271
272 cache
273 .get_font_size(node_data, &dom_id, node_state)
274 .and_then(|v| v.get_property().cloned())
275 .map(|v| {
276 let context = ResolutionContext {
277 element_font_size: DEFAULT_FONT_SIZE,
278 parent_font_size,
279 root_font_size,
280 containing_block_size: PhysicalSize::new(0.0, 0.0),
281 element_size: None,
282 viewport_size: PhysicalSize::new(0.0, 0.0),
283 };
284 v.inner
285 .resolve_with_context(&context, PropertyContext::FontSize)
286 })
287 .unwrap_or(DEFAULT_FONT_SIZE)
288}
289
290pub fn get_parent_font_size(
296 styled_dom: &StyledDom,
297 dom_id: NodeId,
298 _node_state: &StyledNodeState, ) -> f32 {
300 styled_dom
301 .node_hierarchy
302 .as_container()
303 .get(dom_id)
304 .and_then(|node| node.parent_id())
305 .map(|parent_id| {
306 let parent_state = &styled_dom.styled_nodes.as_container()[parent_id].styled_node_state;
307 get_element_font_size(styled_dom, parent_id, parent_state)
308 })
309 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE)
310}
311
312pub fn get_root_font_size(styled_dom: &StyledDom, _node_state: &StyledNodeState) -> f32 {
317 let root_id = NodeId::new(0);
318 let root_state = &styled_dom.styled_nodes.as_container()[root_id].styled_node_state;
319 get_element_font_size(styled_dom, root_id, root_state)
320}
321
322#[derive(Debug, Copy, Clone, PartialEq)]
325pub enum MultiValue<T> {
326 Auto,
328 Initial,
330 Inherit,
332 Exact(T),
334}
335
336impl<T> MultiValue<T> {
337 pub fn is_auto(&self) -> bool {
339 matches!(self, MultiValue::Auto)
340 }
341
342 pub fn is_exact(&self) -> bool {
344 matches!(self, MultiValue::Exact(_))
345 }
346
347 pub fn exact(self) -> Option<T> {
349 match self {
350 MultiValue::Exact(v) => Some(v),
351 _ => None,
352 }
353 }
354
355 pub fn unwrap_or(self, default: T) -> T {
357 match self {
358 MultiValue::Exact(v) => v,
359 _ => default,
360 }
361 }
362
363 pub fn unwrap_or_default(self) -> T
365 where
366 T: Default,
367 {
368 match self {
369 MultiValue::Exact(v) => v,
370 _ => T::default(),
371 }
372 }
373
374 pub fn map<U, F>(self, f: F) -> MultiValue<U>
376 where
377 F: FnOnce(T) -> U,
378 {
379 match self {
380 MultiValue::Exact(v) => MultiValue::Exact(f(v)),
381 MultiValue::Auto => MultiValue::Auto,
382 MultiValue::Initial => MultiValue::Initial,
383 MultiValue::Inherit => MultiValue::Inherit,
384 }
385 }
386}
387
388impl MultiValue<LayoutOverflow> {
390 pub fn is_clipped(&self) -> bool {
393 matches!(
394 self,
395 MultiValue::Exact(
396 LayoutOverflow::Hidden
397 | LayoutOverflow::Clip
398 | LayoutOverflow::Auto
399 | LayoutOverflow::Scroll
400 )
401 )
402 }
403
404 pub fn is_scroll(&self) -> bool {
405 matches!(
406 self,
407 MultiValue::Exact(LayoutOverflow::Scroll | LayoutOverflow::Auto)
408 )
409 }
410
411 pub fn is_auto_overflow(&self) -> bool {
412 matches!(self, MultiValue::Exact(LayoutOverflow::Auto))
413 }
414
415 pub fn is_hidden(&self) -> bool {
416 matches!(self, MultiValue::Exact(LayoutOverflow::Hidden))
417 }
418
419 pub fn is_hidden_or_clip(&self) -> bool {
420 matches!(
421 self,
422 MultiValue::Exact(LayoutOverflow::Hidden | LayoutOverflow::Clip)
423 )
424 }
425
426 pub fn is_scroll_explicit(&self) -> bool {
427 matches!(self, MultiValue::Exact(LayoutOverflow::Scroll))
428 }
429
430 pub fn is_clip(&self) -> bool {
431 matches!(self, MultiValue::Exact(LayoutOverflow::Clip))
432 }
433
434 pub fn is_visible_or_clip(&self) -> bool {
435 matches!(
436 self,
437 MultiValue::Exact(LayoutOverflow::Visible | LayoutOverflow::Clip)
438 )
439 }
440
441 pub fn resolve_computed(&self, other_axis: &MultiValue<LayoutOverflow>) -> MultiValue<LayoutOverflow> {
446 match (self, other_axis) {
447 (MultiValue::Exact(val), MultiValue::Exact(other)) => {
448 MultiValue::Exact(val.resolve_computed(*other))
449 }
450 _ => *self,
451 }
452 }
453}
454
455impl MultiValue<LayoutPosition> {
457 pub fn is_absolute_or_fixed(&self) -> bool {
458 matches!(
459 self,
460 MultiValue::Exact(LayoutPosition::Absolute | LayoutPosition::Fixed)
461 )
462 }
463}
464
465impl MultiValue<LayoutFloat> {
467 pub fn is_none(&self) -> bool {
468 matches!(
469 self,
470 MultiValue::Auto
471 | MultiValue::Initial
472 | MultiValue::Inherit
473 | MultiValue::Exact(LayoutFloat::None)
474 )
475 }
476}
477
478impl<T: Default> Default for MultiValue<T> {
479 fn default() -> Self {
480 MultiValue::Auto
481 }
482}
483
484macro_rules! get_css_property_pixel {
487 ($fn_name:ident, $cache_method:ident, $ua_property:expr, compact_i16 = $compact_method:ident) => {
489 pub fn $fn_name(
490 styled_dom: &StyledDom,
491 node_id: NodeId,
492 node_state: &StyledNodeState,
493 ) -> MultiValue<PixelValue> {
494 if node_state.is_normal() {
496 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
497 let raw = cc.$compact_method(node_id.index());
498 if raw == azul_css::compact_cache::I16_AUTO {
499 return MultiValue::Auto;
500 }
501 if raw == azul_css::compact_cache::I16_INITIAL {
502 return MultiValue::Initial;
503 }
504 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
505 return MultiValue::Exact(PixelValue::px(raw as f32 / 10.0));
507 }
508 }
510 }
511
512 let node_data = &styled_dom.node_data.as_container()[node_id];
513
514 let author_css = styled_dom
515 .css_property_cache
516 .ptr
517 .$cache_method(node_data, &node_id, node_state);
518
519 if let Some(ref val) = author_css {
520 if val.is_auto() {
521 return MultiValue::Auto;
522 }
523 if let Some(exact) = val.get_property().copied() {
524 return MultiValue::Exact(exact.inner);
525 }
526 }
527
528 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
529
530 if let Some(ua_prop) = ua_css {
531 if let Some(inner) = ua_prop.get_pixel_inner() {
532 return MultiValue::Exact(inner);
533 }
534 }
535
536 MultiValue::Initial
537 }
538 };
539 ($fn_name:ident, $cache_method:ident, $ua_property:expr) => {
541 pub fn $fn_name(
542 styled_dom: &StyledDom,
543 node_id: NodeId,
544 node_state: &StyledNodeState,
545 ) -> MultiValue<PixelValue> {
546 let node_data = &styled_dom.node_data.as_container()[node_id];
547
548 let author_css = styled_dom
550 .css_property_cache
551 .ptr
552 .$cache_method(node_data, &node_id, node_state);
553
554 if let Some(ref val) = author_css {
558 if val.is_auto() {
559 return MultiValue::Auto;
560 }
561 if let Some(exact) = val.get_property().copied() {
562 return MultiValue::Exact(exact.inner);
563 }
564 }
566
567 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
569
570 if let Some(ua_prop) = ua_css {
571 if let Some(inner) = ua_prop.get_pixel_inner() {
572 return MultiValue::Exact(inner);
573 }
574 }
575
576 MultiValue::Initial
581 }
582 };
583}
584
585trait CssPropertyPixelInner {
587 fn get_pixel_inner(&self) -> Option<PixelValue>;
588}
589
590impl CssPropertyPixelInner for azul_css::props::property::CssProperty {
591 fn get_pixel_inner(&self) -> Option<PixelValue> {
592 match self {
593 CssProperty::Left(CssPropertyValue::Exact(v)) => Some(v.inner),
594 CssProperty::Right(CssPropertyValue::Exact(v)) => Some(v.inner),
595 CssProperty::Top(CssPropertyValue::Exact(v)) => Some(v.inner),
596 CssProperty::Bottom(CssPropertyValue::Exact(v)) => Some(v.inner),
597 CssProperty::MarginLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
598 CssProperty::MarginRight(CssPropertyValue::Exact(v)) => Some(v.inner),
599 CssProperty::MarginTop(CssPropertyValue::Exact(v)) => Some(v.inner),
600 CssProperty::MarginBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
601 CssProperty::PaddingLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
602 CssProperty::PaddingRight(CssPropertyValue::Exact(v)) => Some(v.inner),
603 CssProperty::PaddingTop(CssPropertyValue::Exact(v)) => Some(v.inner),
604 CssProperty::PaddingBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
605 _ => None,
606 }
607 }
608}
609
610macro_rules! get_css_property {
612 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact = $compact_method:ident) => {
614 pub fn $fn_name(
615 styled_dom: &StyledDom,
616 node_id: NodeId,
617 node_state: &StyledNodeState,
618 ) -> MultiValue<$return_type> {
619 if node_state.is_normal() {
625 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
626 return MultiValue::Exact(cc.$compact_method(node_id.index()));
627 }
628 }
629
630 let node_data = &styled_dom.node_data.as_container()[node_id];
632
633 let author_css = styled_dom
635 .css_property_cache
636 .ptr
637 .$cache_method(node_data, &node_id, node_state);
638
639 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
640 return MultiValue::Exact(val);
641 }
642
643 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
645
646 if let Some(ua_prop) = ua_css {
647 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
648 return MultiValue::Exact(val);
649 }
650 }
651
652 MultiValue::Auto
654 }
655 };
656 ($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) => {
659 pub fn $fn_name(
660 styled_dom: &StyledDom,
661 node_id: NodeId,
662 node_state: &StyledNodeState,
663 ) -> MultiValue<$return_type> {
664 if node_state.is_normal() {
666 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
667 let raw = cc.$compact_raw_method(node_id.index());
668 match raw {
669 azul_css::compact_cache::U32_AUTO => return MultiValue::Auto,
670 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
671 azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
672 azul_css::compact_cache::U32_MIN_CONTENT => return MultiValue::Exact($min_content_variant),
673 azul_css::compact_cache::U32_MAX_CONTENT => return MultiValue::Exact($max_content_variant),
674 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
675 }
677 _ => {
678 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
680 return MultiValue::Exact($px_variant(pv));
681 }
682 }
684 }
685 }
686 }
687
688 let node_data = &styled_dom.node_data.as_container()[node_id];
690
691 let author_css = styled_dom
692 .css_property_cache
693 .ptr
694 .$cache_method(node_data, &node_id, node_state);
695
696 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
697 return MultiValue::Exact(val);
698 }
699
700 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
701
702 if let Some(ua_prop) = ua_css {
703 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
704 return MultiValue::Exact(val);
705 }
706 }
707
708 MultiValue::Auto
709 }
710 };
711 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr, compact_u32_struct = $compact_raw_method:ident) => {
714 pub fn $fn_name(
715 styled_dom: &StyledDom,
716 node_id: NodeId,
717 node_state: &StyledNodeState,
718 ) -> MultiValue<$return_type> {
719 if node_state.is_normal() {
721 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
722 let raw = cc.$compact_raw_method(node_id.index());
723 match raw {
724 azul_css::compact_cache::U32_AUTO | azul_css::compact_cache::U32_NONE => return MultiValue::Auto,
725 azul_css::compact_cache::U32_INITIAL => return MultiValue::Initial,
726 azul_css::compact_cache::U32_SENTINEL | azul_css::compact_cache::U32_INHERIT => {
727 }
729 _ => {
730 if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
731 return MultiValue::Exact(
732 <$return_type as azul_css::props::PixelValueTaker>::from_pixel_value(pv)
733 );
734 }
735 }
736 }
737 }
738 }
739
740 let node_data = &styled_dom.node_data.as_container()[node_id];
742
743 let author_css = styled_dom
744 .css_property_cache
745 .ptr
746 .$cache_method(node_data, &node_id, node_state);
747
748 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
749 return MultiValue::Exact(val);
750 }
751
752 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
753
754 if let Some(ua_prop) = ua_css {
755 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
756 return MultiValue::Exact(val);
757 }
758 }
759
760 MultiValue::Auto
761 }
762 };
763 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr) => {
765 pub fn $fn_name(
766 styled_dom: &StyledDom,
767 node_id: NodeId,
768 node_state: &StyledNodeState,
769 ) -> MultiValue<$return_type> {
770 let node_data = &styled_dom.node_data.as_container()[node_id];
771
772 let author_css = styled_dom
774 .css_property_cache
775 .ptr
776 .$cache_method(node_data, &node_id, node_state);
777
778 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
779 return MultiValue::Exact(val);
780 }
781
782 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
784
785 if let Some(ua_prop) = ua_css {
786 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
787 return MultiValue::Exact(val);
788 }
789 }
790
791 MultiValue::Auto
793 }
794 };
795}
796
797trait ExtractPropertyValue<T> {
799 fn extract(&self) -> Option<T>;
800}
801
802fn extract_property_value<T>(prop: &azul_css::props::property::CssProperty) -> Option<T>
803where
804 azul_css::props::property::CssProperty: ExtractPropertyValue<T>,
805{
806 prop.extract()
807}
808
809impl ExtractPropertyValue<LayoutWidth> for azul_css::props::property::CssProperty {
812 fn extract(&self) -> Option<LayoutWidth> {
813 match self {
814 Self::Width(CssPropertyValue::Exact(v)) => Some(v.clone()),
815 _ => None,
816 }
817 }
818}
819
820impl ExtractPropertyValue<LayoutHeight> for azul_css::props::property::CssProperty {
821 fn extract(&self) -> Option<LayoutHeight> {
822 match self {
823 Self::Height(CssPropertyValue::Exact(v)) => Some(v.clone()),
824 _ => None,
825 }
826 }
827}
828
829impl ExtractPropertyValue<LayoutMinWidth> for azul_css::props::property::CssProperty {
830 fn extract(&self) -> Option<LayoutMinWidth> {
831 match self {
832 Self::MinWidth(CssPropertyValue::Exact(v)) => Some(*v),
833 _ => None,
834 }
835 }
836}
837
838impl ExtractPropertyValue<LayoutMinHeight> for azul_css::props::property::CssProperty {
839 fn extract(&self) -> Option<LayoutMinHeight> {
840 match self {
841 Self::MinHeight(CssPropertyValue::Exact(v)) => Some(*v),
842 _ => None,
843 }
844 }
845}
846
847impl ExtractPropertyValue<LayoutMaxWidth> for azul_css::props::property::CssProperty {
848 fn extract(&self) -> Option<LayoutMaxWidth> {
849 match self {
850 Self::MaxWidth(CssPropertyValue::Exact(v)) => Some(*v),
851 _ => None,
852 }
853 }
854}
855
856impl ExtractPropertyValue<LayoutMaxHeight> for azul_css::props::property::CssProperty {
857 fn extract(&self) -> Option<LayoutMaxHeight> {
858 match self {
859 Self::MaxHeight(CssPropertyValue::Exact(v)) => Some(*v),
860 _ => None,
861 }
862 }
863}
864
865impl ExtractPropertyValue<LayoutDisplay> for azul_css::props::property::CssProperty {
866 fn extract(&self) -> Option<LayoutDisplay> {
867 match self {
868 Self::Display(CssPropertyValue::Exact(v)) => Some(*v),
869 _ => None,
870 }
871 }
872}
873
874impl ExtractPropertyValue<LayoutWritingMode> for azul_css::props::property::CssProperty {
875 fn extract(&self) -> Option<LayoutWritingMode> {
876 match self {
877 Self::WritingMode(CssPropertyValue::Exact(v)) => Some(*v),
878 _ => None,
879 }
880 }
881}
882
883impl ExtractPropertyValue<LayoutFlexWrap> for azul_css::props::property::CssProperty {
884 fn extract(&self) -> Option<LayoutFlexWrap> {
885 match self {
886 Self::FlexWrap(CssPropertyValue::Exact(v)) => Some(*v),
887 _ => None,
888 }
889 }
890}
891
892impl ExtractPropertyValue<LayoutJustifyContent> for azul_css::props::property::CssProperty {
893 fn extract(&self) -> Option<LayoutJustifyContent> {
894 match self {
895 Self::JustifyContent(CssPropertyValue::Exact(v)) => Some(*v),
896 _ => None,
897 }
898 }
899}
900
901impl ExtractPropertyValue<StyleTextAlign> for azul_css::props::property::CssProperty {
902 fn extract(&self) -> Option<StyleTextAlign> {
903 match self {
904 Self::TextAlign(CssPropertyValue::Exact(v)) => Some(*v),
905 _ => None,
906 }
907 }
908}
909
910impl ExtractPropertyValue<LayoutFloat> for azul_css::props::property::CssProperty {
911 fn extract(&self) -> Option<LayoutFloat> {
912 match self {
913 Self::Float(CssPropertyValue::Exact(v)) => Some(*v),
914 _ => None,
915 }
916 }
917}
918
919impl ExtractPropertyValue<LayoutClear> for azul_css::props::property::CssProperty {
920 fn extract(&self) -> Option<LayoutClear> {
921 match self {
922 Self::Clear(CssPropertyValue::Exact(v)) => Some(*v),
923 _ => None,
924 }
925 }
926}
927
928impl ExtractPropertyValue<LayoutOverflow> for azul_css::props::property::CssProperty {
929 fn extract(&self) -> Option<LayoutOverflow> {
930 match self {
931 Self::OverflowX(CssPropertyValue::Exact(v)) => Some(*v),
932 Self::OverflowY(CssPropertyValue::Exact(v)) => Some(*v),
933 Self::OverflowBlock(CssPropertyValue::Exact(v)) => Some(*v),
934 Self::OverflowInline(CssPropertyValue::Exact(v)) => Some(*v),
935 _ => None,
936 }
937 }
938}
939
940impl ExtractPropertyValue<LayoutPosition> for azul_css::props::property::CssProperty {
941 fn extract(&self) -> Option<LayoutPosition> {
942 match self {
943 Self::Position(CssPropertyValue::Exact(v)) => Some(*v),
944 _ => None,
945 }
946 }
947}
948
949impl ExtractPropertyValue<LayoutBoxSizing> for azul_css::props::property::CssProperty {
950 fn extract(&self) -> Option<LayoutBoxSizing> {
951 match self {
952 Self::BoxSizing(CssPropertyValue::Exact(v)) => Some(*v),
953 _ => None,
954 }
955 }
956}
957
958impl ExtractPropertyValue<PixelValue> for azul_css::props::property::CssProperty {
959 fn extract(&self) -> Option<PixelValue> {
960 self.get_pixel_inner()
961 }
962}
963
964impl ExtractPropertyValue<LayoutFlexDirection> for azul_css::props::property::CssProperty {
965 fn extract(&self) -> Option<LayoutFlexDirection> {
966 match self {
967 Self::FlexDirection(CssPropertyValue::Exact(v)) => Some(*v),
968 _ => None,
969 }
970 }
971}
972
973impl ExtractPropertyValue<LayoutAlignItems> for azul_css::props::property::CssProperty {
974 fn extract(&self) -> Option<LayoutAlignItems> {
975 match self {
976 Self::AlignItems(CssPropertyValue::Exact(v)) => Some(*v),
977 _ => None,
978 }
979 }
980}
981
982impl ExtractPropertyValue<LayoutAlignContent> for azul_css::props::property::CssProperty {
983 fn extract(&self) -> Option<LayoutAlignContent> {
984 match self {
985 Self::AlignContent(CssPropertyValue::Exact(v)) => Some(*v),
986 _ => None,
987 }
988 }
989}
990
991impl ExtractPropertyValue<StyleFontWeight> for azul_css::props::property::CssProperty {
992 fn extract(&self) -> Option<StyleFontWeight> {
993 match self {
994 Self::FontWeight(CssPropertyValue::Exact(v)) => Some(*v),
995 _ => None,
996 }
997 }
998}
999
1000impl ExtractPropertyValue<StyleFontStyle> for azul_css::props::property::CssProperty {
1001 fn extract(&self) -> Option<StyleFontStyle> {
1002 match self {
1003 Self::FontStyle(CssPropertyValue::Exact(v)) => Some(*v),
1004 _ => None,
1005 }
1006 }
1007}
1008
1009impl ExtractPropertyValue<StyleVisibility> for azul_css::props::property::CssProperty {
1010 fn extract(&self) -> Option<StyleVisibility> {
1011 match self {
1012 Self::Visibility(CssPropertyValue::Exact(v)) => Some(*v),
1013 _ => None,
1014 }
1015 }
1016}
1017
1018impl ExtractPropertyValue<StyleWhiteSpace> for azul_css::props::property::CssProperty {
1019 fn extract(&self) -> Option<StyleWhiteSpace> {
1020 match self {
1021 Self::WhiteSpace(CssPropertyValue::Exact(v)) => Some(*v),
1022 _ => None,
1023 }
1024 }
1025}
1026
1027impl ExtractPropertyValue<StyleDirection> for azul_css::props::property::CssProperty {
1028 fn extract(&self) -> Option<StyleDirection> {
1029 match self {
1030 Self::Direction(CssPropertyValue::Exact(v)) => Some(*v),
1031 _ => None,
1032 }
1033 }
1034}
1035
1036impl ExtractPropertyValue<StyleUnicodeBidi> for azul_css::props::property::CssProperty {
1037 fn extract(&self) -> Option<StyleUnicodeBidi> {
1038 match self {
1039 Self::UnicodeBidi(CssPropertyValue::Exact(v)) => Some(*v),
1040 _ => None,
1041 }
1042 }
1043}
1044
1045impl ExtractPropertyValue<StyleTextBoxTrim> for azul_css::props::property::CssProperty {
1046 fn extract(&self) -> Option<StyleTextBoxTrim> {
1047 match self {
1048 Self::TextBoxTrim(CssPropertyValue::Exact(v)) => Some(*v),
1049 _ => None,
1050 }
1051 }
1052}
1053
1054impl ExtractPropertyValue<StyleTextBoxEdge> for azul_css::props::property::CssProperty {
1055 fn extract(&self) -> Option<StyleTextBoxEdge> {
1056 match self {
1057 Self::TextBoxEdge(CssPropertyValue::Exact(v)) => Some(*v),
1058 _ => None,
1059 }
1060 }
1061}
1062
1063impl ExtractPropertyValue<StyleDominantBaseline> for azul_css::props::property::CssProperty {
1064 fn extract(&self) -> Option<StyleDominantBaseline> {
1065 match self {
1066 Self::DominantBaseline(CssPropertyValue::Exact(v)) => Some(*v),
1067 _ => None,
1068 }
1069 }
1070}
1071
1072impl ExtractPropertyValue<StyleAlignmentBaseline> for azul_css::props::property::CssProperty {
1073 fn extract(&self) -> Option<StyleAlignmentBaseline> {
1074 match self {
1075 Self::AlignmentBaseline(CssPropertyValue::Exact(v)) => Some(*v),
1076 _ => None,
1077 }
1078 }
1079}
1080
1081impl ExtractPropertyValue<StyleInitialLetterAlign> for azul_css::props::property::CssProperty {
1082 fn extract(&self) -> Option<StyleInitialLetterAlign> {
1083 match self {
1084 Self::InitialLetterAlign(CssPropertyValue::Exact(v)) => Some(*v),
1085 _ => None,
1086 }
1087 }
1088}
1089
1090impl ExtractPropertyValue<StyleInitialLetterWrap> for azul_css::props::property::CssProperty {
1091 fn extract(&self) -> Option<StyleInitialLetterWrap> {
1092 match self {
1093 Self::InitialLetterWrap(CssPropertyValue::Exact(v)) => Some(*v),
1094 _ => None,
1095 }
1096 }
1097}
1098
1099impl ExtractPropertyValue<StyleScrollbarGutter> for azul_css::props::property::CssProperty {
1100 fn extract(&self) -> Option<StyleScrollbarGutter> {
1101 match self {
1102 Self::ScrollbarGutter(CssPropertyValue::Exact(v)) => Some(*v),
1103 _ => None,
1104 }
1105 }
1106}
1107
1108impl ExtractPropertyValue<StyleOverflowClipMargin> for azul_css::props::property::CssProperty {
1109 fn extract(&self) -> Option<StyleOverflowClipMargin> {
1110 match self {
1111 Self::OverflowClipMargin(CssPropertyValue::Exact(v)) => Some(*v),
1112 _ => None,
1113 }
1114 }
1115}
1116
1117impl ExtractPropertyValue<StyleVerticalAlign> for azul_css::props::property::CssProperty {
1118 fn extract(&self) -> Option<StyleVerticalAlign> {
1119 match self {
1120 Self::VerticalAlign(CssPropertyValue::Exact(v)) => Some(*v),
1121 _ => None,
1122 }
1123 }
1124}
1125
1126get_css_property!(
1127 get_writing_mode,
1128 get_writing_mode,
1129 LayoutWritingMode,
1130 azul_css::props::property::CssPropertyType::WritingMode,
1131 compact = get_writing_mode
1132);
1133
1134get_css_property!(
1135 get_css_width,
1136 get_width,
1137 LayoutWidth,
1138 azul_css::props::property::CssPropertyType::Width,
1139 compact_u32_dim = get_width_raw, LayoutWidth::Px, LayoutWidth::Auto, LayoutWidth::MinContent, LayoutWidth::MaxContent
1140);
1141
1142get_css_property!(
1143 get_css_height,
1144 get_height,
1145 LayoutHeight,
1146 azul_css::props::property::CssPropertyType::Height,
1147 compact_u32_dim = get_height_raw, LayoutHeight::Px, LayoutHeight::Auto, LayoutHeight::MinContent, LayoutHeight::MaxContent
1148);
1149
1150get_css_property!(
1151 get_wrap,
1152 get_flex_wrap,
1153 LayoutFlexWrap,
1154 azul_css::props::property::CssPropertyType::FlexWrap,
1155 compact = get_flex_wrap
1156);
1157
1158get_css_property!(
1159 get_justify_content,
1160 get_justify_content,
1161 LayoutJustifyContent,
1162 azul_css::props::property::CssPropertyType::JustifyContent,
1163 compact = get_justify_content
1164);
1165
1166get_css_property!(
1167 get_text_align,
1168 get_text_align,
1169 StyleTextAlign,
1170 azul_css::props::property::CssPropertyType::TextAlign,
1171 compact = get_text_align
1172);
1173
1174get_css_property!(
1175 get_float,
1176 get_float,
1177 LayoutFloat,
1178 azul_css::props::property::CssPropertyType::Float,
1179 compact = get_float
1180);
1181
1182get_css_property!(
1183 get_clear,
1184 get_clear,
1185 LayoutClear,
1186 azul_css::props::property::CssPropertyType::Clear,
1187 compact = get_clear
1188);
1189
1190get_css_property!(
1191 get_overflow_x,
1192 get_overflow_x,
1193 LayoutOverflow,
1194 azul_css::props::property::CssPropertyType::OverflowX,
1195 compact = get_overflow_x
1196);
1197
1198get_css_property!(
1199 get_overflow_y,
1200 get_overflow_y,
1201 LayoutOverflow,
1202 azul_css::props::property::CssPropertyType::OverflowY,
1203 compact = get_overflow_y
1204);
1205
1206get_css_property!(
1208 get_overflow_block,
1209 get_overflow_block,
1210 LayoutOverflow,
1211 azul_css::props::property::CssPropertyType::OverflowBlock
1212);
1213
1214get_css_property!(
1215 get_overflow_inline,
1216 get_overflow_inline,
1217 LayoutOverflow,
1218 azul_css::props::property::CssPropertyType::OverflowInline
1219);
1220
1221get_css_property!(
1222 get_position,
1223 get_position,
1224 LayoutPosition,
1225 azul_css::props::property::CssPropertyType::Position,
1226 compact = get_position
1227);
1228
1229get_css_property!(
1230 get_css_box_sizing,
1231 get_box_sizing,
1232 LayoutBoxSizing,
1233 azul_css::props::property::CssPropertyType::BoxSizing,
1234 compact = get_box_sizing
1235);
1236
1237get_css_property!(
1238 get_flex_direction,
1239 get_flex_direction,
1240 LayoutFlexDirection,
1241 azul_css::props::property::CssPropertyType::FlexDirection,
1242 compact = get_flex_direction
1243);
1244
1245get_css_property!(
1246 get_align_items,
1247 get_align_items,
1248 LayoutAlignItems,
1249 azul_css::props::property::CssPropertyType::AlignItems,
1250 compact = get_align_items
1251);
1252
1253get_css_property!(
1254 get_align_content,
1255 get_align_content,
1256 LayoutAlignContent,
1257 azul_css::props::property::CssPropertyType::AlignContent,
1258 compact = get_align_content
1259);
1260
1261get_css_property!(
1262 get_font_weight_property,
1263 get_font_weight,
1264 StyleFontWeight,
1265 azul_css::props::property::CssPropertyType::FontWeight,
1266 compact = get_font_weight
1267);
1268
1269get_css_property!(
1270 get_font_style_property,
1271 get_font_style,
1272 StyleFontStyle,
1273 azul_css::props::property::CssPropertyType::FontStyle,
1274 compact = get_font_style
1275);
1276
1277get_css_property!(
1278 get_visibility,
1279 get_visibility,
1280 StyleVisibility,
1281 azul_css::props::property::CssPropertyType::Visibility,
1282 compact = get_visibility
1283);
1284
1285get_css_property!(
1286 get_white_space_property,
1287 get_white_space,
1288 StyleWhiteSpace,
1289 azul_css::props::property::CssPropertyType::WhiteSpace,
1290 compact = get_white_space
1291);
1292
1293get_css_property!(
1295 get_direction_property,
1296 get_direction,
1297 StyleDirection,
1298 azul_css::props::property::CssPropertyType::Direction,
1299 compact = get_direction
1300);
1301
1302get_css_property!(
1306 get_unicode_bidi_property,
1307 get_unicode_bidi,
1308 StyleUnicodeBidi,
1309 azul_css::props::property::CssPropertyType::UnicodeBidi
1310);
1311
1312get_css_property!(
1315 get_text_box_trim_property,
1316 get_text_box_trim,
1317 StyleTextBoxTrim,
1318 azul_css::props::property::CssPropertyType::TextBoxTrim
1319);
1320
1321get_css_property!(
1322 get_text_box_edge_property,
1323 get_text_box_edge,
1324 StyleTextBoxEdge,
1325 azul_css::props::property::CssPropertyType::TextBoxEdge
1326);
1327
1328get_css_property!(
1329 get_dominant_baseline_property,
1330 get_dominant_baseline,
1331 StyleDominantBaseline,
1332 azul_css::props::property::CssPropertyType::DominantBaseline
1333);
1334
1335get_css_property!(
1336 get_alignment_baseline_property,
1337 get_alignment_baseline,
1338 StyleAlignmentBaseline,
1339 azul_css::props::property::CssPropertyType::AlignmentBaseline
1340);
1341
1342get_css_property!(
1343 get_initial_letter_align_property,
1344 get_initial_letter_align,
1345 StyleInitialLetterAlign,
1346 azul_css::props::property::CssPropertyType::InitialLetterAlign
1347);
1348
1349get_css_property!(
1350 get_initial_letter_wrap_property,
1351 get_initial_letter_wrap,
1352 StyleInitialLetterWrap,
1353 azul_css::props::property::CssPropertyType::InitialLetterWrap
1354);
1355
1356pub fn get_scrollbar_gutter_property(
1362 styled_dom: &StyledDom,
1363 node_id: NodeId,
1364 node_state: &StyledNodeState,
1365) -> MultiValue<StyleScrollbarGutter> {
1366 if node_state.is_normal() {
1368 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1369 let bits = cc.get_scrollbar_gutter_bits(node_id.index());
1370 let val = match bits {
1371 azul_css::compact_cache::SCROLLBAR_GUTTER_AUTO => StyleScrollbarGutter::Auto,
1372 azul_css::compact_cache::SCROLLBAR_GUTTER_STABLE => StyleScrollbarGutter::Stable,
1373 azul_css::compact_cache::SCROLLBAR_GUTTER_BOTH_EDGES => StyleScrollbarGutter::StableBothEdges,
1374 _ => StyleScrollbarGutter::Auto,
1375 };
1376 return MultiValue::Exact(val);
1377 }
1378 }
1379
1380 let node_data = &styled_dom.node_data.as_container()[node_id];
1382 let author_css = styled_dom
1383 .css_property_cache
1384 .ptr
1385 .get_scrollbar_gutter(node_data, &node_id, node_state);
1386 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
1387 return MultiValue::Exact(val);
1388 }
1389 MultiValue::Auto
1390}
1391
1392get_css_property!(
1393 get_overflow_clip_margin_property,
1394 get_overflow_clip_margin,
1395 StyleOverflowClipMargin,
1396 azul_css::props::property::CssPropertyType::OverflowClipMargin
1397);
1398
1399get_css_property!(
1400 get_object_fit_property,
1401 get_object_fit,
1402 StyleObjectFit,
1403 azul_css::props::property::CssPropertyType::ObjectFit
1404);
1405
1406pub fn get_text_orientation_property(
1412 styled_dom: &StyledDom,
1413 node_id: NodeId,
1414 node_state: &StyledNodeState,
1415) -> MultiValue<StyleTextOrientation> {
1416 if node_state.is_normal() {
1417 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1418 if !cc.has_text_orientation(node_id.index()) {
1419 return MultiValue::Auto;
1420 }
1421 }
1422 }
1423 let node_data = &styled_dom.node_data.as_container()[node_id];
1424 if let Some(val) = styled_dom
1425 .css_property_cache
1426 .ptr
1427 .get_text_orientation(node_data, &node_id, node_state)
1428 .and_then(|v| v.get_property().cloned())
1429 {
1430 return MultiValue::Exact(val);
1431 }
1432 let ua = azul_core::ua_css::get_ua_property(
1433 &node_data.node_type,
1434 azul_css::props::property::CssPropertyType::TextOrientation,
1435 );
1436 if let Some(ua_prop) = ua {
1437 if let Some(val) = extract_property_value::<StyleTextOrientation>(ua_prop) {
1438 return MultiValue::Exact(val);
1439 }
1440 }
1441 MultiValue::Auto
1442}
1443
1444get_css_property!(
1445 get_object_position_property,
1446 get_object_position,
1447 StyleObjectPosition,
1448 azul_css::props::property::CssPropertyType::ObjectPosition
1449);
1450
1451get_css_property!(
1452 get_aspect_ratio_property,
1453 get_aspect_ratio,
1454 StyleAspectRatio,
1455 azul_css::props::property::CssPropertyType::AspectRatio
1456);
1457
1458pub fn get_vertical_align_property(
1462 styled_dom: &StyledDom,
1463 node_id: NodeId,
1464 node_state: &StyledNodeState,
1465) -> MultiValue<StyleVerticalAlign> {
1466 let node_data = &styled_dom.node_data.as_container()[node_id];
1467
1468 let author_css = styled_dom
1469 .css_property_cache
1470 .ptr
1471 .get_vertical_align(node_data, &node_id, node_state);
1472
1473 if let Some(val) = author_css.and_then(|v| v.get_property().cloned()) {
1474 return MultiValue::Exact(val);
1475 }
1476
1477 let ua_css = azul_core::ua_css::get_ua_property(
1478 &node_data.node_type,
1479 azul_css::props::property::CssPropertyType::VerticalAlign,
1480 );
1481
1482 if let Some(ua_prop) = ua_css {
1483 if let Some(val) = extract_property_value::<StyleVerticalAlign>(ua_prop) {
1484 return MultiValue::Exact(val);
1485 }
1486 }
1487
1488 MultiValue::Auto
1489}
1490pub fn get_style_border_radius(
1494 styled_dom: &StyledDom,
1495 node_id: NodeId,
1496 node_state: &StyledNodeState,
1497) -> azul_css::props::style::border_radius::StyleBorderRadius {
1498 use azul_css::props::basic::pixel::PixelValue;
1499 if node_state.is_normal() {
1502 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1503 let idx = node_id.index();
1504 let decode = |raw: i16| -> PixelValue {
1505 if raw >= azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1506 PixelValue::px(0.0)
1507 } else {
1508 PixelValue::px(raw as f32 / 10.0)
1509 }
1510 };
1511 return StyleBorderRadius {
1512 top_left: decode(cc.get_border_top_left_radius_raw(idx)),
1513 top_right: decode(cc.get_border_top_right_radius_raw(idx)),
1514 bottom_right: decode(cc.get_border_bottom_right_radius_raw(idx)),
1515 bottom_left: decode(cc.get_border_bottom_left_radius_raw(idx)),
1516 };
1517 }
1518 }
1519 let node_data = &styled_dom.node_data.as_container()[node_id];
1520
1521 let top_left = styled_dom
1522 .css_property_cache
1523 .ptr
1524 .get_border_top_left_radius(node_data, &node_id, node_state)
1525 .and_then(|br| br.get_property_or_default())
1526 .map(|v| v.inner)
1527 .unwrap_or_default();
1528
1529 let top_right = styled_dom
1530 .css_property_cache
1531 .ptr
1532 .get_border_top_right_radius(node_data, &node_id, node_state)
1533 .and_then(|br| br.get_property_or_default())
1534 .map(|v| v.inner)
1535 .unwrap_or_default();
1536
1537 let bottom_right = styled_dom
1538 .css_property_cache
1539 .ptr
1540 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1541 .and_then(|br| br.get_property_or_default())
1542 .map(|v| v.inner)
1543 .unwrap_or_default();
1544
1545 let bottom_left = styled_dom
1546 .css_property_cache
1547 .ptr
1548 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1549 .and_then(|br| br.get_property_or_default())
1550 .map(|v| v.inner)
1551 .unwrap_or_default();
1552
1553 StyleBorderRadius {
1554 top_left,
1555 top_right,
1556 bottom_right,
1557 bottom_left,
1558 }
1559}
1560
1561pub fn get_border_radius(
1567 styled_dom: &StyledDom,
1568 node_id: NodeId,
1569 node_state: &StyledNodeState,
1570 element_size: PhysicalSizeImport,
1571 viewport_size: LogicalSize,
1572) -> BorderRadius {
1573 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
1574
1575 if node_state.is_normal() {
1579 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1580 let idx = node_id.index();
1581 let tl = cc.get_border_top_left_radius_raw(idx);
1582 let tr = cc.get_border_top_right_radius_raw(idx);
1583 let br = cc.get_border_bottom_right_radius_raw(idx);
1584 let bl = cc.get_border_bottom_left_radius_raw(idx);
1585 let thresh = azul_css::compact_cache::I16_SENTINEL_THRESHOLD;
1587 let decode = |raw: i16| -> f32 {
1588 if raw >= thresh { 0.0 } else { raw as f32 / 10.0 }
1589 };
1590 return BorderRadius {
1591 top_left: decode(tl),
1592 top_right: decode(tr),
1593 bottom_right: decode(br),
1594 bottom_left: decode(bl),
1595 };
1596 }
1597 }
1598
1599 let node_data = &styled_dom.node_data.as_container()[node_id];
1600
1601 let element_font_size = get_element_font_size(styled_dom, node_id, node_state);
1603 let parent_font_size = styled_dom
1604 .node_hierarchy
1605 .as_container()
1606 .get(node_id)
1607 .and_then(|node| node.parent_id())
1608 .map(|p| get_element_font_size(styled_dom, p, node_state))
1609 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
1610 let root_font_size = get_root_font_size(styled_dom, node_state);
1611
1612 let context = ResolutionContext {
1614 element_font_size,
1615 parent_font_size,
1616 root_font_size,
1617 containing_block_size: PhysicalSize::new(0.0, 0.0), element_size: Some(PhysicalSize::new(element_size.width, element_size.height)),
1619 viewport_size: PhysicalSize::new(viewport_size.width, viewport_size.height),
1620 };
1621
1622 let top_left = styled_dom
1623 .css_property_cache
1624 .ptr
1625 .get_border_top_left_radius(node_data, &node_id, node_state)
1626 .and_then(|br| br.get_property().cloned())
1627 .unwrap_or_default();
1628
1629 let top_right = styled_dom
1630 .css_property_cache
1631 .ptr
1632 .get_border_top_right_radius(node_data, &node_id, node_state)
1633 .and_then(|br| br.get_property().cloned())
1634 .unwrap_or_default();
1635
1636 let bottom_right = styled_dom
1637 .css_property_cache
1638 .ptr
1639 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1640 .and_then(|br| br.get_property().cloned())
1641 .unwrap_or_default();
1642
1643 let bottom_left = styled_dom
1644 .css_property_cache
1645 .ptr
1646 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1647 .and_then(|br| br.get_property().cloned())
1648 .unwrap_or_default();
1649
1650 BorderRadius {
1651 top_left: top_left
1652 .inner
1653 .resolve_with_context(&context, PropertyContext::BorderRadius),
1654 top_right: top_right
1655 .inner
1656 .resolve_with_context(&context, PropertyContext::BorderRadius),
1657 bottom_right: bottom_right
1658 .inner
1659 .resolve_with_context(&context, PropertyContext::BorderRadius),
1660 bottom_left: bottom_left
1661 .inner
1662 .resolve_with_context(&context, PropertyContext::BorderRadius),
1663 }
1664}
1665
1666pub fn get_z_index(styled_dom: &StyledDom, node_id: Option<NodeId>) -> i32 {
1674 use azul_css::props::layout::position::LayoutZIndex;
1675
1676 let node_id = match node_id {
1677 Some(id) => id,
1678 None => return 0,
1679 };
1680
1681 let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
1682
1683 if node_state.is_normal() {
1685 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1686 let raw = cc.get_z_index(node_id.index());
1687 if raw == azul_css::compact_cache::I16_AUTO {
1688 return 0;
1689 }
1690 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1691 return raw as i32;
1692 }
1693 }
1695 }
1696
1697 let node_data = &styled_dom.node_data.as_container()[node_id];
1699
1700 styled_dom
1701 .css_property_cache
1702 .ptr
1703 .get_z_index(node_data, &node_id, &node_state)
1704 .and_then(|v| v.get_property())
1705 .map(|z| match z {
1706 LayoutZIndex::Auto => 0,
1707 LayoutZIndex::Integer(i) => *i,
1708 })
1709 .unwrap_or(0)
1710}
1711
1712pub fn is_z_index_auto(styled_dom: &StyledDom, node_id: Option<NodeId>) -> bool {
1717 use azul_css::props::layout::position::LayoutZIndex;
1718
1719 let node_id = match node_id {
1720 Some(id) => id,
1721 None => return true,
1722 };
1723
1724 let node_state = &styled_dom.styled_nodes.as_container()[node_id].styled_node_state;
1725
1726 if node_state.is_normal() {
1728 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1729 let raw = cc.get_z_index(node_id.index());
1730 if raw == azul_css::compact_cache::I16_AUTO {
1731 return true;
1732 }
1733 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
1734 return false; }
1736 }
1738 }
1739
1740 let node_data = &styled_dom.node_data.as_container()[node_id];
1742
1743 styled_dom
1744 .css_property_cache
1745 .ptr
1746 .get_z_index(node_data, &node_id, &node_state)
1747 .and_then(|v| v.get_property())
1748 .map(|z| matches!(z, LayoutZIndex::Auto))
1749 .unwrap_or(true) }
1751
1752pub fn get_background_color(
1775 styled_dom: &StyledDom,
1776 node_id: NodeId,
1777 node_state: &StyledNodeState,
1778) -> ColorU {
1779 let node_data = &styled_dom.node_data.as_container()[node_id];
1780 let cache = &styled_dom.css_property_cache.ptr;
1781
1782 let get_node_bg = |nid: NodeId, ndata: &azul_core::dom::NodeData, state: &StyledNodeState| {
1787 if state.is_normal() {
1788 if let Some(ref cc) = cache.compact_cache {
1789 if !cc.has_background(nid.index()) {
1790 return None;
1791 }
1792 }
1793 }
1794 cache
1795 .get_background_content(ndata, &nid, state)
1796 .and_then(|bg| bg.get_property())
1797 .and_then(|bg_vec| bg_vec.get(0).cloned())
1798 .and_then(|first_bg| match &first_bg {
1799 azul_css::props::style::StyleBackgroundContent::Color(color) => Some(color.clone()),
1800 azul_css::props::style::StyleBackgroundContent::Image(_) => None, _ => None,
1802 })
1803 };
1804
1805 let own_bg = get_node_bg(node_id, node_data, node_state);
1806
1807 if !matches!(node_data.node_type, NodeType::Html) || own_bg.is_some() {
1811 return own_bg.unwrap_or(ColorU {
1813 r: 0,
1814 g: 0,
1815 b: 0,
1816 a: 0,
1817 });
1818 }
1819
1820 let first_child = styled_dom
1822 .node_hierarchy
1823 .as_container()
1824 .get(node_id)
1825 .and_then(|node| node.first_child_id(node_id));
1826
1827 let Some(first_child) = first_child else {
1828 return ColorU {
1829 r: 0,
1830 g: 0,
1831 b: 0,
1832 a: 0,
1833 };
1834 };
1835
1836 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1837
1838 if !matches!(first_child_data.node_type, NodeType::Body) {
1840 return ColorU {
1841 r: 0,
1842 g: 0,
1843 b: 0,
1844 a: 0,
1845 };
1846 }
1847
1848 let first_child_state = &styled_dom.styled_nodes.as_container()[first_child].styled_node_state;
1850 get_node_bg(first_child, first_child_data, first_child_state).unwrap_or(ColorU {
1851 r: 0,
1852 g: 0,
1853 b: 0,
1854 a: 0,
1855 })
1856}
1857
1858pub fn get_background_contents(
1865 styled_dom: &StyledDom,
1866 node_id: NodeId,
1867 node_state: &StyledNodeState,
1868) -> Vec<azul_css::props::style::StyleBackgroundContent> {
1869 use azul_core::dom::NodeType;
1870 use azul_css::props::style::StyleBackgroundContent;
1871
1872 let node_data = &styled_dom.node_data.as_container()[node_id];
1873 let cache = &styled_dom.css_property_cache.ptr;
1874
1875 let get_node_backgrounds =
1879 |nid: NodeId, ndata: &azul_core::dom::NodeData, state: &StyledNodeState|
1880 -> Vec<StyleBackgroundContent> {
1881 if state.is_normal() {
1882 if let Some(ref cc) = cache.compact_cache {
1883 if !cc.has_background(nid.index()) {
1884 return Vec::new();
1885 }
1886 }
1887 }
1888 cache
1889 .get_background_content(ndata, &nid, state)
1890 .and_then(|bg| bg.get_property())
1891 .map(|bg_vec| bg_vec.iter().cloned().collect())
1892 .unwrap_or_default()
1893 };
1894
1895 let own_backgrounds = get_node_backgrounds(node_id, node_data, node_state);
1896
1897 if !matches!(node_data.node_type, NodeType::Html) || !own_backgrounds.is_empty() {
1900 return own_backgrounds;
1901 }
1902
1903 let first_child = styled_dom
1905 .node_hierarchy
1906 .as_container()
1907 .get(node_id)
1908 .and_then(|node| node.first_child_id(node_id));
1909
1910 let Some(first_child) = first_child else {
1911 return own_backgrounds;
1912 };
1913
1914 let first_child_data = &styled_dom.node_data.as_container()[first_child];
1915
1916 if !matches!(first_child_data.node_type, NodeType::Body) {
1918 return own_backgrounds;
1919 }
1920
1921 let first_child_state = &styled_dom.styled_nodes.as_container()[first_child].styled_node_state;
1923 get_node_backgrounds(first_child, first_child_data, first_child_state)
1924}
1925
1926pub struct BorderInfo {
1928 pub widths: crate::solver3::display_list::StyleBorderWidths,
1929 pub colors: crate::solver3::display_list::StyleBorderColors,
1930 pub styles: crate::solver3::display_list::StyleBorderStyles,
1931}
1932
1933pub fn get_border_info(
1934 styled_dom: &StyledDom,
1935 node_id: NodeId,
1936 node_state: &StyledNodeState,
1937) -> BorderInfo {
1938 use crate::solver3::display_list::{StyleBorderColors, StyleBorderStyles, StyleBorderWidths};
1939 use azul_css::css::CssPropertyValue;
1940 use azul_css::props::basic::color::ColorU;
1941 use azul_css::props::basic::pixel::PixelValue;
1942 use azul_css::props::style::{
1943 LayoutBorderTopWidth, LayoutBorderRightWidth,
1944 LayoutBorderBottomWidth, LayoutBorderLeftWidth,
1945 };
1946 use azul_css::props::style::border::{
1947 BorderStyle, StyleBorderTopColor, StyleBorderRightColor,
1948 StyleBorderBottomColor, StyleBorderLeftColor,
1949 StyleBorderTopStyle, StyleBorderRightStyle,
1950 StyleBorderBottomStyle, StyleBorderLeftStyle,
1951 };
1952
1953 if node_state.is_normal() {
1955 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
1956 let idx = node_id.index();
1957
1958 let make_width_px = |raw: i16| -> Option<PixelValue> {
1964 if raw == azul_css::compact_cache::I16_AUTO
1965 || raw == azul_css::compact_cache::I16_INITIAL
1966 || raw >= azul_css::compact_cache::I16_SENTINEL_THRESHOLD
1967 {
1968 None
1969 } else {
1970 Some(PixelValue::px(raw as f32 / 10.0))
1971 }
1972 };
1973 let widths = StyleBorderWidths {
1974 top: make_width_px(cc.get_border_top_width_raw(idx))
1975 .map(|px| CssPropertyValue::Exact(LayoutBorderTopWidth { inner: px })),
1976 right: make_width_px(cc.get_border_right_width_raw(idx))
1977 .map(|px| CssPropertyValue::Exact(LayoutBorderRightWidth { inner: px })),
1978 bottom: make_width_px(cc.get_border_bottom_width_raw(idx))
1979 .map(|px| CssPropertyValue::Exact(LayoutBorderBottomWidth { inner: px })),
1980 left: make_width_px(cc.get_border_left_width_raw(idx))
1981 .map(|px| CssPropertyValue::Exact(LayoutBorderLeftWidth { inner: px })),
1982 };
1983
1984 let make_color = |raw: u32| -> Option<ColorU> {
1986 if raw == 0 { None } else {
1987 Some(ColorU {
1988 r: ((raw >> 24) & 0xFF) as u8,
1989 g: ((raw >> 16) & 0xFF) as u8,
1990 b: ((raw >> 8) & 0xFF) as u8,
1991 a: (raw & 0xFF) as u8,
1992 })
1993 }
1994 };
1995
1996 let colors = StyleBorderColors {
1997 top: make_color(cc.get_border_top_color_raw(idx))
1998 .map(|c| CssPropertyValue::Exact(StyleBorderTopColor { inner: c })),
1999 right: make_color(cc.get_border_right_color_raw(idx))
2000 .map(|c| CssPropertyValue::Exact(StyleBorderRightColor { inner: c })),
2001 bottom: make_color(cc.get_border_bottom_color_raw(idx))
2002 .map(|c| CssPropertyValue::Exact(StyleBorderBottomColor { inner: c })),
2003 left: make_color(cc.get_border_left_color_raw(idx))
2004 .map(|c| CssPropertyValue::Exact(StyleBorderLeftColor { inner: c })),
2005 };
2006
2007 let styles = StyleBorderStyles {
2009 top: Some(CssPropertyValue::Exact(StyleBorderTopStyle {
2010 inner: cc.get_border_top_style(idx),
2011 })),
2012 right: Some(CssPropertyValue::Exact(StyleBorderRightStyle {
2013 inner: cc.get_border_right_style(idx),
2014 })),
2015 bottom: Some(CssPropertyValue::Exact(StyleBorderBottomStyle {
2016 inner: cc.get_border_bottom_style(idx),
2017 })),
2018 left: Some(CssPropertyValue::Exact(StyleBorderLeftStyle {
2019 inner: cc.get_border_left_style(idx),
2020 })),
2021 };
2022
2023 return BorderInfo { widths, colors, styles };
2024 }
2025 }
2026
2027 let node_data = &styled_dom.node_data.as_container()[node_id];
2029
2030 let widths = StyleBorderWidths {
2032 top: styled_dom
2033 .css_property_cache
2034 .ptr
2035 .get_border_top_width(node_data, &node_id, node_state)
2036 .cloned(),
2037 right: styled_dom
2038 .css_property_cache
2039 .ptr
2040 .get_border_right_width(node_data, &node_id, node_state)
2041 .cloned(),
2042 bottom: styled_dom
2043 .css_property_cache
2044 .ptr
2045 .get_border_bottom_width(node_data, &node_id, node_state)
2046 .cloned(),
2047 left: styled_dom
2048 .css_property_cache
2049 .ptr
2050 .get_border_left_width(node_data, &node_id, node_state)
2051 .cloned(),
2052 };
2053
2054 let colors = StyleBorderColors {
2056 top: styled_dom
2057 .css_property_cache
2058 .ptr
2059 .get_border_top_color(node_data, &node_id, node_state)
2060 .cloned(),
2061 right: styled_dom
2062 .css_property_cache
2063 .ptr
2064 .get_border_right_color(node_data, &node_id, node_state)
2065 .cloned(),
2066 bottom: styled_dom
2067 .css_property_cache
2068 .ptr
2069 .get_border_bottom_color(node_data, &node_id, node_state)
2070 .cloned(),
2071 left: styled_dom
2072 .css_property_cache
2073 .ptr
2074 .get_border_left_color(node_data, &node_id, node_state)
2075 .cloned(),
2076 };
2077
2078 let styles = StyleBorderStyles {
2080 top: styled_dom
2081 .css_property_cache
2082 .ptr
2083 .get_border_top_style(node_data, &node_id, node_state)
2084 .cloned(),
2085 right: styled_dom
2086 .css_property_cache
2087 .ptr
2088 .get_border_right_style(node_data, &node_id, node_state)
2089 .cloned(),
2090 bottom: styled_dom
2091 .css_property_cache
2092 .ptr
2093 .get_border_bottom_style(node_data, &node_id, node_state)
2094 .cloned(),
2095 left: styled_dom
2096 .css_property_cache
2097 .ptr
2098 .get_border_left_style(node_data, &node_id, node_state)
2099 .cloned(),
2100 };
2101
2102 BorderInfo {
2103 widths,
2104 colors,
2105 styles,
2106 }
2107}
2108
2109pub fn get_inline_border_info(
2114 styled_dom: &StyledDom,
2115 node_id: NodeId,
2116 node_state: &StyledNodeState,
2117 border_info: &BorderInfo,
2118) -> Option<crate::text3::cache::InlineBorderInfo> {
2119 use crate::text3::cache::InlineBorderInfo;
2120
2121 fn get_border_width_px(
2123 width: &Option<
2124 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderTopWidth>,
2125 >,
2126 ) -> f32 {
2127 width
2128 .as_ref()
2129 .and_then(|v| v.get_property())
2130 .map(|w| w.inner.number.get())
2131 .unwrap_or(0.0)
2132 }
2133
2134 fn get_border_width_px_right(
2135 width: &Option<
2136 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderRightWidth>,
2137 >,
2138 ) -> f32 {
2139 width
2140 .as_ref()
2141 .and_then(|v| v.get_property())
2142 .map(|w| w.inner.number.get())
2143 .unwrap_or(0.0)
2144 }
2145
2146 fn get_border_width_px_bottom(
2147 width: &Option<
2148 azul_css::css::CssPropertyValue<
2149 azul_css::props::style::border::LayoutBorderBottomWidth,
2150 >,
2151 >,
2152 ) -> f32 {
2153 width
2154 .as_ref()
2155 .and_then(|v| v.get_property())
2156 .map(|w| w.inner.number.get())
2157 .unwrap_or(0.0)
2158 }
2159
2160 fn get_border_width_px_left(
2161 width: &Option<
2162 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderLeftWidth>,
2163 >,
2164 ) -> f32 {
2165 width
2166 .as_ref()
2167 .and_then(|v| v.get_property())
2168 .map(|w| w.inner.number.get())
2169 .unwrap_or(0.0)
2170 }
2171
2172 fn get_border_color_top(
2174 color: &Option<
2175 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderTopColor>,
2176 >,
2177 ) -> ColorU {
2178 color
2179 .as_ref()
2180 .and_then(|v| v.get_property())
2181 .map(|c| c.inner)
2182 .unwrap_or(ColorU::BLACK)
2183 }
2184
2185 fn get_border_color_right(
2186 color: &Option<
2187 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderRightColor>,
2188 >,
2189 ) -> ColorU {
2190 color
2191 .as_ref()
2192 .and_then(|v| v.get_property())
2193 .map(|c| c.inner)
2194 .unwrap_or(ColorU::BLACK)
2195 }
2196
2197 fn get_border_color_bottom(
2198 color: &Option<
2199 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderBottomColor>,
2200 >,
2201 ) -> ColorU {
2202 color
2203 .as_ref()
2204 .and_then(|v| v.get_property())
2205 .map(|c| c.inner)
2206 .unwrap_or(ColorU::BLACK)
2207 }
2208
2209 fn get_border_color_left(
2210 color: &Option<
2211 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderLeftColor>,
2212 >,
2213 ) -> ColorU {
2214 color
2215 .as_ref()
2216 .and_then(|v| v.get_property())
2217 .map(|c| c.inner)
2218 .unwrap_or(ColorU::BLACK)
2219 }
2220
2221 fn get_border_radius_px(
2223 styled_dom: &StyledDom,
2224 node_id: NodeId,
2225 node_state: &StyledNodeState,
2226 ) -> Option<f32> {
2227 let node_data = &styled_dom.node_data.as_container()[node_id];
2228
2229 let top_left = styled_dom
2230 .css_property_cache
2231 .ptr
2232 .get_border_top_left_radius(node_data, &node_id, node_state)
2233 .and_then(|br| br.get_property().cloned())
2234 .map(|v| v.inner.number.get());
2235
2236 let top_right = styled_dom
2237 .css_property_cache
2238 .ptr
2239 .get_border_top_right_radius(node_data, &node_id, node_state)
2240 .and_then(|br| br.get_property().cloned())
2241 .map(|v| v.inner.number.get());
2242
2243 let bottom_left = styled_dom
2244 .css_property_cache
2245 .ptr
2246 .get_border_bottom_left_radius(node_data, &node_id, node_state)
2247 .and_then(|br| br.get_property().cloned())
2248 .map(|v| v.inner.number.get());
2249
2250 let bottom_right = styled_dom
2251 .css_property_cache
2252 .ptr
2253 .get_border_bottom_right_radius(node_data, &node_id, node_state)
2254 .and_then(|br| br.get_property().cloned())
2255 .map(|v| v.inner.number.get());
2256
2257 let radii: Vec<f32> = [top_left, top_right, bottom_left, bottom_right]
2259 .into_iter()
2260 .filter_map(|r| r)
2261 .collect();
2262
2263 if radii.is_empty() {
2264 None
2265 } else {
2266 Some(radii.into_iter().fold(0.0f32, |a, b| a.max(b)))
2267 }
2268 }
2269
2270 let top = get_border_width_px(&border_info.widths.top);
2271 let right = get_border_width_px_right(&border_info.widths.right);
2272 let bottom = get_border_width_px_bottom(&border_info.widths.bottom);
2273 let left = get_border_width_px_left(&border_info.widths.left);
2274
2275 fn resolve_padding(mv: MultiValue<PixelValue>) -> f32 {
2277 match mv {
2278 MultiValue::Exact(pv) => {
2279 super::calc::resolve_pixel_value(&pv, 0.0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE)
2280 }
2281 _ => 0.0,
2282 }
2283 }
2284
2285 let p_top = resolve_padding(get_css_padding_top(styled_dom, node_id, node_state));
2286 let p_right = resolve_padding(get_css_padding_right(styled_dom, node_id, node_state));
2287 let p_bottom = resolve_padding(get_css_padding_bottom(styled_dom, node_id, node_state));
2288 let p_left = resolve_padding(get_css_padding_left(styled_dom, node_id, node_state));
2289
2290 let has_border = top > 0.0 || right > 0.0 || bottom > 0.0 || left > 0.0;
2292 let has_padding = p_top > 0.0 || p_right > 0.0 || p_bottom > 0.0 || p_left > 0.0;
2293 if !has_border && !has_padding {
2294 return None;
2295 }
2296
2297 let is_rtl = matches!(
2299 get_direction_property(styled_dom, node_id, node_state),
2300 MultiValue::Exact(StyleDirection::Rtl)
2301 );
2302
2303 Some(InlineBorderInfo {
2304 top,
2305 right,
2306 bottom,
2307 left,
2308 top_color: get_border_color_top(&border_info.colors.top),
2309 right_color: get_border_color_right(&border_info.colors.right),
2310 bottom_color: get_border_color_bottom(&border_info.colors.bottom),
2311 left_color: get_border_color_left(&border_info.colors.left),
2312 radius: get_border_radius_px(styled_dom, node_id, node_state),
2313 padding_top: p_top,
2314 padding_right: p_right,
2315 padding_bottom: p_bottom,
2316 padding_left: p_left,
2317 is_first_fragment: true,
2318 is_last_fragment: true,
2319 is_rtl,
2320 })
2321}
2322
2323#[derive(Debug, Clone, Copy, Default)]
2327pub struct SelectionStyle {
2328 pub bg_color: ColorU,
2330 pub text_color: Option<ColorU>,
2332 pub radius: f32,
2334}
2335
2336pub fn get_selection_style(
2338 styled_dom: &StyledDom,
2339 node_id: Option<NodeId>,
2340 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
2341) -> SelectionStyle {
2342 let Some(node_id) = node_id else {
2343 return SelectionStyle::default();
2344 };
2345
2346 let node_data = &styled_dom.node_data.as_container()[node_id];
2347 let node_state = &StyledNodeState::default();
2348
2349 let default_bg = system_style
2351 .and_then(|ss| ss.colors.selection_background.as_option().copied())
2352 .unwrap_or(ColorU {
2353 r: 51,
2354 g: 153,
2355 b: 255, a: 128, });
2358
2359 let bg_color = styled_dom
2360 .css_property_cache
2361 .ptr
2362 .get_selection_background_color(node_data, &node_id, node_state)
2363 .and_then(|c| c.get_property().cloned())
2364 .map(|c| c.inner)
2365 .unwrap_or(default_bg);
2366
2367 let default_text = system_style
2369 .and_then(|ss| ss.colors.selection_text.as_option().copied());
2370
2371 let text_color = styled_dom
2372 .css_property_cache
2373 .ptr
2374 .get_selection_color(node_data, &node_id, node_state)
2375 .and_then(|c| c.get_property().cloned())
2376 .map(|c| c.inner)
2377 .or(default_text);
2378
2379 let radius = styled_dom
2380 .css_property_cache
2381 .ptr
2382 .get_selection_radius(node_data, &node_id, node_state)
2383 .and_then(|r| r.get_property().cloned())
2384 .map(|r| r.inner.to_pixels_internal(0.0, 16.0, 16.0)) .unwrap_or(0.0);
2386
2387 SelectionStyle {
2388 bg_color,
2389 text_color,
2390 radius,
2391 }
2392}
2393
2394#[derive(Debug, Clone, Copy)]
2396pub struct CaretStyle {
2397 pub color: ColorU,
2399 pub width: f32,
2401 pub animation_duration: u32,
2403}
2404
2405impl Default for CaretStyle {
2406 fn default() -> Self {
2407 Self {
2408 color: ColorU::BLACK,
2409 width: 2.0,
2410 animation_duration: 500,
2411 }
2412 }
2413}
2414
2415pub fn get_caret_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> CaretStyle {
2417 let Some(node_id) = node_id else {
2418 return CaretStyle::default();
2419 };
2420
2421 let node_data = &styled_dom.node_data.as_container()[node_id];
2422 let node_state = &StyledNodeState::default();
2423
2424 let color = styled_dom
2425 .css_property_cache
2426 .ptr
2427 .get_caret_color(node_data, &node_id, node_state)
2428 .and_then(|c| c.get_property().cloned())
2429 .map(|c| c.inner)
2430 .unwrap_or(ColorU::BLACK);
2431
2432 let width = styled_dom
2433 .css_property_cache
2434 .ptr
2435 .get_caret_width(node_data, &node_id, node_state)
2436 .and_then(|w| w.get_property().cloned())
2437 .map(|w| w.inner.to_pixels_internal(0.0, 16.0, 16.0)) .unwrap_or(2.0); let animation_duration = styled_dom
2441 .css_property_cache
2442 .ptr
2443 .get_caret_animation_duration(node_data, &node_id, node_state)
2444 .and_then(|d| d.get_property().cloned())
2445 .map(|d| d.inner.inner) .unwrap_or(500); CaretStyle {
2449 color,
2450 width,
2451 animation_duration,
2452 }
2453}
2454
2455pub fn get_scrollbar_info_from_layout(node: &LayoutNode) -> ScrollbarRequirements {
2467 node.scrollbar_info
2468 .clone()
2469 .unwrap_or_default()
2470}
2471
2472pub fn get_layout_scrollbar_width_px<T: crate::font_traits::ParsedFontTrait>(
2487 ctx: &crate::solver3::LayoutContext<'_, T>,
2488 dom_id: NodeId,
2489 styled_node_state: &StyledNodeState,
2490) -> f32 {
2491 let style = get_scrollbar_style(
2496 ctx.styled_dom,
2497 dom_id,
2498 styled_node_state,
2499 ctx.system_style.as_deref(),
2500 );
2501 style.reserve_width_px
2502}
2503
2504get_css_property!(
2505 get_display_property_internal,
2506 get_display,
2507 LayoutDisplay,
2508 azul_css::props::property::CssPropertyType::Display,
2509 compact = get_display
2510);
2511
2512pub fn get_display_property(
2513 styled_dom: &StyledDom,
2514 dom_id: Option<NodeId>,
2515) -> MultiValue<LayoutDisplay> {
2516 let Some(id) = dom_id else {
2517 return MultiValue::Exact(LayoutDisplay::Inline);
2518 };
2519 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
2520 get_display_property_internal(styled_dom, id, node_state)
2521}
2522
2523pub fn blockify_display(raw_display: LayoutDisplay) -> LayoutDisplay {
2529 match raw_display {
2530 LayoutDisplay::Inline => LayoutDisplay::Block,
2532 LayoutDisplay::InlineBlock => LayoutDisplay::Block,
2535 LayoutDisplay::InlineFlex => LayoutDisplay::Flex,
2536 LayoutDisplay::InlineTable => LayoutDisplay::Table,
2537 LayoutDisplay::InlineGrid => LayoutDisplay::Grid,
2538 LayoutDisplay::TableRowGroup
2541 | LayoutDisplay::TableColumn
2542 | LayoutDisplay::TableColumnGroup
2543 | LayoutDisplay::TableHeaderGroup
2544 | LayoutDisplay::TableFooterGroup
2545 | LayoutDisplay::TableRow
2546 | LayoutDisplay::TableCell
2547 | LayoutDisplay::TableCaption => LayoutDisplay::Block,
2548 other => other,
2550 }
2551}
2552
2553pub fn get_computed_display(
2563 raw_display: LayoutDisplay,
2564 is_absolute_or_fixed: bool,
2565 is_floated: bool,
2566 is_root: bool,
2567 is_flex_grid_child: bool,
2568) -> LayoutDisplay {
2569 if raw_display == LayoutDisplay::None {
2570 return LayoutDisplay::None;
2571 }
2572 if is_absolute_or_fixed || is_floated || is_root || is_flex_grid_child {
2574 blockify_display(raw_display)
2575 } else {
2576 raw_display
2577 }
2578}
2579
2580pub fn get_vertical_align_for_node(
2585 styled_dom: &StyledDom,
2586 dom_id: NodeId,
2587) -> crate::text3::cache::VerticalAlign {
2588 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
2589 let va = match get_vertical_align_property(styled_dom, dom_id, node_state) {
2590 MultiValue::Exact(v) => v,
2591 _ => StyleVerticalAlign::default(),
2592 };
2593 match va {
2594 StyleVerticalAlign::Baseline => crate::text3::cache::VerticalAlign::Baseline,
2595 StyleVerticalAlign::Top => crate::text3::cache::VerticalAlign::Top,
2596 StyleVerticalAlign::Middle => crate::text3::cache::VerticalAlign::Middle,
2597 StyleVerticalAlign::Bottom => crate::text3::cache::VerticalAlign::Bottom,
2598 StyleVerticalAlign::Sub => crate::text3::cache::VerticalAlign::Sub,
2599 StyleVerticalAlign::Superscript => crate::text3::cache::VerticalAlign::Super,
2600 StyleVerticalAlign::TextTop => crate::text3::cache::VerticalAlign::TextTop,
2601 StyleVerticalAlign::TextBottom => crate::text3::cache::VerticalAlign::TextBottom,
2602 StyleVerticalAlign::Percentage(p) => {
2604 let font_size = get_element_font_size(styled_dom, dom_id, node_state);
2605 let line_height = get_line_height_value(styled_dom, dom_id, node_state)
2606 .map(|lh| lh.inner.normalized() * font_size)
2607 .unwrap_or(font_size * 1.2);
2608 crate::text3::cache::VerticalAlign::Offset(p.normalized() * line_height)
2609 }
2610 StyleVerticalAlign::Length(l) => {
2612 let font_size = get_element_font_size(styled_dom, dom_id, node_state);
2613 let px = super::calc::resolve_pixel_value(&l, 0.0, font_size, font_size);
2614 crate::text3::cache::VerticalAlign::Offset(px)
2615 }
2616 }
2617}
2618
2619pub fn get_style_properties(
2620 styled_dom: &StyledDom,
2621 dom_id: NodeId,
2622 system_style: Option<&std::sync::Arc<azul_css::system::SystemStyle>>,
2623 viewport_size: azul_css::props::basic::PhysicalSize,
2624) -> StyleProperties {
2625 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
2626
2627 let node_data = &styled_dom.node_data.as_container()[dom_id];
2628 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
2629 let cache = &styled_dom.css_property_cache.ptr;
2630
2631 use azul_css::props::basic::font::{StyleFontFamily, StyleFontFamilyVec};
2632
2633 let font_families = if node_state.is_normal() {
2636 cache.compact_cache.as_ref()
2637 .and_then(|cc| {
2638 let fh = cc.tier2b_text[dom_id.index()].font_family_hash;
2639 if fh == 0 { return None; }
2640 cc.font_hash_to_families.get(&fh).cloned()
2641 })
2642 .unwrap_or_else(|| {
2643 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2644 })
2645 } else {
2646 cache
2647 .get_font_family(node_data, &dom_id, node_state)
2648 .and_then(|v| v.get_property().cloned())
2649 .unwrap_or_else(|| {
2650 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2651 })
2652 };
2653
2654 let parent_font_size = get_parent_font_size(styled_dom, dom_id, node_state);
2660
2661 let root_font_size = get_root_font_size(styled_dom, node_state);
2662
2663 let font_size_context = ResolutionContext {
2665 element_font_size: azul_css::props::basic::pixel::DEFAULT_FONT_SIZE, parent_font_size,
2667 root_font_size,
2668 containing_block_size: PhysicalSize::new(0.0, 0.0),
2669 element_size: None,
2670 viewport_size,
2671 };
2672
2673 let font_size = {
2677 let mut fast_font_size: Option<f32> = None;
2682 let mut compact_said_inherit = false;
2683 if node_state.is_normal() {
2684 if let Some(ref cc) = cache.compact_cache {
2685 let raw = cc.get_font_size_raw(dom_id.index());
2686 if raw == azul_css::compact_cache::U32_SENTINEL
2687 || raw == azul_css::compact_cache::U32_INHERIT
2688 || raw == azul_css::compact_cache::U32_INITIAL
2689 {
2690 compact_said_inherit = true;
2691 } else if let Some(pv) = azul_css::compact_cache::decode_pixel_value_u32(raw) {
2692 fast_font_size = Some(pv.resolve_with_context(
2693 &font_size_context,
2694 PropertyContext::FontSize,
2695 ));
2696 }
2697 }
2698 }
2699 if let Some(fs) = fast_font_size {
2700 fs
2701 } else if compact_said_inherit {
2702 parent_font_size
2703 } else {
2704 cache
2705 .get_font_size(node_data, &dom_id, node_state)
2706 .and_then(|v| v.get_property().cloned())
2707 .map(|v| {
2708 v.inner
2709 .resolve_with_context(&font_size_context, PropertyContext::FontSize)
2710 })
2711 .unwrap_or(parent_font_size)
2712 }
2713 };
2714
2715 let color_from_cache = {
2716 let mut fast_color = None;
2718 if node_state.is_normal() {
2719 if let Some(ref cc) = cache.compact_cache {
2720 let raw = cc.get_text_color_raw(dom_id.index());
2721 if raw != 0 {
2722 fast_color = Some(ColorU {
2724 r: (raw >> 24) as u8,
2725 g: (raw >> 16) as u8,
2726 b: (raw >> 8) as u8,
2727 a: raw as u8,
2728 });
2729 }
2730 }
2731 }
2732 fast_color.or_else(|| {
2733 cache
2734 .get_text_color(node_data, &dom_id, node_state)
2735 .and_then(|v| v.get_property().cloned())
2736 .map(|v| v.inner)
2737 })
2738 };
2739
2740 let color = color_from_cache.unwrap_or(ColorU::BLACK);
2746
2747 let line_height = {
2749 let mut fast_lh = None;
2758 let mut sentinel_normal = false;
2759 if node_state.is_normal() {
2760 if let Some(ref cc) = cache.compact_cache {
2761 if let Some(normalized) = cc.get_line_height(dom_id.index()) {
2762 fast_lh = Some(crate::text3::cache::LineHeight::Px(normalized / 100.0 * font_size));
2763 } else {
2764 sentinel_normal = true;
2766 }
2767 }
2768 }
2769 if sentinel_normal {
2770 crate::text3::cache::LineHeight::Normal
2771 } else {
2772 fast_lh.unwrap_or_else(|| {
2773 cache
2774 .get_line_height(node_data, &dom_id, node_state)
2775 .and_then(|v| v.get_property().cloned())
2776 .map(|v| crate::text3::cache::LineHeight::Px(v.inner.normalized() * font_size))
2777 .unwrap_or(crate::text3::cache::LineHeight::Normal)
2778 })
2779 }
2780 };
2781
2782 use azul_css::props::layout::LayoutDisplay;
2793 let display = match get_display_property(styled_dom, Some(dom_id)) {
2794 MultiValue::Exact(v) => v,
2795 _ => LayoutDisplay::Inline,
2796 };
2797
2798 let (background_color, background_content, border) =
2801 if matches!(display, LayoutDisplay::Inline | LayoutDisplay::InlineBlock) {
2802 let bg = get_background_color(styled_dom, dom_id, node_state);
2803 let bg_color = if bg.a > 0 { Some(bg) } else { None };
2804
2805 let bg_contents = get_background_contents(styled_dom, dom_id, node_state);
2807
2808 let border_info = get_border_info(styled_dom, dom_id, node_state);
2810 let inline_border =
2811 get_inline_border_info(styled_dom, dom_id, node_state, &border_info);
2812
2813 (bg_color, bg_contents, inline_border)
2814 } else {
2815 (None, Vec::new(), None)
2818 };
2819
2820 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
2822 MultiValue::Exact(v) => v,
2823 _ => StyleFontWeight::Normal,
2824 };
2825
2826 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
2828 MultiValue::Exact(v) => v,
2829 _ => StyleFontStyle::Normal,
2830 };
2831
2832 let fc_weight = super::fc::convert_font_weight(font_weight);
2834 let fc_style = super::fc::convert_font_style(font_style);
2835
2836 let font_stack = {
2839 let font_ref = (0..font_families.len())
2841 .find_map(|i| {
2842 match font_families.get(i).unwrap() {
2843 azul_css::props::basic::font::StyleFontFamily::Ref(r) => Some(r.clone()),
2844 _ => None,
2845 }
2846 });
2847
2848 let platform = system_style.map(|ss| &ss.platform);
2850
2851 if let Some(font_ref) = font_ref {
2852 FontStack::Ref(font_ref)
2854 } else {
2855 let mut stack = Vec::with_capacity(font_families.len() + 3);
2857
2858 for i in 0..font_families.len() {
2859 let family = font_families.get(i).unwrap();
2860
2861 if let azul_css::props::basic::font::StyleFontFamily::SystemType(system_type) = family {
2864 if let Some(platform) = platform {
2865 let font_names = system_type.get_fallback_chain(platform);
2866 let system_weight = if system_type.is_bold() {
2867 rust_fontconfig::FcWeight::Bold
2868 } else {
2869 fc_weight
2870 };
2871 let system_style_val = if system_type.is_italic() {
2872 crate::text3::cache::FontStyle::Italic
2873 } else {
2874 fc_style
2875 };
2876 for font_name in font_names {
2877 stack.push(crate::text3::cache::FontSelector {
2878 family: font_name.to_string(),
2879 weight: system_weight,
2880 style: system_style_val,
2881 unicode_ranges: Vec::new(),
2882 });
2883 }
2884 } else {
2885 stack.push(crate::text3::cache::FontSelector {
2887 family: "sans-serif".to_string(),
2888 weight: fc_weight,
2889 style: fc_style,
2890 unicode_ranges: Vec::new(),
2891 });
2892 }
2893 } else {
2894 stack.push(crate::text3::cache::FontSelector {
2895 family: family.as_string(),
2896 weight: fc_weight,
2897 style: fc_style,
2898 unicode_ranges: Vec::new(),
2899 });
2900 }
2901 }
2902
2903 let generic_fallbacks = ["sans-serif", "serif", "monospace"];
2905 for fallback in &generic_fallbacks {
2906 if !stack
2907 .iter()
2908 .any(|f| f.family.to_lowercase() == fallback.to_lowercase())
2909 {
2910 stack.push(crate::text3::cache::FontSelector {
2911 family: fallback.to_string(),
2912 weight: rust_fontconfig::FcWeight::Normal,
2913 style: crate::text3::cache::FontStyle::Normal,
2914 unicode_ranges: Vec::new(),
2915 });
2916 }
2917 }
2918
2919 FontStack::Stack(stack)
2920 }
2921 };
2922
2923 let letter_spacing = {
2925 let mut fast_ls = None;
2927 if node_state.is_normal() {
2928 if let Some(ref cc) = cache.compact_cache {
2929 if let Some(px_val) = cc.get_letter_spacing(dom_id.index()) {
2930 fast_ls = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
2931 }
2932 }
2933 }
2934 fast_ls.unwrap_or_else(|| {
2935 cache
2936 .get_letter_spacing(node_data, &dom_id, node_state)
2937 .and_then(|v| v.get_property().cloned())
2938 .map(|v| {
2939 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
2940 crate::text3::cache::Spacing::Px(px_value.round() as i32)
2941 })
2942 .unwrap_or_default()
2943 })
2944 };
2945
2946 let word_spacing = {
2948 let mut fast_ws = None;
2950 if node_state.is_normal() {
2951 if let Some(ref cc) = cache.compact_cache {
2952 if let Some(px_val) = cc.get_word_spacing(dom_id.index()) {
2953 fast_ws = Some(crate::text3::cache::Spacing::Px(px_val.round() as i32));
2954 }
2955 }
2956 }
2957 fast_ws.unwrap_or_else(|| {
2958 cache
2959 .get_word_spacing(node_data, &dom_id, node_state)
2960 .and_then(|v| v.get_property().cloned())
2961 .map(|v| {
2962 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
2963 crate::text3::cache::Spacing::Px(px_value.round() as i32)
2964 })
2965 .unwrap_or_default()
2966 })
2967 };
2968
2969 let text_decoration = {
2976 let mut skip_walk = false;
2977 if node_state.is_normal() {
2978 if let Some(ref cc) = cache.compact_cache {
2979 if !cc.has_text_decoration(dom_id.index()) {
2980 skip_walk = true;
2981 }
2982 }
2983 }
2984 if skip_walk {
2985 crate::text3::cache::TextDecoration::default()
2986 } else {
2987 cache
2988 .get_text_decoration(node_data, &dom_id, node_state)
2989 .and_then(|v| v.get_property().cloned())
2990 .map(|v| crate::text3::cache::TextDecoration::from_css(v))
2991 .unwrap_or_default()
2992 }
2993 };
2994
2995 let tab_size = {
3007 let mut fast_tab = None;
3008 if node_state.is_normal() {
3009 if let Some(ref cc) = cache.compact_cache {
3010 let raw = cc.get_tab_size_raw(dom_id.index());
3011 if raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD {
3012 fast_tab = Some(raw as f32 / 10.0);
3013 } else {
3014 fast_tab = Some(8.0);
3016 }
3017 }
3018 }
3019 fast_tab.unwrap_or_else(|| {
3020 cache
3021 .get_tab_size(node_data, &dom_id, node_state)
3022 .and_then(|v| v.get_property().cloned())
3023 .map(|v| v.inner.number.get())
3024 .unwrap_or(8.0)
3025 })
3026 };
3027
3028 let properties = StyleProperties {
3029 font_stack,
3030 font_size_px: font_size,
3031 color,
3032 background_color,
3033 background_content,
3034 border,
3035 line_height,
3036 letter_spacing,
3037 word_spacing,
3038 text_decoration,
3039 tab_size,
3040 ..Default::default()
3044 };
3045
3046 properties
3047}
3048
3049pub fn get_list_style_type(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> StyleListStyleType {
3050 let Some(id) = dom_id else {
3051 return StyleListStyleType::default();
3052 };
3053 let node_data = &styled_dom.node_data.as_container()[id];
3054 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3055 styled_dom
3056 .css_property_cache
3057 .ptr
3058 .get_list_style_type(node_data, &id, node_state)
3059 .and_then(|v| v.get_property().copied())
3060 .unwrap_or_default()
3061}
3062
3063pub fn get_list_style_position(
3064 styled_dom: &StyledDom,
3065 dom_id: Option<NodeId>,
3066) -> StyleListStylePosition {
3067 let Some(id) = dom_id else {
3068 return StyleListStylePosition::default();
3069 };
3070 let node_data = &styled_dom.node_data.as_container()[id];
3071 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3072 styled_dom
3073 .css_property_cache
3074 .ptr
3075 .get_list_style_position(node_data, &id, node_state)
3076 .and_then(|v| v.get_property().copied())
3077 .unwrap_or_default()
3078}
3079
3080use azul_css::props::layout::{
3083 LayoutInsetBottom, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight,
3084 LayoutMarginTop, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth,
3085 LayoutPaddingBottom, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutRight,
3086 LayoutTop,
3087};
3088
3089get_css_property_pixel!(
3091 get_css_left,
3092 get_left,
3093 azul_css::props::property::CssPropertyType::Left,
3094 compact_i16 = get_left
3095);
3096get_css_property_pixel!(
3097 get_css_right,
3098 get_right,
3099 azul_css::props::property::CssPropertyType::Right,
3100 compact_i16 = get_right
3101);
3102get_css_property_pixel!(
3103 get_css_top,
3104 get_top,
3105 azul_css::props::property::CssPropertyType::Top,
3106 compact_i16 = get_top
3107);
3108get_css_property_pixel!(
3109 get_css_bottom,
3110 get_bottom,
3111 azul_css::props::property::CssPropertyType::Bottom,
3112 compact_i16 = get_bottom
3113);
3114
3115get_css_property_pixel!(
3117 get_css_margin_left,
3118 get_margin_left,
3119 azul_css::props::property::CssPropertyType::MarginLeft,
3120 compact_i16 = get_margin_left_raw
3121);
3122get_css_property_pixel!(
3123 get_css_margin_right,
3124 get_margin_right,
3125 azul_css::props::property::CssPropertyType::MarginRight,
3126 compact_i16 = get_margin_right_raw
3127);
3128get_css_property_pixel!(
3129 get_css_margin_top,
3130 get_margin_top,
3131 azul_css::props::property::CssPropertyType::MarginTop,
3132 compact_i16 = get_margin_top_raw
3133);
3134get_css_property_pixel!(
3135 get_css_margin_bottom,
3136 get_margin_bottom,
3137 azul_css::props::property::CssPropertyType::MarginBottom,
3138 compact_i16 = get_margin_bottom_raw
3139);
3140
3141get_css_property_pixel!(
3143 get_css_padding_left,
3144 get_padding_left,
3145 azul_css::props::property::CssPropertyType::PaddingLeft,
3146 compact_i16 = get_padding_left_raw
3147);
3148get_css_property_pixel!(
3149 get_css_padding_right,
3150 get_padding_right,
3151 azul_css::props::property::CssPropertyType::PaddingRight,
3152 compact_i16 = get_padding_right_raw
3153);
3154get_css_property_pixel!(
3155 get_css_padding_top,
3156 get_padding_top,
3157 azul_css::props::property::CssPropertyType::PaddingTop,
3158 compact_i16 = get_padding_top_raw
3159);
3160get_css_property_pixel!(
3161 get_css_padding_bottom,
3162 get_padding_bottom,
3163 azul_css::props::property::CssPropertyType::PaddingBottom,
3164 compact_i16 = get_padding_bottom_raw
3165);
3166
3167get_css_property!(
3169 get_css_min_width,
3170 get_min_width,
3171 LayoutMinWidth,
3172 azul_css::props::property::CssPropertyType::MinWidth,
3173 compact_u32_struct = get_min_width_raw
3174);
3175
3176get_css_property!(
3177 get_css_min_height,
3178 get_min_height,
3179 LayoutMinHeight,
3180 azul_css::props::property::CssPropertyType::MinHeight,
3181 compact_u32_struct = get_min_height_raw
3182);
3183
3184get_css_property!(
3185 get_css_max_width,
3186 get_max_width,
3187 LayoutMaxWidth,
3188 azul_css::props::property::CssPropertyType::MaxWidth,
3189 compact_u32_struct = get_max_width_raw
3190);
3191
3192get_css_property!(
3193 get_css_max_height,
3194 get_max_height,
3195 LayoutMaxHeight,
3196 azul_css::props::property::CssPropertyType::MaxHeight,
3197 compact_u32_struct = get_max_height_raw
3198);
3199
3200get_css_property_pixel!(
3202 get_css_border_left_width,
3203 get_border_left_width,
3204 azul_css::props::property::CssPropertyType::BorderLeftWidth,
3205 compact_i16 = get_border_left_width_raw
3206);
3207get_css_property_pixel!(
3208 get_css_border_right_width,
3209 get_border_right_width,
3210 azul_css::props::property::CssPropertyType::BorderRightWidth,
3211 compact_i16 = get_border_right_width_raw
3212);
3213get_css_property_pixel!(
3214 get_css_border_top_width,
3215 get_border_top_width,
3216 azul_css::props::property::CssPropertyType::BorderTopWidth,
3217 compact_i16 = get_border_top_width_raw
3218);
3219get_css_property_pixel!(
3220 get_css_border_bottom_width,
3221 get_border_bottom_width,
3222 azul_css::props::property::CssPropertyType::BorderBottomWidth,
3223 compact_i16 = get_border_bottom_width_raw
3224);
3225
3226pub fn get_break_before(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
3230 let Some(id) = dom_id else {
3231 return PageBreak::Auto;
3232 };
3233 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3234 if node_state.is_normal() {
3236 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
3237 if !cc.has_break(id.index()) {
3238 return PageBreak::Auto;
3239 }
3240 }
3241 }
3242 let node_data = &styled_dom.node_data.as_container()[id];
3243 styled_dom
3244 .css_property_cache
3245 .ptr
3246 .get_break_before(node_data, &id, node_state)
3247 .and_then(|v| v.get_property().cloned())
3248 .unwrap_or(PageBreak::Auto)
3249}
3250
3251pub fn get_break_after(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
3253 let Some(id) = dom_id else {
3254 return PageBreak::Auto;
3255 };
3256 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3257 if node_state.is_normal() {
3258 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
3259 if !cc.has_break(id.index()) {
3260 return PageBreak::Auto;
3261 }
3262 }
3263 }
3264 let node_data = &styled_dom.node_data.as_container()[id];
3265 styled_dom
3266 .css_property_cache
3267 .ptr
3268 .get_break_after(node_data, &id, node_state)
3269 .and_then(|v| v.get_property().cloned())
3270 .unwrap_or(PageBreak::Auto)
3271}
3272
3273pub fn is_forced_page_break(page_break: PageBreak) -> bool {
3275 matches!(
3276 page_break,
3277 PageBreak::Always
3278 | PageBreak::Page
3279 | PageBreak::Left
3280 | PageBreak::Right
3281 | PageBreak::Recto
3282 | PageBreak::Verso
3283 | PageBreak::All
3284 )
3285}
3286
3287pub fn get_break_inside(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> BreakInside {
3289 let Some(id) = dom_id else {
3290 return BreakInside::Auto;
3291 };
3292 let node_data = &styled_dom.node_data.as_container()[id];
3293 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3294 styled_dom
3295 .css_property_cache
3296 .ptr
3297 .get_break_inside(node_data, &id, node_state)
3298 .and_then(|v| v.get_property().cloned())
3299 .unwrap_or(BreakInside::Auto)
3300}
3301
3302pub fn get_orphans(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
3304 let Some(id) = dom_id else {
3305 return 2; };
3307 let node_data = &styled_dom.node_data.as_container()[id];
3308 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3309 styled_dom
3310 .css_property_cache
3311 .ptr
3312 .get_orphans(node_data, &id, node_state)
3313 .and_then(|v| v.get_property().cloned())
3314 .map(|o| o.inner)
3315 .unwrap_or(2)
3316}
3317
3318pub fn get_widows(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
3320 let Some(id) = dom_id else {
3321 return 2; };
3323 let node_data = &styled_dom.node_data.as_container()[id];
3324 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3325 styled_dom
3326 .css_property_cache
3327 .ptr
3328 .get_widows(node_data, &id, node_state)
3329 .and_then(|v| v.get_property().cloned())
3330 .map(|w| w.inner)
3331 .unwrap_or(2)
3332}
3333
3334pub fn get_box_decoration_break(
3336 styled_dom: &StyledDom,
3337 dom_id: Option<NodeId>,
3338) -> BoxDecorationBreak {
3339 let Some(id) = dom_id else {
3340 return BoxDecorationBreak::Slice;
3341 };
3342 let node_data = &styled_dom.node_data.as_container()[id];
3343 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
3344 styled_dom
3345 .css_property_cache
3346 .ptr
3347 .get_box_decoration_break(node_data, &id, node_state)
3348 .and_then(|v| v.get_property().cloned())
3349 .unwrap_or(BoxDecorationBreak::Slice)
3350}
3351
3352pub fn is_avoid_page_break(page_break: &PageBreak) -> bool {
3356 matches!(page_break, PageBreak::Avoid | PageBreak::AvoidPage)
3357}
3358
3359pub fn is_avoid_break_inside(break_inside: &BreakInside) -> bool {
3361 matches!(
3362 break_inside,
3363 BreakInside::Avoid | BreakInside::AvoidPage | BreakInside::AvoidColumn
3364 )
3365}
3366
3367use std::collections::HashMap;
3370
3371use rust_fontconfig::{
3372 FcFontCache, FcWeight, FontFallbackChain, PatternMatch, UnicodeRange,
3373 DEFAULT_UNICODE_FALLBACK_SCRIPTS,
3374};
3375
3376use crate::text3::cache::{FontChainKey, FontChainKeyOrRef, FontSelector, FontStack, FontStyle};
3377
3378#[derive(Debug, Clone)]
3381pub struct CollectedFontStacks {
3382 pub font_stacks: Vec<Vec<FontSelector>>,
3384 pub hash_to_index: HashMap<u64, usize>,
3386 pub font_refs: HashMap<usize, azul_css::props::basic::font::FontRef>,
3389}
3390
3391#[derive(Debug, Clone)]
3394pub struct ResolvedFontChains {
3395 pub chains: HashMap<FontChainKeyOrRef, FontFallbackChain>,
3399}
3400
3401impl ResolvedFontChains {
3402 pub fn get(&self, key: &FontChainKeyOrRef) -> Option<&FontFallbackChain> {
3404 self.chains.get(key)
3405 }
3406
3407 pub fn get_by_chain_key(&self, key: &FontChainKey) -> Option<&FontFallbackChain> {
3409 self.chains.get(&FontChainKeyOrRef::Chain(key.clone()))
3410 }
3411
3412 pub fn get_for_font_stack(&self, font_stack: &[FontSelector]) -> Option<&FontFallbackChain> {
3414 let key = FontChainKeyOrRef::Chain(FontChainKey::from_selectors(font_stack));
3415 self.chains.get(&key)
3416 }
3417
3418 pub fn get_for_font_ref(&self, ptr: usize) -> Option<&FontFallbackChain> {
3420 self.chains.get(&FontChainKeyOrRef::Ref(ptr))
3421 }
3422
3423 pub fn into_inner(self) -> HashMap<FontChainKeyOrRef, FontFallbackChain> {
3427 self.chains
3428 }
3429
3430 pub fn into_fontconfig_chains(self) -> HashMap<FontChainKey, FontFallbackChain> {
3435 self.chains
3436 .into_iter()
3437 .filter_map(|(key, chain)| {
3438 match key {
3439 FontChainKeyOrRef::Chain(chain_key) => Some((chain_key, chain)),
3440 FontChainKeyOrRef::Ref(_) => None,
3441 }
3442 })
3443 .collect()
3444 }
3445
3446 pub fn len(&self) -> usize {
3448 self.chains.len()
3449 }
3450
3451 pub fn is_empty(&self) -> bool {
3453 self.chains.is_empty()
3454 }
3455
3456 pub fn font_refs_len(&self) -> usize {
3458 self.chains.keys().filter(|k| k.is_ref()).count()
3459 }
3460}
3461
3462pub fn collect_font_stacks_from_styled_dom(
3474 styled_dom: &StyledDom,
3475 platform: &azul_css::system::Platform,
3476) -> CollectedFontStacks {
3477 use azul_css::compact_cache::{FONT_WEIGHT_SHIFT, FONT_WEIGHT_MASK, FONT_STYLE_SHIFT, FONT_STYLE_MASK};
3478
3479 let mut font_stacks = Vec::new();
3480 let mut hash_to_index: HashMap<u64, usize> = HashMap::new();
3481 let mut font_refs: HashMap<usize, azul_css::props::basic::font::FontRef> = HashMap::new();
3482
3483 let node_data = styled_dom.node_data.as_container();
3484 let cache = &styled_dom.css_property_cache.ptr;
3485 let compact = match cache.compact_cache.as_ref() {
3486 Some(c) => c,
3487 None => return CollectedFontStacks { font_stacks, hash_to_index, font_refs },
3488 };
3489
3490 let mut unique_font_keys: HashMap<(u64, u8, u8), usize> = HashMap::new();
3495 let node_count = node_data.internal.len();
3496
3497 for i in 0..node_count {
3498 if !matches!(node_data.internal[i].node_type, NodeType::Text(_)) {
3500 continue;
3501 }
3502 let fh = compact.tier2b_text[i].font_family_hash;
3503 let t1 = compact.tier1_enums[i];
3504 let weight_bits = ((t1 >> FONT_WEIGHT_SHIFT) & FONT_WEIGHT_MASK) as u8;
3505 let style_bits = ((t1 >> FONT_STYLE_SHIFT) & FONT_STYLE_MASK) as u8;
3506 let key = (fh, weight_bits, style_bits);
3507 unique_font_keys.entry(key).or_insert(i);
3508 }
3509
3510 let styled_nodes = styled_dom.styled_nodes.as_container();
3513
3514 for (&(fh, _wb, _sb), &repr_idx) in &unique_font_keys {
3515 let dom_id = match NodeId::from_usize(repr_idx) {
3516 Some(id) => id,
3517 None => continue,
3518 };
3519 let node_state = &styled_nodes[dom_id].styled_node_state;
3520
3521 let font_families = compact.font_hash_to_families
3525 .get(&fh)
3526 .cloned()
3527 .unwrap_or_else(|| {
3528 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
3529 });
3530
3531 if let Some(first_family) = font_families.get(0) {
3533 if let StyleFontFamily::Ref(font_ref) = first_family {
3534 let ptr = font_ref.parsed as usize;
3535 font_refs.entry(ptr).or_insert_with(|| font_ref.clone());
3536 continue;
3537 }
3538 }
3539
3540 let font_weight = match get_font_weight_property(styled_dom, dom_id, node_state) {
3541 MultiValue::Exact(v) => v,
3542 _ => StyleFontWeight::Normal,
3543 };
3544 let font_style = match get_font_style_property(styled_dom, dom_id, node_state) {
3545 MultiValue::Exact(v) => v,
3546 _ => StyleFontStyle::Normal,
3547 };
3548
3549 let fc_weight = super::fc::convert_font_weight(font_weight);
3550 let fc_style = super::fc::convert_font_style(font_style);
3551
3552 let mut font_stack = Vec::with_capacity(font_families.len() + 3);
3553
3554 for i in 0..font_families.len() {
3555 let family = font_families.get(i).unwrap();
3556 if matches!(family, StyleFontFamily::Ref(_)) {
3557 continue;
3558 }
3559 if let StyleFontFamily::SystemType(system_type) = family {
3560 let font_names = system_type.get_fallback_chain(platform);
3561 let system_weight = if system_type.is_bold() { FcWeight::Bold } else { fc_weight };
3562 let system_style = if system_type.is_italic() { FontStyle::Italic } else { fc_style };
3563 for font_name in font_names {
3564 font_stack.push(FontSelector {
3565 family: font_name.to_string(),
3566 weight: system_weight,
3567 style: system_style,
3568 unicode_ranges: Vec::new(),
3569 });
3570 }
3571 } else {
3572 font_stack.push(FontSelector {
3573 family: family.as_string(),
3574 weight: fc_weight,
3575 style: fc_style,
3576 unicode_ranges: Vec::new(),
3577 });
3578 }
3579 }
3580
3581 for fallback in &["sans-serif", "serif", "monospace"] {
3583 if !font_stack.iter().any(|f| f.family.eq_ignore_ascii_case(fallback)) {
3584 font_stack.push(FontSelector {
3585 family: fallback.to_string(),
3586 weight: FcWeight::Normal,
3587 style: FontStyle::Normal,
3588 unicode_ranges: Vec::new(),
3589 });
3590 }
3591 }
3592
3593 if font_stack.is_empty() {
3594 continue;
3595 }
3596
3597 let key = FontChainKey::from_selectors(&font_stack);
3598 let hash = {
3599 use std::hash::{Hash, Hasher};
3600 let mut hasher = std::collections::hash_map::DefaultHasher::new();
3601 key.hash(&mut hasher);
3602 hasher.finish()
3603 };
3604
3605 if !hash_to_index.contains_key(&hash) {
3606 let idx = font_stacks.len();
3607 font_stacks.push(font_stack);
3608 hash_to_index.insert(hash, idx);
3609 }
3610 }
3611
3612 CollectedFontStacks {
3613 font_stacks,
3614 hash_to_index,
3615 font_refs,
3616 }
3617}
3618
3619pub fn collect_used_codepoints(
3642 styled_dom: &StyledDom,
3643) -> std::collections::BTreeSet<u32> {
3644 let mut out = std::collections::BTreeSet::new();
3645 let node_data = styled_dom.node_data.as_container();
3646 for node in node_data.internal.iter() {
3647 let azul_core::dom::NodeType::Text(s) = &node.node_type else {
3648 continue;
3649 };
3650 for c in s.as_str().chars() {
3651 let cp = c as u32;
3652 if cp >= 0x80 {
3653 out.insert(cp);
3654 }
3655 }
3656 }
3657 out
3658}
3659
3660pub fn collect_used_codepoints_all(
3672 styled_dom: &StyledDom,
3673) -> std::collections::BTreeSet<char> {
3674 let mut out = std::collections::BTreeSet::new();
3675 let node_data = styled_dom.node_data.as_container();
3676 for node in node_data.internal.iter() {
3677 let azul_core::dom::NodeType::Text(s) = &node.node_type else {
3678 continue;
3679 };
3680 for c in s.as_str().chars() {
3681 out.insert(c);
3682 }
3683 }
3684 out
3685}
3686
3687pub fn prune_chain_to_used_chars(
3709 chain: &mut rust_fontconfig::FontFallbackChain,
3710 used_chars: &std::collections::BTreeSet<u32>,
3711) {
3712 fn fm_covers(fm: &rust_fontconfig::FontMatch, cp: u32) -> bool {
3713 fm.unicode_ranges
3714 .iter()
3715 .any(|r| cp >= r.start && cp <= r.end)
3716 }
3717
3718 for group in &mut chain.css_fallbacks {
3719 if group.fonts.is_empty() {
3720 continue;
3721 }
3722 let mut needed: Vec<u32> = used_chars.iter().copied().collect();
3725 needed.retain(|&cp| !fm_covers(&group.fonts[0], cp));
3726 let mut keep = 1;
3727 for fm in group.fonts.iter().skip(1) {
3728 if needed.is_empty() {
3729 break;
3730 }
3731 keep += 1;
3732 needed.retain(|&cp| !fm_covers(fm, cp));
3733 }
3734 group.fonts.truncate(keep);
3735 }
3736
3737 chain.unicode_fallbacks.retain(|fm| {
3738 used_chars.iter().any(|&cp| fm_covers(fm, cp))
3739 });
3740}
3741
3742pub fn scripts_present_in_styled_dom(styled_dom: &StyledDom) -> Vec<UnicodeRange> {
3755 let scripts = DEFAULT_UNICODE_FALLBACK_SCRIPTS;
3756 let mut seen = vec![false; scripts.len()];
3757 let mut hits = 0usize;
3758 let node_data = styled_dom.node_data.as_container();
3759 'outer: for node in node_data.internal.iter() {
3760 let text: &str = match &node.node_type {
3761 azul_core::dom::NodeType::Text(s) => s.as_str(),
3762 _ => continue,
3763 };
3764 for c in text.chars() {
3765 let cp = c as u32;
3766 if cp < 0x0400 {
3770 continue;
3771 }
3772 for (idx, r) in scripts.iter().enumerate() {
3773 if !seen[idx] && cp >= r.start && cp <= r.end {
3774 seen[idx] = true;
3775 hits += 1;
3776 if hits == scripts.len() {
3777 break 'outer;
3778 }
3779 break;
3780 }
3781 }
3782 }
3783 }
3784 scripts
3785 .iter()
3786 .enumerate()
3787 .filter_map(|(i, r)| if seen[i] { Some(*r) } else { None })
3788 .collect()
3789}
3790
3791pub fn resolve_font_chains(
3806 collected: &CollectedFontStacks,
3807 fc_cache: &FcFontCache,
3808 scripts_hint: Option<&[UnicodeRange]>,
3809) -> ResolvedFontChains {
3810 resolve_font_chains_with_registry(collected, fc_cache, None, scripts_hint)
3811}
3812
3813pub fn resolve_font_chains_with_registry(
3826 collected: &CollectedFontStacks,
3827 fc_cache: &FcFontCache,
3828 registry: Option<&rust_fontconfig::registry::FcFontRegistry>,
3829 scripts_hint: Option<&[UnicodeRange]>,
3830) -> ResolvedFontChains {
3831 let mut chains = HashMap::new();
3832
3833 for font_stack in &collected.font_stacks {
3835 if font_stack.is_empty() {
3836 continue;
3837 }
3838
3839 let font_families: Vec<String> = font_stack
3841 .iter()
3842 .map(|s| s.family.clone())
3843 .filter(|f| !f.is_empty())
3844 .collect();
3845
3846 let font_families = if font_families.is_empty() {
3847 vec!["sans-serif".to_string()]
3848 } else {
3849 font_families
3850 };
3851
3852 let weight = font_stack[0].weight;
3853 let is_italic = font_stack[0].style == FontStyle::Italic;
3854 let is_oblique = font_stack[0].style == FontStyle::Oblique;
3855
3856 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
3857 font_families: font_families.clone(),
3858 weight,
3859 italic: is_italic,
3860 oblique: is_oblique,
3861 });
3862
3863 if chains.contains_key(&cache_key) {
3865 continue;
3866 }
3867
3868 let italic = if is_italic {
3873 PatternMatch::True
3874 } else {
3875 PatternMatch::False
3876 };
3877 let oblique = if is_oblique {
3878 PatternMatch::True
3879 } else {
3880 PatternMatch::False
3881 };
3882
3883 let chain = if let Some(reg) = registry {
3886 reg.request_and_resolve_with_scripts(
3887 &font_families, weight, italic, oblique, scripts_hint,
3888 )
3889 } else {
3890 let mut trace = Vec::new();
3891 fc_cache.resolve_font_chain_with_scripts(
3892 &font_families, weight, italic, oblique, scripts_hint, &mut trace,
3893 )
3894 };
3895
3896 chains.insert(cache_key, chain);
3897 }
3898
3899 ResolvedFontChains { chains }
3904}
3905
3906pub fn collect_and_resolve_font_chains_with_registration<T: crate::font_traits::ParsedFontTrait>(
3920 styled_dom: &StyledDom,
3921 fc_cache: &FcFontCache,
3922 font_manager: &crate::text3::cache::FontManager<T>,
3923 platform: &azul_css::system::Platform,
3924) -> ResolvedFontChains {
3925 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
3926
3927 for (_ptr, font_ref) in &collected.font_refs {
3929 font_manager.register_embedded_font(font_ref);
3930 }
3931
3932 if let Some(registry) = font_manager.registry.as_deref() {
3945 let used_chars = collect_used_codepoints_all(styled_dom);
3946 if !used_chars.is_empty() {
3947 return resolve_font_chains_fast(
3948 &collected,
3949 registry,
3950 &used_chars,
3951 );
3952 }
3953 }
3954
3955 let scripts = scripts_present_in_styled_dom(styled_dom);
3959 let mut resolved = resolve_font_chains_with_registry(
3960 &collected,
3961 fc_cache,
3962 font_manager.registry.as_deref(),
3963 Some(&scripts),
3964 );
3965
3966 let used_chars = collect_used_codepoints(styled_dom);
3967 for chain in resolved.chains.values_mut() {
3968 prune_chain_to_used_chars(chain, &used_chars);
3969 }
3970 resolved
3971}
3972
3973pub fn resolve_font_chains_fast(
3983 collected: &CollectedFontStacks,
3984 registry: &rust_fontconfig::registry::FcFontRegistry,
3985 codepoints: &std::collections::BTreeSet<char>,
3986) -> ResolvedFontChains {
3987 use rust_fontconfig::PatternMatch;
3988
3989 static DBG: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
3990 let dbg = *DBG.get_or_init(|| std::env::var_os("AZ_FAST_RESOLVE_DEBUG").is_some());
3991
3992 let mut chains: HashMap<FontChainKeyOrRef, rust_fontconfig::FontFallbackChain> =
3993 HashMap::new();
3994
3995 for font_stack in &collected.font_stacks {
3996 if font_stack.is_empty() {
3997 continue;
3998 }
3999
4000 let font_families: Vec<String> = font_stack
4001 .iter()
4002 .map(|s| s.family.clone())
4003 .filter(|f| !f.is_empty())
4004 .collect();
4005
4006 let font_families = if font_families.is_empty() {
4007 vec!["sans-serif".to_string()]
4008 } else {
4009 font_families
4010 };
4011
4012 let weight = font_stack[0].weight;
4013 let is_italic = font_stack[0].style == FontStyle::Italic;
4014 let is_oblique = font_stack[0].style == FontStyle::Oblique;
4015
4016 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
4017 font_families: font_families.clone(),
4018 weight,
4019 italic: is_italic,
4020 oblique: is_oblique,
4021 });
4022
4023 if chains.contains_key(&cache_key) {
4024 continue;
4025 }
4026
4027 let italic_match = if is_italic {
4028 PatternMatch::True
4029 } else {
4030 PatternMatch::False
4031 };
4032
4033 let request = vec![(font_families.clone(), codepoints.clone())];
4034 let mut chains_out = registry.request_fonts_fast(&request, weight, italic_match);
4035 if dbg {
4036 let total_fonts: usize = chains_out
4037 .iter()
4038 .map(|c| c.css_fallbacks.iter().map(|g| g.fonts.len()).sum::<usize>())
4039 .sum();
4040 eprintln!(
4041 "[FAST] stack {:?} w={:?} i={:?} → {} groups, {} faces",
4042 font_families,
4043 weight,
4044 italic_match,
4045 chains_out.first().map(|c| c.css_fallbacks.len()).unwrap_or(0),
4046 total_fonts,
4047 );
4048 }
4049 if let Some(chain) = chains_out.pop() {
4050 chains.insert(cache_key, chain);
4051 }
4052 }
4053
4054 ResolvedFontChains { chains }
4055}
4056
4057pub fn collect_and_resolve_font_chains(
4061 styled_dom: &StyledDom,
4062 fc_cache: &FcFontCache,
4063 platform: &azul_css::system::Platform,
4064) -> ResolvedFontChains {
4065 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
4066 resolve_font_chains(&collected, fc_cache, None)
4067}
4068
4069pub fn register_embedded_fonts_from_styled_dom<T: crate::font_traits::ParsedFontTrait>(
4071 styled_dom: &StyledDom,
4072 font_manager: &crate::text3::cache::FontManager<T>,
4073 platform: &azul_css::system::Platform,
4074) {
4075 let collected = collect_font_stacks_from_styled_dom(styled_dom, platform);
4076 for (_ptr, font_ref) in &collected.font_refs {
4077 font_manager.register_embedded_font(font_ref);
4078 }
4079}
4080
4081use std::collections::HashSet;
4084
4085use rust_fontconfig::FontId;
4086
4087pub fn collect_font_ids_from_chains(chains: &ResolvedFontChains) -> HashSet<FontId> {
4092 let mut font_ids = HashSet::new();
4093
4094 if chains.chains.is_empty() {
4098 return font_ids;
4099 }
4100
4101 for chain in chains.chains.values() {
4102 for group in &chain.css_fallbacks {
4104 for font in &group.fonts {
4105 font_ids.insert(font.id);
4106 }
4107 }
4108
4109 for font in &chain.unicode_fallbacks {
4111 font_ids.insert(font.id);
4112 }
4113 }
4114
4115 font_ids
4116}
4117
4118pub fn compute_fonts_to_load(
4127 required_fonts: &HashSet<FontId>,
4128 already_loaded: &HashSet<FontId>,
4129) -> HashSet<FontId> {
4130 if required_fonts.is_empty() {
4133 return HashSet::new();
4134 }
4135 required_fonts.difference(already_loaded).cloned().collect()
4136}
4137
4138#[derive(Debug)]
4140pub struct FontLoadResult<T> {
4141 pub loaded: HashMap<FontId, T>,
4143 pub failed: Vec<(FontId, String)>,
4145}
4146
4147pub fn load_fonts_from_disk<T, F>(
4161 font_ids: &HashSet<FontId>,
4162 fc_cache: &FcFontCache,
4163 load_fn: F,
4164) -> FontLoadResult<T>
4165where
4166 F: Fn(std::sync::Arc<rust_fontconfig::FontBytes>, usize) -> Result<T, crate::text3::cache::LayoutError>,
4171{
4172 let mut loaded = HashMap::new();
4173 let mut failed = Vec::new();
4174
4175 for font_id in font_ids {
4176 let font_bytes = match fc_cache.get_font_bytes(font_id) {
4180 Some(bytes) => bytes,
4181 None => {
4182 failed.push((
4183 *font_id,
4184 format!("Could not get font bytes for {:?}", font_id),
4185 ));
4186 continue;
4187 }
4188 };
4189
4190 let font_index = fc_cache
4192 .get_font_by_id(font_id)
4193 .and_then(|source| match source {
4194 rust_fontconfig::OwnedFontSource::Disk(path) => Some(path.font_index),
4195 rust_fontconfig::OwnedFontSource::Memory(font) => Some(font.font_index),
4196 })
4197 .unwrap_or(0) as usize;
4198
4199 match load_fn(font_bytes, font_index) {
4201 Ok(font) => {
4202 loaded.insert(*font_id, font);
4203 }
4204 Err(e) => {
4205 failed.push((
4206 *font_id,
4207 format!("Failed to parse font {:?}: {:?}", font_id, e),
4208 ));
4209 }
4210 }
4211 }
4212
4213 FontLoadResult { loaded, failed }
4214}
4215
4216pub fn resolve_and_load_fonts<T, F>(
4235 styled_dom: &StyledDom,
4236 fc_cache: &FcFontCache,
4237 already_loaded: &HashSet<FontId>,
4238 load_fn: F,
4239 platform: &azul_css::system::Platform,
4240) -> (ResolvedFontChains, FontLoadResult<T>)
4241where
4242 F: Fn(std::sync::Arc<rust_fontconfig::FontBytes>, usize) -> Result<T, crate::text3::cache::LayoutError>,
4243{
4244 let chains = collect_and_resolve_font_chains(styled_dom, fc_cache, platform);
4246
4247 let required_fonts = collect_font_ids_from_chains(&chains);
4249
4250 let fonts_to_load = compute_fonts_to_load(&required_fonts, already_loaded);
4252
4253 let load_result = load_fonts_from_disk(&fonts_to_load, fc_cache, load_fn);
4255
4256 (chains, load_result)
4257}
4258
4259use azul_css::props::style::scrollbar::{
4264 LayoutScrollbarWidth, ScrollbarVisibilityMode,
4265 StyleScrollbarColor,
4266};
4267
4268#[derive(Debug, Clone)]
4285pub struct ComputedScrollbarStyle {
4286 pub width_mode: LayoutScrollbarWidth,
4288 pub visual_width_px: f32,
4291 pub reserve_width_px: f32,
4294 pub thumb_color: ColorU,
4296 pub track_color: ColorU,
4298 pub button_color: ColorU,
4300 pub corner_color: ColorU,
4302 pub clip_to_container_border: bool,
4304 pub fade_delay_ms: u32,
4306 pub fade_duration_ms: u32,
4308 pub visibility: ScrollbarVisibilityMode,
4310 pub show_scroll_buttons: bool,
4313 pub scroll_button_size_px: f32,
4316 pub show_corner_rect: bool,
4318 pub thumb_color_hover: Option<ColorU>,
4320 pub thumb_color_active: Option<ColorU>,
4322 pub track_color_hover: Option<ColorU>,
4324 pub visual_width_px_hover: Option<f32>,
4326 pub visual_width_px_active: Option<f32>,
4328}
4329
4330impl Default for ComputedScrollbarStyle {
4331 fn default() -> Self {
4332 let ctx = azul_css::dynamic_selector::DynamicSelectorContext::default();
4335 let ua = azul_core::ua_css::evaluate_ua_scrollbar_css(&ctx);
4336 Self::from_ua_resolved(&ua)
4337 }
4338}
4339
4340impl ComputedScrollbarStyle {
4341 fn from_ua_resolved(ua: &azul_core::ua_css::ResolvedUaScrollbar) -> Self {
4345 let width_mode = ua.width;
4346 let visibility = ua.visibility;
4347 let fade_delay_ms = ua.fade_delay.ms;
4348 let fade_duration_ms = ua.fade_duration.ms;
4349
4350 let visual_width_px = match width_mode {
4351 LayoutScrollbarWidth::Thin => 8.0,
4352 LayoutScrollbarWidth::Auto => 12.0,
4353 LayoutScrollbarWidth::None => 0.0,
4354 };
4355
4356 let reserve_width_px = if visibility == ScrollbarVisibilityMode::WhenScrolling {
4358 0.0
4359 } else {
4360 visual_width_px
4361 };
4362
4363 let clip = visibility == ScrollbarVisibilityMode::WhenScrolling;
4364
4365 let is_overlay = visibility == ScrollbarVisibilityMode::WhenScrolling;
4367 let show_scroll_buttons = !is_overlay;
4368 let scroll_button_size_px = if is_overlay { 0.0 } else { visual_width_px };
4369 let show_corner_rect = !is_overlay;
4370
4371 let (thumb_color, track_color) = match ua.color {
4372 StyleScrollbarColor::Custom(c) => (c.thumb, c.track),
4373 _ => (ColorU::TRANSPARENT, ColorU::TRANSPARENT),
4374 };
4375
4376 let thumb_hover = ColorU {
4380 r: thumb_color.r.saturating_add(30),
4381 g: thumb_color.g.saturating_add(30),
4382 b: thumb_color.b.saturating_add(30),
4383 a: thumb_color.a.saturating_add(40).min(255),
4384 };
4385 let thumb_active = ColorU {
4386 r: thumb_color.r.saturating_sub(15),
4387 g: thumb_color.g.saturating_sub(15),
4388 b: thumb_color.b.saturating_sub(15),
4389 a: 255, };
4391 let track_hover = ColorU {
4392 r: track_color.r,
4393 g: track_color.g,
4394 b: track_color.b,
4395 a: track_color.a.saturating_add(40).min(255),
4396 };
4397 let hover_width = visual_width_px + 4.0;
4398 let active_width = visual_width_px + 4.0;
4399
4400 Self {
4401 width_mode,
4402 visual_width_px,
4403 reserve_width_px,
4404 thumb_color,
4405 track_color,
4406 button_color: ColorU::TRANSPARENT,
4407 corner_color: ColorU::TRANSPARENT,
4408 clip_to_container_border: clip,
4409 fade_delay_ms,
4410 fade_duration_ms,
4411 visibility,
4412 show_scroll_buttons,
4413 scroll_button_size_px,
4414 show_corner_rect,
4415 thumb_color_hover: Some(thumb_hover),
4416 thumb_color_active: Some(thumb_active),
4417 track_color_hover: Some(track_hover),
4418 visual_width_px_hover: Some(hover_width),
4419 visual_width_px_active: Some(active_width),
4420 }
4421 }
4422}
4423
4424pub fn get_scrollbar_style(
4439 styled_dom: &StyledDom,
4440 node_id: NodeId,
4441 node_state: &StyledNodeState,
4442 system_style: Option<&azul_css::system::SystemStyle>,
4443) -> ComputedScrollbarStyle {
4444 let node_data = &styled_dom.node_data.as_container()[node_id];
4445
4446 let ctx = match system_style {
4448 Some(sys) => {
4449 azul_css::dynamic_selector::DynamicSelectorContext::from_system_style(sys)
4450 }
4451 None => azul_css::dynamic_selector::DynamicSelectorContext::default(),
4452 };
4453 let ua = azul_core::ua_css::evaluate_ua_scrollbar_css(&ctx);
4454 let result = ComputedScrollbarStyle::from_ua_resolved(&ua);
4455
4456 if node_state.is_normal() {
4458 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
4459 if !cc.has_scrollbar_css(node_id.index()) {
4460 return result;
4461 }
4462 }
4463 }
4464 let mut result = result;
4465
4466 if let Some(track) = styled_dom
4468 .css_property_cache
4469 .ptr
4470 .get_scrollbar_track(node_data, &node_id, node_state)
4471 .and_then(|v| v.get_property())
4472 {
4473 result.track_color = extract_color_from_background(track);
4474 }
4475 if let Some(thumb) = styled_dom
4476 .css_property_cache
4477 .ptr
4478 .get_scrollbar_thumb(node_data, &node_id, node_state)
4479 .and_then(|v| v.get_property())
4480 {
4481 result.thumb_color = extract_color_from_background(thumb);
4482 }
4483 if let Some(button) = styled_dom
4484 .css_property_cache
4485 .ptr
4486 .get_scrollbar_button(node_data, &node_id, node_state)
4487 .and_then(|v| v.get_property())
4488 {
4489 result.button_color = extract_color_from_background(button);
4490 }
4491 if let Some(corner) = styled_dom
4492 .css_property_cache
4493 .ptr
4494 .get_scrollbar_corner(node_data, &node_id, node_state)
4495 .and_then(|v| v.get_property())
4496 {
4497 result.corner_color = extract_color_from_background(corner);
4498 }
4499
4500 if let Some(scrollbar_width) = styled_dom
4502 .css_property_cache
4503 .ptr
4504 .get_scrollbar_width(node_data, &node_id, node_state)
4505 .and_then(|v| v.get_property())
4506 {
4507 result.width_mode = *scrollbar_width;
4508 let w = match scrollbar_width {
4509 LayoutScrollbarWidth::Auto => 12.0,
4510 LayoutScrollbarWidth::Thin => 8.0,
4511 LayoutScrollbarWidth::None => 0.0,
4512 };
4513 result.visual_width_px = w;
4514 if result.visibility != ScrollbarVisibilityMode::WhenScrolling {
4515 result.reserve_width_px = w;
4516 }
4517 }
4518
4519 if let Some(scrollbar_color) = styled_dom
4521 .css_property_cache
4522 .ptr
4523 .get_scrollbar_color(node_data, &node_id, node_state)
4524 .and_then(|v| v.get_property())
4525 {
4526 match scrollbar_color {
4527 StyleScrollbarColor::Auto => { }
4528 StyleScrollbarColor::Custom(custom) => {
4529 result.thumb_color = custom.thumb;
4530 result.track_color = custom.track;
4531 }
4532 }
4533 }
4534
4535 if let Some(vis) = styled_dom
4537 .css_property_cache
4538 .ptr
4539 .get_scrollbar_visibility(node_data, &node_id, node_state)
4540 .and_then(|v| v.get_property())
4541 {
4542 result.visibility = *vis;
4543 result.clip_to_container_border = *vis == ScrollbarVisibilityMode::WhenScrolling;
4544 let is_overlay = *vis == ScrollbarVisibilityMode::WhenScrolling;
4546 if is_overlay {
4547 result.reserve_width_px = 0.0;
4548 result.show_scroll_buttons = false;
4549 result.scroll_button_size_px = 0.0;
4550 result.show_corner_rect = false;
4551 } else {
4552 result.reserve_width_px = result.visual_width_px;
4553 }
4554 }
4555
4556 if let Some(delay) = styled_dom
4558 .css_property_cache
4559 .ptr
4560 .get_scrollbar_fade_delay(node_data, &node_id, node_state)
4561 .and_then(|v| v.get_property())
4562 {
4563 result.fade_delay_ms = delay.ms;
4564 }
4565
4566 if let Some(dur) = styled_dom
4568 .css_property_cache
4569 .ptr
4570 .get_scrollbar_fade_duration(node_data, &node_id, node_state)
4571 .and_then(|v| v.get_property())
4572 {
4573 result.fade_duration_ms = dur.ms;
4574 }
4575
4576 result
4577}
4578
4579pub fn get_scrollbar_style_cached<T: crate::font_traits::ParsedFontTrait>(
4590 ctx: &crate::solver3::LayoutContext<'_, T>,
4591 node_id: NodeId,
4592 node_state: &StyledNodeState,
4593) -> ComputedScrollbarStyle {
4594 if let Some(s) = ctx.scrollbar_style_cache.borrow().get(&node_id) {
4595 return s.clone();
4596 }
4597 let style = get_scrollbar_style(
4598 ctx.styled_dom,
4599 node_id,
4600 node_state,
4601 ctx.system_style.as_deref(),
4602 );
4603 ctx.scrollbar_style_cache.borrow_mut().insert(node_id, style.clone());
4604 style
4605}
4606
4607fn extract_color_from_background(
4609 bg: &azul_css::props::style::background::StyleBackgroundContent,
4610) -> ColorU {
4611 use azul_css::props::style::background::StyleBackgroundContent;
4612 match bg {
4613 StyleBackgroundContent::Color(c) => *c,
4614 _ => ColorU::TRANSPARENT,
4615 }
4616}
4617
4618pub fn should_clip_scrollbar_to_border(
4620 styled_dom: &StyledDom,
4621 node_id: NodeId,
4622 node_state: &StyledNodeState,
4623) -> bool {
4624 let style = get_scrollbar_style(styled_dom, node_id, node_state, None);
4625 style.clip_to_container_border
4626}
4627
4628pub fn get_scrollbar_width_px(
4630 styled_dom: &StyledDom,
4631 node_id: NodeId,
4632 node_state: &StyledNodeState,
4633) -> f32 {
4634 let style = get_scrollbar_style(styled_dom, node_id, node_state, None);
4635 style.visual_width_px
4636}
4637
4638pub fn is_text_selectable(
4643 styled_dom: &StyledDom,
4644 node_id: NodeId,
4645 node_state: &StyledNodeState,
4646) -> bool {
4647 let node_data = &styled_dom.node_data.as_container()[node_id];
4648
4649 styled_dom
4650 .css_property_cache
4651 .ptr
4652 .get_user_select(node_data, &node_id, node_state)
4653 .and_then(|v| v.get_property())
4654 .map(|us| *us != StyleUserSelect::None)
4655 .unwrap_or(true) }
4657
4658pub fn is_node_contenteditable(styled_dom: &StyledDom, node_id: NodeId) -> bool {
4666 use azul_core::dom::AttributeType;
4667
4668 let node_data = &styled_dom.node_data.as_container()[node_id];
4669
4670 if node_data.is_contenteditable() {
4672 return true;
4673 }
4674
4675 node_data.attributes().as_ref().iter().any(|attr| {
4678 matches!(attr, AttributeType::ContentEditable(true))
4679 })
4680}
4681use azul_css::props::layout::text::LayoutTextJustify;
4686use azul_css::props::layout::table::{LayoutTableLayout, StyleBorderCollapse, StyleCaptionSide, StyleEmptyCells};
4687use azul_css::props::style::text::StyleHyphens;
4688use azul_css::props::style::text::StyleWordBreak;
4689use azul_css::props::style::text::StyleOverflowWrap;
4690use azul_css::props::style::text::StyleLineBreak;
4691use azul_css::props::style::text::StyleTextAlignLast;
4692use azul_css::props::style::effects::StyleCursor;
4693use azul_css::props::style::effects::StyleObjectFit;
4694use azul_css::props::style::effects::StyleObjectPosition;
4695use azul_css::props::style::effects::StyleAspectRatio;
4696use azul_css::props::style::effects::StyleTextOrientation;
4697
4698impl ExtractPropertyValue<LayoutTextJustify> for CssProperty {
4699 fn extract(&self) -> Option<LayoutTextJustify> {
4700 match self {
4701 Self::TextJustify(CssPropertyValue::Exact(v)) => Some(*v),
4702 _ => None,
4703 }
4704 }
4705}
4706
4707impl ExtractPropertyValue<StyleHyphens> for CssProperty {
4708 fn extract(&self) -> Option<StyleHyphens> {
4709 match self {
4710 Self::Hyphens(CssPropertyValue::Exact(v)) => Some(*v),
4711 _ => None,
4712 }
4713 }
4714}
4715
4716impl ExtractPropertyValue<StyleWordBreak> for CssProperty {
4717 fn extract(&self) -> Option<StyleWordBreak> {
4718 match self {
4719 Self::WordBreak(CssPropertyValue::Exact(v)) => Some(*v),
4720 _ => None,
4721 }
4722 }
4723}
4724
4725impl ExtractPropertyValue<StyleOverflowWrap> for CssProperty {
4726 fn extract(&self) -> Option<StyleOverflowWrap> {
4727 match self {
4728 Self::OverflowWrap(CssPropertyValue::Exact(v)) => Some(*v),
4729 _ => None,
4730 }
4731 }
4732}
4733
4734impl ExtractPropertyValue<StyleLineBreak> for CssProperty {
4735 fn extract(&self) -> Option<StyleLineBreak> {
4736 match self {
4737 Self::LineBreak(CssPropertyValue::Exact(v)) => Some(*v),
4738 _ => None,
4739 }
4740 }
4741}
4742
4743impl ExtractPropertyValue<StyleTextAlignLast> for CssProperty {
4744 fn extract(&self) -> Option<StyleTextAlignLast> {
4745 match self {
4746 Self::TextAlignLast(CssPropertyValue::Exact(v)) => Some(*v),
4747 _ => None,
4748 }
4749 }
4750}
4751
4752impl ExtractPropertyValue<StyleObjectFit> for CssProperty {
4753 fn extract(&self) -> Option<StyleObjectFit> {
4754 match self {
4755 Self::ObjectFit(CssPropertyValue::Exact(v)) => Some(*v),
4756 _ => None,
4757 }
4758 }
4759}
4760
4761impl ExtractPropertyValue<StyleTextOrientation> for CssProperty {
4762 fn extract(&self) -> Option<StyleTextOrientation> {
4763 match self {
4764 Self::TextOrientation(CssPropertyValue::Exact(v)) => Some(*v),
4765 _ => None,
4766 }
4767 }
4768}
4769
4770impl ExtractPropertyValue<StyleObjectPosition> for CssProperty {
4771 fn extract(&self) -> Option<StyleObjectPosition> {
4772 match self {
4773 Self::ObjectPosition(CssPropertyValue::Exact(v)) => Some(*v),
4774 _ => None,
4775 }
4776 }
4777}
4778
4779impl ExtractPropertyValue<StyleAspectRatio> for CssProperty {
4780 fn extract(&self) -> Option<StyleAspectRatio> {
4781 match self {
4782 Self::AspectRatio(CssPropertyValue::Exact(v)) => Some(*v),
4783 _ => None,
4784 }
4785 }
4786}
4787
4788impl ExtractPropertyValue<LayoutTableLayout> for CssProperty {
4789 fn extract(&self) -> Option<LayoutTableLayout> {
4790 match self {
4791 Self::TableLayout(CssPropertyValue::Exact(v)) => Some(*v),
4792 _ => None,
4793 }
4794 }
4795}
4796
4797impl ExtractPropertyValue<StyleBorderCollapse> for CssProperty {
4798 fn extract(&self) -> Option<StyleBorderCollapse> {
4799 match self {
4800 Self::BorderCollapse(CssPropertyValue::Exact(v)) => Some(*v),
4801 _ => None,
4802 }
4803 }
4804}
4805
4806impl ExtractPropertyValue<StyleCaptionSide> for CssProperty {
4807 fn extract(&self) -> Option<StyleCaptionSide> {
4808 match self {
4809 Self::CaptionSide(CssPropertyValue::Exact(v)) => Some(*v),
4810 _ => None,
4811 }
4812 }
4813}
4814
4815impl ExtractPropertyValue<StyleEmptyCells> for CssProperty {
4816 fn extract(&self) -> Option<StyleEmptyCells> {
4817 match self {
4818 Self::EmptyCells(CssPropertyValue::Exact(v)) => Some(*v),
4819 _ => None,
4820 }
4821 }
4822}
4823
4824impl ExtractPropertyValue<StyleCursor> for CssProperty {
4825 fn extract(&self) -> Option<StyleCursor> {
4826 match self {
4827 Self::Cursor(CssPropertyValue::Exact(v)) => Some(v.clone()),
4828 _ => None,
4829 }
4830 }
4831}
4832
4833get_css_property!(
4838 get_text_justify,
4839 get_text_justify,
4840 LayoutTextJustify,
4841 CssPropertyType::TextJustify
4842);
4843
4844get_css_property!(
4845 get_hyphens,
4846 get_hyphens,
4847 StyleHyphens,
4848 CssPropertyType::Hyphens
4849);
4850
4851get_css_property!(
4852 get_word_break,
4853 get_word_break,
4854 StyleWordBreak,
4855 CssPropertyType::WordBreak
4856);
4857
4858get_css_property!(
4859 get_overflow_wrap,
4860 get_overflow_wrap,
4861 StyleOverflowWrap,
4862 CssPropertyType::OverflowWrap
4863);
4864
4865get_css_property!(
4866 get_line_break,
4867 get_line_break,
4868 StyleLineBreak,
4869 CssPropertyType::LineBreak
4870);
4871
4872get_css_property!(
4873 get_text_align_last,
4874 get_text_align_last,
4875 StyleTextAlignLast,
4876 CssPropertyType::TextAlignLast
4877);
4878
4879get_css_property!(
4880 get_table_layout,
4881 get_table_layout,
4882 LayoutTableLayout,
4883 CssPropertyType::TableLayout
4884);
4885
4886get_css_property!(
4887 get_border_collapse,
4888 get_border_collapse,
4889 StyleBorderCollapse,
4890 CssPropertyType::BorderCollapse,
4891 compact = get_border_collapse
4892);
4893
4894get_css_property!(
4895 get_caption_side,
4896 get_caption_side,
4897 StyleCaptionSide,
4898 CssPropertyType::CaptionSide
4899);
4900
4901get_css_property!(
4902 get_empty_cells,
4903 get_empty_cells,
4904 StyleEmptyCells,
4905 CssPropertyType::EmptyCells
4906);
4907
4908get_css_property!(
4909 get_cursor_property,
4910 get_cursor,
4911 StyleCursor,
4912 CssPropertyType::Cursor
4913);
4914
4915pub fn get_height_value(
4921 styled_dom: &StyledDom,
4922 node_id: NodeId,
4923 node_state: &StyledNodeState,
4924) -> Option<LayoutHeight> {
4925 let node_data = &styled_dom.node_data.as_container()[node_id];
4926 styled_dom.css_property_cache.ptr
4927 .get_height(node_data, &node_id, node_state)
4928 .and_then(|v| v.get_property())
4929 .cloned()
4930}
4931
4932pub fn get_shape_inside(
4934 styled_dom: &StyledDom,
4935 node_id: NodeId,
4936 node_state: &StyledNodeState,
4937) -> Option<azul_css::props::layout::shape::ShapeInside> {
4938 let node_data = &styled_dom.node_data.as_container()[node_id];
4939 styled_dom.css_property_cache.ptr
4940 .get_shape_inside(node_data, &node_id, node_state)
4941 .and_then(|v| v.get_property())
4942 .cloned()
4943}
4944
4945pub fn get_shape_outside(
4947 styled_dom: &StyledDom,
4948 node_id: NodeId,
4949 node_state: &StyledNodeState,
4950) -> Option<azul_css::props::layout::shape::ShapeOutside> {
4951 let node_data = &styled_dom.node_data.as_container()[node_id];
4952 styled_dom.css_property_cache.ptr
4953 .get_shape_outside(node_data, &node_id, node_state)
4954 .and_then(|v| v.get_property())
4955 .cloned()
4956}
4957
4958pub fn get_line_height_value(
4960 styled_dom: &StyledDom,
4961 node_id: NodeId,
4962 node_state: &StyledNodeState,
4963) -> Option<azul_css::props::style::text::StyleLineHeight> {
4964 let node_data = &styled_dom.node_data.as_container()[node_id];
4965 styled_dom.css_property_cache.ptr
4966 .get_line_height(node_data, &node_id, node_state)
4967 .and_then(|v| v.get_property())
4968 .cloned()
4969}
4970
4971pub fn get_text_indent_value(
4973 styled_dom: &StyledDom,
4974 node_id: NodeId,
4975 node_state: &StyledNodeState,
4976) -> Option<azul_css::props::style::text::StyleTextIndent> {
4977 let node_data = &styled_dom.node_data.as_container()[node_id];
4978 styled_dom.css_property_cache.ptr
4979 .get_text_indent(node_data, &node_id, node_state)
4980 .and_then(|v| v.get_property())
4981 .cloned()
4982}
4983
4984pub fn get_column_count(
4986 styled_dom: &StyledDom,
4987 node_id: NodeId,
4988 node_state: &StyledNodeState,
4989) -> Option<azul_css::props::layout::column::ColumnCount> {
4990 let node_data = &styled_dom.node_data.as_container()[node_id];
4991 styled_dom.css_property_cache.ptr
4992 .get_column_count(node_data, &node_id, node_state)
4993 .and_then(|v| v.get_property())
4994 .cloned()
4995}
4996
4997pub fn get_initial_letter(
4999 styled_dom: &StyledDom,
5000 node_id: NodeId,
5001 node_state: &StyledNodeState,
5002) -> Option<azul_css::props::style::text::StyleInitialLetter> {
5003 let node_data = &styled_dom.node_data.as_container()[node_id];
5004 styled_dom.css_property_cache.ptr
5005 .get_initial_letter(node_data, &node_id, node_state)
5006 .and_then(|v| v.get_property())
5007 .cloned()
5008}
5009
5010pub fn get_line_clamp(
5012 styled_dom: &StyledDom,
5013 node_id: NodeId,
5014 node_state: &StyledNodeState,
5015) -> Option<azul_css::props::style::text::StyleLineClamp> {
5016 let node_data = &styled_dom.node_data.as_container()[node_id];
5017 styled_dom.css_property_cache.ptr
5018 .get_line_clamp(node_data, &node_id, node_state)
5019 .and_then(|v| v.get_property())
5020 .cloned()
5021}
5022
5023pub fn get_hanging_punctuation(
5025 styled_dom: &StyledDom,
5026 node_id: NodeId,
5027 node_state: &StyledNodeState,
5028) -> Option<azul_css::props::style::text::StyleHangingPunctuation> {
5029 let node_data = &styled_dom.node_data.as_container()[node_id];
5030 styled_dom.css_property_cache.ptr
5031 .get_hanging_punctuation(node_data, &node_id, node_state)
5032 .and_then(|v| v.get_property())
5033 .cloned()
5034}
5035
5036pub fn get_text_combine_upright(
5038 styled_dom: &StyledDom,
5039 node_id: NodeId,
5040 node_state: &StyledNodeState,
5041) -> Option<azul_css::props::style::text::StyleTextCombineUpright> {
5042 let node_data = &styled_dom.node_data.as_container()[node_id];
5043 styled_dom.css_property_cache.ptr
5044 .get_text_combine_upright(node_data, &node_id, node_state)
5045 .and_then(|v| v.get_property())
5046 .cloned()
5047}
5048
5049pub fn get_exclusion_margin(
5051 styled_dom: &StyledDom,
5052 node_id: NodeId,
5053 node_state: &StyledNodeState,
5054) -> f32 {
5055 let node_data = &styled_dom.node_data.as_container()[node_id];
5056 styled_dom.css_property_cache.ptr
5057 .get_exclusion_margin(node_data, &node_id, node_state)
5058 .and_then(|v| v.get_property())
5059 .map(|v| v.inner.get() as f32)
5060 .unwrap_or(0.0)
5061}
5062
5063pub fn get_hyphenation_language(
5065 styled_dom: &StyledDom,
5066 node_id: NodeId,
5067 node_state: &StyledNodeState,
5068) -> Option<azul_css::props::style::azul_exclusion::StyleHyphenationLanguage> {
5069 let node_data = &styled_dom.node_data.as_container()[node_id];
5070 styled_dom.css_property_cache.ptr
5071 .get_hyphenation_language(node_data, &node_id, node_state)
5072 .and_then(|v| v.get_property())
5073 .cloned()
5074}
5075
5076pub fn get_border_spacing(
5078 styled_dom: &StyledDom,
5079 node_id: NodeId,
5080 node_state: &StyledNodeState,
5081) -> azul_css::props::layout::table::LayoutBorderSpacing {
5082 use azul_css::props::basic::pixel::PixelValue;
5083
5084 if node_state.is_normal() {
5086 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5087 let h_raw = cc.get_border_spacing_h_raw(node_id.index());
5088 let v_raw = cc.get_border_spacing_v_raw(node_id.index());
5089 if h_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
5092 && v_raw < azul_css::compact_cache::I16_SENTINEL_THRESHOLD
5093 {
5094 return azul_css::props::layout::table::LayoutBorderSpacing {
5095 horizontal: PixelValue::px(h_raw as f32 / 10.0),
5096 vertical: PixelValue::px(v_raw as f32 / 10.0),
5097 };
5098 }
5099 }
5100 }
5101
5102 let node_data = &styled_dom.node_data.as_container()[node_id];
5104 styled_dom.css_property_cache.ptr
5105 .get_border_spacing(node_data, &node_id, node_state)
5106 .and_then(|v| v.get_property())
5107 .cloned()
5108 .unwrap_or_default()
5109}
5110
5111pub fn get_opacity(
5117 styled_dom: &StyledDom,
5118 node_id: NodeId,
5119 node_state: &StyledNodeState,
5120) -> f32 {
5121 if node_state.is_normal() {
5123 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5124 let raw = cc.get_opacity_raw(node_id.index());
5125 if raw == azul_css::compact_cache::OPACITY_SENTINEL {
5126 return 1.0;
5127 }
5128 return (raw as f32) / 254.0;
5129 }
5130 }
5131 let node_data = &styled_dom.node_data.as_container()[node_id];
5133 styled_dom.css_property_cache.ptr
5134 .get_opacity(node_data, &node_id, node_state)
5135 .and_then(|v| v.get_property())
5136 .map(|v| v.inner.normalized())
5137 .unwrap_or(1.0)
5138}
5139
5140pub fn get_filter(
5142 styled_dom: &StyledDom,
5143 node_id: NodeId,
5144 node_state: &StyledNodeState,
5145) -> Option<azul_css::props::style::filter::StyleFilterVec> {
5146 if node_state.is_normal() {
5147 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5148 if !cc.has_filter(node_id.index()) { return None; }
5149 }
5150 }
5151 let node_data = &styled_dom.node_data.as_container()[node_id];
5152 styled_dom.css_property_cache.ptr
5153 .get_filter(node_data, &node_id, node_state)
5154 .and_then(|v| v.get_property())
5155 .cloned()
5156}
5157
5158pub fn get_backdrop_filter(
5160 styled_dom: &StyledDom,
5161 node_id: NodeId,
5162 node_state: &StyledNodeState,
5163) -> Option<azul_css::props::style::filter::StyleFilterVec> {
5164 if node_state.is_normal() {
5165 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5166 if !cc.has_backdrop_filter(node_id.index()) { return None; }
5167 }
5168 }
5169 let node_data = &styled_dom.node_data.as_container()[node_id];
5170 styled_dom.css_property_cache.ptr
5171 .get_backdrop_filter(node_data, &node_id, node_state)
5172 .and_then(|v| v.get_property())
5173 .cloned()
5174}
5175
5176#[inline]
5179fn box_shadow_fast_bail(
5180 styled_dom: &StyledDom,
5181 node_id: NodeId,
5182 node_state: &StyledNodeState,
5183) -> bool {
5184 if !node_state.is_normal() { return false; }
5185 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5186 return !cc.has_box_shadow(node_id.index());
5187 }
5188 false
5189}
5190
5191pub fn get_box_shadow_left(
5193 styled_dom: &StyledDom,
5194 node_id: NodeId,
5195 node_state: &StyledNodeState,
5196) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5197 if box_shadow_fast_bail(styled_dom, node_id, node_state) { return None; }
5198 let node_data = &styled_dom.node_data.as_container()[node_id];
5199 styled_dom.css_property_cache.ptr
5200 .get_box_shadow_left(node_data, &node_id, node_state)
5201 .and_then(|v| v.get_property())
5202 .map(|v| (**v).clone())
5203}
5204
5205pub fn get_box_shadow_right(
5207 styled_dom: &StyledDom,
5208 node_id: NodeId,
5209 node_state: &StyledNodeState,
5210) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5211 if box_shadow_fast_bail(styled_dom, node_id, node_state) { return None; }
5212 let node_data = &styled_dom.node_data.as_container()[node_id];
5213 styled_dom.css_property_cache.ptr
5214 .get_box_shadow_right(node_data, &node_id, node_state)
5215 .and_then(|v| v.get_property())
5216 .map(|v| (**v).clone())
5217}
5218
5219pub fn get_box_shadow_top(
5221 styled_dom: &StyledDom,
5222 node_id: NodeId,
5223 node_state: &StyledNodeState,
5224) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5225 if box_shadow_fast_bail(styled_dom, node_id, node_state) { return None; }
5226 let node_data = &styled_dom.node_data.as_container()[node_id];
5227 styled_dom.css_property_cache.ptr
5228 .get_box_shadow_top(node_data, &node_id, node_state)
5229 .and_then(|v| v.get_property())
5230 .map(|v| (**v).clone())
5231}
5232
5233pub fn get_box_shadow_bottom(
5235 styled_dom: &StyledDom,
5236 node_id: NodeId,
5237 node_state: &StyledNodeState,
5238) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5239 if box_shadow_fast_bail(styled_dom, node_id, node_state) { return None; }
5240 let node_data = &styled_dom.node_data.as_container()[node_id];
5241 styled_dom.css_property_cache.ptr
5242 .get_box_shadow_bottom(node_data, &node_id, node_state)
5243 .and_then(|v| v.get_property())
5244 .map(|v| (**v).clone())
5245}
5246
5247pub fn get_text_shadow(
5249 styled_dom: &StyledDom,
5250 node_id: NodeId,
5251 node_state: &StyledNodeState,
5252) -> Option<azul_css::props::style::box_shadow::StyleBoxShadow> {
5253 if node_state.is_normal() {
5254 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5255 if !cc.has_text_shadow(node_id.index()) { return None; }
5256 }
5257 }
5258 let node_data = &styled_dom.node_data.as_container()[node_id];
5259 styled_dom.css_property_cache.ptr
5260 .get_text_shadow(node_data, &node_id, node_state)
5261 .and_then(|v| v.get_property())
5262 .map(|v| (**v).clone())
5263}
5264
5265pub fn get_transform(
5272 styled_dom: &StyledDom,
5273 node_id: NodeId,
5274 node_state: &StyledNodeState,
5275) -> Option<azul_css::props::style::transform::StyleTransformVec> {
5276 if node_state.is_normal() {
5278 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5279 if !cc.has_transform(node_id.index()) {
5280 return None;
5281 }
5282 }
5284 }
5285 let node_data = &styled_dom.node_data.as_container()[node_id];
5286 styled_dom.css_property_cache.ptr
5287 .get_transform(node_data, &node_id, node_state)
5288 .and_then(|v| v.get_property())
5289 .cloned()
5290}
5291
5292pub fn get_counter_reset(
5294 styled_dom: &StyledDom,
5295 node_id: NodeId,
5296 node_state: &StyledNodeState,
5297) -> Option<azul_css::props::style::content::CounterReset> {
5298 let node_data = &styled_dom.node_data.as_container()[node_id];
5299 styled_dom.css_property_cache.ptr
5300 .get_counter_reset(node_data, &node_id, node_state)
5301 .and_then(|v| v.get_property())
5302 .cloned()
5303}
5304
5305pub fn get_counter_increment(
5307 styled_dom: &StyledDom,
5308 node_id: NodeId,
5309 node_state: &StyledNodeState,
5310) -> Option<azul_css::props::style::content::CounterIncrement> {
5311 let node_data = &styled_dom.node_data.as_container()[node_id];
5312 styled_dom.css_property_cache.ptr
5313 .get_counter_increment(node_data, &node_id, node_state)
5314 .and_then(|v| v.get_property())
5315 .cloned()
5316}
5317
5318pub fn is_node_contenteditable_inherited(styled_dom: &StyledDom, node_id: NodeId) -> bool {
5344 use azul_core::dom::AttributeType;
5345
5346 let node_data_container = styled_dom.node_data.as_container();
5347 let hierarchy = styled_dom.node_hierarchy.as_container();
5348
5349 let mut current_node_id = Some(node_id);
5350
5351 while let Some(nid) = current_node_id {
5352 let node_data = &node_data_container[nid];
5353
5354 if node_data.is_contenteditable() {
5357 return true;
5358 }
5359
5360 for attr in node_data.attributes().as_ref().iter() {
5363 if let AttributeType::ContentEditable(is_editable) = attr {
5364 return *is_editable;
5367 }
5368 }
5369
5370 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
5372 }
5373
5374 false
5376}
5377
5378pub fn find_contenteditable_ancestor(styled_dom: &StyledDom, node_id: NodeId) -> Option<NodeId> {
5388 use azul_core::dom::AttributeType;
5389
5390 let node_data_container = styled_dom.node_data.as_container();
5391 let hierarchy = styled_dom.node_hierarchy.as_container();
5392
5393 let mut current_node_id = Some(node_id);
5394
5395 while let Some(nid) = current_node_id {
5396 let node_data = &node_data_container[nid];
5397
5398 if node_data.is_contenteditable() {
5400 return Some(nid);
5401 }
5402
5403 for attr in node_data.attributes().as_ref().iter() {
5405 if let AttributeType::ContentEditable(is_editable) = attr {
5406 if *is_editable {
5407 return Some(nid);
5408 } else {
5409 return None;
5411 }
5412 }
5413 }
5414
5415 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
5417 }
5418
5419 None
5420}
5421
5422macro_rules! get_css_property_value {
5430 ($fn_name:ident, $cache_method:ident, $ret_type:ty) => {
5431 pub fn $fn_name(
5432 styled_dom: &StyledDom,
5433 node_id: NodeId,
5434 node_state: &StyledNodeState,
5435 ) -> Option<$ret_type> {
5436 let node_data = &styled_dom.node_data.as_container()[node_id];
5437 styled_dom
5438 .css_property_cache
5439 .ptr
5440 .$cache_method(node_data, &node_id, node_state)
5441 .cloned()
5442 }
5443 };
5444}
5445
5446get_css_property_value!(get_flex_direction_prop, get_flex_direction, LayoutFlexDirectionValue);
5448get_css_property_value!(get_flex_wrap_prop, get_flex_wrap, LayoutFlexWrapValue);
5449get_css_property_value!(get_flex_grow_prop, get_flex_grow, LayoutFlexGrowValue);
5450get_css_property_value!(get_flex_shrink_prop, get_flex_shrink, LayoutFlexShrinkValue);
5451get_css_property_value!(get_flex_basis_prop, get_flex_basis, LayoutFlexBasisValue);
5452
5453get_css_property_value!(get_align_items_prop, get_align_items, LayoutAlignItemsValue);
5455get_css_property_value!(get_align_self_prop, get_align_self, LayoutAlignSelfValue);
5456get_css_property_value!(get_align_content_prop, get_align_content, LayoutAlignContentValue);
5457get_css_property_value!(get_justify_content_prop, get_justify_content, LayoutJustifyContentValue);
5458get_css_property_value!(get_justify_items_prop, get_justify_items, LayoutJustifyItemsValue);
5459get_css_property_value!(get_justify_self_prop, get_justify_self, LayoutJustifySelfValue);
5460
5461get_css_property_value!(get_gap_prop, get_gap, LayoutGapValue);
5463
5464get_css_property_value!(get_grid_template_rows_prop, get_grid_template_rows, LayoutGridTemplateRowsValue);
5466get_css_property_value!(get_grid_template_columns_prop, get_grid_template_columns, LayoutGridTemplateColumnsValue);
5467get_css_property_value!(get_grid_auto_rows_prop, get_grid_auto_rows, LayoutGridAutoRowsValue);
5468get_css_property_value!(get_grid_auto_columns_prop, get_grid_auto_columns, LayoutGridAutoColumnsValue);
5469get_css_property_value!(get_grid_auto_flow_prop, get_grid_auto_flow, LayoutGridAutoFlowValue);
5470get_css_property_value!(get_grid_column_prop, get_grid_column, LayoutGridColumnValue);
5471get_css_property_value!(get_grid_row_prop, get_grid_row, LayoutGridRowValue);
5472
5473pub fn get_grid_template_areas_prop(
5477 styled_dom: &StyledDom,
5478 node_id: NodeId,
5479 node_state: &StyledNodeState,
5480) -> Option<GridTemplateAreas> {
5481 let node_data = &styled_dom.node_data.as_container()[node_id];
5482 styled_dom
5483 .css_property_cache
5484 .ptr
5485 .get_property(node_data, &node_id, node_state, &CssPropertyType::GridTemplateAreas)
5486 .and_then(|p| {
5487 if let CssProperty::GridTemplateAreas(v) = p {
5488 v.get_property().cloned()
5489 } else {
5490 None
5491 }
5492 })
5493}
5494
5495pub fn get_clip_path(
5501 styled_dom: &StyledDom,
5502 node_id: NodeId,
5503 node_state: &StyledNodeState,
5504) -> Option<azul_css::props::layout::shape::ClipPath> {
5505 if node_state.is_normal() {
5507 if let Some(ref cc) = styled_dom.css_property_cache.ptr.compact_cache {
5508 if !cc.has_clip_path(node_id.index()) {
5509 return None;
5510 }
5511 }
5512 }
5513 let node_data = &styled_dom.node_data.as_container()[node_id];
5514 styled_dom.css_property_cache.ptr
5515 .get_clip_path(node_data, &node_id, node_state)
5516 .and_then(|v| v.get_property())
5517 .cloned()
5518}