Skip to main content

zenthra_widgets/
container.rs

1use crate::ui::{DrawCommand, RectDraw, Ui};
2use zenthra_core::{Color, Align, BorderAlignment};
3use zenthra_render::RectInstance;
4
5#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum Direction {
7    Row,
8    Column,
9    Stack,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq)]
13pub enum Wrap {
14    NoWrap,
15    Wrap,
16    WrapReverse,
17    RightToLeft,
18    RightToLeftReverse,
19}
20
21pub struct ContainerBuilder<'u, 'a> {
22    ui: &'u mut Ui<'a>,
23    direction: Direction,
24    halign: Align,
25    valign: Align,
26    wrap: Wrap,
27    children_draws: Vec<DrawCommand>,
28    child_sizes: Vec<(f32, f32)>,
29    child_ranges: Vec<(usize, usize)>,
30    child_origins: Vec<(f32, f32)>,
31    start_x: f32,
32    start_y: f32,
33    pos_x: Option<f32>,
34    pos_y: Option<f32>,
35    width: Option<f32>,
36    height: Option<f32>,
37    fill_x: bool,
38    fill_y: bool,
39    padding_top: f32,
40    padding_bottom: f32,
41    padding_left: f32,
42    padding_right: f32,
43    gap: f32,
44    bg: Option<Color>,
45    border_color: Option<Color>,
46    border_width: f32,
47    shadow_blur: f32,
48    shadow_color: Option<Color>,
49    shadow_offset: [f32; 2],
50    shadow_opacity: f32,
51    opacity: f32,
52    render_mode: Option<zenthra_core::RenderMode>,
53    max_width: Option<f32>,
54    max_height: Option<f32>,
55    min_width: Option<f32>,
56    min_height: Option<f32>,
57    scroll_x: bool,
58    scroll_y: bool,
59    clip: bool,
60    pub id: zenthra_core::Id,
61    radius: [f32; 4],
62    border_alignment: BorderAlignment,
63    is_absolute: bool,
64    is_overlay: bool,
65    hover_bg: Option<Color>,
66    hover_border_color: Option<Color>,
67    hover_border_width: Option<f32>,
68    hover_scale: f32,
69    active_bg: Option<Color>,
70    active_border_color: Option<Color>,
71    active_border_width: Option<f32>,
72    active_scale: f32,
73}
74
75impl<'u, 'a> ContainerBuilder<'u, 'a> {
76    pub fn new(ui: &'u mut Ui<'a>) -> Self {
77        let id = ui.id();
78        Self {
79            ui,
80            direction: Direction::Column,
81            halign: Align::Left,
82            valign: Align::Top,
83            wrap: Wrap::NoWrap,
84            children_draws: Vec::new(),
85            child_sizes: Vec::new(),
86            child_ranges: Vec::new(),
87            child_origins: Vec::new(),
88            start_x: 0.0,
89            start_y: 0.0,
90            pos_x: None,
91            pos_y: None,
92            width: None,
93            height: None,
94            fill_x: false,
95            fill_y: false,
96            padding_top: 0.0,
97            padding_bottom: 0.0,
98            padding_left: 0.0,
99            padding_right: 0.0,
100            gap: 0.0,
101            bg: None,
102            border_color: None,
103            border_width: 0.0,
104            shadow_blur: 0.0,
105            shadow_color: None,
106            shadow_offset: [0.0, 0.0],
107            shadow_opacity: 1.0,
108            opacity: 1.0,
109            render_mode: None,
110            max_width: None,
111            max_height: None,
112            min_width: None,
113            min_height: None,
114            scroll_x: false,
115            scroll_y: false,
116            clip: false,
117            id,
118            radius: [0.0; 4],
119            border_alignment: BorderAlignment::Inside,
120            is_absolute: false,
121            is_overlay: false,
122            hover_bg: None,
123            hover_border_color: None,
124            hover_border_width: None,
125            hover_scale: 1.0,
126            active_bg: None,
127            active_border_color: None,
128            active_border_width: None,
129            active_scale: 1.0,
130        }
131    }
132
133    pub fn absolute(mut self, x: f32, y: f32) -> Self {
134        self.pos_x = Some(x);
135        self.pos_y = Some(y);
136        self.is_absolute = true;
137        self
138    }
139    pub fn pos(mut self, x: f32, y: f32) -> Self {
140        self.pos_x = Some(x);
141        self.pos_y = Some(y);
142        self
143    }
144    pub fn width(mut self, w: f32) -> Self {
145        self.width = Some(w);
146        self
147    }
148    pub fn height(mut self, h: f32) -> Self {
149        self.height = Some(h);
150        self
151    }
152    pub fn fill_x(mut self) -> Self {
153        self.fill_x = true;
154        self
155    }
156    pub fn fill_y(mut self) -> Self {
157        self.fill_y = true;
158        self
159    }
160    pub fn fill(mut self) -> Self {
161        self.fill_x = true;
162        self.fill_y = true;
163        self
164    }
165    pub fn full_width(mut self) -> Self {
166        self.fill_x = true;
167        self
168    }
169    pub fn full_height(mut self) -> Self {
170        self.fill_y = true;
171        self
172    }
173    pub fn max_width(mut self, w: f32) -> Self {
174        self.max_width = Some(w);
175        self
176    }
177    pub fn max_height(mut self, h: f32) -> Self {
178        self.max_height = Some(h);
179        self
180    }
181    pub fn min_width(mut self, w: f32) -> Self {
182        self.min_width = Some(w);
183        self
184    }
185    pub fn min_height(mut self, h: f32) -> Self {
186        self.min_height = Some(h);
187        self
188    }
189    pub fn scrollable(mut self, x: bool, y: bool) -> Self {
190        self.scroll_x = x;
191        self.scroll_y = y;
192        self
193    }
194    pub fn scroll_x(mut self, e: bool) -> Self {
195        self.scroll_x = e;
196        if e { self.clip = true; }
197        self
198    }
199    pub fn scroll_y(mut self, e: bool) -> Self {
200        self.scroll_y = e;
201        if e { self.clip = true; }
202        self
203    }
204    /// Force clip the overflowing content bounds of this container regardless of scrolling state
205    pub fn clip(mut self, enabled: bool) -> Self {
206        self.clip = enabled;
207        self
208    }
209    pub fn padding(mut self, t: f32, r: f32, b: f32, l: f32) -> Self {
210        self.padding_top = t;
211        self.padding_bottom = b;
212        self.padding_left = l;
213        self.padding_right = r;
214        self
215    }
216    pub fn padding_all(mut self, p: f32) -> Self {
217        self.padding_top = p;
218        self.padding_bottom = p;
219        self.padding_left = p;
220        self.padding_right = p;
221        self
222    }
223    pub fn padding_x(mut self, p: f32) -> Self {
224        self.padding_left = p;
225        self.padding_right = p;
226        self
227    }
228    pub fn padding_y(mut self, p: f32) -> Self {
229        self.padding_top = p;
230        self.padding_bottom = p;
231        self
232    }
233    pub fn padding_top(mut self, p: f32) -> Self {
234        self.padding_top = p;
235        self
236    }
237    pub fn padding_bottom(mut self, p: f32) -> Self {
238        self.padding_bottom = p;
239        self
240    }
241    pub fn padding_left(mut self, p: f32) -> Self {
242        self.padding_left = p;
243        self
244    }
245    pub fn padding_right(mut self, p: f32) -> Self {
246        self.padding_right = p;
247        self
248    }
249
250
251    pub fn row(mut self) -> Self {
252        self.direction = Direction::Row;
253        self
254    }
255
256    pub fn column(mut self) -> Self {
257        self.direction = Direction::Column;
258        self
259    }
260
261    pub fn wrap(mut self, strategy: Wrap) -> Self {
262        self.wrap = strategy;
263        self
264    }
265
266    pub fn no_wrap(mut self) -> Self {
267        self.wrap = Wrap::NoWrap;
268        self
269    }
270
271    pub fn align(mut self, align: Align) -> Self {
272        self.halign = align;
273        self.valign = align;
274        self
275    }
276
277    pub fn radius(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
278        self.radius = [tl, tr, br, bl];
279        self
280    }
281
282    pub fn radius_all(mut self, r: f32) -> Self {
283        self.radius = [r, r, r, r];
284        self
285    }
286
287    pub fn halign(mut self, align: Align) -> Self {
288        self.halign = align;
289        self
290    }
291
292    pub fn valign(mut self, align: Align) -> Self {
293        self.valign = align;
294        self
295    }
296
297    pub fn radius_top_left(mut self, r: f32) -> Self {
298        self.radius[0] = r;
299        self
300    }
301    pub fn radius_top_right(mut self, r: f32) -> Self {
302        self.radius[1] = r;
303        self
304    }
305    pub fn radius_bottom_right(mut self, r: f32) -> Self {
306        self.radius[2] = r;
307        self
308    }
309    pub fn radius_bottom_left(mut self, r: f32) -> Self {
310        self.radius[3] = r;
311        self
312    }
313
314    pub fn radius_top(mut self, r: f32) -> Self {
315        self.radius[0] = r;
316        self.radius[1] = r;
317        self
318    }
319    pub fn radius_bottom(mut self, r: f32) -> Self {
320        self.radius[2] = r;
321        self.radius[3] = r;
322        self
323    }
324    pub fn radius_left(mut self, r: f32) -> Self {
325        self.radius[0] = r;
326        self.radius[3] = r;
327        self
328    }
329    pub fn radius_right(mut self, r: f32) -> Self {
330        self.radius[1] = r;
331        self.radius[2] = r;
332        self
333    }
334    pub fn radius_x(mut self, r: f32) -> Self {
335        self.radius = [r, r, r, r];
336        self
337    }
338    pub fn radius_y(mut self, r: f32) -> Self {
339        self.radius = [r, r, r, r];
340        self
341    }
342
343    pub fn active_bg(mut self, color: Color) -> Self {
344        self.active_bg = Some(color);
345        self
346    }
347
348    pub fn active_border(mut self, color: Color, width: f32) -> Self {
349        self.active_border_color = Some(color);
350        self.active_border_width = Some(width);
351        self
352    }
353
354    pub fn active_scale(mut self, scale: f32) -> Self {
355        self.active_scale = scale;
356        self
357    }
358
359    pub fn gap(mut self, g: f32) -> Self {
360        self.gap = g;
361        self
362    }
363    pub fn bg(mut self, c: Color) -> Self {
364        self.bg = Some(c);
365        self
366    }
367    pub fn border(mut self, c: Color, w: f32) -> Self {
368        self.border_color = Some(c);
369        self.border_width = w;
370        self
371    }
372    pub fn shadow(mut self, color: Color, x: f32, y: f32, blur: f32) -> Self {
373        self.shadow_color = Some(color);
374        self.shadow_offset = [x, y];
375        self.shadow_blur = blur;
376        self
377    }
378
379    pub fn shadow_opacity(mut self, opacity: f32) -> Self {
380        self.shadow_opacity = opacity;
381        self
382    }
383    pub fn opacity(mut self, o: f32) -> Self {
384        self.opacity = o;
385        self
386    }
387
388    pub fn id(mut self, id: impl std::hash::Hash) -> Self {
389        let mut hasher = std::collections::hash_map::DefaultHasher::new();
390        use std::hash::{Hash, Hasher};
391        id.hash(&mut hasher);
392        if let Some(parent) = self.ui.semantic_stack.last() {
393            parent.hash(&mut hasher);
394        }
395        self.id = zenthra_core::Id::from_u64(hasher.finish());
396        self
397    }
398
399    pub fn hover_bg(mut self, color: Color) -> Self {
400        self.hover_bg = Some(color);
401        self
402    }
403
404    pub fn hover_border(mut self, color: Color, width: f32) -> Self {
405        self.hover_border_color = Some(color);
406        self.hover_border_width = Some(width);
407        self
408    }
409
410    pub fn hover_scale(mut self, scale: f32) -> Self {
411        self.hover_scale = scale;
412        self
413    }
414
415    pub fn border_alignment(mut self, alignment: BorderAlignment) -> Self {
416        self.border_alignment = alignment;
417        self
418    }
419
420    pub fn render_mode(mut self, mode: zenthra_core::RenderMode) -> Self {
421        self.render_mode = Some(mode);
422        self
423    }
424
425    pub fn overlay(mut self) -> Self {
426        self.is_overlay = true;
427        self
428    }
429
430    pub fn on_click<F>(self, mut f: F) -> Self
431    where
432        F: FnMut() + 'a,
433    {
434        self.ui.add_listener(self.id, crate::ui::EventPhase::Bubble, move |_, event| {
435            if let crate::ui::WidgetEvent::Click = event {
436                f();
437            }
438        });
439        self
440    }
441
442    pub fn on_hover<F>(self, mut f: F) -> Self
443    where
444        F: FnMut(bool) + 'a,
445    {
446        self.ui.add_listener(self.id, crate::ui::EventPhase::Bubble, move |_, event| {
447            if let crate::ui::WidgetEvent::Hover(hovered) = event {
448                f(*hovered);
449            }
450        });
451        self
452    }
453
454    pub fn on_scroll<F>(self, mut f: F) -> Self
455    where
456        F: FnMut(f32, f32) + 'a,
457    {
458        self.ui.add_listener(self.id, crate::ui::EventPhase::Bubble, move |_, event| {
459            if let crate::ui::WidgetEvent::Scroll(dx, dy) = event {
460                f(*dx, *dy);
461            }
462        });
463        self
464    }
465
466    pub fn on_event<F>(self, phase: crate::ui::EventPhase, f: F) -> Self
467    where
468        F: FnMut(&mut crate::ui::EventContext, &crate::ui::WidgetEvent) + 'a,
469    {
470        self.ui.add_listener(self.id, phase, f);
471        self
472    }
473
474    pub fn show<F>(mut self, f: F) -> zenthra_core::Response
475    where
476        F: FnOnce(&mut Ui),
477    {
478        // 1. Capture the start position and own ID
479        let id = self.id;
480
481        self.start_x = self.ui.cursor_x;
482        self.start_y = self.ui.cursor_y;
483        let ox = self.pos_x.unwrap_or(self.start_x);
484        let oy = self.pos_y.unwrap_or(self.start_y);
485
486        // -- Save current Ui environment --
487        let prev_dir = self.ui.direction;
488        let prev_line_h = self.ui.line_height;
489        let prev_base_x = self.ui.base_x;
490        let prev_base_y = self.ui.base_y;
491        let prev_offset_x = self.ui.offset_x;
492        let prev_offset_y = self.ui.offset_y;
493        let prev_viewport = self.ui.current_viewport;
494        
495        // Take and isolate state maps for children
496        let prev_child_sizes = std::mem::take(&mut self.ui.child_sizes);
497        let prev_child_ranges = std::mem::take(&mut self.ui.child_draw_ranges);
498        let prev_child_origins = std::mem::take(&mut self.ui.child_origins);
499        let prev_id_ranges = std::mem::take(&mut self.ui.id_ranges);
500        let prev_id_log = std::mem::take(&mut self.ui.id_log);
501        
502        let prev_max_x = self.ui.max_x;
503        let prev_max_y = self.ui.max_y;
504        
505        let _id_log_start_idx = self.ui.id_log.len();
506
507        // Set child environment
508        if let Some(w) = self.width {
509            self.ui.max_x = (ox + w - self.padding_right).min(prev_max_x);
510        }
511        if let Some(h) = self.height {
512            self.ui.max_y = (oy + h - self.padding_bottom).min(prev_max_y);
513        }
514
515        self.ui.direction = self.direction;
516        self.ui.line_height = 0.0;
517
518        let mode = self.render_mode.unwrap_or(self.ui.current_render_mode());
519        self.ui.render_mode_stack.push(mode);
520        if mode == zenthra_core::RenderMode::Continuous {
521            self.ui.needs_redraw = true;
522        }
523
524        let avail_w = if let Some(w) = self.width {
525            w - self.padding_left - self.padding_right - 2.0 * self.border_width
526        } else {
527            (prev_max_x - ox - self.padding_left - self.padding_right - 2.0 * self.border_width).max(0.0)
528        };
529        let avail_h = if let Some(h) = self.height {
530            h - self.padding_top - self.padding_bottom - 2.0 * self.border_width
531        } else {
532            (prev_max_y - oy - self.padding_top - self.padding_bottom - 2.0 * self.border_width).max(0.0)
533        };
534
535        // Important: If we are absolute, our internal boundary is NOT capped by prev_max_x.
536        // This allows us to have a full width even at the edge of the screen.
537        self.ui.max_x = ox + self.padding_left + self.border_width + avail_w;
538        self.ui.max_y = oy + self.padding_top + self.border_width + avail_h;
539
540        self.ui.semantic_stack.push(id);
541        self.ui.register_semantic(zenthra_core::SemanticNode::new(id, zenthra_core::Role::Container, zenthra_core::Rect::new(ox, oy, 0.0, 0.0)));
542
543        let parent_draws = std::mem::take(&mut self.ui.draws);
544
545        // Update global offsets for children (for visibility/culling logic)
546        let prev_global_ox = self.ui.offset_x;
547        let prev_global_oy = self.ui.offset_y;
548        let prev_avail_w = self.ui.available_width;
549        
550        let (sx, sy) = if self.scroll_x || self.scroll_y {
551            *self.ui.scroll_state.get(&id).unwrap_or(&(0.0, 0.0))
552        } else {
553            (0.0, 0.0)
554        };
555
556        self.ui.offset_x -= sx;
557        self.ui.offset_y -= sy;
558        self.ui.available_width = avail_w;
559
560        self.ui.base_x = ox + self.padding_left + self.border_width;
561        self.ui.base_y = oy + self.padding_top + self.border_width;
562        self.ui.cursor_x = self.ui.base_x;
563        self.ui.cursor_y = self.ui.base_y;
564
565        // Update viewport for children if clipping is enabled
566        if self.clip {
567            if let Some((rect, _)) = self.ui.get_recorded_layout(id) {
568                let my_screen_rect = [rect.origin.x + prev_global_ox, rect.origin.y + prev_global_oy, rect.size.width, rect.size.height];
569                let parent_rect = [prev_viewport.origin.x, prev_viewport.origin.y, prev_viewport.size.width, prev_viewport.size.height];
570                let intersected = intersect_rects(my_screen_rect, parent_rect);
571                self.ui.current_viewport = zenthra_core::Rect::new(intersected[0], intersected[1], intersected[2], intersected[3]);
572            }
573        }
574
575        // -- Run Children --
576        f(self.ui);
577
578        // Restore global offsets
579        self.ui.offset_x = prev_global_ox;
580        self.ui.offset_y = prev_global_oy;
581        self.ui.available_width = prev_avail_w;
582
583        // -- Restore Stacks and Environment --
584        self.ui.render_mode_stack.pop();
585        self.ui.semantic_stack.pop();
586        self.ui.current_viewport = prev_viewport;
587        
588        // Capture everything the children created
589        let child_ids_only = std::mem::replace(&mut self.ui.id_log, prev_id_log);
590        let child_id_ranges = std::mem::replace(&mut self.ui.id_ranges, prev_id_ranges);
591        self.children_draws = std::mem::replace(&mut self.ui.draws, parent_draws);
592        self.child_sizes = std::mem::replace(&mut self.ui.child_sizes, prev_child_sizes);
593        self.child_ranges = std::mem::replace(&mut self.ui.child_draw_ranges, prev_child_ranges);
594        self.child_origins = std::mem::replace(&mut self.ui.child_origins, prev_child_origins);
595        
596        let _id_log_end_idx = child_ids_only.len();
597
598        // Restore cursor/base
599        self.ui.direction = prev_dir;
600        self.ui.line_height = prev_line_h;
601        self.ui.base_x = prev_base_x;
602        self.ui.base_y = prev_base_y;
603        self.ui.offset_x = prev_offset_x;
604        self.ui.offset_y = prev_offset_y;
605        self.ui.cursor_x = self.start_x;
606        self.ui.cursor_y = self.start_y;
607        self.ui.max_x = prev_max_x;
608        self.ui.max_y = prev_max_y;
609
610        // -- Layout Engine --
611        let n = self.child_sizes.len();
612        let mut target_positions: Vec<(f32, f32)> = vec![(0.0, 0.0); n];
613        let (content_w, content_h) = match self.wrap {
614            Wrap::NoWrap => self.layout_no_wrap(ox, oy, avail_w, avail_h, &mut target_positions),
615            _ => self.layout_wrap(ox, oy, avail_w, avail_h, &mut target_positions),
616        };
617
618        let mut w = if self.fill_x {
619            self.ui.max_x - ox  // use parent's right boundary, not global window width
620        } else {
621            self.width.unwrap_or(content_w + self.padding_left + self.padding_right + 2.0 * self.border_width)
622        };
623        
624        if let Some(mw) = self.max_width { w = w.min(mw); }
625        if let Some(mw) = self.min_width { w = w.max(mw); }
626
627        let mut h = if self.fill_y {
628            self.ui.max_y - oy  // use parent's bottom boundary, not global window height
629        } else {
630            self.height.unwrap_or(content_h + self.padding_top + self.padding_bottom + 2.0 * self.border_width)
631        };
632
633        if let Some(mh) = self.max_height { h = h.min(mh); }
634        if let Some(mh) = self.min_height { h = h.max(mh); }
635
636        let draw_start = self.ui.draws.len();
637        self.ui.record_layout(id, zenthra_core::Rect::new(ox, oy, w, h));
638
639        let max_sx = (content_w + self.padding_left + self.padding_right - w).max(0.0);
640        let max_sy = (content_h + self.padding_top + self.padding_bottom - h).max(0.0);
641
642        let (actual_ox, actual_oy) = if let Some((rect, _)) = self.ui.get_recorded_layout(id) {
643            (rect.origin.x + prev_global_ox, rect.origin.y + prev_global_oy)
644        } else {
645            (ox + prev_global_ox, oy + prev_global_oy)
646        };
647
648        let mouse_in_parent = if self.ui.skip_clip_stack.last().cloned().unwrap_or(false) {
649            true
650        } else {
651            prev_viewport.contains(zenthra_core::Point::new(self.ui.mouse_x, self.ui.mouse_y))
652        };
653
654        let container_hover = mouse_in_parent && self.ui.mouse_in_rect(actual_ox, actual_oy, w, h) && !self.ui.is_occluded(id, self.ui.mouse_x, self.ui.mouse_y);
655        let container_active = container_hover && self.ui.mouse_down;
656        let mut final_scale = 1.0;
657
658        let (scroll_x, scroll_y) = if self.scroll_x || self.scroll_y {
659            let (mut sx, mut sy) = *self.ui.scroll_state.get(&id).unwrap_or(&(0.0, 0.0));
660            
661            if container_hover {
662                let mut events = std::mem::take(&mut self.ui.input_events);
663                events.retain(|event| {
664                    let mut keep = true;
665                    if let zenthra_platform::event::PlatformEvent::MouseWheel { delta_y, delta_x, .. } = event {
666                        let mut consumed = false;
667                        if self.scroll_y && *delta_y != 0.0 {
668                            let can_up = sy > 0.0 && *delta_y > 0.0;
669                            let can_down = sy < max_sy && *delta_y < 0.0;
670                            if can_up || can_down {
671                                sy -= delta_y * 15.0;
672                                consumed = true;
673                            }
674                        }
675                        if self.scroll_x && *delta_x != 0.0 {
676                            let can_left = sx > 0.0 && *delta_x > 0.0;
677                            let can_right = sx < max_sx && *delta_x < 0.0;
678                            if can_left || can_right {
679                                sx -= delta_x * 15.0;
680                                consumed = true;
681                            }
682                        }
683                        if consumed {
684                            keep = false;
685                            self.ui.needs_redraw = true;
686                        }
687                    }
688                    keep
689                });
690                self.ui.input_events = events;
691            }
692            
693            sx = sx.clamp(0.0, max_sx);
694            sy = sy.clamp(0.0, max_sy);
695            
696            self.ui.scroll_state.insert(id, (sx, sy));
697            (sx, sy)
698        } else {
699            (0.0, 0.0)
700        };
701
702        let clip = [ox, oy, w, h];
703
704        // Background
705        if let Some(mut bg) = self.bg {
706            let mut bw = self.border_width;
707            let mut bc = self.border_color.unwrap_or(Color::TRANSPARENT);
708
709            if container_active {
710                bg = self.active_bg.or(self.hover_bg).unwrap_or(bg);
711                bw = self.active_border_width.or(self.hover_border_width).unwrap_or(bw);
712                bc = self.active_border_color.or(self.hover_border_color).unwrap_or(bc);
713                final_scale = self.active_scale;
714            } else if container_hover {
715                bg = self.hover_bg.unwrap_or(bg);
716                bw = self.hover_border_width.unwrap_or(bw);
717                bc = self.hover_border_color.unwrap_or(bc);
718                final_scale = self.hover_scale;
719            }
720
721            let visual_w = w * final_scale;
722            let visual_h = h * final_scale;
723            let visual_ox = ox - (visual_w - w) / 2.0;
724            let visual_oy = oy - (visual_h - h) / 2.0;
725
726            if self.is_overlay {
727                self.ui.overlays.push(DrawCommand::Rect(RectDraw {
728                    instance: RectInstance {
729                        pos: [visual_ox, visual_oy],
730                        size: [visual_w, visual_h],
731                        color: bg.to_array(),
732                        radius: [
733                            self.radius[3], // Bottom-Left -> Top-Left
734                            self.radius[2], // Bottom-Right -> Top-Right
735                            self.radius[1], // Top-Right -> Bottom-Right
736                            self.radius[0], // Top-Left -> Bottom-Left
737                        ],
738                        border_width: bw,
739                        border_color: bc.to_array(),
740                        shadow_color: self.shadow_color.map(|c| {
741                            let mut a = c.to_array();
742                            a[3] *= self.shadow_opacity;
743                            a
744                        }).unwrap_or([0.0, 0.0, 0.0, 0.0]),
745                        shadow_offset: self.shadow_offset,
746                        shadow_blur: self.shadow_blur,
747                        clip_rect: [-100000.0, -100000.0, 2000000.0, 2000000.0],
748                        grayscale: 0.0,
749                        brightness: 1.0,
750                        opacity: self.opacity,
751                        border_alignment: match self.border_alignment {
752                            BorderAlignment::Inside => 0.0,
753                            BorderAlignment::Center => 0.5,
754                            BorderAlignment::Outside => 1.0,
755                        },
756                    }
757                }));
758            } else {
759                self.ui.draws.push(DrawCommand::Rect(RectDraw {
760                    instance: RectInstance {
761                        pos: [visual_ox, visual_oy],
762                        size: [visual_w, visual_h],
763                        color: bg.to_array(),
764                        radius: [
765                            self.radius[3], // Bottom-Left -> Top-Left
766                            self.radius[2], // Bottom-Right -> Top-Right
767                            self.radius[1], // Top-Right -> Bottom-Right
768                            self.radius[0], // Top-Left -> Bottom-Left
769                        ],
770                        border_width: bw,
771                        border_color: bc.to_array(),
772                        shadow_color: self.shadow_color.map(|c| {
773                            let mut a = c.to_array();
774                            a[3] *= self.shadow_opacity;
775                            a
776                        }).unwrap_or([0.0, 0.0, 0.0, 0.0]),
777                        shadow_offset: self.shadow_offset,
778                        shadow_blur: self.shadow_blur,
779                        clip_rect: [-100000.0, -100000.0, 2000000.0, 2000000.0],
780                        grayscale: 0.0,
781                        brightness: 1.0,
782                        opacity: self.opacity,
783                        border_alignment: match self.border_alignment {
784                            BorderAlignment::Inside => 0.0,
785                            BorderAlignment::Center => 0.5,
786                            BorderAlignment::Outside => 1.0,
787                        },
788                    }
789                }));
790            }
791        }
792
793        // Final Translation pass (Always run to keep cache in sync)
794        for (i, (start, end)) in self.child_ranges.iter().enumerate() {
795            if i >= target_positions.len() { break; }
796            let (tx, ty) = target_positions[i];
797            
798            let (origin_x, origin_y) = self.child_origins.get(i).copied().unwrap_or_else(|| {
799                self.children_draws
800                    .get(*start)
801                    .map(|d| draw_origin(d))
802                    .unwrap_or((ox + self.padding_left + self.border_width, oy + self.padding_top + self.border_width))
803            });
804
805            let dx = tx - origin_x;
806            let dy = ty - origin_y;
807            
808            let final_dx = dx - scroll_x;
809            let final_dy = dy - scroll_y;
810
811            // Shift visual commands
812            for draw in &mut self.children_draws[*start..*end] {
813                offset_draw(draw, final_dx, final_dy);
814                if self.scroll_x || self.scroll_y || self.clip {
815                    set_clip(draw, clip);
816                }
817            }
818            
819            // Shift IDs RECURSIVELY (Layout shift only, scrolling is handled live)
820            if let Some(&(ids_start, ids_end)) = child_id_ranges.get(i) {
821                for j in ids_start..ids_end {
822                    let cid = child_ids_only[j];
823                    if let Some((rect, _)) = self.ui.next_layout_cache.get_mut(&cid) {
824                        rect.origin.x += dx;
825                        rect.origin.y += dy;
826                    }
827                }
828            }
829        }
830
831
832        // Flush children draws to parent
833        for draw in self.children_draws.drain(..) {
834            if self.is_overlay {
835                self.ui.overlays.push(draw);
836            } else {
837                self.ui.draws.push(draw);
838            }
839        }
840        
841        // --- Visual Scrollbars & Dragging ---
842        if self.scroll_x || self.scroll_y {
843            let bar_thickness = 4.0;
844            let bar_margin = 0.0;
845
846            // Vertical Scrollbar
847            if self.scroll_y && max_sy > 0.0 {
848                let thumb_h = (h / (content_h + self.padding_top + self.padding_bottom)) * h;
849                let thumb_h = thumb_h.max(20.0);
850                let scroll_ratio = scroll_y / max_sy;
851                let is_hover = self.ui.mouse_in_rect(actual_ox + w - bar_thickness - bar_margin - 2.0, actual_oy + (h - thumb_h) * scroll_ratio, bar_thickness + 4.0, thumb_h) && !self.ui.is_occluded(id, self.ui.mouse_x, self.ui.mouse_y);
852                let is_dragging = self.ui.active_drag.as_ref().map(|d| d.id == id && d.start_mouse <= -1000.0).unwrap_or(false); 
853
854                // Handle Drag Y
855                if self.ui.clicked && is_hover {
856                    self.ui.active_drag = Some(crate::ui::ScrollDrag {
857                        id,
858                        start_mouse: -1000.0 - self.ui.mouse_y, 
859                        start_scroll: scroll_y,
860                    });
861                }
862                
863                if let Some(drag) = &self.ui.active_drag {
864                    if drag.id == id && drag.start_mouse <= -1000.0 {
865                        let current_marker = -1000.0 - self.ui.mouse_y;
866                        let delta = drag.start_mouse - current_marker;
867                        let scroll_range = h - thumb_h;
868                        if scroll_range > 0.0 {
869                            let new_sy = (drag.start_scroll + (delta / scroll_range) * max_sy).clamp(0.0, max_sy);
870                            self.ui.scroll_state.insert(id, (scroll_x, new_sy));
871                            self.ui.needs_redraw = true;
872                        }
873                    }
874                }
875
876                {
877                    let color = if is_hover || is_dragging { 
878                        Color::rgba(1.0, 1.0, 1.0, 0.5) 
879                    } else if container_hover {
880                        Color::rgba(1.0, 1.0, 1.0, 0.2)
881                    } else {
882                        Color::rgba(1.0, 1.0, 1.0, 0.1)
883                    };
884                    
885                    // Use layout coordinates (ox, oy) for drawing so they are shifted by parents correctly.
886                    // But interaction uses actual_ox/actual_oy (screen space).
887                    let visual_thumb_x = ox + w - bar_thickness - bar_margin - self.border_width;
888                    let visual_thumb_y = oy + (h - thumb_h) * scroll_ratio;
889
890                    let draw = crate::ui::DrawCommand::OverlayRect(crate::ui::OverlayRectDraw {
891                        x: visual_thumb_x,
892                        y: visual_thumb_y,
893                        width: bar_thickness,
894                        height: thumb_h,
895                        color,
896                        clip: [ox, oy, w, h], 
897                    });
898                    if self.is_overlay { self.ui.overlays.push(draw); } else { self.ui.draws.push(draw); }
899                }
900            }
901
902            // Horizontal Scrollbar
903            if self.scroll_x && max_sx > 0.0 {
904                let thumb_w = (w / (content_w + self.padding_left + self.padding_right)) * w;
905                let thumb_w = thumb_w.max(20.0);
906                let scroll_ratio = scroll_x / max_sx;
907                let is_hover = self.ui.mouse_in_rect(actual_ox + (w - thumb_w) * scroll_ratio, actual_oy + h - bar_thickness - bar_margin - 2.0, thumb_w, bar_thickness + 4.0) && !self.ui.is_occluded(id, self.ui.mouse_x, self.ui.mouse_y);
908                let is_dragging = self.ui.active_drag.as_ref().map(|d| d.id == id && d.start_mouse > -1000.0).unwrap_or(false);
909
910                if self.ui.clicked && is_hover {
911                    self.ui.active_drag = Some(crate::ui::ScrollDrag {
912                        id,
913                        start_mouse: self.ui.mouse_x,
914                        start_scroll: scroll_x,
915                    });
916                }
917
918                if let Some(drag) = &self.ui.active_drag {
919                    if drag.id == id && drag.start_mouse > -1000.0 {
920                        let delta = self.ui.mouse_x - drag.start_mouse;
921                        let scroll_range = w - thumb_w;
922                        if scroll_range > 0.0 {
923                            let new_sx = (drag.start_scroll + (delta / scroll_range) * max_sx).clamp(0.0, max_sx);
924                            let (_, current_sy) = *self.ui.scroll_state.get(&id).unwrap_or(&(0.0, 0.0));
925                            self.ui.scroll_state.insert(id, (new_sx, current_sy));
926                            self.ui.needs_redraw = true;
927                        }
928                    }
929                }
930
931                {
932                    let color = if is_hover || is_dragging { 
933                        Color::rgba(1.0, 1.0, 1.0, 0.5) 
934                    } else if container_hover {
935                        Color::rgba(1.0, 1.0, 1.0, 0.2)
936                    } else {
937                        Color::rgba(1.0, 1.0, 1.0, 0.1)
938                    };
939                    
940                    let visual_thumb_x = ox + (w - thumb_w) * scroll_ratio;
941                    let visual_thumb_y = oy + h - bar_thickness - bar_margin - self.border_width;
942
943                    let draw = crate::ui::DrawCommand::OverlayRect(crate::ui::OverlayRectDraw {
944                        x: visual_thumb_x,
945                        y: visual_thumb_y,
946                        width: thumb_w,
947                        height: bar_thickness,
948                        color,
949                        clip: [ox, oy, w, h],
950                    });
951                    if self.is_overlay { self.ui.overlays.push(draw); } else { self.ui.draws.push(draw); }
952                }
953            }
954        }
955
956        // Bubble IDs up to parent's scope
957        self.ui.id_log.extend(child_ids_only);
958        
959        if !self.is_absolute {
960            self.ui.advance(w, h, draw_start);
961        }
962
963        // Dispatch events
964        self.ui.dispatch_event(id, crate::ui::WidgetEvent::Hover(container_hover));
965
966        if self.ui.clicked && container_hover {
967            self.ui.dispatch_event(id, crate::ui::WidgetEvent::Click);
968        }
969
970        let events = std::mem::take(&mut self.ui.input_events);
971        for event in &events {
972            if let zenthra_platform::event::PlatformEvent::MouseWheel { delta_x, delta_y } = event {
973                if container_hover {
974                    self.ui.dispatch_event(id, crate::ui::WidgetEvent::Scroll(*delta_x, *delta_y));
975                }
976            }
977        }
978        self.ui.input_events = events;
979
980        zenthra_core::Response {
981            clicked: self.ui.clicked && container_hover,
982            hovered: container_hover,
983            pressed: container_active,
984        }
985    }
986
987    fn layout_no_wrap(
988        &self,
989        ox: f32,
990        oy: f32,
991        inner_w: f32,
992        inner_h: f32,
993        targets: &mut [(f32, f32)],
994    ) -> (f32, f32) {
995        let n = self.child_sizes.len();
996        if n == 0 { return (0.0, 0.0); }
997
998        let (content_w, content_h) = match self.direction {
999            Direction::Row => {
1000                let w = self.child_sizes.iter().map(|(w, _)| w).sum::<f32>()
1001                    + self.gap * (n.saturating_sub(1)) as f32;
1002                let h = self.child_sizes.iter().map(|(_, h)| *h).fold(0.0f32, f32::max);
1003                (w, h)
1004            }
1005            Direction::Column => {
1006                let w = self.child_sizes.iter().map(|(w, _)| *w).fold(0.0f32, f32::max);
1007                let h = self.child_sizes.iter().map(|(_, h)| h).sum::<f32>()
1008                    + self.gap * (n.saturating_sub(1)) as f32;
1009                (w, h)
1010            }
1011            Direction::Stack => {
1012                let w = self.child_sizes.iter().map(|(w, _)| *w).fold(0.0f32, f32::max);
1013                let h = self.child_sizes.iter().map(|(_, h)| *h).fold(0.0f32, f32::max);
1014                (w, h)
1015            }
1016        };
1017
1018        let real_w = if self.width.is_some() || self.fill_x { inner_w } else { content_w };
1019        let real_h = if self.height.is_some() || self.fill_y { inner_h } else { content_h };
1020
1021        match self.target_halign_valign(ox, oy, real_w, real_h, content_w, content_h, targets) {
1022            (w, h) => (w, h),
1023        }
1024    }
1025    
1026    fn target_halign_valign(&self, ox: f32, oy: f32, real_w: f32, real_h: f32, content_w: f32, content_h: f32, targets: &mut [(f32, f32)]) -> (f32, f32) {
1027        let n = self.child_sizes.len();
1028        match self.direction {
1029            Direction::Row => {
1030                let extra = (real_w - content_w).max(0.0);
1031                let mut cx = ox + self.padding_left + self.border_width + match self.halign {
1032                    Align::Left => 0.0,
1033                    Align::Center => extra / 2.0,
1034                    Align::Right => extra,
1035                    Align::SpaceBetween => 0.0,
1036                    Align::SpaceAround => extra / (n as f32 * 2.0),
1037                    _ => 0.0,
1038                };
1039                let gap = match self.halign {
1040                    Align::SpaceBetween if n > 1 => extra / (n - 1) as f32,
1041                    Align::SpaceAround => extra / n as f32,
1042                    _ => self.gap,
1043                };
1044                for (i, (cw, ch)) in self.child_sizes.iter().enumerate() {
1045                    let cy = oy + self.padding_top + self.border_width + match self.valign {
1046                        Align::Top => 0.0,
1047                        Align::Center => (real_h - ch) / 2.0,
1048                        Align::Bottom => real_h - ch,
1049                        _ => 0.0,
1050                    };
1051                    targets[i] = (cx, cy);
1052                    cx += cw + gap;
1053                }
1054            }
1055            Direction::Column => {
1056                let extra = (real_h - content_h).max(0.0);
1057                let mut cy = oy + self.padding_top + self.border_width + match self.valign {
1058                    Align::Top => 0.0,
1059                    Align::Center => extra / 2.0,
1060                    Align::Bottom => extra,
1061                    Align::SpaceBetween => 0.0,
1062                    Align::SpaceAround => extra / (n as f32 * 2.0),
1063                    _ => 0.0,
1064                };
1065                let gap = match self.valign {
1066                    Align::SpaceBetween if n > 1 => extra / (n - 1) as f32,
1067                    Align::SpaceAround => extra / n as f32,
1068                    _ => self.gap,
1069                };
1070                for (i, (cw, ch)) in self.child_sizes.iter().enumerate() {
1071                    let cx = ox + self.padding_left + self.border_width + match self.halign {
1072                        Align::Left => 0.0,
1073                        Align::Center => (real_w - cw) / 2.0,
1074                        Align::Right => real_w - cw,
1075                        _ => 0.0,
1076                    };
1077                    targets[i] = (cx, cy);
1078                    cy += ch + gap;
1079                }
1080            }
1081            Direction::Stack => {
1082                for (i, (cw, ch)) in self.child_sizes.iter().enumerate() {
1083                    let cx = ox + self.padding_left + self.border_width + match self.halign {
1084                        Align::Center => (real_w - cw).max(0.0) / 2.0,
1085                        Align::Right => real_w - cw,
1086                        _ => 0.0,
1087                    };
1088                    let cy = oy + self.padding_top + self.border_width + match self.valign {
1089                        Align::Center => (real_h - ch).max(0.0) / 2.0,
1090                        Align::Bottom => real_h - ch,
1091                        _ => 0.0,
1092                    };
1093                    targets[i] = (cx, cy);
1094                }
1095            }
1096        }
1097        (content_w, content_h)
1098    }
1099
1100    fn layout_wrap(
1101        &self,
1102        ox: f32,
1103        oy: f32,
1104        inner_w: f32,
1105        inner_h: f32,
1106        targets: &mut [(f32, f32)],
1107    ) -> (f32, f32) {
1108        let n = self.child_sizes.len();
1109        if n == 0 { return (0.0, 0.0); }
1110
1111        let (main_reversed, cross_reversed) = match self.wrap {
1112            Wrap::Wrap => (false, false),
1113            Wrap::WrapReverse => (false, true),
1114            Wrap::RightToLeft => (true, false),
1115            Wrap::RightToLeftReverse => (true, true),
1116            _ => (false, false),
1117        };
1118
1119        match self.direction {
1120            Direction::Row => {
1121                let mut rows: Vec<Vec<usize>> = Vec::new();
1122                let mut current_row = Vec::new();
1123                let mut row_w = 0.0f32;
1124
1125                for (i, (cw, _)) in self.child_sizes.iter().enumerate() {
1126                    let needed = if current_row.is_empty() { *cw } else { row_w + self.gap + cw };
1127                    if needed > inner_w && !current_row.is_empty() {
1128                        rows.push(std::mem::take(&mut current_row));
1129                        current_row.push(i);
1130                        row_w = *cw;
1131                    } else {
1132                        current_row.push(i);
1133                        row_w = needed;
1134                    }
1135                }
1136                if !current_row.is_empty() { rows.push(current_row); }
1137
1138                let row_heights: Vec<f32> = rows.iter().map(|row| {
1139                    row.iter().map(|&i| self.child_sizes[i].1).fold(0.0f32, f32::max)
1140                }).collect();
1141
1142                let total_h = row_heights.iter().sum::<f32>() + self.gap * (rows.len().saturating_sub(1)) as f32;
1143                let max_row_w = rows.iter().map(|row| {
1144                    row.iter().map(|&i| self.child_sizes[i].0).sum::<f32>() + self.gap * (row.len().saturating_sub(1)) as f32
1145                }).fold(0.0f32, f32::max);
1146
1147                let real_h = if self.height.is_some() || self.fill_y { inner_h } else { total_h };
1148                let real_w = if self.width.is_some() || self.fill_x { inner_w } else { max_row_w };
1149
1150                let mut cy = if cross_reversed {
1151                    oy + self.padding_top + self.border_width + match self.valign {
1152                        Align::Top => total_h,
1153                        Align::Center => (real_h + total_h) / 2.0,
1154                        Align::Bottom => real_h,
1155                        _ => total_h,
1156                    }
1157                } else {
1158                    oy + self.padding_top + self.border_width + match self.valign {
1159                        Align::Center => (real_h - total_h).max(0.0) / 2.0,
1160                        Align::Bottom => (real_h - total_h).max(0.0),
1161                        _ => 0.0,
1162                    }
1163                };
1164
1165                for (ri, row) in rows.iter().enumerate() {
1166                    let row_h = row_heights[ri];
1167                    let row_content_w = row.iter().map(|&i| self.child_sizes[i].0).sum::<f32>() + self.gap * (row.len().saturating_sub(1)) as f32;
1168                    let extra = (real_w - row_content_w).max(0.0);
1169                    if cross_reversed { cy -= row_h; }
1170
1171                    let mut cx = if main_reversed {
1172                        ox + self.padding_left + self.border_width + match self.halign {
1173                            Align::Left => row_content_w,
1174                            Align::Center => (real_w + row_content_w) / 2.0,
1175                            Align::Right => real_w,
1176                            _ => row_content_w,
1177                        }
1178                    } else {
1179                        ox + self.padding_left + self.border_width + match self.halign {
1180                            Align::Left => 0.0,
1181                            Align::Center => extra / 2.0,
1182                            Align::Right => extra,
1183                            Align::SpaceBetween => 0.0,
1184                            Align::SpaceAround => extra / (row.len() as f32 * 2.0),
1185                            _ => 0.0,
1186                        }
1187                    };
1188
1189                    let gap = match self.halign {
1190                        Align::SpaceBetween if row.len() > 1 && !main_reversed => extra / (row.len() - 1) as f32,
1191                        Align::SpaceAround if !main_reversed => extra / row.len() as f32,
1192                        _ => self.gap,
1193                    };
1194
1195                    for &ci in row {
1196                        let (cw, ch) = self.child_sizes[ci];
1197                        if main_reversed { cx -= cw; }
1198                        let child_y = cy + match self.valign {
1199                            Align::Center => (row_h - ch) / 2.0,
1200                            Align::Bottom => row_h - ch,
1201                            _ => 0.0,
1202                        };
1203                        targets[ci] = (cx, child_y);
1204                        if main_reversed { cx -= gap; } else { cx += cw + gap; }
1205                    }
1206                    if cross_reversed { cy -= self.gap; } else { cy += row_h + self.gap; }
1207                }
1208                (max_row_w, total_h)
1209            }
1210            Direction::Column => {
1211                let mut cols: Vec<Vec<usize>> = Vec::new();
1212                let mut current_col = Vec::new();
1213                let mut col_h = 0.0f32;
1214
1215                for (i, (_, ch)) in self.child_sizes.iter().enumerate() {
1216                    let needed = if current_col.is_empty() { *ch } else { col_h + self.gap + ch };
1217                    if needed > inner_h && !current_col.is_empty() {
1218                        cols.push(std::mem::take(&mut current_col));
1219                        current_col.push(i);
1220                        col_h = *ch;
1221                    } else {
1222                        current_col.push(i);
1223                        col_h = needed;
1224                    }
1225                }
1226                if !current_col.is_empty() { cols.push(current_col); }
1227
1228                let col_widths: Vec<f32> = cols.iter().map(|col| {
1229                    col.iter().map(|&i| self.child_sizes[i].0).fold(0.0f32, f32::max)
1230                }).collect();
1231
1232                let total_w = col_widths.iter().sum::<f32>() + self.gap * (cols.len().saturating_sub(1)) as f32;
1233                let max_col_h = cols.iter().map(|col| {
1234                    col.iter().map(|&i| self.child_sizes[i].1).sum::<f32>() + self.gap * (col.len().saturating_sub(1)) as f32
1235                }).fold(0.0f32, f32::max);
1236
1237                let real_w = if self.width.is_some() || self.fill_x { inner_w } else { total_w };
1238                let real_h = if self.height.is_some() || self.fill_y { inner_h } else { max_col_h };
1239
1240                let mut cx = if cross_reversed {
1241                    ox + self.padding_left + self.border_width + match self.halign {
1242                        Align::Left => total_w,
1243                        Align::Center => (real_w + total_w) / 2.0,
1244                        Align::Right => real_w,
1245                        _ => total_w,
1246                    }
1247                } else {
1248                    ox + self.padding_left + self.border_width + match self.halign {
1249                        Align::Center => (real_w - total_w).max(0.0) / 2.0,
1250                        Align::Right => (real_w - total_w).max(0.0),
1251                        _ => 0.0,
1252                    }
1253                };
1254
1255                for (ci_idx, col) in cols.iter().enumerate() {
1256                    let col_w = col_widths[ci_idx];
1257                    let col_content_h = col.iter().map(|&i| self.child_sizes[i].1).sum::<f32>() + self.gap * (col.len().saturating_sub(1)) as f32;
1258                    let extra = (real_h - col_content_h).max(0.0);
1259                    if cross_reversed { cx -= col_w; }
1260
1261                    let mut cy = if main_reversed {
1262                        oy + self.padding_top + self.border_width + match self.valign {
1263                            Align::Top => col_content_h,
1264                            Align::Center => (real_h + col_content_h) / 2.0,
1265                            Align::Bottom => real_h,
1266                            _ => col_content_h,
1267                        }
1268                    } else {
1269                        oy + self.padding_top + self.border_width + match self.valign {
1270                            Align::Top => 0.0,
1271                            Align::Center => extra / 2.0,
1272                            Align::Bottom => extra,
1273                            Align::SpaceBetween => 0.0,
1274                            Align::SpaceAround => extra / (col.len() as f32 * 2.0),
1275                            _ => 0.0,
1276                        }
1277                    };
1278
1279                    let gap = match self.valign {
1280                        Align::SpaceBetween if col.len() > 1 && !main_reversed => extra / (col.len() - 1) as f32,
1281                        Align::SpaceAround if !main_reversed => extra / col.len() as f32,
1282                        _ => self.gap,
1283                    };
1284
1285                    for &ci in col {
1286                        let (cw, ch) = self.child_sizes[ci];
1287                        if main_reversed { cy -= ch; }
1288                    let child_x = cx + match self.halign {
1289                        Align::Center => (col_w - cw) / 2.0,
1290                        Align::Right => col_w - cw,
1291                        _ => 0.0,
1292                    };
1293                        targets[ci] = (child_x, cy);
1294                        if main_reversed { cy -= gap; } else { cy += ch + gap; }
1295                    }
1296                    if cross_reversed { cx -= self.gap; } else { cx += col_w + self.gap; }
1297                }
1298                (total_w, max_col_h)
1299            }
1300            Direction::Stack => {
1301                let w = self.child_sizes.iter().map(|(w, _)| *w).fold(0.0f32, f32::max);
1302                let h = self.child_sizes.iter().map(|(_, h)| *h).fold(0.0f32, f32::max);
1303                for (i, (cw, ch)) in self.child_sizes.iter().enumerate() {
1304                    let cx = ox + self.padding_left + self.border_width + match self.halign {
1305                        Align::Center => (inner_w - cw).max(0.0) / 2.0,
1306                        Align::Right => inner_w - cw,
1307                        _ => 0.0,
1308                    };
1309                    let cy = oy + self.padding_top + self.border_width + match self.valign {
1310                        Align::Center => (inner_h - ch).max(0.0) / 2.0,
1311                        Align::Bottom => inner_h - ch,
1312                        _ => 0.0,
1313                    };
1314                    targets[i] = (cx, cy);
1315                }
1316                (w, h)
1317            }
1318        }
1319    }
1320}
1321
1322/// Offset a draw command's position and its clipping rect by (dx, dy).
1323fn offset_draw(cmd: &mut DrawCommand, dx: f32, dy: f32) {
1324    match cmd {
1325        DrawCommand::Rect(r) => {
1326            r.instance.pos[0] += dx;
1327            r.instance.pos[1] += dy;
1328            // Translate the widget's internal clip rect so it stays with the widget
1329            r.instance.clip_rect[0] += dx;
1330            r.instance.clip_rect[1] += dy;
1331        }
1332        DrawCommand::Text(t) => {
1333            t.pos[0] += dx;
1334            t.pos[1] += dy;
1335            t.clip[0] += dx;
1336            t.clip[1] += dy;
1337        }
1338        DrawCommand::OverlayRect(c) => {
1339            c.x += dx;
1340            c.y += dy;
1341            c.clip[0] += dx;
1342            c.clip[1] += dy;
1343        }
1344        DrawCommand::Image(i) => {
1345            i.instance.pos[0] += dx;
1346            i.instance.pos[1] += dy;
1347            i.instance.clip_rect[0] += dx;
1348            i.instance.clip_rect[1] += dy;
1349        }
1350    }
1351}
1352
1353fn draw_origin(cmd: &DrawCommand) -> (f32, f32) {
1354    match cmd {
1355        DrawCommand::Rect(r) => (r.instance.pos[0], r.instance.pos[1]),
1356        DrawCommand::Text(t) => (t.pos[0], t.pos[1]),
1357        DrawCommand::OverlayRect(c) => (c.x, c.y),
1358        DrawCommand::Image(i) => (i.instance.pos[0], i.instance.pos[1]),
1359    }
1360}
1361
1362fn set_clip(cmd: &mut DrawCommand, clip: [f32; 4]) {
1363    match cmd {
1364        DrawCommand::Rect(r) => r.instance.clip_rect = intersect_rects(r.instance.clip_rect, clip),
1365        DrawCommand::Text(t) => t.clip = intersect_rects(t.clip, clip),
1366        DrawCommand::OverlayRect(c) => c.clip = intersect_rects(c.clip, clip),
1367        DrawCommand::Image(i) => i.instance.clip_rect = intersect_rects(i.instance.clip_rect, clip),
1368    }
1369}
1370
1371fn intersect_rects(a: [f32; 4], b: [f32; 4]) -> [f32; 4] {
1372    let x1 = a[0].max(b[0]);
1373    let y1 = a[1].max(b[1]);
1374    let x2 = (a[0] + a[2]).min(b[0] + b[2]);
1375    let y2 = (a[1] + a[3]).min(b[1] + b[3]);
1376    
1377    let w = (x2 - x1).max(0.0);
1378    let h = (y2 - y1).max(0.0);
1379    
1380    [x1, y1, w, h]
1381}