1use azul_core::{
7 dom::{NodeId, NodeType},
8 geom::LogicalSize,
9 id::NodeId as CoreNodeId,
10 styled_dom::{StyledDom, StyledNodeState},
11};
12use azul_css::{
13 css::CssPropertyValue,
14 props::{
15 basic::{
16 font::{StyleFontFamily, StyleFontFamilyVec},
17 pixel::{DEFAULT_FONT_SIZE, PT_TO_PX},
18 ColorU, PhysicalSize, PixelValue, PropertyContext, ResolutionContext,
19 },
20 layout::{
21 BoxDecorationBreak, BreakInside, LayoutBoxSizing, LayoutClear, LayoutDisplay,
22 LayoutFlexWrap, LayoutFloat, LayoutHeight, LayoutJustifyContent, LayoutOverflow,
23 LayoutPosition, LayoutWidth, LayoutWritingMode, Orphans, PageBreak, Widows,
24 },
25 property::{CssProperty, CssPropertyType},
26 style::{
27 border_radius::StyleBorderRadius,
28 lists::{StyleListStylePosition, StyleListStyleType},
29 StyleTextAlign, StyleUserSelect,
30 },
31 },
32};
33
34use crate::{
35 font_traits::{ParsedFontTrait, StyleProperties},
36 solver3::{
37 display_list::{BorderRadius, PhysicalSizeImport},
38 layout_tree::LayoutNode,
39 scrollbar::ScrollbarRequirements,
40 },
41};
42
43pub fn get_element_font_size(
47 styled_dom: &StyledDom,
48 dom_id: NodeId,
49 node_state: &StyledNodeState,
50) -> f32 {
51 let node_data = &styled_dom.node_data.as_container()[dom_id];
52 let cache = &styled_dom.css_property_cache.ptr;
53
54 let cached_font_size = cache
56 .dependency_chains
57 .get(&dom_id)
58 .and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
59 .and_then(|chain| chain.cached_pixels);
60
61 if let Some(cached) = cached_font_size {
62 return cached;
63 }
64
65 let parent_font_size = styled_dom
67 .node_hierarchy
68 .as_container()
69 .get(dom_id)
70 .and_then(|node| node.parent_id())
71 .and_then(|parent_id| {
72 cache
74 .dependency_chains
75 .get(&parent_id)
76 .and_then(|chains| {
77 chains.get(&azul_css::props::property::CssPropertyType::FontSize)
78 })
79 .and_then(|chain| chain.cached_pixels)
80 })
81 .unwrap_or(DEFAULT_FONT_SIZE);
82
83 let root_font_size = {
85 let root_id = NodeId::new(0);
86 cache
87 .dependency_chains
88 .get(&root_id)
89 .and_then(|chains| chains.get(&azul_css::props::property::CssPropertyType::FontSize))
90 .and_then(|chain| chain.cached_pixels)
91 .unwrap_or(DEFAULT_FONT_SIZE)
92 };
93
94 cache
96 .get_font_size(node_data, &dom_id, node_state)
97 .and_then(|v| v.get_property().cloned())
98 .map(|v| {
99 let context = ResolutionContext {
100 element_font_size: DEFAULT_FONT_SIZE, parent_font_size,
102 root_font_size,
103 containing_block_size: PhysicalSize::new(0.0, 0.0),
104 element_size: None,
105 viewport_size: PhysicalSize::new(0.0, 0.0), };
107
108 v.inner
109 .resolve_with_context(&context, PropertyContext::FontSize)
110 })
111 .unwrap_or(DEFAULT_FONT_SIZE)
112}
113
114pub fn get_parent_font_size(
116 styled_dom: &StyledDom,
117 dom_id: NodeId,
118 node_state: &StyledNodeState,
119) -> f32 {
120 styled_dom
121 .node_hierarchy
122 .as_container()
123 .get(dom_id)
124 .and_then(|node| node.parent_id())
125 .map(|parent_id| get_element_font_size(styled_dom, parent_id, node_state))
126 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE)
127}
128
129pub fn get_root_font_size(styled_dom: &StyledDom, node_state: &StyledNodeState) -> f32 {
131 get_element_font_size(styled_dom, NodeId::new(0), node_state)
133}
134
135#[derive(Debug, Copy, Clone, PartialEq)]
138pub enum MultiValue<T> {
139 Auto,
141 Initial,
143 Inherit,
145 Exact(T),
147}
148
149impl<T> MultiValue<T> {
150 pub fn is_auto(&self) -> bool {
152 matches!(self, MultiValue::Auto)
153 }
154
155 pub fn is_exact(&self) -> bool {
157 matches!(self, MultiValue::Exact(_))
158 }
159
160 pub fn exact(self) -> Option<T> {
162 match self {
163 MultiValue::Exact(v) => Some(v),
164 _ => None,
165 }
166 }
167
168 pub fn unwrap_or(self, default: T) -> T {
170 match self {
171 MultiValue::Exact(v) => v,
172 _ => default,
173 }
174 }
175
176 pub fn unwrap_or_default(self) -> T
178 where
179 T: Default,
180 {
181 match self {
182 MultiValue::Exact(v) => v,
183 _ => T::default(),
184 }
185 }
186
187 pub fn map<U, F>(self, f: F) -> MultiValue<U>
189 where
190 F: FnOnce(T) -> U,
191 {
192 match self {
193 MultiValue::Exact(v) => MultiValue::Exact(f(v)),
194 MultiValue::Auto => MultiValue::Auto,
195 MultiValue::Initial => MultiValue::Initial,
196 MultiValue::Inherit => MultiValue::Inherit,
197 }
198 }
199}
200
201impl MultiValue<LayoutOverflow> {
203 pub fn is_clipped(&self) -> bool {
206 matches!(
207 self,
208 MultiValue::Exact(
209 LayoutOverflow::Hidden
210 | LayoutOverflow::Clip
211 | LayoutOverflow::Auto
212 | LayoutOverflow::Scroll
213 )
214 )
215 }
216
217 pub fn is_scroll(&self) -> bool {
218 matches!(
219 self,
220 MultiValue::Exact(LayoutOverflow::Scroll | LayoutOverflow::Auto)
221 )
222 }
223
224 pub fn is_auto_overflow(&self) -> bool {
225 matches!(self, MultiValue::Exact(LayoutOverflow::Auto))
226 }
227
228 pub fn is_hidden(&self) -> bool {
229 matches!(self, MultiValue::Exact(LayoutOverflow::Hidden))
230 }
231
232 pub fn is_hidden_or_clip(&self) -> bool {
233 matches!(
234 self,
235 MultiValue::Exact(LayoutOverflow::Hidden | LayoutOverflow::Clip)
236 )
237 }
238
239 pub fn is_scroll_explicit(&self) -> bool {
240 matches!(self, MultiValue::Exact(LayoutOverflow::Scroll))
241 }
242
243 pub fn is_visible_or_clip(&self) -> bool {
244 matches!(
245 self,
246 MultiValue::Exact(LayoutOverflow::Visible | LayoutOverflow::Clip)
247 )
248 }
249}
250
251impl MultiValue<LayoutPosition> {
253 pub fn is_absolute_or_fixed(&self) -> bool {
254 matches!(
255 self,
256 MultiValue::Exact(LayoutPosition::Absolute | LayoutPosition::Fixed)
257 )
258 }
259}
260
261impl MultiValue<LayoutFloat> {
263 pub fn is_none(&self) -> bool {
264 matches!(
265 self,
266 MultiValue::Auto
267 | MultiValue::Initial
268 | MultiValue::Inherit
269 | MultiValue::Exact(LayoutFloat::None)
270 )
271 }
272}
273
274impl<T: Default> Default for MultiValue<T> {
275 fn default() -> Self {
276 MultiValue::Auto
277 }
278}
279
280macro_rules! get_css_property_pixel {
283 ($fn_name:ident, $cache_method:ident, $ua_property:expr) => {
284 pub fn $fn_name(
285 styled_dom: &StyledDom,
286 node_id: NodeId,
287 node_state: &StyledNodeState,
288 ) -> MultiValue<PixelValue> {
289 let node_data = &styled_dom.node_data.as_container()[node_id];
290
291 let author_css = styled_dom
293 .css_property_cache
294 .ptr
295 .$cache_method(node_data, &node_id, node_state);
296
297 if let Some(val) = author_css.and_then(|v| v.get_property().copied()) {
298 return MultiValue::Exact(val.inner);
299 }
300
301 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
303
304 if let Some(ua_prop) = ua_css {
305 if let Some(inner) = ua_prop.get_pixel_inner() {
306 return MultiValue::Exact(inner);
307 }
308 }
309
310 MultiValue::Auto
312 }
313 };
314}
315
316trait CssPropertyPixelInner {
318 fn get_pixel_inner(&self) -> Option<PixelValue>;
319}
320
321impl CssPropertyPixelInner for azul_css::props::property::CssProperty {
322 fn get_pixel_inner(&self) -> Option<PixelValue> {
323 match self {
324 CssProperty::Left(CssPropertyValue::Exact(v)) => Some(v.inner),
325 CssProperty::Right(CssPropertyValue::Exact(v)) => Some(v.inner),
326 CssProperty::Top(CssPropertyValue::Exact(v)) => Some(v.inner),
327 CssProperty::Bottom(CssPropertyValue::Exact(v)) => Some(v.inner),
328 CssProperty::MarginLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
329 CssProperty::MarginRight(CssPropertyValue::Exact(v)) => Some(v.inner),
330 CssProperty::MarginTop(CssPropertyValue::Exact(v)) => Some(v.inner),
331 CssProperty::MarginBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
332 CssProperty::PaddingLeft(CssPropertyValue::Exact(v)) => Some(v.inner),
333 CssProperty::PaddingRight(CssPropertyValue::Exact(v)) => Some(v.inner),
334 CssProperty::PaddingTop(CssPropertyValue::Exact(v)) => Some(v.inner),
335 CssProperty::PaddingBottom(CssPropertyValue::Exact(v)) => Some(v.inner),
336 _ => None,
337 }
338 }
339}
340
341macro_rules! get_css_property {
343 ($fn_name:ident, $cache_method:ident, $return_type:ty, $ua_property:expr) => {
344 pub fn $fn_name(
345 styled_dom: &StyledDom,
346 node_id: NodeId,
347 node_state: &StyledNodeState,
348 ) -> MultiValue<$return_type> {
349 let node_data = &styled_dom.node_data.as_container()[node_id];
350
351 let author_css = styled_dom
353 .css_property_cache
354 .ptr
355 .$cache_method(node_data, &node_id, node_state);
356
357 if let Some(val) = author_css.and_then(|v| v.get_property().copied()) {
358 return MultiValue::Exact(val);
359 }
360
361 let ua_css = azul_core::ua_css::get_ua_property(&node_data.node_type, $ua_property);
363
364 if let Some(ua_prop) = ua_css {
365 if let Some(val) = extract_property_value::<$return_type>(ua_prop) {
366 return MultiValue::Exact(val);
367 }
368 }
369
370 MultiValue::Auto
372 }
373 };
374}
375
376trait ExtractPropertyValue<T> {
378 fn extract(&self) -> Option<T>;
379}
380
381fn extract_property_value<T>(prop: &azul_css::props::property::CssProperty) -> Option<T>
382where
383 azul_css::props::property::CssProperty: ExtractPropertyValue<T>,
384{
385 prop.extract()
386}
387
388impl ExtractPropertyValue<LayoutWidth> for azul_css::props::property::CssProperty {
391 fn extract(&self) -> Option<LayoutWidth> {
392 match self {
393 Self::Width(CssPropertyValue::Exact(v)) => Some(*v),
394 _ => None,
395 }
396 }
397}
398
399impl ExtractPropertyValue<LayoutHeight> for azul_css::props::property::CssProperty {
400 fn extract(&self) -> Option<LayoutHeight> {
401 match self {
402 Self::Height(CssPropertyValue::Exact(v)) => Some(*v),
403 _ => None,
404 }
405 }
406}
407
408impl ExtractPropertyValue<LayoutMinWidth> for azul_css::props::property::CssProperty {
409 fn extract(&self) -> Option<LayoutMinWidth> {
410 match self {
411 Self::MinWidth(CssPropertyValue::Exact(v)) => Some(*v),
412 _ => None,
413 }
414 }
415}
416
417impl ExtractPropertyValue<LayoutMinHeight> for azul_css::props::property::CssProperty {
418 fn extract(&self) -> Option<LayoutMinHeight> {
419 match self {
420 Self::MinHeight(CssPropertyValue::Exact(v)) => Some(*v),
421 _ => None,
422 }
423 }
424}
425
426impl ExtractPropertyValue<LayoutMaxWidth> for azul_css::props::property::CssProperty {
427 fn extract(&self) -> Option<LayoutMaxWidth> {
428 match self {
429 Self::MaxWidth(CssPropertyValue::Exact(v)) => Some(*v),
430 _ => None,
431 }
432 }
433}
434
435impl ExtractPropertyValue<LayoutMaxHeight> for azul_css::props::property::CssProperty {
436 fn extract(&self) -> Option<LayoutMaxHeight> {
437 match self {
438 Self::MaxHeight(CssPropertyValue::Exact(v)) => Some(*v),
439 _ => None,
440 }
441 }
442}
443
444impl ExtractPropertyValue<LayoutDisplay> for azul_css::props::property::CssProperty {
445 fn extract(&self) -> Option<LayoutDisplay> {
446 match self {
447 Self::Display(CssPropertyValue::Exact(v)) => Some(*v),
448 _ => None,
449 }
450 }
451}
452
453impl ExtractPropertyValue<LayoutWritingMode> for azul_css::props::property::CssProperty {
454 fn extract(&self) -> Option<LayoutWritingMode> {
455 match self {
456 Self::WritingMode(CssPropertyValue::Exact(v)) => Some(*v),
457 _ => None,
458 }
459 }
460}
461
462impl ExtractPropertyValue<LayoutFlexWrap> for azul_css::props::property::CssProperty {
463 fn extract(&self) -> Option<LayoutFlexWrap> {
464 match self {
465 Self::FlexWrap(CssPropertyValue::Exact(v)) => Some(*v),
466 _ => None,
467 }
468 }
469}
470
471impl ExtractPropertyValue<LayoutJustifyContent> for azul_css::props::property::CssProperty {
472 fn extract(&self) -> Option<LayoutJustifyContent> {
473 match self {
474 Self::JustifyContent(CssPropertyValue::Exact(v)) => Some(*v),
475 _ => None,
476 }
477 }
478}
479
480impl ExtractPropertyValue<StyleTextAlign> for azul_css::props::property::CssProperty {
481 fn extract(&self) -> Option<StyleTextAlign> {
482 match self {
483 Self::TextAlign(CssPropertyValue::Exact(v)) => Some(*v),
484 _ => None,
485 }
486 }
487}
488
489impl ExtractPropertyValue<LayoutFloat> for azul_css::props::property::CssProperty {
490 fn extract(&self) -> Option<LayoutFloat> {
491 match self {
492 Self::Float(CssPropertyValue::Exact(v)) => Some(*v),
493 _ => None,
494 }
495 }
496}
497
498impl ExtractPropertyValue<LayoutClear> for azul_css::props::property::CssProperty {
499 fn extract(&self) -> Option<LayoutClear> {
500 match self {
501 Self::Clear(CssPropertyValue::Exact(v)) => Some(*v),
502 _ => None,
503 }
504 }
505}
506
507impl ExtractPropertyValue<LayoutOverflow> for azul_css::props::property::CssProperty {
508 fn extract(&self) -> Option<LayoutOverflow> {
509 match self {
510 Self::OverflowX(CssPropertyValue::Exact(v)) => Some(*v),
511 Self::OverflowY(CssPropertyValue::Exact(v)) => Some(*v),
512 _ => None,
513 }
514 }
515}
516
517impl ExtractPropertyValue<LayoutPosition> for azul_css::props::property::CssProperty {
518 fn extract(&self) -> Option<LayoutPosition> {
519 match self {
520 Self::Position(CssPropertyValue::Exact(v)) => Some(*v),
521 _ => None,
522 }
523 }
524}
525
526impl ExtractPropertyValue<LayoutBoxSizing> for azul_css::props::property::CssProperty {
527 fn extract(&self) -> Option<LayoutBoxSizing> {
528 match self {
529 Self::BoxSizing(CssPropertyValue::Exact(v)) => Some(*v),
530 _ => None,
531 }
532 }
533}
534
535impl ExtractPropertyValue<PixelValue> for azul_css::props::property::CssProperty {
536 fn extract(&self) -> Option<PixelValue> {
537 self.get_pixel_inner()
538 }
539}
540
541get_css_property!(
542 get_writing_mode,
543 get_writing_mode,
544 LayoutWritingMode,
545 azul_css::props::property::CssPropertyType::WritingMode
546);
547
548get_css_property!(
549 get_css_width,
550 get_width,
551 LayoutWidth,
552 azul_css::props::property::CssPropertyType::Width
553);
554
555get_css_property!(
556 get_css_height,
557 get_height,
558 LayoutHeight,
559 azul_css::props::property::CssPropertyType::Height
560);
561
562get_css_property!(
563 get_wrap,
564 get_flex_wrap,
565 LayoutFlexWrap,
566 azul_css::props::property::CssPropertyType::FlexWrap
567);
568
569get_css_property!(
570 get_justify_content,
571 get_justify_content,
572 LayoutJustifyContent,
573 azul_css::props::property::CssPropertyType::JustifyContent
574);
575
576get_css_property!(
577 get_text_align,
578 get_text_align,
579 StyleTextAlign,
580 azul_css::props::property::CssPropertyType::TextAlign
581);
582
583get_css_property!(
584 get_float,
585 get_float,
586 LayoutFloat,
587 azul_css::props::property::CssPropertyType::Float
588);
589
590get_css_property!(
591 get_clear,
592 get_clear,
593 LayoutClear,
594 azul_css::props::property::CssPropertyType::Clear
595);
596
597get_css_property!(
598 get_overflow_x,
599 get_overflow_x,
600 LayoutOverflow,
601 azul_css::props::property::CssPropertyType::OverflowX
602);
603
604get_css_property!(
605 get_overflow_y,
606 get_overflow_y,
607 LayoutOverflow,
608 azul_css::props::property::CssPropertyType::OverflowY
609);
610
611get_css_property!(
612 get_position,
613 get_position,
614 LayoutPosition,
615 azul_css::props::property::CssPropertyType::Position
616);
617
618get_css_property!(
619 get_css_box_sizing,
620 get_box_sizing,
621 LayoutBoxSizing,
622 azul_css::props::property::CssPropertyType::BoxSizing
623);
624pub fn get_style_border_radius(
628 styled_dom: &StyledDom,
629 node_id: NodeId,
630 node_state: &StyledNodeState,
631) -> azul_css::props::style::border_radius::StyleBorderRadius {
632 let node_data = &styled_dom.node_data.as_container()[node_id];
633
634 let top_left = styled_dom
635 .css_property_cache
636 .ptr
637 .get_border_top_left_radius(node_data, &node_id, node_state)
638 .and_then(|br| br.get_property_or_default())
639 .map(|v| v.inner)
640 .unwrap_or_default();
641
642 let top_right = styled_dom
643 .css_property_cache
644 .ptr
645 .get_border_top_right_radius(node_data, &node_id, node_state)
646 .and_then(|br| br.get_property_or_default())
647 .map(|v| v.inner)
648 .unwrap_or_default();
649
650 let bottom_right = styled_dom
651 .css_property_cache
652 .ptr
653 .get_border_bottom_right_radius(node_data, &node_id, node_state)
654 .and_then(|br| br.get_property_or_default())
655 .map(|v| v.inner)
656 .unwrap_or_default();
657
658 let bottom_left = styled_dom
659 .css_property_cache
660 .ptr
661 .get_border_bottom_left_radius(node_data, &node_id, node_state)
662 .and_then(|br| br.get_property_or_default())
663 .map(|v| v.inner)
664 .unwrap_or_default();
665
666 StyleBorderRadius {
667 top_left,
668 top_right,
669 bottom_right,
670 bottom_left,
671 }
672}
673
674pub fn get_border_radius(
680 styled_dom: &StyledDom,
681 node_id: NodeId,
682 node_state: &StyledNodeState,
683 element_size: PhysicalSizeImport,
684 viewport_size: LogicalSize,
685) -> BorderRadius {
686 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
687
688 let node_data = &styled_dom.node_data.as_container()[node_id];
689
690 let element_font_size = get_element_font_size(styled_dom, node_id, node_state);
692 let parent_font_size = styled_dom
693 .node_hierarchy
694 .as_container()
695 .get(node_id)
696 .and_then(|node| node.parent_id())
697 .map(|p| get_element_font_size(styled_dom, p, node_state))
698 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
699 let root_font_size = get_root_font_size(styled_dom, node_state);
700
701 let context = ResolutionContext {
703 element_font_size,
704 parent_font_size,
705 root_font_size,
706 containing_block_size: PhysicalSize::new(0.0, 0.0), element_size: Some(PhysicalSize::new(element_size.width, element_size.height)),
708 viewport_size: PhysicalSize::new(viewport_size.width, viewport_size.height),
709 };
710
711 let top_left = styled_dom
712 .css_property_cache
713 .ptr
714 .get_border_top_left_radius(node_data, &node_id, node_state)
715 .and_then(|br| br.get_property().cloned())
716 .unwrap_or_default();
717
718 let top_right = styled_dom
719 .css_property_cache
720 .ptr
721 .get_border_top_right_radius(node_data, &node_id, node_state)
722 .and_then(|br| br.get_property().cloned())
723 .unwrap_or_default();
724
725 let bottom_right = styled_dom
726 .css_property_cache
727 .ptr
728 .get_border_bottom_right_radius(node_data, &node_id, node_state)
729 .and_then(|br| br.get_property().cloned())
730 .unwrap_or_default();
731
732 let bottom_left = styled_dom
733 .css_property_cache
734 .ptr
735 .get_border_bottom_left_radius(node_data, &node_id, node_state)
736 .and_then(|br| br.get_property().cloned())
737 .unwrap_or_default();
738
739 BorderRadius {
740 top_left: top_left
741 .inner
742 .resolve_with_context(&context, PropertyContext::BorderRadius),
743 top_right: top_right
744 .inner
745 .resolve_with_context(&context, PropertyContext::BorderRadius),
746 bottom_right: bottom_right
747 .inner
748 .resolve_with_context(&context, PropertyContext::BorderRadius),
749 bottom_left: bottom_left
750 .inner
751 .resolve_with_context(&context, PropertyContext::BorderRadius),
752 }
753}
754
755pub fn get_z_index(styled_dom: &StyledDom, node_id: Option<NodeId>) -> i32 {
760 let _ = (styled_dom, node_id);
762 0
763}
764
765pub fn get_background_color(
788 styled_dom: &StyledDom,
789 node_id: NodeId,
790 node_state: &StyledNodeState,
791) -> ColorU {
792 let node_data = &styled_dom.node_data.as_container()[node_id];
793
794 let get_node_bg = |node_id: NodeId, node_data: &azul_core::dom::NodeData| {
796 styled_dom
797 .css_property_cache
798 .ptr
799 .get_background_content(node_data, &node_id, node_state)
800 .and_then(|bg| bg.get_property())
801 .and_then(|bg_vec| bg_vec.get(0))
802 .and_then(|first_bg| match first_bg {
803 azul_css::props::style::StyleBackgroundContent::Color(color) => Some(color.clone()),
804 azul_css::props::style::StyleBackgroundContent::Image(_) => None, _ => None,
806 })
807 };
808
809 let own_bg = get_node_bg(node_id, node_data);
810
811 if !matches!(node_data.node_type, NodeType::Html) || own_bg.is_some() {
815 return own_bg.unwrap_or(ColorU {
817 r: 0,
818 g: 0,
819 b: 0,
820 a: 0,
821 });
822 }
823
824 let first_child = styled_dom
826 .node_hierarchy
827 .as_container()
828 .get(node_id)
829 .and_then(|node| node.first_child_id(node_id));
830
831 let Some(first_child) = first_child else {
832 return ColorU {
833 r: 0,
834 g: 0,
835 b: 0,
836 a: 0,
837 };
838 };
839
840 let first_child_data = &styled_dom.node_data.as_container()[first_child];
841
842 if !matches!(first_child_data.node_type, NodeType::Body) {
844 return ColorU {
845 r: 0,
846 g: 0,
847 b: 0,
848 a: 0,
849 };
850 }
851
852 get_node_bg(first_child, first_child_data).unwrap_or(ColorU {
854 r: 0,
855 g: 0,
856 b: 0,
857 a: 0,
858 })
859}
860
861pub fn get_background_contents(
868 styled_dom: &StyledDom,
869 node_id: NodeId,
870 node_state: &StyledNodeState,
871) -> Vec<azul_css::props::style::StyleBackgroundContent> {
872 use azul_core::dom::NodeType;
873 use azul_css::props::style::StyleBackgroundContent;
874
875 let node_data = &styled_dom.node_data.as_container()[node_id];
876
877 let get_node_backgrounds =
879 |nid: NodeId, ndata: &azul_core::dom::NodeData| -> Vec<StyleBackgroundContent> {
880 styled_dom
881 .css_property_cache
882 .ptr
883 .get_background_content(ndata, &nid, node_state)
884 .and_then(|bg| bg.get_property())
885 .map(|bg_vec| bg_vec.iter().cloned().collect())
886 .unwrap_or_default()
887 };
888
889 let own_backgrounds = get_node_backgrounds(node_id, node_data);
890
891 if !matches!(node_data.node_type, NodeType::Html) || !own_backgrounds.is_empty() {
894 return own_backgrounds;
895 }
896
897 let first_child = styled_dom
899 .node_hierarchy
900 .as_container()
901 .get(node_id)
902 .and_then(|node| node.first_child_id(node_id));
903
904 let Some(first_child) = first_child else {
905 return own_backgrounds;
906 };
907
908 let first_child_data = &styled_dom.node_data.as_container()[first_child];
909
910 if !matches!(first_child_data.node_type, NodeType::Body) {
912 return own_backgrounds;
913 }
914
915 get_node_backgrounds(first_child, first_child_data)
917}
918
919pub struct BorderInfo {
921 pub widths: crate::solver3::display_list::StyleBorderWidths,
922 pub colors: crate::solver3::display_list::StyleBorderColors,
923 pub styles: crate::solver3::display_list::StyleBorderStyles,
924}
925
926pub fn get_border_info(
927 styled_dom: &StyledDom,
928 node_id: NodeId,
929 node_state: &StyledNodeState,
930) -> BorderInfo {
931 use crate::solver3::display_list::{StyleBorderColors, StyleBorderStyles, StyleBorderWidths};
932
933 let node_data = &styled_dom.node_data.as_container()[node_id];
934
935 let widths = StyleBorderWidths {
937 top: styled_dom
938 .css_property_cache
939 .ptr
940 .get_border_top_width(node_data, &node_id, node_state)
941 .cloned(),
942 right: styled_dom
943 .css_property_cache
944 .ptr
945 .get_border_right_width(node_data, &node_id, node_state)
946 .cloned(),
947 bottom: styled_dom
948 .css_property_cache
949 .ptr
950 .get_border_bottom_width(node_data, &node_id, node_state)
951 .cloned(),
952 left: styled_dom
953 .css_property_cache
954 .ptr
955 .get_border_left_width(node_data, &node_id, node_state)
956 .cloned(),
957 };
958
959 let colors = StyleBorderColors {
961 top: styled_dom
962 .css_property_cache
963 .ptr
964 .get_border_top_color(node_data, &node_id, node_state)
965 .cloned(),
966 right: styled_dom
967 .css_property_cache
968 .ptr
969 .get_border_right_color(node_data, &node_id, node_state)
970 .cloned(),
971 bottom: styled_dom
972 .css_property_cache
973 .ptr
974 .get_border_bottom_color(node_data, &node_id, node_state)
975 .cloned(),
976 left: styled_dom
977 .css_property_cache
978 .ptr
979 .get_border_left_color(node_data, &node_id, node_state)
980 .cloned(),
981 };
982
983 let styles = StyleBorderStyles {
985 top: styled_dom
986 .css_property_cache
987 .ptr
988 .get_border_top_style(node_data, &node_id, node_state)
989 .cloned(),
990 right: styled_dom
991 .css_property_cache
992 .ptr
993 .get_border_right_style(node_data, &node_id, node_state)
994 .cloned(),
995 bottom: styled_dom
996 .css_property_cache
997 .ptr
998 .get_border_bottom_style(node_data, &node_id, node_state)
999 .cloned(),
1000 left: styled_dom
1001 .css_property_cache
1002 .ptr
1003 .get_border_left_style(node_data, &node_id, node_state)
1004 .cloned(),
1005 };
1006
1007 BorderInfo {
1008 widths,
1009 colors,
1010 styles,
1011 }
1012}
1013
1014pub fn get_inline_border_info(
1019 styled_dom: &StyledDom,
1020 node_id: NodeId,
1021 node_state: &StyledNodeState,
1022 border_info: &BorderInfo,
1023) -> Option<crate::text3::cache::InlineBorderInfo> {
1024 use crate::text3::cache::InlineBorderInfo;
1025
1026 fn get_border_width_px(
1028 width: &Option<
1029 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderTopWidth>,
1030 >,
1031 ) -> f32 {
1032 width
1033 .as_ref()
1034 .and_then(|v| v.get_property())
1035 .map(|w| w.inner.number.get())
1036 .unwrap_or(0.0)
1037 }
1038
1039 fn get_border_width_px_right(
1040 width: &Option<
1041 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderRightWidth>,
1042 >,
1043 ) -> f32 {
1044 width
1045 .as_ref()
1046 .and_then(|v| v.get_property())
1047 .map(|w| w.inner.number.get())
1048 .unwrap_or(0.0)
1049 }
1050
1051 fn get_border_width_px_bottom(
1052 width: &Option<
1053 azul_css::css::CssPropertyValue<
1054 azul_css::props::style::border::LayoutBorderBottomWidth,
1055 >,
1056 >,
1057 ) -> f32 {
1058 width
1059 .as_ref()
1060 .and_then(|v| v.get_property())
1061 .map(|w| w.inner.number.get())
1062 .unwrap_or(0.0)
1063 }
1064
1065 fn get_border_width_px_left(
1066 width: &Option<
1067 azul_css::css::CssPropertyValue<azul_css::props::style::border::LayoutBorderLeftWidth>,
1068 >,
1069 ) -> f32 {
1070 width
1071 .as_ref()
1072 .and_then(|v| v.get_property())
1073 .map(|w| w.inner.number.get())
1074 .unwrap_or(0.0)
1075 }
1076
1077 fn get_border_color_top(
1079 color: &Option<
1080 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderTopColor>,
1081 >,
1082 ) -> ColorU {
1083 color
1084 .as_ref()
1085 .and_then(|v| v.get_property())
1086 .map(|c| c.inner)
1087 .unwrap_or(ColorU::BLACK)
1088 }
1089
1090 fn get_border_color_right(
1091 color: &Option<
1092 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderRightColor>,
1093 >,
1094 ) -> ColorU {
1095 color
1096 .as_ref()
1097 .and_then(|v| v.get_property())
1098 .map(|c| c.inner)
1099 .unwrap_or(ColorU::BLACK)
1100 }
1101
1102 fn get_border_color_bottom(
1103 color: &Option<
1104 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderBottomColor>,
1105 >,
1106 ) -> ColorU {
1107 color
1108 .as_ref()
1109 .and_then(|v| v.get_property())
1110 .map(|c| c.inner)
1111 .unwrap_or(ColorU::BLACK)
1112 }
1113
1114 fn get_border_color_left(
1115 color: &Option<
1116 azul_css::css::CssPropertyValue<azul_css::props::style::border::StyleBorderLeftColor>,
1117 >,
1118 ) -> ColorU {
1119 color
1120 .as_ref()
1121 .and_then(|v| v.get_property())
1122 .map(|c| c.inner)
1123 .unwrap_or(ColorU::BLACK)
1124 }
1125
1126 fn get_border_radius_px(
1128 styled_dom: &StyledDom,
1129 node_id: NodeId,
1130 node_state: &StyledNodeState,
1131 ) -> Option<f32> {
1132 let node_data = &styled_dom.node_data.as_container()[node_id];
1133
1134 let top_left = styled_dom
1135 .css_property_cache
1136 .ptr
1137 .get_border_top_left_radius(node_data, &node_id, node_state)
1138 .and_then(|br| br.get_property().cloned())
1139 .map(|v| v.inner.number.get());
1140
1141 let top_right = styled_dom
1142 .css_property_cache
1143 .ptr
1144 .get_border_top_right_radius(node_data, &node_id, node_state)
1145 .and_then(|br| br.get_property().cloned())
1146 .map(|v| v.inner.number.get());
1147
1148 let bottom_left = styled_dom
1149 .css_property_cache
1150 .ptr
1151 .get_border_bottom_left_radius(node_data, &node_id, node_state)
1152 .and_then(|br| br.get_property().cloned())
1153 .map(|v| v.inner.number.get());
1154
1155 let bottom_right = styled_dom
1156 .css_property_cache
1157 .ptr
1158 .get_border_bottom_right_radius(node_data, &node_id, node_state)
1159 .and_then(|br| br.get_property().cloned())
1160 .map(|v| v.inner.number.get());
1161
1162 let radii: Vec<f32> = [top_left, top_right, bottom_left, bottom_right]
1164 .into_iter()
1165 .filter_map(|r| r)
1166 .collect();
1167
1168 if radii.is_empty() {
1169 None
1170 } else {
1171 Some(radii.into_iter().fold(0.0f32, |a, b| a.max(b)))
1172 }
1173 }
1174
1175 let top = get_border_width_px(&border_info.widths.top);
1176 let right = get_border_width_px_right(&border_info.widths.right);
1177 let bottom = get_border_width_px_bottom(&border_info.widths.bottom);
1178 let left = get_border_width_px_left(&border_info.widths.left);
1179
1180 if top == 0.0 && right == 0.0 && bottom == 0.0 && left == 0.0 {
1182 return None;
1183 }
1184
1185 Some(InlineBorderInfo {
1186 top,
1187 right,
1188 bottom,
1189 left,
1190 top_color: get_border_color_top(&border_info.colors.top),
1191 right_color: get_border_color_right(&border_info.colors.right),
1192 bottom_color: get_border_color_bottom(&border_info.colors.bottom),
1193 left_color: get_border_color_left(&border_info.colors.left),
1194 radius: get_border_radius_px(styled_dom, node_id, node_state),
1195 })
1196}
1197
1198#[derive(Debug, Clone, Copy, Default)]
1202pub struct SelectionStyle {
1203 pub bg_color: ColorU,
1205 pub text_color: Option<ColorU>,
1207 pub radius: f32,
1209}
1210
1211pub fn get_selection_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> SelectionStyle {
1213 let Some(node_id) = node_id else {
1214 return SelectionStyle::default();
1215 };
1216
1217 let node_data = &styled_dom.node_data.as_container()[node_id];
1218 let node_state = &StyledNodeState::default();
1219
1220 let bg_color = styled_dom
1221 .css_property_cache
1222 .ptr
1223 .get_selection_background_color(node_data, &node_id, node_state)
1224 .and_then(|c| c.get_property().cloned())
1225 .map(|c| c.inner)
1226 .unwrap_or(ColorU {
1227 r: 51,
1228 g: 153,
1229 b: 255, a: 128, });
1232
1233 let text_color = styled_dom
1234 .css_property_cache
1235 .ptr
1236 .get_selection_color(node_data, &node_id, node_state)
1237 .and_then(|c| c.get_property().cloned())
1238 .map(|c| c.inner);
1239
1240 let radius = styled_dom
1241 .css_property_cache
1242 .ptr
1243 .get_selection_radius(node_data, &node_id, node_state)
1244 .and_then(|r| r.get_property().cloned())
1245 .map(|r| r.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(0.0);
1247
1248 SelectionStyle {
1249 bg_color,
1250 text_color,
1251 radius,
1252 }
1253}
1254
1255#[derive(Debug, Clone, Copy, Default)]
1257pub struct CaretStyle {
1258 pub color: ColorU,
1259 pub width: f32,
1260 pub animation_duration: u32,
1261}
1262
1263pub fn get_caret_style(styled_dom: &StyledDom, node_id: Option<NodeId>) -> CaretStyle {
1265 let Some(node_id) = node_id else {
1266 return CaretStyle::default();
1267 };
1268
1269 let node_data = &styled_dom.node_data.as_container()[node_id];
1270 let node_state = &StyledNodeState::default();
1271
1272 let color = styled_dom
1273 .css_property_cache
1274 .ptr
1275 .get_caret_color(node_data, &node_id, node_state)
1276 .and_then(|c| c.get_property().cloned())
1277 .map(|c| c.inner)
1278 .unwrap_or(ColorU {
1279 r: 255,
1280 g: 255,
1281 b: 255,
1282 a: 255, });
1284
1285 let width = styled_dom
1286 .css_property_cache
1287 .ptr
1288 .get_caret_width(node_data, &node_id, node_state)
1289 .and_then(|w| w.get_property().cloned())
1290 .map(|w| w.inner.to_pixels_internal(0.0, 16.0)) .unwrap_or(2.0); let animation_duration = styled_dom
1294 .css_property_cache
1295 .ptr
1296 .get_caret_animation_duration(node_data, &node_id, node_state)
1297 .and_then(|d| d.get_property().cloned())
1298 .map(|d| d.inner.inner) .unwrap_or(500); CaretStyle {
1302 color,
1303 width,
1304 animation_duration,
1305 }
1306}
1307
1308pub fn get_scrollbar_info_from_layout(node: &LayoutNode) -> ScrollbarRequirements {
1312 if let Some(ref info) = node.scrollbar_info {
1314 return info.clone();
1315 }
1316
1317 let container_size = node.used_size.unwrap_or_default();
1319
1320 let content_size = if let Some(ref inline_layout) = node.inline_layout_result {
1322 let bounds = inline_layout.layout.bounds();
1324 LogicalSize::new(bounds.width, bounds.height)
1325 } else if !node.children.is_empty() {
1326 let mut max_bottom: f32 = 0.0;
1330 let mut max_right: f32 = 0.0;
1331
1332 let num_children = node.children.len();
1342 if num_children > 3 {
1343 LogicalSize::new(
1345 container_size.width,
1346 container_size.height * 2.0, )
1348 } else {
1349 container_size
1350 }
1351 } else {
1352 container_size
1354 };
1355
1356 const SCROLLBAR_SIZE: f32 = 12.0;
1358
1359 let needs_vertical = content_size.height > container_size.height + 1.0;
1361 let needs_horizontal = content_size.width > container_size.width + 1.0;
1362
1363 ScrollbarRequirements {
1364 needs_vertical,
1365 needs_horizontal,
1366 scrollbar_width: if needs_vertical { SCROLLBAR_SIZE } else { 0.0 },
1367 scrollbar_height: if needs_horizontal {
1368 SCROLLBAR_SIZE
1369 } else {
1370 0.0
1371 },
1372 }
1373}
1374
1375get_css_property!(
1376 get_display_property_internal,
1377 get_display,
1378 LayoutDisplay,
1379 azul_css::props::property::CssPropertyType::Display
1380);
1381
1382pub fn get_display_property(
1383 styled_dom: &StyledDom,
1384 dom_id: Option<NodeId>,
1385) -> MultiValue<LayoutDisplay> {
1386 let Some(id) = dom_id else {
1387 return MultiValue::Exact(LayoutDisplay::Inline);
1388 };
1389 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1390 get_display_property_internal(styled_dom, id, node_state)
1391}
1392
1393pub fn get_style_properties(styled_dom: &StyledDom, dom_id: NodeId) -> StyleProperties {
1394 use azul_css::props::basic::{PhysicalSize, PropertyContext, ResolutionContext};
1395
1396 let node_data = &styled_dom.node_data.as_container()[dom_id];
1397 let node_state = &styled_dom.styled_nodes.as_container()[dom_id].styled_node_state;
1398 let cache = &styled_dom.css_property_cache.ptr;
1399
1400 use azul_css::props::basic::font::{StyleFontFamily, StyleFontFamilyVec};
1402
1403 let font_families = cache
1404 .get_font_family(node_data, &dom_id, node_state)
1405 .and_then(|v| v.get_property().cloned())
1406 .unwrap_or_else(|| {
1407 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
1409 });
1410
1411 let parent_font_size = styled_dom
1413 .node_hierarchy
1414 .as_container()
1415 .get(dom_id)
1416 .and_then(|node| {
1417 let parent_id = CoreNodeId::from_usize(node.parent)?;
1418 cache
1420 .get_font_size(
1421 &styled_dom.node_data.as_container()[parent_id],
1422 &parent_id,
1423 &styled_dom.styled_nodes.as_container()[parent_id].styled_node_state,
1424 )
1425 .and_then(|v| v.get_property().cloned())
1426 .map(|v| {
1427 use azul_css::props::basic::pixel::DEFAULT_FONT_SIZE;
1429 v.inner.to_pixels_internal(0.0, DEFAULT_FONT_SIZE)
1430 })
1431 })
1432 .unwrap_or(azul_css::props::basic::pixel::DEFAULT_FONT_SIZE);
1433
1434 let root_font_size = get_root_font_size(styled_dom, node_state);
1435
1436 let font_size_context = ResolutionContext {
1438 element_font_size: azul_css::props::basic::pixel::DEFAULT_FONT_SIZE, parent_font_size,
1440 root_font_size,
1441 containing_block_size: PhysicalSize::new(0.0, 0.0),
1442 element_size: None,
1443 viewport_size: PhysicalSize::new(0.0, 0.0), };
1445
1446 let font_size = cache
1450 .get_font_size(node_data, &dom_id, node_state)
1451 .and_then(|v| v.get_property().cloned())
1452 .map(|v| {
1453 v.inner
1454 .resolve_with_context(&font_size_context, PropertyContext::FontSize)
1455 })
1456 .unwrap_or(parent_font_size);
1457
1458 let color_from_cache = cache
1459 .get_text_color(node_data, &dom_id, node_state)
1460 .and_then(|v| v.get_property().cloned())
1461 .map(|v| v.inner);
1462
1463 let color = color_from_cache.unwrap_or_default();
1464
1465 let line_height = cache
1466 .get_line_height(node_data, &dom_id, node_state)
1467 .and_then(|v| v.get_property().cloned())
1468 .map(|v| v.inner.normalized() * font_size)
1469 .unwrap_or(font_size * 1.2);
1470
1471 use azul_css::props::layout::LayoutDisplay;
1477 let display = cache
1478 .get_display(node_data, &dom_id, node_state)
1479 .and_then(|v| v.get_property().cloned())
1480 .unwrap_or(LayoutDisplay::Inline);
1481
1482 let (background_color, background_content, border) =
1485 if matches!(display, LayoutDisplay::Inline | LayoutDisplay::InlineBlock) {
1486 let bg = get_background_color(styled_dom, dom_id, node_state);
1487 let bg_color = if bg.a > 0 { Some(bg) } else { None };
1488
1489 let bg_contents = get_background_contents(styled_dom, dom_id, node_state);
1491
1492 let border_info = get_border_info(styled_dom, dom_id, node_state);
1494 let inline_border =
1495 get_inline_border_info(styled_dom, dom_id, node_state, &border_info);
1496
1497 (bg_color, bg_contents, inline_border)
1498 } else {
1499 (None, Vec::new(), None)
1502 };
1503
1504 let font_weight = cache
1506 .get_font_weight(node_data, &dom_id, node_state)
1507 .and_then(|v| v.get_property().copied())
1508 .unwrap_or(azul_css::props::basic::font::StyleFontWeight::Normal);
1509
1510 let font_style = cache
1512 .get_font_style(node_data, &dom_id, node_state)
1513 .and_then(|v| v.get_property().copied())
1514 .unwrap_or(azul_css::props::basic::font::StyleFontStyle::Normal);
1515
1516 let fc_weight = super::fc::convert_font_weight(font_weight);
1518 let fc_style = super::fc::convert_font_style(font_style);
1519
1520 let font_stack = {
1523 let font_ref = (0..font_families.len())
1525 .find_map(|i| {
1526 match font_families.get(i).unwrap() {
1527 azul_css::props::basic::font::StyleFontFamily::Ref(r) => Some(r.clone()),
1528 _ => None,
1529 }
1530 });
1531
1532 if let Some(font_ref) = font_ref {
1533 FontStack::Ref(font_ref)
1535 } else {
1536 let mut stack = Vec::with_capacity(font_families.len() + 3);
1538
1539 for i in 0..font_families.len() {
1540 stack.push(crate::text3::cache::FontSelector {
1541 family: font_families.get(i).unwrap().as_string(),
1542 weight: fc_weight,
1543 style: fc_style,
1544 unicode_ranges: Vec::new(),
1545 });
1546 }
1547
1548 let generic_fallbacks = ["sans-serif", "serif", "monospace"];
1550 for fallback in &generic_fallbacks {
1551 if !stack
1552 .iter()
1553 .any(|f| f.family.to_lowercase() == fallback.to_lowercase())
1554 {
1555 stack.push(crate::text3::cache::FontSelector {
1556 family: fallback.to_string(),
1557 weight: rust_fontconfig::FcWeight::Normal,
1558 style: crate::text3::cache::FontStyle::Normal,
1559 unicode_ranges: Vec::new(),
1560 });
1561 }
1562 }
1563
1564 FontStack::Stack(stack)
1565 }
1566 };
1567
1568 let letter_spacing = cache
1570 .get_letter_spacing(node_data, &dom_id, node_state)
1571 .and_then(|v| v.get_property().cloned())
1572 .map(|v| {
1573 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
1576 crate::text3::cache::Spacing::Px(px_value.round() as i32)
1577 })
1578 .unwrap_or_default();
1579
1580 let word_spacing = cache
1582 .get_word_spacing(node_data, &dom_id, node_state)
1583 .and_then(|v| v.get_property().cloned())
1584 .map(|v| {
1585 let px_value = v.inner.resolve_with_context(&font_size_context, PropertyContext::FontSize);
1586 crate::text3::cache::Spacing::Px(px_value.round() as i32)
1587 })
1588 .unwrap_or_default();
1589
1590 let text_decoration = cache
1592 .get_text_decoration(node_data, &dom_id, node_state)
1593 .and_then(|v| v.get_property().cloned())
1594 .map(|v| crate::text3::cache::TextDecoration::from_css(v))
1595 .unwrap_or_default();
1596
1597 let tab_size = cache
1599 .get_tab_width(node_data, &dom_id, node_state)
1600 .and_then(|v| v.get_property().cloned())
1601 .map(|v| v.inner.number.get())
1602 .unwrap_or(8.0);
1603
1604 let properties = StyleProperties {
1605 font_stack,
1606 font_size_px: font_size,
1607 color,
1608 background_color,
1609 background_content,
1610 border,
1611 line_height,
1612 letter_spacing,
1613 word_spacing,
1614 text_decoration,
1615 tab_size,
1616 ..Default::default()
1620 };
1621
1622 properties
1623}
1624
1625pub fn get_list_style_type(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> StyleListStyleType {
1626 let Some(id) = dom_id else {
1627 return StyleListStyleType::default();
1628 };
1629 let node_data = &styled_dom.node_data.as_container()[id];
1630 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1631 styled_dom
1632 .css_property_cache
1633 .ptr
1634 .get_list_style_type(node_data, &id, node_state)
1635 .and_then(|v| v.get_property().copied())
1636 .unwrap_or_default()
1637}
1638
1639pub fn get_list_style_position(
1640 styled_dom: &StyledDom,
1641 dom_id: Option<NodeId>,
1642) -> StyleListStylePosition {
1643 let Some(id) = dom_id else {
1644 return StyleListStylePosition::default();
1645 };
1646 let node_data = &styled_dom.node_data.as_container()[id];
1647 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1648 styled_dom
1649 .css_property_cache
1650 .ptr
1651 .get_list_style_position(node_data, &id, node_state)
1652 .and_then(|v| v.get_property().copied())
1653 .unwrap_or_default()
1654}
1655
1656use azul_css::props::layout::{
1659 LayoutInsetBottom, LayoutLeft, LayoutMarginBottom, LayoutMarginLeft, LayoutMarginRight,
1660 LayoutMarginTop, LayoutMaxHeight, LayoutMaxWidth, LayoutMinHeight, LayoutMinWidth,
1661 LayoutPaddingBottom, LayoutPaddingLeft, LayoutPaddingRight, LayoutPaddingTop, LayoutRight,
1662 LayoutTop,
1663};
1664
1665get_css_property_pixel!(
1667 get_css_left,
1668 get_left,
1669 azul_css::props::property::CssPropertyType::Left
1670);
1671get_css_property_pixel!(
1672 get_css_right,
1673 get_right,
1674 azul_css::props::property::CssPropertyType::Right
1675);
1676get_css_property_pixel!(
1677 get_css_top,
1678 get_top,
1679 azul_css::props::property::CssPropertyType::Top
1680);
1681get_css_property_pixel!(
1682 get_css_bottom,
1683 get_bottom,
1684 azul_css::props::property::CssPropertyType::Bottom
1685);
1686
1687get_css_property_pixel!(
1689 get_css_margin_left,
1690 get_margin_left,
1691 azul_css::props::property::CssPropertyType::MarginLeft
1692);
1693get_css_property_pixel!(
1694 get_css_margin_right,
1695 get_margin_right,
1696 azul_css::props::property::CssPropertyType::MarginRight
1697);
1698get_css_property_pixel!(
1699 get_css_margin_top,
1700 get_margin_top,
1701 azul_css::props::property::CssPropertyType::MarginTop
1702);
1703get_css_property_pixel!(
1704 get_css_margin_bottom,
1705 get_margin_bottom,
1706 azul_css::props::property::CssPropertyType::MarginBottom
1707);
1708
1709get_css_property_pixel!(
1711 get_css_padding_left,
1712 get_padding_left,
1713 azul_css::props::property::CssPropertyType::PaddingLeft
1714);
1715get_css_property_pixel!(
1716 get_css_padding_right,
1717 get_padding_right,
1718 azul_css::props::property::CssPropertyType::PaddingRight
1719);
1720get_css_property_pixel!(
1721 get_css_padding_top,
1722 get_padding_top,
1723 azul_css::props::property::CssPropertyType::PaddingTop
1724);
1725get_css_property_pixel!(
1726 get_css_padding_bottom,
1727 get_padding_bottom,
1728 azul_css::props::property::CssPropertyType::PaddingBottom
1729);
1730
1731get_css_property!(
1733 get_css_min_width,
1734 get_min_width,
1735 LayoutMinWidth,
1736 azul_css::props::property::CssPropertyType::MinWidth
1737);
1738
1739get_css_property!(
1740 get_css_min_height,
1741 get_min_height,
1742 LayoutMinHeight,
1743 azul_css::props::property::CssPropertyType::MinHeight
1744);
1745
1746get_css_property!(
1747 get_css_max_width,
1748 get_max_width,
1749 LayoutMaxWidth,
1750 azul_css::props::property::CssPropertyType::MaxWidth
1751);
1752
1753get_css_property!(
1754 get_css_max_height,
1755 get_max_height,
1756 LayoutMaxHeight,
1757 azul_css::props::property::CssPropertyType::MaxHeight
1758);
1759
1760get_css_property_pixel!(
1762 get_css_border_left_width,
1763 get_border_left_width,
1764 azul_css::props::property::CssPropertyType::BorderLeftWidth
1765);
1766get_css_property_pixel!(
1767 get_css_border_right_width,
1768 get_border_right_width,
1769 azul_css::props::property::CssPropertyType::BorderRightWidth
1770);
1771get_css_property_pixel!(
1772 get_css_border_top_width,
1773 get_border_top_width,
1774 azul_css::props::property::CssPropertyType::BorderTopWidth
1775);
1776get_css_property_pixel!(
1777 get_css_border_bottom_width,
1778 get_border_bottom_width,
1779 azul_css::props::property::CssPropertyType::BorderBottomWidth
1780);
1781
1782pub fn get_break_before(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
1786 let Some(id) = dom_id else {
1787 return PageBreak::Auto;
1788 };
1789 let node_data = &styled_dom.node_data.as_container()[id];
1790 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1791 styled_dom
1792 .css_property_cache
1793 .ptr
1794 .get_break_before(node_data, &id, node_state)
1795 .and_then(|v| v.get_property().cloned())
1796 .unwrap_or(PageBreak::Auto)
1797}
1798
1799pub fn get_break_after(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> PageBreak {
1801 let Some(id) = dom_id else {
1802 return PageBreak::Auto;
1803 };
1804 let node_data = &styled_dom.node_data.as_container()[id];
1805 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1806 styled_dom
1807 .css_property_cache
1808 .ptr
1809 .get_break_after(node_data, &id, node_state)
1810 .and_then(|v| v.get_property().cloned())
1811 .unwrap_or(PageBreak::Auto)
1812}
1813
1814pub fn is_forced_page_break(page_break: PageBreak) -> bool {
1816 matches!(
1817 page_break,
1818 PageBreak::Always
1819 | PageBreak::Page
1820 | PageBreak::Left
1821 | PageBreak::Right
1822 | PageBreak::Recto
1823 | PageBreak::Verso
1824 | PageBreak::All
1825 )
1826}
1827
1828pub fn get_break_inside(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> BreakInside {
1830 let Some(id) = dom_id else {
1831 return BreakInside::Auto;
1832 };
1833 let node_data = &styled_dom.node_data.as_container()[id];
1834 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1835 styled_dom
1836 .css_property_cache
1837 .ptr
1838 .get_break_inside(node_data, &id, node_state)
1839 .and_then(|v| v.get_property().cloned())
1840 .unwrap_or(BreakInside::Auto)
1841}
1842
1843pub fn get_orphans(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
1845 let Some(id) = dom_id else {
1846 return 2; };
1848 let node_data = &styled_dom.node_data.as_container()[id];
1849 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1850 styled_dom
1851 .css_property_cache
1852 .ptr
1853 .get_orphans(node_data, &id, node_state)
1854 .and_then(|v| v.get_property().cloned())
1855 .map(|o| o.inner)
1856 .unwrap_or(2)
1857}
1858
1859pub fn get_widows(styled_dom: &StyledDom, dom_id: Option<NodeId>) -> u32 {
1861 let Some(id) = dom_id else {
1862 return 2; };
1864 let node_data = &styled_dom.node_data.as_container()[id];
1865 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1866 styled_dom
1867 .css_property_cache
1868 .ptr
1869 .get_widows(node_data, &id, node_state)
1870 .and_then(|v| v.get_property().cloned())
1871 .map(|w| w.inner)
1872 .unwrap_or(2)
1873}
1874
1875pub fn get_box_decoration_break(
1877 styled_dom: &StyledDom,
1878 dom_id: Option<NodeId>,
1879) -> BoxDecorationBreak {
1880 let Some(id) = dom_id else {
1881 return BoxDecorationBreak::Slice;
1882 };
1883 let node_data = &styled_dom.node_data.as_container()[id];
1884 let node_state = &styled_dom.styled_nodes.as_container()[id].styled_node_state;
1885 styled_dom
1886 .css_property_cache
1887 .ptr
1888 .get_box_decoration_break(node_data, &id, node_state)
1889 .and_then(|v| v.get_property().cloned())
1890 .unwrap_or(BoxDecorationBreak::Slice)
1891}
1892
1893pub fn is_avoid_page_break(page_break: &PageBreak) -> bool {
1897 matches!(page_break, PageBreak::Avoid | PageBreak::AvoidPage)
1898}
1899
1900pub fn is_avoid_break_inside(break_inside: &BreakInside) -> bool {
1902 matches!(
1903 break_inside,
1904 BreakInside::Avoid | BreakInside::AvoidPage | BreakInside::AvoidColumn
1905 )
1906}
1907
1908use std::collections::HashMap;
1911
1912use rust_fontconfig::{FcFontCache, FcWeight, FontFallbackChain, PatternMatch};
1913
1914use crate::text3::cache::{FontChainKey, FontChainKeyOrRef, FontSelector, FontStack, FontStyle};
1915
1916#[derive(Debug, Clone)]
1919pub struct CollectedFontStacks {
1920 pub font_stacks: Vec<Vec<FontSelector>>,
1922 pub hash_to_index: HashMap<u64, usize>,
1924 pub font_refs: HashMap<usize, azul_css::props::basic::font::FontRef>,
1927}
1928
1929#[derive(Debug, Clone)]
1932pub struct ResolvedFontChains {
1933 pub chains: HashMap<FontChainKeyOrRef, FontFallbackChain>,
1937}
1938
1939impl ResolvedFontChains {
1940 pub fn get(&self, key: &FontChainKeyOrRef) -> Option<&FontFallbackChain> {
1942 self.chains.get(key)
1943 }
1944
1945 pub fn get_by_chain_key(&self, key: &FontChainKey) -> Option<&FontFallbackChain> {
1947 self.chains.get(&FontChainKeyOrRef::Chain(key.clone()))
1948 }
1949
1950 pub fn get_for_font_stack(&self, font_stack: &[FontSelector]) -> Option<&FontFallbackChain> {
1952 let key = FontChainKeyOrRef::Chain(FontChainKey::from_selectors(font_stack));
1953 self.chains.get(&key)
1954 }
1955
1956 pub fn get_for_font_ref(&self, ptr: usize) -> Option<&FontFallbackChain> {
1958 self.chains.get(&FontChainKeyOrRef::Ref(ptr))
1959 }
1960
1961 pub fn into_inner(self) -> HashMap<FontChainKeyOrRef, FontFallbackChain> {
1965 self.chains
1966 }
1967
1968 pub fn into_fontconfig_chains(self) -> HashMap<FontChainKey, FontFallbackChain> {
1973 self.chains
1974 .into_iter()
1975 .filter_map(|(key, chain)| {
1976 match key {
1977 FontChainKeyOrRef::Chain(chain_key) => Some((chain_key, chain)),
1978 FontChainKeyOrRef::Ref(_) => None,
1979 }
1980 })
1981 .collect()
1982 }
1983
1984 pub fn len(&self) -> usize {
1986 self.chains.len()
1987 }
1988
1989 pub fn is_empty(&self) -> bool {
1991 self.chains.is_empty()
1992 }
1993
1994 pub fn font_refs_len(&self) -> usize {
1996 self.chains.keys().filter(|k| k.is_ref()).count()
1997 }
1998}
1999
2000pub fn collect_font_stacks_from_styled_dom(styled_dom: &StyledDom) -> CollectedFontStacks {
2011 let mut font_stacks = Vec::new();
2012 let mut hash_to_index: HashMap<u64, usize> = HashMap::new();
2013 let mut seen_hashes = std::collections::HashSet::new();
2014 let mut font_refs: HashMap<usize, azul_css::props::basic::font::FontRef> = HashMap::new();
2015
2016 let node_data_container = styled_dom.node_data.as_container();
2017 let styled_nodes_container = styled_dom.styled_nodes.as_container();
2018 let cache = &styled_dom.css_property_cache.ptr;
2019
2020 for (node_idx, node_data) in node_data_container.internal.iter().enumerate() {
2022 if !matches!(node_data.node_type, NodeType::Text(_)) {
2024 continue;
2025 }
2026
2027 let dom_id = match NodeId::from_usize(node_idx) {
2028 Some(id) => id,
2029 None => continue,
2030 };
2031
2032 let node_state = &styled_nodes_container[dom_id].styled_node_state;
2033
2034 let font_families = cache
2036 .get_font_family(node_data, &dom_id, node_state)
2037 .and_then(|v| v.get_property().cloned())
2038 .unwrap_or_else(|| {
2039 StyleFontFamilyVec::from_vec(vec![StyleFontFamily::System("serif".into())])
2040 });
2041
2042 if let Some(first_family) = font_families.get(0) {
2045 if let StyleFontFamily::Ref(font_ref) = first_family {
2046 let ptr = font_ref.parsed as usize;
2047 if !font_refs.contains_key(&ptr) {
2048 font_refs.insert(ptr, font_ref.clone());
2049 }
2050 continue;
2052 }
2053 }
2054
2055 let font_weight = cache
2057 .get_font_weight(node_data, &dom_id, node_state)
2058 .and_then(|v| v.get_property().copied())
2059 .unwrap_or(azul_css::props::basic::font::StyleFontWeight::Normal);
2060
2061 let font_style = cache
2062 .get_font_style(node_data, &dom_id, node_state)
2063 .and_then(|v| v.get_property().copied())
2064 .unwrap_or(azul_css::props::basic::font::StyleFontStyle::Normal);
2065
2066 let fc_weight = super::fc::convert_font_weight(font_weight);
2068 let fc_style = super::fc::convert_font_style(font_style);
2069
2070 let mut font_stack = Vec::with_capacity(font_families.len() + 3);
2072
2073 for i in 0..font_families.len() {
2074 let family = font_families.get(i).unwrap();
2075 if matches!(family, StyleFontFamily::Ref(_)) {
2077 continue;
2078 }
2079 font_stack.push(FontSelector {
2080 family: family.as_string(),
2081 weight: fc_weight,
2082 style: fc_style,
2083 unicode_ranges: Vec::new(),
2084 });
2085 }
2086
2087 let generic_fallbacks = ["sans-serif", "serif", "monospace"];
2089 for fallback in &generic_fallbacks {
2090 if !font_stack
2091 .iter()
2092 .any(|f| f.family.to_lowercase() == fallback.to_lowercase())
2093 {
2094 font_stack.push(FontSelector {
2095 family: fallback.to_string(),
2096 weight: FcWeight::Normal,
2097 style: FontStyle::Normal,
2098 unicode_ranges: Vec::new(),
2099 });
2100 }
2101 }
2102
2103 if font_stack.is_empty() {
2105 continue;
2106 }
2107
2108 let key = FontChainKey::from_selectors(&font_stack);
2110 let hash = {
2111 use std::hash::{Hash, Hasher};
2112 let mut hasher = std::collections::hash_map::DefaultHasher::new();
2113 key.hash(&mut hasher);
2114 hasher.finish()
2115 };
2116
2117 if !seen_hashes.contains(&hash) {
2119 seen_hashes.insert(hash);
2120 let idx = font_stacks.len();
2121 font_stacks.push(font_stack);
2122 hash_to_index.insert(hash, idx);
2123 }
2124 }
2125
2126 CollectedFontStacks {
2127 font_stacks,
2128 hash_to_index,
2129 font_refs,
2130 }
2131}
2132
2133pub fn resolve_font_chains(
2145 collected: &CollectedFontStacks,
2146 fc_cache: &FcFontCache,
2147) -> ResolvedFontChains {
2148 let mut chains = HashMap::new();
2149
2150 for font_stack in &collected.font_stacks {
2152 if font_stack.is_empty() {
2153 continue;
2154 }
2155
2156 let font_families: Vec<String> = font_stack
2158 .iter()
2159 .map(|s| s.family.clone())
2160 .filter(|f| !f.is_empty())
2161 .collect();
2162
2163 let font_families = if font_families.is_empty() {
2164 vec!["sans-serif".to_string()]
2165 } else {
2166 font_families
2167 };
2168
2169 let weight = font_stack[0].weight;
2170 let is_italic = font_stack[0].style == FontStyle::Italic;
2171 let is_oblique = font_stack[0].style == FontStyle::Oblique;
2172
2173 let cache_key = FontChainKeyOrRef::Chain(FontChainKey {
2174 font_families: font_families.clone(),
2175 weight,
2176 italic: is_italic,
2177 oblique: is_oblique,
2178 });
2179
2180 if chains.contains_key(&cache_key) {
2182 continue;
2183 }
2184
2185 let italic = if is_italic {
2187 PatternMatch::True
2188 } else {
2189 PatternMatch::DontCare
2190 };
2191 let oblique = if is_oblique {
2192 PatternMatch::True
2193 } else {
2194 PatternMatch::DontCare
2195 };
2196
2197 let mut trace = Vec::new();
2198 let chain =
2199 fc_cache.resolve_font_chain(&font_families, weight, italic, oblique, &mut trace);
2200
2201 chains.insert(cache_key, chain);
2202 }
2203
2204 for (ptr, _font_ref) in &collected.font_refs {
2210 let cache_key = FontChainKeyOrRef::Ref(*ptr);
2211
2212 let _ = cache_key; }
2217
2218 ResolvedFontChains { chains }
2219}
2220
2221pub fn collect_and_resolve_font_chains(
2230 styled_dom: &StyledDom,
2231 fc_cache: &FcFontCache,
2232) -> ResolvedFontChains {
2233 let collected = collect_font_stacks_from_styled_dom(styled_dom);
2234 resolve_font_chains(&collected, fc_cache)
2235}
2236
2237pub fn register_embedded_fonts_from_styled_dom<T: crate::font_traits::ParsedFontTrait>(
2242 styled_dom: &StyledDom,
2243 font_manager: &crate::text3::cache::FontManager<T>,
2244) {
2245 let collected = collect_font_stacks_from_styled_dom(styled_dom);
2246 for (_ptr, font_ref) in &collected.font_refs {
2247 font_manager.register_embedded_font(font_ref);
2248 }
2249}
2250
2251use std::collections::HashSet;
2254
2255use rust_fontconfig::FontId;
2256
2257pub fn collect_font_ids_from_chains(chains: &ResolvedFontChains) -> HashSet<FontId> {
2262 let mut font_ids = HashSet::new();
2263
2264 for chain in chains.chains.values() {
2265 for group in &chain.css_fallbacks {
2267 for font in &group.fonts {
2268 font_ids.insert(font.id);
2269 }
2270 }
2271
2272 for font in &chain.unicode_fallbacks {
2274 font_ids.insert(font.id);
2275 }
2276 }
2277
2278 font_ids
2279}
2280
2281pub fn compute_fonts_to_load(
2290 required_fonts: &HashSet<FontId>,
2291 already_loaded: &HashSet<FontId>,
2292) -> HashSet<FontId> {
2293 required_fonts.difference(already_loaded).cloned().collect()
2294}
2295
2296#[derive(Debug)]
2298pub struct FontLoadResult<T> {
2299 pub loaded: HashMap<FontId, T>,
2301 pub failed: Vec<(FontId, String)>,
2303}
2304
2305pub fn load_fonts_from_disk<T, F>(
2319 font_ids: &HashSet<FontId>,
2320 fc_cache: &FcFontCache,
2321 load_fn: F,
2322) -> FontLoadResult<T>
2323where
2324 F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
2325{
2326 let mut loaded = HashMap::new();
2327 let mut failed = Vec::new();
2328
2329 for font_id in font_ids {
2330 let font_bytes = match fc_cache.get_font_bytes(font_id) {
2332 Some(bytes) => bytes,
2333 None => {
2334 failed.push((
2335 *font_id,
2336 format!("Could not get font bytes for {:?}", font_id),
2337 ));
2338 continue;
2339 }
2340 };
2341
2342 let font_index = fc_cache
2344 .get_font_by_id(font_id)
2345 .and_then(|source| match source {
2346 rust_fontconfig::FontSource::Disk(path) => Some(path.font_index),
2347 rust_fontconfig::FontSource::Memory(font) => Some(font.font_index),
2348 })
2349 .unwrap_or(0) as usize;
2350
2351 match load_fn(&font_bytes, font_index) {
2353 Ok(font) => {
2354 loaded.insert(*font_id, font);
2355 }
2356 Err(e) => {
2357 failed.push((
2358 *font_id,
2359 format!("Failed to parse font {:?}: {:?}", font_id, e),
2360 ));
2361 }
2362 }
2363 }
2364
2365 FontLoadResult { loaded, failed }
2366}
2367
2368pub fn resolve_and_load_fonts<T, F>(
2386 styled_dom: &StyledDom,
2387 fc_cache: &FcFontCache,
2388 already_loaded: &HashSet<FontId>,
2389 load_fn: F,
2390) -> (ResolvedFontChains, FontLoadResult<T>)
2391where
2392 F: Fn(&[u8], usize) -> Result<T, crate::text3::cache::LayoutError>,
2393{
2394 let chains = collect_and_resolve_font_chains(styled_dom, fc_cache);
2396
2397 let required_fonts = collect_font_ids_from_chains(&chains);
2399
2400 let fonts_to_load = compute_fonts_to_load(&required_fonts, already_loaded);
2402
2403 let load_result = load_fonts_from_disk(&fonts_to_load, fc_cache, load_fn);
2405
2406 (chains, load_result)
2407}
2408
2409use azul_css::props::style::scrollbar::{
2414 LayoutScrollbarWidth, ScrollbarColorCustom, ScrollbarInfo, StyleScrollbarColor,
2415 SCROLLBAR_CLASSIC_LIGHT,
2416};
2417
2418#[derive(Debug, Clone)]
2420pub struct ComputedScrollbarStyle {
2421 pub width_mode: LayoutScrollbarWidth,
2423 pub width_px: f32,
2425 pub thumb_color: ColorU,
2427 pub track_color: ColorU,
2429 pub button_color: ColorU,
2431 pub corner_color: ColorU,
2433 pub clip_to_container_border: bool,
2435}
2436
2437impl Default for ComputedScrollbarStyle {
2438 fn default() -> Self {
2439 Self {
2440 width_mode: LayoutScrollbarWidth::Auto,
2441 width_px: 16.0, thumb_color: ColorU::new(255, 0, 255, 255), track_color: ColorU::new(255, 165, 0, 255), button_color: ColorU::new(0, 255, 0, 255), corner_color: ColorU::new(0, 0, 255, 255), clip_to_container_border: false,
2448 }
2449 }
2450}
2451
2452pub fn get_scrollbar_style(
2459 styled_dom: &StyledDom,
2460 node_id: NodeId,
2461 node_state: &StyledNodeState,
2462) -> ComputedScrollbarStyle {
2463 let node_data = &styled_dom.node_data.as_container()[node_id];
2464
2465 let mut result = ComputedScrollbarStyle::default();
2467
2468 if let Some(scrollbar_style) = styled_dom
2470 .css_property_cache
2471 .ptr
2472 .get_scrollbar_style(node_data, &node_id, node_state)
2473 .and_then(|v| v.get_property())
2474 {
2475 result.width_px = match scrollbar_style.horizontal.width {
2477 azul_css::props::layout::dimensions::LayoutWidth::Px(px) => {
2478 px.to_pixels_internal(16.0, 16.0)
2480 }
2481 _ => 16.0,
2482 };
2483 result.thumb_color = extract_color_from_background(&scrollbar_style.horizontal.thumb);
2484 result.track_color = extract_color_from_background(&scrollbar_style.horizontal.track);
2485 result.button_color = extract_color_from_background(&scrollbar_style.horizontal.button);
2486 result.corner_color = extract_color_from_background(&scrollbar_style.horizontal.corner);
2487 result.clip_to_container_border = scrollbar_style.horizontal.clip_to_container_border;
2488 }
2489
2490 if let Some(scrollbar_width) = styled_dom
2492 .css_property_cache
2493 .ptr
2494 .get_scrollbar_width(node_data, &node_id, node_state)
2495 .and_then(|v| v.get_property())
2496 {
2497 result.width_mode = *scrollbar_width;
2498 result.width_px = match scrollbar_width {
2499 LayoutScrollbarWidth::Auto => 16.0,
2500 LayoutScrollbarWidth::Thin => 8.0,
2501 LayoutScrollbarWidth::None => 0.0,
2502 };
2503 }
2504
2505 if let Some(scrollbar_color) = styled_dom
2507 .css_property_cache
2508 .ptr
2509 .get_scrollbar_color(node_data, &node_id, node_state)
2510 .and_then(|v| v.get_property())
2511 {
2512 match scrollbar_color {
2513 StyleScrollbarColor::Auto => {
2514 }
2516 StyleScrollbarColor::Custom(custom) => {
2517 result.thumb_color = custom.thumb;
2518 result.track_color = custom.track;
2519 }
2520 }
2521 }
2522
2523 result
2524}
2525
2526fn extract_color_from_background(
2528 bg: &azul_css::props::style::background::StyleBackgroundContent,
2529) -> ColorU {
2530 use azul_css::props::style::background::StyleBackgroundContent;
2531 match bg {
2532 StyleBackgroundContent::Color(c) => *c,
2533 _ => ColorU::TRANSPARENT,
2534 }
2535}
2536
2537pub fn should_clip_scrollbar_to_border(
2539 styled_dom: &StyledDom,
2540 node_id: NodeId,
2541 node_state: &StyledNodeState,
2542) -> bool {
2543 let style = get_scrollbar_style(styled_dom, node_id, node_state);
2544 style.clip_to_container_border
2545}
2546
2547pub fn get_scrollbar_width_px(
2549 styled_dom: &StyledDom,
2550 node_id: NodeId,
2551 node_state: &StyledNodeState,
2552) -> f32 {
2553 let style = get_scrollbar_style(styled_dom, node_id, node_state);
2554 style.width_px
2555}
2556
2557pub fn is_text_selectable(
2562 styled_dom: &StyledDom,
2563 node_id: NodeId,
2564 node_state: &StyledNodeState,
2565) -> bool {
2566 let node_data = &styled_dom.node_data.as_container()[node_id];
2567
2568 styled_dom
2569 .css_property_cache
2570 .ptr
2571 .get_user_select(node_data, &node_id, node_state)
2572 .and_then(|v| v.get_property())
2573 .map(|us| *us != StyleUserSelect::None)
2574 .unwrap_or(true) }
2576
2577pub fn is_node_contenteditable(styled_dom: &StyledDom, node_id: NodeId) -> bool {
2585 use azul_core::dom::AttributeType;
2586
2587 let node_data = &styled_dom.node_data.as_container()[node_id];
2588
2589 if node_data.is_contenteditable() {
2591 return true;
2592 }
2593
2594 node_data.attributes.as_ref().iter().any(|attr| {
2597 matches!(attr, AttributeType::ContentEditable(true))
2598 })
2599}
2600
2601pub fn is_node_contenteditable_inherited(styled_dom: &StyledDom, node_id: NodeId) -> bool {
2627 use azul_core::dom::AttributeType;
2628
2629 let node_data_container = styled_dom.node_data.as_container();
2630 let hierarchy = styled_dom.node_hierarchy.as_container();
2631
2632 let mut current_node_id = Some(node_id);
2633
2634 while let Some(nid) = current_node_id {
2635 let node_data = &node_data_container[nid];
2636
2637 if node_data.is_contenteditable() {
2640 return true;
2641 }
2642
2643 for attr in node_data.attributes.as_ref().iter() {
2646 if let AttributeType::ContentEditable(is_editable) = attr {
2647 return *is_editable;
2650 }
2651 }
2652
2653 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
2655 }
2656
2657 false
2659}
2660
2661pub fn find_contenteditable_ancestor(styled_dom: &StyledDom, node_id: NodeId) -> Option<NodeId> {
2671 use azul_core::dom::AttributeType;
2672
2673 let node_data_container = styled_dom.node_data.as_container();
2674 let hierarchy = styled_dom.node_hierarchy.as_container();
2675
2676 let mut current_node_id = Some(node_id);
2677
2678 while let Some(nid) = current_node_id {
2679 let node_data = &node_data_container[nid];
2680
2681 if node_data.is_contenteditable() {
2683 return Some(nid);
2684 }
2685
2686 for attr in node_data.attributes.as_ref().iter() {
2688 if let AttributeType::ContentEditable(is_editable) = attr {
2689 if *is_editable {
2690 return Some(nid);
2691 } else {
2692 return None;
2694 }
2695 }
2696 }
2697
2698 current_node_id = hierarchy.get(nid).and_then(|h| h.parent_id());
2700 }
2701
2702 None
2703}