azul_core/
display_list.rs

1use std::{
2    fmt,
3    collections::BTreeMap,
4    rc::Rc,
5};
6use azul_css::{
7    LayoutPoint, LayoutSize, LayoutRect,
8    StyleBackgroundRepeat, StyleBackgroundPosition, ColorU, BoxShadowClipMode,
9    LinearGradient, RadialGradient, BoxShadowPreDisplayItem, StyleBackgroundSize,
10    CssPropertyValue, CssProperty, RectStyle, RectLayout,
11
12    StyleBorderTopWidth, StyleBorderRightWidth, StyleBorderBottomWidth, StyleBorderLeftWidth,
13    StyleBorderTopColor, StyleBorderRightColor, StyleBorderBottomColor, StyleBorderLeftColor,
14    StyleBorderTopStyle, StyleBorderRightStyle, StyleBorderBottomStyle, StyleBorderLeftStyle,
15    StyleBorderTopLeftRadius, StyleBorderTopRightRadius, StyleBorderBottomLeftRadius, StyleBorderBottomRightRadius,
16};
17use crate::{
18    FastHashMap,
19    callbacks::PipelineId,
20    ui_solver::{
21        PositionedRectangle, ResolvedOffsets, ExternalScrollId,
22        LayoutResult, ScrolledNodes, OverflowingScrollNode,
23        PositionInfo,
24    },
25    window::FullWindowState,
26    app_resources::{
27        AppResources, AddImageMsg, FontImageApi, ImageDescriptor, ImageDescriptorFlags,
28        ImageKey, FontInstanceKey, ImageInfo, ImageId, LayoutedGlyphs, PrimitiveFlags,
29        Epoch, ExternalImageId, GlyphOptions, LoadFontFn, LoadImageFn,
30    },
31    ui_state::UiState,
32    ui_description::{UiDescription, StyledNode},
33    id_tree::{NodeDataContainer, NodeId, NodeHierarchy},
34    dom::{
35        DomId, NodeData, TagId, ScrollTagId, DomString,
36    },
37};
38#[cfg(feature = "opengl")]
39use crate::gl::Texture;
40#[cfg(feature = "opengl")]
41use gleam::gl::Gl;
42
43pub type GlyphIndex = u32;
44
45/// Parse a string in the format of "600x100" -> (600, 100)
46pub fn parse_display_list_size(output_size: &str) -> Option<(f32, f32)> {
47    let output_size = output_size.trim();
48    let mut iter = output_size.split("x");
49    let w = iter.next()?;
50    let h = iter.next()?;
51    let w = w.trim();
52    let h = h.trim();
53    let w = w.parse::<f32>().ok()?;
54    let h = h.parse::<f32>().ok()?;
55    Some((w, h))
56}
57
58#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
59pub struct GlyphInstance {
60    pub index: GlyphIndex,
61    pub point: LayoutPoint,
62    pub size: LayoutSize,
63}
64
65#[derive(Debug, Clone, PartialEq, PartialOrd)]
66pub struct CachedDisplayList {
67    pub root: DisplayListMsg,
68}
69
70impl CachedDisplayList {
71    pub fn empty(size: LayoutSize, origin: LayoutPoint) -> Self {
72        Self { root: DisplayListMsg::Frame(DisplayListFrame::root(size, origin)) }
73    }
74
75    pub fn new(
76            epoch: Epoch,
77            pipeline_id: PipelineId,
78            full_window_state: &FullWindowState,
79            ui_state_cache: &BTreeMap<DomId, UiState>,
80            layout_result_cache: &SolvedLayoutCache,
81            gl_texture_cache: &GlTextureCache,
82            app_resources: &AppResources,
83    ) -> Self {
84        const DOM_ID: DomId = DomId::ROOT_ID;
85        CachedDisplayList {
86            root: push_rectangles_into_displaylist(
87                &layout_result_cache.rects_in_rendering_order[&DOM_ID],
88                &DisplayListParametersRef {
89                    dom_id: DOM_ID,
90                    epoch,
91                    full_window_state,
92                    pipeline_id,
93                    layout_result: layout_result_cache,
94                    gl_texture_cache,
95                    ui_state_cache,
96                    app_resources,
97                },
98            )
99        }
100    }
101}
102
103#[derive(Debug, Clone, PartialEq, PartialOrd)]
104pub enum DisplayListMsg {
105    Frame(DisplayListFrame),
106    ScrollFrame(DisplayListScrollFrame),
107}
108
109impl DisplayListMsg {
110
111    pub fn get_position(&self) -> PositionInfo {
112        use self::DisplayListMsg::*;
113        match self {
114            Frame(f) => f.position,
115            ScrollFrame(sf) => sf.frame.position,
116        }
117    }
118
119    pub fn append_child(&mut self, child: Self) {
120        use self::DisplayListMsg::*;
121        match self {
122            Frame(f) => { f.children.push(child); },
123            ScrollFrame(sf) => { sf.frame.children.push(child); },
124        }
125    }
126
127    pub fn append_children(&mut self, mut children: Vec<Self>) {
128        use self::DisplayListMsg::*;
129        match self {
130            Frame(f) => { f.children.append(&mut children); },
131            ScrollFrame(sf) => { sf.frame.children.append(&mut children); },
132        }
133    }
134
135    pub fn get_size(&self) -> LayoutSize {
136        use self::DisplayListMsg::*;
137        match self {
138            Frame(f) => f.size,
139            ScrollFrame(sf) => sf.frame.size,
140        }
141    }
142}
143
144#[derive(Clone, PartialEq, PartialOrd)]
145pub struct DisplayListScrollFrame {
146    /// Bounding rect of the (overflowing) content of the scroll frame
147    pub content_rect: LayoutRect,
148    /// The scroll ID is the hash of the DOM node, so that scrolling
149    /// positions can be tracked across multiple frames
150    pub scroll_id: ExternalScrollId,
151    /// The scroll tag is used for hit-testing
152    pub scroll_tag: ScrollTagId,
153    /// Content + children of the scroll clip
154    pub frame: DisplayListFrame,
155}
156
157impl fmt::Debug for DisplayListScrollFrame {
158    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159        write!(f, "DisplayListScrollFrame {{\r\n")?;
160        write!(f, "    content_rect: {}\r\n", self.content_rect)?;
161        write!(f, "    scroll_tag: {}\r\n", self.scroll_tag)?;
162        write!(f, "    frame: DisplayListFrame {{\r\n")?;
163        let frame = format!("{:#?}", self.frame);
164        let frame = frame.lines().map(|l| format!("        {}", l)).collect::<Vec<_>>().join("\r\n");
165        write!(f, "{}\r\n", frame)?;
166        write!(f, "    }}\r\n")?;
167        write!(f, "}}")?;
168        Ok(())
169    }
170}
171
172#[derive(Clone, PartialEq, PartialOrd)]
173pub struct DisplayListFrame {
174    pub size: LayoutSize,
175    pub position: PositionInfo,
176    /// Border radius, set to none only if overflow: visible is set!
177    pub border_radius: StyleBorderRadius,
178    pub tag: Option<TagId>,
179    pub content: Vec<LayoutRectContent>,
180    pub children: Vec<DisplayListMsg>,
181    pub flags: PrimitiveFlags,
182}
183
184impl fmt::Debug for DisplayListFrame {
185    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
186        let print_no_comma_rect =
187            !self.border_radius.is_none() ||
188            self.tag.is_some() ||
189            !self.content.is_empty() ||
190            !self.children.is_empty();
191
192        write!(f, "rect: {:#?} @ {:?}{}", self.size, self.position, if !print_no_comma_rect { "" } else { "," })?;
193
194        if !self.border_radius.is_none() {
195            write!(f, "\r\nborder_radius: {:#?}", self.border_radius)?;
196        }
197        if let Some(tag) = &self.tag {
198            write!(f, "\r\ntag: {}", tag.0)?;
199        }
200        if !self.content.is_empty() {
201            write!(f, "\r\ncontent: {:#?}", self.content)?;
202        }
203        if !self.children.is_empty() {
204            write!(f, "\r\nchildren: {:#?}", self.children)?;
205        }
206
207        Ok(())
208    }
209}
210
211impl DisplayListFrame {
212    pub fn root(dimensions: LayoutSize, root_origin: LayoutPoint) -> Self {
213        DisplayListFrame {
214            tag: None,
215            size: dimensions,
216            position: PositionInfo::Static { x_offset: root_origin.x, y_offset: root_origin.y },
217            flags: PrimitiveFlags {
218                is_backface_visible: true,
219                is_scrollbar_container: false,
220                is_scrollbar_thumb: false,
221            },
222            border_radius: StyleBorderRadius::default(),
223            content: vec![],
224            children: vec![],
225        }
226    }
227}
228
229#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
230pub enum ImageRendering {
231    Auto,
232    CrispEdges,
233    Pixelated,
234}
235
236#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
237pub enum AlphaType {
238    Alpha,
239    PremultipliedAlpha,
240}
241
242#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
243pub struct StyleBorderRadius {
244    pub top_left: Option<CssPropertyValue<StyleBorderTopLeftRadius>>,
245    pub top_right: Option<CssPropertyValue<StyleBorderTopRightRadius>>,
246    pub bottom_left: Option<CssPropertyValue<StyleBorderBottomLeftRadius>>,
247    pub bottom_right: Option<CssPropertyValue<StyleBorderBottomRightRadius>>,
248}
249
250impl StyleBorderRadius {
251    pub fn is_none(&self) -> bool {
252        self.top_left.is_none() &&
253        self.top_right.is_none() &&
254        self.bottom_left.is_none() &&
255        self.bottom_right.is_none()
256    }
257}
258impl fmt::Debug for StyleBorderRadius {
259    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
260        write!(f, "StyleBorderRadius {{")?;
261        if let Some(tl) = &self.top_left {
262            write!(f, "\r\n\ttop-left: {},", tl)?;
263        }
264        if let Some(tr) = &self.top_right {
265            write!(f, "\r\n\ttop-right: {},", tr)?;
266        }
267        if let Some(bl) = &self.bottom_left {
268            write!(f, "\r\n\tbottom-left: {},", bl)?;
269        }
270        if let Some(br) = &self.bottom_right {
271            write!(f, "\r\n\tbottom-right: {},", br)?;
272        }
273        write!(f, "\r\n}}")
274    }
275}
276
277macro_rules! tlbr_debug {($struct_name:ident) => (
278    impl fmt::Debug for $struct_name {
279        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
280            write!(f, "{} {{", stringify!($struct_name))?;
281            if let Some(t) = &self.top {
282                write!(f, "\r\n\ttop: {},", t)?;
283            }
284            if let Some(r) = &self.right {
285                write!(f, "\r\n\tright: {},", r)?;
286            }
287            if let Some(b) = &self.bottom {
288                write!(f, "\r\n\tbottom: {},", b)?;
289            }
290            if let Some(l) = &self.left {
291                write!(f, "\r\n\tleft: {},", l)?;
292            }
293            write!(f, "\r\n}}")
294        }
295    }
296)}
297
298#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
299pub struct StyleBorderWidths {
300    pub top: Option<CssPropertyValue<StyleBorderTopWidth>>,
301    pub right: Option<CssPropertyValue<StyleBorderRightWidth>>,
302    pub bottom: Option<CssPropertyValue<StyleBorderBottomWidth>>,
303    pub left: Option<CssPropertyValue<StyleBorderLeftWidth>>,
304}
305
306impl StyleBorderWidths {
307
308    #[inline]
309    pub fn left_width(&self) -> f32 {
310        self.left.unwrap_or_default().get_property_owned().unwrap_or_default().0.to_pixels(0.0)
311    }
312
313    #[inline]
314    pub fn right_width(&self) -> f32 {
315        self.right.unwrap_or_default().get_property_owned().unwrap_or_default().0.to_pixels(0.0)
316    }
317
318    #[inline]
319    pub fn top_width(&self) -> f32 {
320        self.top.unwrap_or_default().get_property_owned().unwrap_or_default().0.to_pixels(0.0)
321    }
322
323    #[inline]
324    pub fn bottom_width(&self) -> f32 {
325        self.bottom.unwrap_or_default().get_property_owned().unwrap_or_default().0.to_pixels(0.0)
326    }
327
328    #[inline]
329    pub fn total_horizontal(&self) -> f32 {
330        self.left_width() + self.right_width()
331    }
332
333    #[inline]
334    pub fn total_vertical(&self) -> f32 {
335        self.top_width() + self.bottom_width()
336    }
337}
338
339tlbr_debug!(StyleBorderWidths);
340
341#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
342pub struct StyleBorderColors {
343    pub top: Option<CssPropertyValue<StyleBorderTopColor>>,
344    pub right: Option<CssPropertyValue<StyleBorderRightColor>>,
345    pub bottom: Option<CssPropertyValue<StyleBorderBottomColor>>,
346    pub left: Option<CssPropertyValue<StyleBorderLeftColor>>,
347}
348
349tlbr_debug!(StyleBorderColors);
350
351#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
352pub struct StyleBorderStyles {
353    pub top: Option<CssPropertyValue<StyleBorderTopStyle>>,
354    pub right: Option<CssPropertyValue<StyleBorderRightStyle>>,
355    pub bottom: Option<CssPropertyValue<StyleBorderBottomStyle>>,
356    pub left: Option<CssPropertyValue<StyleBorderLeftStyle>>,
357}
358
359tlbr_debug!(StyleBorderStyles);
360
361#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
362pub struct StyleBoxShadow {
363    pub top: Option<CssPropertyValue<BoxShadowPreDisplayItem>>,
364    pub right: Option<CssPropertyValue<BoxShadowPreDisplayItem>>,
365    pub bottom: Option<CssPropertyValue<BoxShadowPreDisplayItem>>,
366    pub left: Option<CssPropertyValue<BoxShadowPreDisplayItem>>,
367}
368
369tlbr_debug!(StyleBoxShadow);
370
371#[derive(Clone, PartialEq, PartialOrd)]
372pub enum LayoutRectContent {
373    Text {
374        glyphs: Vec<GlyphInstance>,
375        font_instance_key: FontInstanceKey,
376        color: ColorU,
377        glyph_options: Option<GlyphOptions>,
378        overflow: (bool, bool),
379    },
380    Background {
381        content: RectBackground,
382        size: Option<StyleBackgroundSize>,
383        offset: Option<StyleBackgroundPosition>,
384        repeat: Option<StyleBackgroundRepeat>,
385    },
386    Image {
387        size: LayoutSize,
388        offset: LayoutPoint,
389        image_rendering: ImageRendering,
390        alpha_type: AlphaType,
391        image_key: ImageKey,
392        background_color: ColorU,
393    },
394    Border {
395        widths: StyleBorderWidths,
396        colors: StyleBorderColors,
397        styles: StyleBorderStyles,
398    },
399    BoxShadow {
400        shadow: StyleBoxShadow,
401        clip_mode: BoxShadowClipMode,
402    },
403}
404
405impl fmt::Debug for LayoutRectContent {
406    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
407        use self::LayoutRectContent::*;
408        match self {
409            Text { glyphs, font_instance_key, color, glyph_options, overflow } => {
410                write!(f,
411                    "Text {{\r\n\
412                        glyphs: {:?},\r\n\
413                        font_instance_key: {:?},\r\n\
414                        color: {:?},\r\n\
415                        glyph_options: {:?},\r\n\
416                        overflow: {:?},\r\n\
417                    }}",
418                    glyphs, font_instance_key, color, glyph_options, overflow
419                )
420            },
421            Background { content, size, offset, repeat } => {
422                write!(f, "Background {{\r\n")?;
423                write!(f, "    content: {:?},\r\n", content)?;
424                if let Some(size) = size {
425                    write!(f, "    size: {:?},\r\n", size)?;
426                }
427                if let Some(offset) = offset {
428                    write!(f, "    offset: {:?},\r\n", offset)?;
429                }
430                if let Some(repeat) = repeat {
431                    write!(f, "    repeat: {:?},\r\n", repeat)?;
432                }
433                write!(f, "}}")
434            },
435            Image { size, offset, image_rendering, alpha_type, image_key, background_color } => {
436                write!(f,
437                    "Image {{\r\n\
438                        size: {:?},\r\n\
439                        offset: {:?},\r\n\
440                        image_rendering: {:?},\r\n\
441                        alpha_type: {:?},\r\n\
442                        image_key: {:?},\r\n\
443                        background_color: {:?}\r\n\
444                    }}",
445                    size, offset, image_rendering, alpha_type, image_key, background_color
446                )
447            },
448            Border { widths, colors, styles, } => {
449                write!(f,
450                    "Border {{\r\n\
451                        widths: {:?},\r\n\
452                        colors: {:?},\r\n\
453                        styles: {:?}\r\n\
454                    }}",
455                    widths, colors, styles,
456                )
457            },
458            BoxShadow { shadow, clip_mode } => {
459                write!(f,
460                    "BoxShadow {{\r\n\
461                        shadow: {:?},\r\n\
462                        clip_mode: {:?}\r\n\
463                    }}",
464                    shadow, clip_mode,
465                )
466            },
467        }
468    }
469}
470
471#[derive(Clone, PartialEq, PartialOrd)]
472pub enum RectBackground {
473    LinearGradient(LinearGradient),
474    RadialGradient(RadialGradient),
475    Image(ImageInfo),
476    Color(ColorU),
477}
478
479impl fmt::Debug for RectBackground {
480    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
481        use self::RectBackground::*;
482        match self {
483            LinearGradient(l) => write!(f, "{}", l),
484            RadialGradient(r) => write!(f, "{}", r),
485            Image(id) => write!(f, "image({:#?})", id),
486            Color(c) => write!(f, "{}", c),
487        }
488    }
489}
490
491impl RectBackground {
492    pub fn get_content_size(&self) -> Option<(f32, f32)> {
493        match self {
494            RectBackground::Image(info) => { let dim = info.get_dimensions(); Some((dim.0 as f32, dim.1 as f32)) }
495            _ => None,
496        }
497    }
498}
499
500// ------------------- NEW DISPLAY LIST CODE
501
502pub struct DisplayList {
503    pub rectangles: NodeDataContainer<DisplayRectangle>
504}
505
506impl DisplayList {
507
508    /// NOTE: This function assumes that the UiDescription has an initialized arena
509    pub fn new(ui_description: &UiDescription, ui_state: &UiState) -> Self {
510
511        let arena = &ui_state.dom.arena;
512
513        let mut override_warnings = Vec::new();
514
515        let display_rect_arena = arena.node_data.transform(|_, node_id| {
516            let tag = ui_state.node_ids_to_tag_ids.get(&node_id).map(|tag| *tag);
517            let style = &ui_description.styled_nodes[node_id];
518            let mut rect = DisplayRectangle::new(tag);
519            override_warnings.append(&mut populate_css_properties(&mut rect, node_id, &ui_description.dynamic_css_overrides, &style));
520            rect
521        });
522
523        #[cfg(feature = "logging")] {
524            for warning in override_warnings {
525                error!(
526                    "Cannot override {} with {:?}",
527                    warning.default.get_type(), warning.overridden_property,
528                )
529            }
530        }
531
532        DisplayList {
533            rectangles: display_rect_arena,
534        }
535    }
536}
537
538/// Since the display list can take a lot of parameters, we don't want to
539/// continually pass them as parameters of the function and rather use a
540/// struct to pass them around. This is purely for ergonomic reasons.
541///
542/// `DisplayListParametersRef` has only members that are
543///  **immutable references** to other things that need to be passed down the display list
544#[derive(Clone)]
545pub struct DisplayListParametersRef<'a> {
546    /// ID of this Dom
547    pub dom_id: DomId,
548    /// Epoch of all the OpenGL textures
549    pub epoch: Epoch,
550    /// The CSS that should be applied to the DOM
551    pub full_window_state: &'a FullWindowState,
552    /// The current pipeline of the display list
553    pub pipeline_id: PipelineId,
554    /// Cached layouts (+ solved layouts for iframes)
555    pub layout_result: &'a SolvedLayoutCache,
556    /// Cached rendered OpenGL textures
557    pub gl_texture_cache: &'a GlTextureCache,
558    /// Reference to the UIState, for access to `node_hierarchy` and `node_data`
559    pub ui_state_cache: &'a BTreeMap<DomId, UiState>,
560    /// Reference to the AppResources, necessary to query info about image and font keys
561    pub app_resources: &'a AppResources,
562}
563
564/// DisplayRectangle is the main type which the layout parsing step gets operated on.
565#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
566pub struct DisplayRectangle {
567    /// `Some(id)` if this rectangle has a callback attached to it
568    /// Note: this is not the same as the `NodeId`!
569    /// These two are completely separate numbers!
570    pub tag: Option<TagId>,
571    /// The style properties of the node, parsed
572    pub style: RectStyle,
573    /// The layout properties of the node, parsed
574    pub layout: RectLayout,
575}
576
577impl DisplayRectangle {
578    #[inline]
579    pub fn new(tag: Option<TagId>) -> Self {
580        Self {
581            tag,
582            style: RectStyle::default(),
583            layout: RectLayout::default(),
584        }
585    }
586}
587
588#[derive(Debug, Clone, PartialEq)]
589pub struct ContentGroup {
590    /// The parent of the current node group, i.e. either the root node (0)
591    /// or the last positioned node ()
592    pub root: NodeId,
593    /// Node ids in order of drawing
594    pub children: Vec<ContentGroup>,
595}
596
597#[derive(Default)]
598pub struct SolvedLayoutCache {
599    pub solved_layouts: BTreeMap<DomId, LayoutResult>,
600    pub display_lists: BTreeMap<DomId, DisplayList>,
601    pub iframe_mappings: BTreeMap<(DomId, NodeId), DomId>,
602    pub scrollable_nodes: BTreeMap<DomId, ScrolledNodes>,
603    pub rects_in_rendering_order: BTreeMap<DomId, ContentGroup>,
604}
605
606#[derive(Default)]
607pub struct GlTextureCache {
608    pub solved_textures: BTreeMap<DomId, BTreeMap<NodeId, (ImageKey, ImageDescriptor)>>,
609}
610
611// todo: very unclean
612pub type LayoutFn = fn(&NodeHierarchy, &NodeDataContainer<NodeData>, &NodeDataContainer<DisplayRectangle>, &AppResources, &PipelineId, LayoutRect) -> LayoutResult;
613#[cfg(feature = "opengl")]
614pub type GlStoreImageFn = fn(PipelineId, Epoch, Texture) -> ExternalImageId;
615
616#[derive(Default)]
617pub struct SolvedLayout {
618    pub solved_layout_cache: SolvedLayoutCache,
619    pub gl_texture_cache: GlTextureCache,
620}
621
622impl SolvedLayout {
623
624    /// Does the layout, updates the image + font resources for the RenderAPI
625    #[cfg(feature = "opengl")]
626    pub fn new<U: FontImageApi>(
627        epoch: Epoch,
628        pipeline_id: PipelineId,
629        full_window_state: &FullWindowState,
630        gl_context: Rc<dyn Gl>,
631        render_api: &mut U,
632        app_resources: &mut AppResources,
633        ui_states: &mut BTreeMap<DomId, UiState>,
634        ui_descriptions: &mut BTreeMap<DomId, UiDescription>,
635        insert_into_active_gl_textures: GlStoreImageFn,
636        layout_func: LayoutFn,
637        load_font_fn: LoadFontFn,
638        load_image_fn: LoadImageFn,
639    ) -> Self {
640
641        use crate::{
642            app_resources::{
643                RawImageFormat, AddImage, ExternalImageData, TextureTarget, ExternalImageType,
644                ImageData, add_resources, garbage_collect_fonts_and_images,
645            },
646        };
647
648        #[cfg(feature = "opengl")]
649        fn recurse<U: FontImageApi>(
650            layout_cache: &mut SolvedLayoutCache,
651            solved_textures: &mut BTreeMap<DomId, BTreeMap<NodeId, Texture>>,
652            iframe_ui_states: &mut BTreeMap<DomId, UiState>,
653            iframe_ui_descriptions: &mut BTreeMap<DomId, UiDescription>,
654            app_resources: &mut AppResources,
655            render_api: &mut U,
656            full_window_state: &FullWindowState,
657            ui_state: &UiState,
658            ui_description: &UiDescription,
659            pipeline_id: &PipelineId,
660            bounds: LayoutRect,
661            gl_context: Rc<dyn Gl>,
662            layout_func: LayoutFn,
663            load_font_fn: LoadFontFn,
664            load_image_fn: LoadImageFn,
665        ) {
666            use gleam::gl;
667            use crate::{
668                callbacks::{
669                    HidpiAdjustedBounds, LayoutInfo,
670                    IFrameCallbackInfo, GlCallbackInfo
671                },
672                app_resources::add_fonts_and_images,
673            };
674
675            // Right now the IFrameCallbacks and GlTextureCallbacks need to know how large their
676            // containers are in order to be solved properly
677            let display_list = DisplayList::new(ui_description, ui_state);
678            let dom_id = ui_state.dom_id.clone();
679
680            let rects_in_rendering_order = determine_rendering_order(
681                &ui_state.dom.arena.node_hierarchy,
682                &display_list.rectangles
683            );
684
685            // In order to calculate the layout, font + image metrics have to be calculated first
686            add_fonts_and_images(
687                app_resources,
688                render_api,
689                &pipeline_id,
690                &display_list,
691                &ui_state.dom.arena.node_data,
692                load_font_fn,
693                load_image_fn,
694            );
695
696            let layout_result = (layout_func)(
697                &ui_state.dom.arena.node_hierarchy,
698                &ui_state.dom.arena.node_data,
699                &display_list.rectangles,
700                &app_resources,
701                pipeline_id,
702                bounds,
703            );
704
705            let scrollable_nodes = get_nodes_that_need_scroll_clip(
706                &ui_state.dom.arena.node_hierarchy,
707                &display_list.rectangles,
708                &ui_state.dom.arena.node_data,
709                &layout_result.rects,
710                &layout_result.node_depths,
711                *pipeline_id,
712            );
713
714            // Now the size of rects are known, render all the OpenGL textures
715            for (node_id, (cb, ptr)) in ui_state.scan_for_gltexture_callbacks() {
716
717                // Invoke OpenGL callback, render texture
718                let rect_size = layout_result.rects[node_id].size;
719
720                // TODO: Unused!
721                let mut window_size_width_stops = Vec::new();
722                let mut window_size_height_stops = Vec::new();
723
724                let texture = {
725
726                    let tex = (cb.0)(GlCallbackInfo {
727                        state: ptr,
728                        layout_info: LayoutInfo {
729                            window_size: &full_window_state.size,
730                            window_size_width_stops: &mut window_size_width_stops,
731                            window_size_height_stops: &mut window_size_height_stops,
732                            gl_context: gl_context.clone(),
733                            resources: &app_resources,
734                        },
735                        bounds: HidpiAdjustedBounds::from_bounds(
736                            rect_size,
737                            full_window_state.size.hidpi_factor,
738                        ),
739                    });
740
741                    // Reset the framebuffer and SRGB color target to 0
742                    gl_context.bind_framebuffer(gl::FRAMEBUFFER, 0);
743                    gl_context.disable(gl::FRAMEBUFFER_SRGB);
744                    gl_context.disable(gl::MULTISAMPLE);
745
746                    tex
747                };
748
749                if let Some(t) = texture {
750                    solved_textures
751                        .entry(dom_id.clone())
752                        .or_insert_with(|| BTreeMap::default())
753                        .insert(node_id, t);
754                }
755            }
756
757            // Call IFrames and recurse
758            for (node_id, (cb, ptr)) in ui_state.scan_for_iframe_callbacks() {
759
760                let size = layout_result.rects[node_id].size;
761                let hidpi_bounds = HidpiAdjustedBounds::from_bounds(
762                    size,
763                    full_window_state.size.hidpi_factor,
764                );
765
766                // TODO: Unused!
767                let mut window_size_width_stops = Vec::new();
768                let mut window_size_height_stops = Vec::new();
769
770                let iframe_dom = {
771                    (cb.0)(IFrameCallbackInfo {
772                        state: ptr,
773                        layout_info: LayoutInfo {
774                            window_size: &full_window_state.size,
775                            window_size_width_stops: &mut window_size_width_stops,
776                            window_size_height_stops: &mut window_size_height_stops,
777                            gl_context: gl_context.clone(),
778                            resources: &app_resources,
779                        },
780                        bounds: hidpi_bounds,
781                    })
782                };
783
784                if let Some(iframe_dom) = iframe_dom {
785                    let is_mouse_down = full_window_state.mouse_state.mouse_down();
786                    let mut iframe_ui_state = UiState::new(iframe_dom, Some((dom_id.clone(), node_id)));
787                    let iframe_dom_id = iframe_ui_state.dom_id.clone();
788                    let hovered_nodes = full_window_state.hovered_nodes.get(&iframe_dom_id).cloned().unwrap_or_default();
789                    let iframe_ui_description = UiDescription::new(
790                        &mut iframe_ui_state,
791                        &full_window_state.css,
792                        &full_window_state.focused_node,
793                        &hovered_nodes,
794                        is_mouse_down,
795                    );
796                    layout_cache.iframe_mappings.insert((dom_id.clone(), node_id), iframe_dom_id.clone());
797                    recurse(
798                        layout_cache,
799                        solved_textures,
800                        iframe_ui_states,
801                        iframe_ui_descriptions,
802                        app_resources,
803                        render_api,
804                        full_window_state,
805                        &iframe_ui_state,
806                        &iframe_ui_description,
807                        pipeline_id,
808                        bounds,
809                        gl_context.clone(),
810                        layout_func,
811                        load_font_fn,
812                        load_image_fn,
813                    );
814                    iframe_ui_states.insert(iframe_dom_id.clone(), iframe_ui_state);
815                    iframe_ui_descriptions.insert(iframe_dom_id.clone(), iframe_ui_description);
816                }
817            }
818
819            layout_cache.solved_layouts.insert(dom_id.clone(), layout_result);
820            layout_cache.display_lists.insert(dom_id.clone(), display_list);
821            layout_cache.rects_in_rendering_order.insert(dom_id.clone(), rects_in_rendering_order);
822            layout_cache.scrollable_nodes.insert(dom_id.clone(), scrollable_nodes);
823        }
824
825        let mut solved_layout_cache = SolvedLayoutCache::default();
826        let mut solved_textures = BTreeMap::new();
827        let mut iframe_ui_states = BTreeMap::new();
828        let mut iframe_ui_descriptions = BTreeMap::new();
829
830        for (dom_id, ui_state) in ui_states.iter_mut() {
831
832            let ui_description = &ui_descriptions[dom_id];
833
834            recurse(
835                &mut solved_layout_cache,
836                &mut solved_textures,
837                &mut iframe_ui_states,
838                &mut iframe_ui_descriptions,
839                app_resources,
840                render_api,
841                full_window_state,
842                ui_state,
843                ui_description,
844                &pipeline_id,
845                LayoutRect {
846                    origin: LayoutPoint::new(0.0, 0.0),
847                    size: LayoutSize::new(full_window_state.size.dimensions.width, full_window_state.size.dimensions.height),
848                },
849                gl_context.clone(),
850                layout_func,
851                load_font_fn,
852                load_image_fn,
853            );
854        }
855
856        ui_states.extend(iframe_ui_states.into_iter());
857        ui_descriptions.extend(iframe_ui_descriptions.into_iter());
858
859        let mut gl_texture_cache = GlTextureCache {
860            solved_textures: BTreeMap::new(),
861        };
862
863        let mut image_resource_updates = Vec::new();
864
865        for (dom_id, textures) in solved_textures {
866            for (node_id, texture) in textures {
867
868            // Note: The ImageDescriptor has no effect on how large the image appears on-screen
869            let descriptor = ImageDescriptor {
870                format: RawImageFormat::RGBA8,
871                dimensions: (texture.size.width as usize, texture.size.height as usize),
872                stride: None,
873                offset: 0,
874                flags: ImageDescriptorFlags {
875                    is_opaque: texture.flags.is_opaque,
876                    // The texture gets mapped 1:1 onto the display, so there is no need for mipmaps
877                    allow_mipmaps: false,
878                },
879            };
880
881            let key = render_api.new_image_key();
882            let external_image_id = (insert_into_active_gl_textures)(pipeline_id, epoch, texture);
883
884            let add_img_msg = AddImageMsg(
885                AddImage {
886                    key,
887                    descriptor,
888                    data: ImageData::External(ExternalImageData {
889                        id: external_image_id,
890                        channel_index: 0,
891                        image_type: ExternalImageType::TextureHandle(TextureTarget::Default),
892                    }),
893                    tiling: None,
894                },
895                ImageInfo { key, descriptor }
896            );
897
898            image_resource_updates.push((ImageId::new(), add_img_msg));
899
900            gl_texture_cache.solved_textures
901                .entry(dom_id.clone())
902                .or_insert_with(|| BTreeMap::new())
903                .insert(node_id, (key, descriptor));
904            }
905        }
906
907        // Delete unused font and image keys (that were not used in this display list)
908        garbage_collect_fonts_and_images(app_resources, render_api, &pipeline_id);
909        // Add the new GL textures to the RenderApi
910        add_resources(app_resources, render_api, &pipeline_id, Vec::new(), image_resource_updates);
911
912        Self {
913            solved_layout_cache,
914            gl_texture_cache,
915        }
916    }
917}
918
919pub fn determine_rendering_order<'a>(
920    node_hierarchy: &NodeHierarchy,
921    rectangles: &NodeDataContainer<DisplayRectangle>,
922) -> ContentGroup {
923
924    let children_sorted: BTreeMap<NodeId, Vec<NodeId>> = node_hierarchy
925        .get_parents_sorted_by_depth()
926        .into_iter()
927        .map(|(_, parent_id)| (parent_id, sort_children_by_position(parent_id, node_hierarchy, rectangles)))
928        .collect();
929
930    let mut root_content_group = ContentGroup { root: NodeId::ZERO, children: Vec::new() };
931    fill_content_group_children(&mut root_content_group, &children_sorted);
932    root_content_group
933}
934
935pub fn fill_content_group_children(group: &mut ContentGroup, children_sorted: &BTreeMap<NodeId, Vec<NodeId>>) {
936    if let Some(c) = children_sorted.get(&group.root) { // returns None for leaf nodes
937        group.children = c
938            .iter()
939            .map(|child| ContentGroup { root: *child, children: Vec::new() })
940            .collect();
941
942        for c in &mut group.children {
943            fill_content_group_children(c, children_sorted);
944        }
945    }
946}
947
948pub fn sort_children_by_position(
949    parent: NodeId,
950    node_hierarchy: &NodeHierarchy,
951    rectangles: &NodeDataContainer<DisplayRectangle>
952) -> Vec<NodeId> {
953    use azul_css::LayoutPosition::*;
954
955    let mut not_absolute_children = parent
956        .children(node_hierarchy)
957        .filter(|id| rectangles[*id].layout.position.and_then(|p| p.get_property_or_default()).unwrap_or_default() != Absolute)
958        .collect::<Vec<NodeId>>();
959
960    let mut absolute_children = parent
961        .children(node_hierarchy)
962        .filter(|id| rectangles[*id].layout.position.and_then(|p| p.get_property_or_default()).unwrap_or_default() == Absolute)
963        .collect::<Vec<NodeId>>();
964
965    // Append the position:absolute children after the regular children
966    not_absolute_children.append(&mut absolute_children);
967    not_absolute_children
968}
969
970/// Returns all node IDs where the children overflow the parent, together with the
971/// `(parent_rect, child_rect)` - the child rect is the sum of the children.
972///
973/// TODO: The performance of this function can be theoretically improved:
974///
975/// - Unioning the rectangles is heavier than just looping through the children and
976/// summing up their width / height / padding + margin.
977/// - Scroll nodes only need to be inserted if the parent doesn't have `overflow: hidden`
978/// activated
979/// - Overflow for X and Y needs to be tracked seperately (for overflow-x / overflow-y separation),
980/// so there we'd need to track in which direction the inner_rect is overflowing.
981pub fn get_nodes_that_need_scroll_clip(
982    node_hierarchy: &NodeHierarchy,
983    display_list_rects: &NodeDataContainer<DisplayRectangle>,
984    dom_rects: &NodeDataContainer<NodeData>,
985    layouted_rects: &NodeDataContainer<PositionedRectangle>,
986    parents: &[(usize, NodeId)],
987    pipeline_id: PipelineId,
988) -> ScrolledNodes {
989
990    use azul_css::Overflow;
991
992    let mut nodes = BTreeMap::new();
993    let mut tags_to_node_ids = BTreeMap::new();
994
995    for (_, parent) in parents {
996
997        let parent_rect = &layouted_rects[*parent];
998
999        let children_rects = parent
1000        .children(&node_hierarchy)
1001        .filter_map(|child_id| layouted_rects[child_id].get_static_bounds())
1002        .collect::<Vec<_>>();
1003
1004        let children_scroll_rect = match parent_rect
1005        .get_static_bounds()
1006        .and_then(|pr| pr.get_scroll_rect(children_rects.into_iter())) {
1007            None => continue,
1008            Some(sum) => sum,
1009        };
1010
1011        // Check if the scroll rect overflows the parent bounds
1012        if contains_rect_rounded(&LayoutRect::new(LayoutPoint::zero(), parent_rect.size), children_scroll_rect) {
1013            continue;
1014        }
1015
1016        // If the overflow isn't "scroll", then there doesn't need to be a scroll frame
1017        if parent_rect.overflow == Overflow::Visible || parent_rect.overflow == Overflow::Hidden {
1018            continue;
1019        }
1020
1021        let parent_dom_hash = dom_rects[*parent].calculate_node_data_hash();
1022
1023        // Create an external scroll id. This id is required to preserve its
1024        // scroll state accross multiple frames.
1025        let parent_external_scroll_id  = ExternalScrollId(parent_dom_hash.0, pipeline_id);
1026
1027        // Create a unique scroll tag for hit-testing
1028        let scroll_tag_id = match display_list_rects.get(*parent).and_then(|node| node.tag) {
1029            Some(existing_tag) => ScrollTagId(existing_tag),
1030            None => ScrollTagId::new(),
1031        };
1032
1033        tags_to_node_ids.insert(scroll_tag_id, *parent);
1034        nodes.insert(*parent, OverflowingScrollNode {
1035            child_rect: children_scroll_rect,
1036            parent_external_scroll_id,
1037            parent_dom_hash,
1038            scroll_tag_id,
1039        });
1040    }
1041
1042    ScrolledNodes { overflowing_nodes: nodes, tags_to_node_ids }
1043}
1044
1045// Since there can be a small floating point error, round the item to the nearest pixel,
1046// then compare the rects
1047pub fn contains_rect_rounded(a: &LayoutRect, b: LayoutRect) -> bool {
1048    let a_x = a.origin.x.round() as isize;
1049    let a_y = a.origin.x.round() as isize;
1050    let a_width = a.size.width.round() as isize;
1051    let a_height = a.size.height.round() as isize;
1052
1053    let b_x = b.origin.x.round() as isize;
1054    let b_y = b.origin.x.round() as isize;
1055    let b_width = b.size.width.round() as isize;
1056    let b_height = b.size.height.round() as isize;
1057
1058    b_x >= a_x &&
1059    b_y >= a_y &&
1060    b_x + b_width <= a_x + a_width &&
1061    b_y + b_height <= a_y + a_height
1062}
1063
1064pub fn node_needs_to_clip_children(layout: &RectLayout) -> bool {
1065    !(layout.is_horizontal_overflow_visible() || layout.is_vertical_overflow_visible())
1066}
1067
1068pub fn push_rectangles_into_displaylist<'a>(
1069    root_content_group: &ContentGroup,
1070    referenced_content: &DisplayListParametersRef<'a>,
1071) -> DisplayListMsg {
1072
1073    let mut content = displaylist_handle_rect(
1074        root_content_group.root,
1075        referenced_content,
1076    );
1077
1078    let children = root_content_group.children.iter().map(|child_content_group| {
1079        push_rectangles_into_displaylist(
1080            child_content_group,
1081            referenced_content,
1082        )
1083    }).collect();
1084
1085    content.append_children(children);
1086
1087    content
1088}
1089
1090/// Push a single rectangle into the display list builder
1091pub fn displaylist_handle_rect<'a>(
1092    rect_idx: NodeId,
1093    referenced_content: &DisplayListParametersRef<'a>,
1094) -> DisplayListMsg {
1095
1096    use crate::dom::NodeType::*;
1097
1098    let DisplayListParametersRef {
1099        dom_id,
1100        pipeline_id,
1101        ui_state_cache,
1102        layout_result,
1103        gl_texture_cache,
1104        app_resources,
1105        ..
1106    } = referenced_content;
1107
1108    let rect = &layout_result.display_lists[dom_id].rectangles[rect_idx];
1109    let bounds = &layout_result.solved_layouts[dom_id].rects[rect_idx];
1110    let html_node = &ui_state_cache[&dom_id].dom.arena.node_data[rect_idx].get_node_type();
1111
1112    let tag_id = rect.tag.or({
1113        layout_result.scrollable_nodes[dom_id].overflowing_nodes
1114        .get(&rect_idx)
1115        .map(|scrolled| scrolled.scroll_tag_id.0)
1116    });
1117
1118    let (size, position) = bounds.get_background_bounds();
1119
1120    let mut frame = DisplayListFrame {
1121        tag: tag_id,
1122        size,
1123        position,
1124        border_radius: StyleBorderRadius {
1125            top_left: rect.style.border_top_left_radius,
1126            top_right: rect.style.border_top_right_radius,
1127            bottom_left: rect.style.border_bottom_left_radius,
1128            bottom_right: rect.style.border_bottom_right_radius,
1129        },
1130        flags: PrimitiveFlags {
1131            is_backface_visible: true,
1132            is_scrollbar_container: false,
1133            is_scrollbar_thumb: false,
1134        },
1135        content: Vec::new(),
1136        children: Vec::new(),
1137    };
1138
1139    if rect.style.has_box_shadow() {
1140        frame.content.push(LayoutRectContent::BoxShadow {
1141            shadow: StyleBoxShadow {
1142                left: rect.style.box_shadow_left,
1143                right: rect.style.box_shadow_right,
1144                top: rect.style.box_shadow_top,
1145                bottom: rect.style.box_shadow_bottom,
1146            },
1147            clip_mode: BoxShadowClipMode::Outset,
1148        });
1149    }
1150
1151    // If the rect is hit-testing relevant, we need to push a rect anyway.
1152    // Otherwise the hit-testing gets confused
1153    if let Some(bg) = rect.style.background.as_ref().and_then(|br| br.get_property()) {
1154
1155        use azul_css::{CssImageId, StyleBackgroundContent::*};
1156
1157        fn get_image_info(app_resources: &AppResources, pipeline_id: &PipelineId, style_image_id: &CssImageId) -> Option<RectBackground> {
1158            let image_id = app_resources.get_css_image_id(&style_image_id.0)?;
1159            let image_info = app_resources.get_image_info(pipeline_id, image_id)?;
1160            Some(RectBackground::Image(*image_info))
1161        }
1162
1163        let background_content = match bg {
1164            LinearGradient(lg) => Some(RectBackground::LinearGradient(lg.clone())),
1165            RadialGradient(rg) => Some(RectBackground::RadialGradient(rg.clone())),
1166            Image(style_image_id) => get_image_info(&app_resources, &pipeline_id, style_image_id),
1167            Color(c) => Some(RectBackground::Color(*c)),
1168        };
1169
1170        if let Some(background_content) = background_content {
1171            frame.content.push(LayoutRectContent::Background {
1172                content: background_content,
1173                size: rect.style.background_size.and_then(|bs| bs.get_property().cloned()),
1174                offset: rect.style.background_position.and_then(|bs| bs.get_property().cloned()),
1175                repeat: rect.style.background_repeat.and_then(|bs| bs.get_property().cloned()),
1176            });
1177        }
1178    }
1179
1180    match html_node {
1181        Div | Body => { },
1182        Text(_) | Label(_) => {
1183            if let Some(layouted_glyphs) = layout_result.solved_layouts.get(dom_id).and_then(|lr| lr.layouted_glyph_cache.get(&rect_idx)).cloned() {
1184
1185                use crate::ui_solver::DEFAULT_FONT_COLOR;
1186
1187                let text_color = rect.style.text_color.and_then(|tc| tc.get_property().cloned()).unwrap_or(DEFAULT_FONT_COLOR).0;
1188                let positioned_words = &layout_result.solved_layouts[dom_id].positioned_word_cache[&rect_idx];
1189                let font_instance_key = positioned_words.1;
1190
1191                frame.content.push(get_text(
1192                    layouted_glyphs,
1193                    font_instance_key,
1194                    text_color,
1195                    &rect.layout,
1196                ));
1197            }
1198        },
1199        Image(image_id) => {
1200            if let Some(image_info) = app_resources.get_image_info(pipeline_id, image_id) {
1201                frame.content.push(LayoutRectContent::Image {
1202                    size: LayoutSize::new(bounds.size.width, bounds.size.height),
1203                    offset: LayoutPoint::new(0.0, 0.0),
1204                    image_rendering: ImageRendering::Auto,
1205                    alpha_type: AlphaType::PremultipliedAlpha,
1206                    image_key: image_info.key,
1207                    background_color: ColorU::WHITE,
1208                });
1209            }
1210        },
1211        #[cfg(feature = "opengl")]
1212        GlTexture(_) => {
1213            if let Some((key, descriptor)) = gl_texture_cache.solved_textures.get(&dom_id).and_then(|textures| textures.get(&rect_idx)) {
1214                frame.content.push(LayoutRectContent::Image {
1215                    size: LayoutSize::new(descriptor.dimensions.0 as f32, descriptor.dimensions.1 as f32),
1216                    offset: LayoutPoint::new(0.0, 0.0),
1217                    image_rendering: ImageRendering::Auto,
1218                    alpha_type: AlphaType::Alpha,
1219                    image_key: *key,
1220                    background_color: ColorU::WHITE,
1221                })
1222            }
1223        },
1224        IFrame(_) => {
1225            if let Some(iframe_dom_id) = layout_result.iframe_mappings.get(&(dom_id.clone(), rect_idx)) {
1226                frame.children.push(push_rectangles_into_displaylist(
1227                    &layout_result.rects_in_rendering_order[&iframe_dom_id],
1228                    // layout_result.rects_in_rendering_order.root,
1229                    &DisplayListParametersRef {
1230                        // Important: Need to update the DOM ID,
1231                        // otherwise this function would be endlessly recurse
1232                        dom_id: iframe_dom_id.clone(),
1233                        .. *referenced_content
1234                    }
1235                ));
1236            }
1237        },
1238    };
1239
1240    if rect.style.has_border() {
1241        frame.content.push(LayoutRectContent::Border {
1242            widths: StyleBorderWidths {
1243                top: rect.layout.border_top_width,
1244                left: rect.layout.border_left_width,
1245                bottom: rect.layout.border_bottom_width,
1246                right: rect.layout.border_right_width,
1247            },
1248            colors: StyleBorderColors {
1249                top: rect.style.border_top_color,
1250                left: rect.style.border_left_color,
1251                bottom: rect.style.border_bottom_color,
1252                right: rect.style.border_right_color,
1253            },
1254            styles: StyleBorderStyles {
1255                top: rect.style.border_top_style,
1256                left: rect.style.border_left_style,
1257                bottom: rect.style.border_bottom_style,
1258                right: rect.style.border_right_style,
1259            },
1260        });
1261    }
1262
1263    if rect.style.has_box_shadow() {
1264        frame.content.push(LayoutRectContent::BoxShadow {
1265            shadow: StyleBoxShadow {
1266                left: rect.style.box_shadow_left,
1267                right: rect.style.box_shadow_right,
1268                top: rect.style.box_shadow_top,
1269                bottom: rect.style.box_shadow_bottom,
1270            },
1271            clip_mode: BoxShadowClipMode::Inset,
1272        });
1273    }
1274
1275    match layout_result.scrollable_nodes[dom_id].overflowing_nodes.get(&rect_idx) {
1276        Some(scroll_node) => DisplayListMsg::ScrollFrame(DisplayListScrollFrame {
1277            content_rect: scroll_node.child_rect,
1278            scroll_id: scroll_node.parent_external_scroll_id,
1279            scroll_tag: scroll_node.scroll_tag_id,
1280            frame,
1281        }),
1282        None => DisplayListMsg::Frame(frame),
1283    }
1284}
1285
1286pub fn get_text(
1287    layouted_glyphs: LayoutedGlyphs,
1288    font_instance_key: FontInstanceKey,
1289    font_color: ColorU,
1290    rect_layout: &RectLayout,
1291) -> LayoutRectContent {
1292
1293    let overflow_horizontal_visible = rect_layout.is_horizontal_overflow_visible();
1294    let overflow_vertical_visible = rect_layout.is_horizontal_overflow_visible();
1295
1296    LayoutRectContent::Text {
1297        glyphs: layouted_glyphs.glyphs,
1298        font_instance_key,
1299        color: font_color,
1300        glyph_options: None,
1301        overflow: (overflow_horizontal_visible, overflow_vertical_visible),
1302    }
1303}
1304
1305/// Subtracts the padding from the size, returning the new size
1306///
1307/// Warning: The resulting rectangle may have negative width or height
1308#[inline]
1309pub fn subtract_padding(size: &LayoutSize, padding: &ResolvedOffsets) -> LayoutSize {
1310    LayoutSize {
1311        width: size.width - padding.right + padding.left,
1312        height: size.height - padding.top + padding.bottom,
1313    }
1314}
1315
1316#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
1317pub struct OverrideWarning {
1318    pub default: CssProperty,
1319    pub overridden_property: CssProperty,
1320}
1321
1322/// Populate the style properties of the `DisplayRectangle`, apply static / dynamic properties
1323pub fn populate_css_properties(
1324    rect: &mut DisplayRectangle,
1325    node_id: NodeId,
1326    css_overrides: &BTreeMap<NodeId, FastHashMap<DomString, CssProperty>>,
1327    styled_node: &StyledNode,
1328) -> Vec<OverrideWarning> {
1329
1330    use azul_css::CssDeclaration::*;
1331    use std::mem;
1332
1333    let rect_style = &mut rect.style;
1334    let rect_layout = &mut rect.layout;
1335    let css_constraints = &styled_node.css_constraints;
1336
1337   css_constraints
1338    .values()
1339    .filter_map(|constraint| match constraint {
1340        Static(static_property) => {
1341            apply_style_property(rect_style, rect_layout, &static_property);
1342            None
1343        },
1344        Dynamic(dynamic_property) => {
1345            let overridden_property = css_overrides.get(&node_id).and_then(|overrides| overrides.get(&dynamic_property.dynamic_id.clone().into()))?;
1346
1347            // Apply the property default if the discriminant of the two types matches
1348            if mem::discriminant(overridden_property) == mem::discriminant(&dynamic_property.default_value) {
1349                apply_style_property(rect_style, rect_layout, overridden_property);
1350                None
1351            } else {
1352                Some(OverrideWarning {
1353                    default: dynamic_property.default_value.clone(),
1354                    overridden_property: overridden_property.clone(),
1355                })
1356            }
1357        },
1358    })
1359    .collect()
1360}
1361
1362pub fn apply_style_property(style: &mut RectStyle, layout: &mut RectLayout, property: &CssProperty) {
1363
1364    use azul_css::CssProperty::*;
1365
1366    match property {
1367
1368        Display(d)                      => layout.display = Some(*d),
1369        Float(f)                        => layout.float = Some(*f),
1370        BoxSizing(bs)                   => layout.box_sizing = Some(*bs),
1371
1372        TextColor(c)                    => style.text_color = Some(*c),
1373        FontSize(fs)                    => style.font_size = Some(*fs),
1374        FontFamily(ff)                  => style.font_family = Some(ff.clone()),
1375        TextAlign(ta)                   => style.text_align = Some(*ta),
1376
1377        LetterSpacing(ls)               => style.letter_spacing = Some(*ls),
1378        LineHeight(lh)                  => style.line_height = Some(*lh),
1379        WordSpacing(ws)                 => style.word_spacing = Some(*ws),
1380        TabWidth(tw)                    => style.tab_width = Some(*tw),
1381        Cursor(c)                       => style.cursor = Some(*c),
1382
1383        Width(w)                        => layout.width = Some(*w),
1384        Height(h)                       => layout.height = Some(*h),
1385        MinWidth(mw)                    => layout.min_width = Some(*mw),
1386        MinHeight(mh)                   => layout.min_height = Some(*mh),
1387        MaxWidth(mw)                    => layout.max_width = Some(*mw),
1388        MaxHeight(mh)                   => layout.max_height = Some(*mh),
1389
1390        Position(p)                     => layout.position = Some(*p),
1391        Top(t)                          => layout.top = Some(*t),
1392        Bottom(b)                       => layout.bottom = Some(*b),
1393        Right(r)                        => layout.right = Some(*r),
1394        Left(l)                         => layout.left = Some(*l),
1395
1396        FlexWrap(fw)                    => layout.wrap = Some(*fw),
1397        FlexDirection(fd)               => layout.direction = Some(*fd),
1398        FlexGrow(fg)                    => layout.flex_grow = Some(*fg),
1399        FlexShrink(fs)                  => layout.flex_shrink = Some(*fs),
1400        JustifyContent(jc)              => layout.justify_content = Some(*jc),
1401        AlignItems(ai)                  => layout.align_items = Some(*ai),
1402        AlignContent(ac)                => layout.align_content = Some(*ac),
1403
1404        BackgroundContent(bc)           => style.background = Some(bc.clone()),
1405        BackgroundPosition(bp)          => style.background_position = Some(*bp),
1406        BackgroundSize(bs)              => style.background_size = Some(*bs),
1407        BackgroundRepeat(br)            => style.background_repeat = Some(*br),
1408
1409        OverflowX(ox)                   => layout.overflow_x = Some(*ox),
1410        OverflowY(oy)                   => layout.overflow_y = Some(*oy),
1411
1412        PaddingTop(pt)                  => layout.padding_top = Some(*pt),
1413        PaddingLeft(pl)                 => layout.padding_left = Some(*pl),
1414        PaddingRight(pr)                => layout.padding_right = Some(*pr),
1415        PaddingBottom(pb)               => layout.padding_bottom = Some(*pb),
1416
1417        MarginTop(mt)                   => layout.margin_top = Some(*mt),
1418        MarginLeft(ml)                  => layout.margin_left = Some(*ml),
1419        MarginRight(mr)                 => layout.margin_right = Some(*mr),
1420        MarginBottom(mb)                => layout.margin_bottom = Some(*mb),
1421
1422        BorderTopLeftRadius(btl)        => style.border_top_left_radius = Some(*btl),
1423        BorderTopRightRadius(btr)       => style.border_top_right_radius = Some(*btr),
1424        BorderBottomLeftRadius(bbl)     => style.border_bottom_left_radius = Some(*bbl),
1425        BorderBottomRightRadius(bbr)    => style.border_bottom_right_radius = Some(*bbr),
1426
1427        BorderTopColor(btc)             => style.border_top_color = Some(*btc),
1428        BorderRightColor(brc)           => style.border_right_color = Some(*brc),
1429        BorderLeftColor(blc)            => style.border_left_color = Some(*blc),
1430        BorderBottomColor(bbc)          => style.border_bottom_color = Some(*bbc),
1431
1432        BorderTopStyle(bts)             => style.border_top_style = Some(*bts),
1433        BorderRightStyle(brs)           => style.border_right_style = Some(*brs),
1434        BorderLeftStyle(bls)            => style.border_left_style = Some(*bls),
1435        BorderBottomStyle(bbs)          => style.border_bottom_style = Some(*bbs),
1436
1437        BorderTopWidth(btw)             => layout.border_top_width = Some(*btw),
1438        BorderRightWidth(brw)           => layout.border_right_width = Some(*brw),
1439        BorderLeftWidth(blw)            => layout.border_left_width = Some(*blw),
1440        BorderBottomWidth(bbw)          => layout.border_bottom_width = Some(*bbw),
1441
1442        BoxShadowLeft(bsl)              => style.box_shadow_left = Some(*bsl),
1443        BoxShadowRight(bsr)             => style.box_shadow_right = Some(*bsr),
1444        BoxShadowTop(bst)               => style.box_shadow_top = Some(*bst),
1445        BoxShadowBottom(bsb)            => style.box_shadow_bottom = Some(*bsb),
1446    }
1447}
1448
1449#[test]
1450fn test_overflow_parsing() {
1451    use azul_css::Overflow;
1452
1453    let layout1 = RectLayout::default();
1454
1455    // The default for overflowing is overflow: auto, which clips
1456    // children, so this should evaluate to true by default
1457    assert_eq!(node_needs_to_clip_children(&layout1), true);
1458
1459    let layout2 = RectLayout {
1460        overflow_x: Some(CssPropertyValue::Exact(Overflow::Visible)),
1461        overflow_y: Some(CssPropertyValue::Exact(Overflow::Visible)),
1462        .. Default::default()
1463    };
1464    assert_eq!(node_needs_to_clip_children(&layout2), false);
1465
1466    let layout3 = RectLayout {
1467        overflow_x: Some(CssPropertyValue::Exact(Overflow::Hidden)),
1468        overflow_y: Some(CssPropertyValue::Exact(Overflow::Hidden)),
1469        .. Default::default()
1470    };
1471    assert_eq!(node_needs_to_clip_children(&layout3), true);
1472}