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
45pub 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 pub content_rect: LayoutRect,
148 pub scroll_id: ExternalScrollId,
151 pub scroll_tag: ScrollTagId,
153 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 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
500pub struct DisplayList {
503 pub rectangles: NodeDataContainer<DisplayRectangle>
504}
505
506impl DisplayList {
507
508 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#[derive(Clone)]
545pub struct DisplayListParametersRef<'a> {
546 pub dom_id: DomId,
548 pub epoch: Epoch,
550 pub full_window_state: &'a FullWindowState,
552 pub pipeline_id: PipelineId,
554 pub layout_result: &'a SolvedLayoutCache,
556 pub gl_texture_cache: &'a GlTextureCache,
558 pub ui_state_cache: &'a BTreeMap<DomId, UiState>,
560 pub app_resources: &'a AppResources,
562}
563
564#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
566pub struct DisplayRectangle {
567 pub tag: Option<TagId>,
571 pub style: RectStyle,
573 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 pub root: NodeId,
593 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
611pub 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 #[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 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 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 for (node_id, (cb, ptr)) in ui_state.scan_for_gltexture_callbacks() {
716
717 let rect_size = layout_result.rects[node_id].size;
719
720 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 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 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 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 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 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 garbage_collect_fonts_and_images(app_resources, render_api, &pipeline_id);
909 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) { 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 not_absolute_children.append(&mut absolute_children);
967 not_absolute_children
968}
969
970pub 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 if contains_rect_rounded(&LayoutRect::new(LayoutPoint::zero(), parent_rect.size), children_scroll_rect) {
1013 continue;
1014 }
1015
1016 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 let parent_external_scroll_id = ExternalScrollId(parent_dom_hash.0, pipeline_id);
1026
1027 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
1045pub 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
1090pub 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 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 &DisplayListParametersRef {
1230 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#[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
1322pub 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 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 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}