makepad_render/
turtle.rs

1use crate::cx::*;
2use makepad_tinyserde::*;
3
4impl Cx {
5    //pub fn debug_pt(&self, x:f32, y:f32, color:i32){
6    //self.debug_pts.borrow_mut().push((x,y,color));
7    //}
8    
9    pub fn set_count_of_aligned_instance(&mut self, instance_count: usize) -> Area {
10        let mut area = self.align_list.last_mut().unwrap();
11        if let Area::Instance(inst) = &mut area {
12            inst.instance_count = instance_count;
13        }
14        area.clone()
15    }
16    
17    // begin a new turtle with a layout
18    pub fn begin_turtle(&mut self, layout: Layout, guard_area: Area) {
19        
20        if !self.is_in_redraw_cycle {
21            panic!("calling begin_turtle outside of redraw cycle is not possible!");
22        }
23        
24        // fetch origin and size from parent
25        let (mut origin, mut abs_size) = if let Some(parent) = self.turtles.last() {
26            (Vec2 {x: layout.walk.margin.l + parent.pos.x, y: layout.walk.margin.t + parent.pos.y}, parent.abs_size)
27        }
28        else {
29            (Vec2 {x: layout.walk.margin.l, y: layout.walk.margin.t}, Vec2::default())
30        };
31        
32        // see if layout overrode size
33        if let Some(layout_abs_size) = layout.abs_size {
34            abs_size = layout_abs_size;
35        }
36        
37        // same for origin
38        let is_abs_origin;
39        if let Some(abs_origin) = layout.abs_origin {
40            origin = abs_origin;
41            is_abs_origin = true;
42        }
43        else {
44            is_abs_origin = false;
45        }
46        
47        // abs origin overrides the computation of width/height to use the parent abs_origin
48        let (width, min_width) = layout.walk.width.eval_width(self, layout.walk.margin, is_abs_origin, abs_size.x);
49        let (height, min_height) = layout.walk.height.eval_height(self, layout.walk.margin, is_abs_origin, abs_size.y);
50
51        let turtle = Turtle {
52            align_list_x: self.align_list.len(),
53            align_list_y: self.align_list.len(),
54            origin: origin,
55            pos: Vec2 {x: origin.x + layout.padding.l, y: origin.y + layout.padding.t},
56            layout: layout,
57            biggest: 0.0,
58            bound_left_top: Vec2 {x: std::f32::INFINITY, y: std::f32::INFINITY},
59            bound_right_bottom: Vec2 {x: std::f32::NEG_INFINITY, y: std::f32::NEG_INFINITY},
60            width: width,
61            height: height,
62            min_width: min_width,
63            min_height: min_height,
64            width_used: 0.,
65            height_used: 0.,
66            abs_size: abs_size,
67            guard_area: guard_area,
68            //..Default::default()
69        };
70        
71        self.turtles.push(turtle);
72    }
73    
74    pub fn end_turtle(&mut self, guard_area: Area) -> Rect {
75        let old = self.turtles.pop().unwrap();
76        if guard_area != old.guard_area {
77            panic!("End turtle guard area misaligned!, begin/end pair not matched begin {:?} end {:?}", old.guard_area, guard_area)
78        }
79        
80        let w = if old.width.is_nan() {
81            if old.bound_right_bottom.x == std::f32::NEG_INFINITY { // nothing happened, use padding
82                Width::Fix(old.layout.padding.l + old.layout.padding.r)
83            }
84            else { // use the bounding box
85                Width::Fix(max_zero_keep_nan(old.bound_right_bottom.x - old.origin.x + old.layout.padding.r).max(old.min_width))
86            }
87        }
88        else {
89            Width::Fix(old.width)
90        };
91        
92        let h = if old.height.is_nan() {
93            if old.bound_right_bottom.y == std::f32::NEG_INFINITY { // nothing happened use the padding
94                Height::Fix(old.layout.padding.t + old.layout.padding.b)
95            }
96            else { // use the bounding box
97                Height::Fix(max_zero_keep_nan(old.bound_right_bottom.y - old.origin.y + old.layout.padding.b).max(old.min_height))
98            }
99        }
100        else {
101            Height::Fix(old.height)
102        };
103        
104        let margin = old.layout.walk.margin.clone();
105        // if we have alignment set, we should now align our childnodes
106        let dx = Self::compute_align_turtle_x(&old);
107        if dx > 0.0 {
108            self.do_align_x(dx, old.align_list_x);
109        }
110        let dy = Self::compute_align_turtle_y(&old);
111        if dy > 0.0 {
112            self.do_align_y(dy, old.align_list_y);
113        }
114        
115        // when a turtle is x-abs / y-abs you dont walk the parent
116        if !old.layout.abs_origin.is_none() {
117            let abs_origin = if let Some(abs_origin) = old.layout.abs_origin {abs_origin} else {Vec2::default()};
118            let w = if let Width::Fix(vw) = w {vw} else {0.};
119            let h = if let Height::Fix(vh) = h {vh} else {0.};
120            return Rect {x: abs_origin.x, y: abs_origin.y, w: w, h: h};
121        }
122        
123        return self.walk_turtle_with_old(Walk {width: w, height: h, margin}, Some(&old))
124    }
125    
126    pub fn walk_turtle(&mut self, walk: Walk) -> Rect {
127        self.walk_turtle_with_old(walk, None)
128    }
129    
130    // walk the turtle with a 'w/h' and a margin
131    pub fn walk_turtle_with_old(&mut self, walk: Walk, old_turtle: Option<&Turtle>) -> Rect {
132        let mut align_dx = 0.0;
133        let mut align_dy = 0.0;
134        let (w,_mw) = walk.width.eval_width(self, walk.margin, false, 0.0);
135        let (h,_mh) = walk.height.eval_height(self, walk.margin, false, 0.0);
136        
137        let ret = if let Some(turtle) = self.turtles.last_mut() {
138            let (x, y) = match turtle.layout.direction {
139                Direction::Right => {
140                    match turtle.layout.line_wrap {
141                        LineWrap::NewLine => {
142                            if (turtle.pos.x + walk.margin.l + w) >
143                            (turtle.origin.x + turtle.width - turtle.layout.padding.r) {
144                                // what is the move delta.
145                                let old_x = turtle.pos.x;
146                                let old_y = turtle.pos.y;
147                                turtle.pos.x = turtle.origin.x + turtle.layout.padding.l;
148                                turtle.pos.y += turtle.biggest;
149                                turtle.biggest = 0.0;
150                                align_dx = turtle.pos.x - old_x;
151                                align_dy = turtle.pos.y - old_y;
152                            }
153                        },
154                        LineWrap::MaxSize(max_size) => {
155                            let new_size = turtle.pos.x + walk.margin.l + w;
156                            if new_size > (turtle.origin.x + turtle.width - turtle.layout.padding.r)
157                                || new_size > (turtle.origin.x + max_size - turtle.layout.padding.r) {
158                                // what is the move delta.
159                                let old_x = turtle.pos.x;
160                                let old_y = turtle.pos.y;
161                                turtle.pos.x = turtle.origin.x + turtle.layout.padding.l;
162                                turtle.pos.y += turtle.biggest;
163                                turtle.biggest = 0.0;
164                                align_dx = turtle.pos.x - old_x;
165                                align_dy = turtle.pos.y - old_y;
166                            }
167                        },
168                        LineWrap::None => {
169                        }
170                    }
171                    
172                    let x = turtle.pos.x + walk.margin.l;
173                    let y = turtle.pos.y + walk.margin.t;
174                    // walk it normally
175                    turtle.pos.x += w + walk.margin.l + walk.margin.r;
176                    
177                    // keep track of biggest item in the line (include item margin bottom)
178                    let biggest = h + walk.margin.t + walk.margin.b;
179                    if biggest > turtle.biggest {
180                        turtle.biggest = biggest;
181                    }
182                    (x, y)
183                },
184                Direction::Down => {
185                    match turtle.layout.line_wrap {
186                        LineWrap::NewLine => {
187                            if (turtle.pos.y + walk.margin.t + h) >
188                            (turtle.origin.y + turtle.height - turtle.layout.padding.b) {
189                                // what is the move delta.
190                                let old_x = turtle.pos.x;
191                                let old_y = turtle.pos.y;
192                                turtle.pos.y = turtle.origin.y + turtle.layout.padding.t;
193                                turtle.pos.x += turtle.biggest;
194                                turtle.biggest = 0.0;
195                                align_dx = turtle.pos.x - old_x;
196                                align_dy = turtle.pos.y - old_y;
197                            }
198                        },
199                        LineWrap::MaxSize(max_size) => {
200                            let new_size = turtle.pos.y + walk.margin.t + h;
201                            if new_size > (turtle.origin.y + turtle.height - turtle.layout.padding.b)
202                                || new_size > (turtle.origin.y + max_size - turtle.layout.padding.b) {
203                                // what is the move delta.
204                                let old_x = turtle.pos.x;
205                                let old_y = turtle.pos.y;
206                                turtle.pos.y = turtle.origin.y + turtle.layout.padding.t;
207                                turtle.pos.x += turtle.biggest;
208                                turtle.biggest = 0.0;
209                                align_dx = turtle.pos.x - old_x;
210                                align_dy = turtle.pos.y - old_y;
211                            }
212                        },
213                        LineWrap::None => {
214                        }
215                    }
216                    
217                    let x = turtle.pos.x + walk.margin.l;
218                    let y = turtle.pos.y + walk.margin.t;
219                    // walk it normally
220                    turtle.pos.y += h + walk.margin.t + walk.margin.b;
221                    
222                    // keep track of biggest item in the line (include item margin bottom)
223                    let biggest = w + walk.margin.r + walk.margin.l;
224                    if biggest > turtle.biggest {
225                        turtle.biggest = biggest;
226                    }
227                    (x, y)
228                },
229                _ => {
230                    (turtle.pos.x + walk.margin.l, turtle.pos.y + walk.margin.t)
231                }
232            };
233            
234            let bound_x2 = x + w + if walk.margin.r < 0. {walk.margin.r} else {0.};
235            if bound_x2 > turtle.bound_right_bottom.x {
236                turtle.bound_right_bottom.x = bound_x2;
237            }
238            // update y2 bounds (margin bottom is only added if its negative)
239            let bound_y2 = y + h + walk.margin.t + if walk.margin.b < 0. {walk.margin.b} else {0.};
240            if bound_y2 > turtle.bound_right_bottom.y {
241                turtle.bound_right_bottom.y = bound_y2;
242            }
243            
244            if x < turtle.bound_left_top.x {
245                turtle.bound_left_top.x = x;
246            }
247            if y < turtle.bound_left_top.y {
248                turtle.bound_left_top.y = y;
249            }
250            // we could directly h or v align this thing
251            
252            Rect {
253                x: x,
254                y: y,
255                w: w,
256                h: h
257            }
258        }
259        else {
260            Rect {
261                x: 0.0,
262                y: 0.0,
263                w: w,
264                h: h
265            }
266        };
267        
268        if align_dx != 0.0 {
269            if let Some(old_turtle) = old_turtle {
270                self.do_align_x(align_dx, old_turtle.align_list_x);
271            }
272        };
273        if align_dy != 0.0 {
274            if let Some(old_turtle) = old_turtle {
275                self.do_align_y(align_dy, old_turtle.align_list_y);
276            }
277        };
278        
279        ret
280    }
281    
282    // high perf turtle with no indirections and compute visibility
283    pub fn walk_turtle_right_no_wrap(&mut self, w: f32, h: f32, scroll: Vec2) -> Option<Rect> {
284        if let Some(turtle) = self.turtles.last_mut() {
285            let x = turtle.pos.x;
286            let y = turtle.pos.y;
287            // walk it normally
288            turtle.pos.x += w;
289            
290            // keep track of biggest item in the line (include item margin bottom)
291            let biggest = h;
292            if biggest > turtle.biggest {
293                turtle.biggest = biggest;
294            }
295            // update x2 bounds (margin right is only added if its negative)
296            let bound_x2 = x + w;
297            if bound_x2 > turtle.bound_right_bottom.x {
298                turtle.bound_right_bottom.x = bound_x2;
299            }
300            // update y2 bounds (margin bottom is only added if its negative)
301            let bound_y2 = turtle.pos.y + h;
302            if bound_y2 > turtle.bound_right_bottom.y {
303                turtle.bound_right_bottom.y = bound_y2;
304            }
305            
306            let vx = turtle.origin.x + scroll.x;
307            let vy = turtle.origin.y + scroll.y;
308            let vw = turtle.width;
309            let vh = turtle.height;
310            
311            if x > vx + vw || x + w < vx || y > vy + vh || y + h < vy {
312                None
313            }
314            else {
315                Some(Rect {
316                    x: x,
317                    y: y,
318                    w: w,
319                    h: h
320                })
321            }
322        }
323        else {
324            None
325        }
326    }
327    
328    pub fn turtle_new_line(&mut self) {
329        if let Some(turtle) = self.turtles.last_mut() {
330            match turtle.layout.direction {
331                Direction::Right => {
332                    turtle.pos.x = turtle.origin.x + turtle.layout.padding.l;
333                    turtle.pos.y += turtle.biggest + turtle.layout.new_line_padding;
334                    turtle.biggest = 0.0;
335                },
336                Direction::Down => {
337                    turtle.pos.y = turtle.origin.y + turtle.layout.padding.t;
338                    turtle.pos.x += turtle.biggest + turtle.layout.new_line_padding;
339                    turtle.biggest = 0.0;
340                },
341                _ => ()
342            }
343        }
344    }
345    
346    pub fn turtle_line_is_visible(&mut self, min_height: f32, scroll: Vec2) -> bool {
347        if let Some(turtle) = self.turtles.last_mut() {
348            let y = turtle.pos.y;
349            let h = turtle.biggest.max(min_height);
350            let vy = turtle.origin.y + scroll.y;
351            let vh = turtle.height;
352            
353            if y > vy + vh || y + h < vy {
354                return false
355            }
356            else {
357                return true
358            }
359        }
360        false
361    }
362    
363    pub fn turtle_new_line_min_height(&mut self, min_height: f32) {
364        if let Some(turtle) = self.turtles.last_mut() {
365            turtle.pos.x = turtle.origin.x + turtle.layout.padding.l;
366            turtle.pos.y += turtle.biggest.max(min_height);
367            turtle.biggest = 0.0;
368        }
369    }
370    
371    fn do_align_x(&mut self, dx: f32, align_start: usize) {
372        let dx = (dx * self.current_dpi_factor).floor() / self.current_dpi_factor;
373        for i in align_start..self.align_list.len() {
374            let align_item = &self.align_list[i];
375            match align_item {
376                Area::Instance(inst) => {
377                    let cxview = &mut self.views[inst.view_id];
378                    let draw_call = &mut cxview.draw_calls[inst.draw_call_id];
379                    let sh = &self.shaders[draw_call.shader_id];
380                    for i in 0..inst.instance_count {
381                        if let Some(x) = sh.mapping.rect_instance_props.x {
382                            draw_call.instance[inst.instance_offset + x + i * sh.mapping.instance_slots] += dx;
383                        }
384                    }
385                },
386                _ => (),
387            }
388        }
389    }
390    
391    fn do_align_y(&mut self, dy: f32, align_start: usize) {
392        let dy = (dy * self.current_dpi_factor).floor() / self.current_dpi_factor;
393        for i in align_start..self.align_list.len() {
394            let align_item = &self.align_list[i];
395            match align_item {
396                Area::Instance(inst) => {
397                    let cxview = &mut self.views[inst.view_id];
398                    let draw_call = &mut cxview.draw_calls[inst.draw_call_id];
399                    let sh = &self.shaders[draw_call.shader_id];
400                    for i in 0..inst.instance_count {
401                        if let Some(y) = sh.mapping.rect_instance_props.y {
402                            draw_call.instance[inst.instance_offset + y + i * sh.mapping.instance_slots] += dy;
403                        }
404                    }
405                },
406                _ => (),
407            }
408        }
409    }
410    
411    pub fn get_turtle_rect(&self) -> Rect {
412        if let Some(turtle) = self.turtles.last() {
413            return Rect {
414                x: turtle.origin.x,
415                y: turtle.origin.y,
416                w: turtle.width,
417                h: turtle.height
418            }
419        };
420        return Rect::default();
421    }
422    
423    pub fn get_turtle_biggest(&self) -> f32 {
424        if let Some(turtle) = self.turtles.last() {
425            turtle.biggest
426        }
427        else {
428            0.
429        }
430    }
431    
432    pub fn get_turtle_bounds(&self) -> Vec2 {
433        if let Some(turtle) = self.turtles.last() {
434            
435            return Vec2 {
436                x: if turtle.bound_right_bottom.x<0. {0.}else {turtle.bound_right_bottom.x} + turtle.layout.padding.r - turtle.origin.x,
437                y: if turtle.bound_right_bottom.y<0. {0.}else {turtle.bound_right_bottom.y} + turtle.layout.padding.b - turtle.origin.y
438            };
439        }
440        return Vec2::default()
441    }
442    
443    pub fn set_turtle_bounds(&mut self, bound: Vec2) {
444        if let Some(turtle) = self.turtles.last_mut() {
445            turtle.bound_right_bottom = Vec2 {
446                x: bound.x - turtle.layout.padding.r + turtle.origin.x,
447                y: bound.y - turtle.layout.padding.b + turtle.origin.y
448            }
449        }
450    }
451    
452    pub fn get_turtle_origin(&self) -> Vec2 {
453        if let Some(turtle) = self.turtles.last() {
454            return turtle.origin;
455        }
456        return Vec2::default()
457    }
458    
459    pub fn move_turtle(&mut self, dx: f32, dy: f32) {
460        if let Some(turtle) = self.turtles.last_mut() {
461            turtle.pos.x += dx;
462            turtle.pos.y += dy;
463        }
464    }
465    
466    pub fn get_turtle_pos(&self) -> Vec2 {
467        if let Some(turtle) = self.turtles.last() {
468            turtle.pos
469        }
470        else {
471            Vec2::default()
472        }
473    }
474    
475    pub fn set_turtle_pos(&mut self, pos: Vec2) {
476        if let Some(turtle) = self.turtles.last_mut() {
477            turtle.pos = pos
478        }
479    }
480    
481    pub fn get_rel_turtle_pos(&self) -> Vec2 {
482        if let Some(turtle) = self.turtles.last() {
483            Vec2 {x: turtle.pos.x - turtle.origin.x, y: turtle.pos.y - turtle.origin.y}
484        }
485        else {
486            Vec2::default()
487        }
488    }
489    
490    pub fn set_turtle_padding(&mut self, padding: Padding) {
491        if let Some(turtle) = self.turtles.last_mut() {
492            turtle.layout.padding = padding
493        }
494    }
495    
496    pub fn visible_in_turtle(&self, geom: Rect, scroll: Vec2) -> bool {
497        if let Some(turtle) = self.turtles.last() {
498            let view = Rect {
499                x: scroll.x, //- margin.l,
500                y: scroll.y, // - margin.t,
501                w: turtle.width, // + margin.l + margin.r,
502                h: turtle.height, // + margin.t + margin.b
503            };
504            
505            return view.intersects(geom)
506        }
507        else {
508            false
509        }
510    }
511    
512    fn compute_align_turtle_x(turtle: &Turtle) -> f32 {
513        if turtle.layout.align.fx > 0.0 {
514            let dx = turtle.layout.align.fx *
515            ((turtle.width - turtle.width_used - (turtle.layout.padding.l + turtle.layout.padding.r)) - (turtle.bound_right_bottom.x - (turtle.origin.x + turtle.layout.padding.l)));
516            if dx.is_nan() {return 0.0}
517            dx
518        }
519        else {
520            0.
521        }
522    }
523    
524    fn compute_align_turtle_y(turtle: &Turtle) -> f32 {
525        if turtle.layout.align.fy > 0.0 {
526            let dy = turtle.layout.align.fy *
527            ((turtle.height - turtle.height_used - (turtle.layout.padding.t + turtle.layout.padding.b)) - (turtle.bound_right_bottom.y - (turtle.origin.y + turtle.layout.padding.t)));
528            if dy.is_nan() {return 0.0}
529            dy
530        }
531        else {
532            0.
533        }
534    }
535    
536    pub fn compute_turtle_width(&mut self) {
537        if let Some(turtle) = self.turtles.last_mut() {
538            if turtle.width.is_nan() {
539                if turtle.bound_right_bottom.x != std::f32::NEG_INFINITY { // nothing happened, use padding
540                    turtle.width = max_zero_keep_nan(turtle.bound_right_bottom.x - turtle.origin.x + turtle.layout.padding.r);
541                    turtle.width_used = 0.;
542                    turtle.bound_right_bottom.x = 0.;
543                }
544            }
545        }
546    }
547    
548    pub fn compute_turtle_height(&mut self) {
549        if let Some(turtle) = self.turtles.last_mut() {
550            if turtle.height.is_nan() {
551                if turtle.bound_right_bottom.y != std::f32::NEG_INFINITY { // nothing happened use the padding
552                    turtle.height = max_zero_keep_nan(turtle.bound_right_bottom.y - turtle.origin.y + turtle.layout.padding.b);
553                    turtle.height_used = 0.;
554                    turtle.bound_right_bottom.y = 0.;
555                }
556            }
557        }
558    }
559    
560    // used for a<b>c layouts horizontally
561    pub fn change_turtle_align_x(&mut self, fx: f32) {
562        let (dx, align_origin_x) = if let Some(turtle) = self.turtles.last_mut() {
563            (Self::compute_align_turtle_x(&turtle), turtle.align_list_x)
564        }
565        else {
566            (0., 0)
567        };
568        if dx > 0.0 {
569            self.do_align_x(dx, align_origin_x);
570        }
571        // reset turtle props
572        if let Some(turtle) = self.turtles.last_mut() {
573            turtle.align_list_x = self.align_list.len();
574            turtle.layout.align.fx = fx;
575            turtle.width_used = turtle.bound_right_bottom.x - turtle.origin.x;
576            turtle.bound_right_bottom.x = std::f32::NEG_INFINITY;
577        }
578    }
579    
580    // used for a<b>c layouts vertically
581    pub fn change_turtle_align_y(&mut self, fy: f32) {
582        let (dy, align_origin_y) = if let Some(turtle) = self.turtles.last_mut() {
583            (Self::compute_align_turtle_y(&turtle), turtle.align_list_y)
584        }
585        else {
586            (0.0, 0)
587        };
588        if dy > 0.0 {
589            self.do_align_y(dy, align_origin_y);
590        }
591        // reset turtle props
592        if let Some(turtle) = self.turtles.last_mut() {
593            turtle.align_list_y = self.align_list.len();
594            turtle.layout.align.fy = fy;
595            turtle.height_used = turtle.bound_right_bottom.y - turtle.origin.y;
596            turtle.bound_right_bottom.y = std::f32::NEG_INFINITY;
597        }
598    }
599    
600    // call this every time to align the last group on the y axis
601    pub fn turtle_align_y(&mut self) {
602        let fy = if let Some(turtle) = self.turtles.last_mut() {
603            turtle.layout.align.fy
604        }
605        else {
606            return
607        };
608        self.change_turtle_align_y(fy);
609        if let Some(turtle) = self.turtles.last_mut() {
610            turtle.height_used = 0.;
611        }
612    }
613    
614    pub fn turtle_align_x(&mut self) {
615        let fx = if let Some(turtle) = self.turtles.last_mut() {
616            turtle.layout.align.fx
617        }
618        else {
619            return
620        };
621        self.change_turtle_align_x(fx);
622        if let Some(turtle) = self.turtles.last_mut() {
623            turtle.width_used = 0.;
624        }
625    }
626    
627    pub fn reset_turtle_bounds(&mut self) {
628        if let Some(turtle) = self.turtles.last_mut() {
629            turtle.bound_left_top = Vec2 {x: std::f32::INFINITY, y: std::f32::INFINITY};
630            turtle.bound_right_bottom = Vec2 {x: std::f32::NEG_INFINITY, y: std::f32::NEG_INFINITY};
631        }
632    }
633    
634    pub fn reset_turtle_pos(&mut self) {
635        if let Some(turtle) = self.turtles.last_mut() {
636            // subtract used size so 'fill' works
637            turtle.pos = Vec2 {
638                x: turtle.origin.x + turtle.layout.padding.l,
639                y: turtle.origin.y + turtle.layout.padding.t
640            };
641        }
642    }
643    
644    
645    fn _get_width_left(&self, abs: bool, abs_size: f32) -> f32 {
646        if !abs {
647            self.get_width_left()
648        }
649        else {
650            abs_size
651        }
652    }
653    
654    pub fn get_width_left(&self) -> f32 {
655        if let Some(turtle) = self.turtles.last() {
656            let nan_val = max_zero_keep_nan(turtle.width - turtle.width_used - (turtle.pos.x - turtle.origin.x));
657            if nan_val.is_nan() { // if we are a computed height, if some value is known, use that
658                if turtle.bound_right_bottom.x != std::f32::NEG_INFINITY {
659                    return turtle.bound_right_bottom.x - turtle.origin.x
660                }
661            }
662            return nan_val
663        }
664        0.
665    }
666    
667    fn _get_width_total(&self, abs: bool, abs_size: f32) -> f32 {
668        if !abs {
669            self.get_width_total()
670        }
671        else {
672            abs_size
673        }
674    }
675    
676    pub fn get_width_total(&self) -> f32 {
677        if let Some(turtle) = self.turtles.last() {
678            let nan_val = max_zero_keep_nan(turtle.width/* - (turtle.layout.padding.l + turtle.layout.padding.r)*/);
679            if nan_val.is_nan() { // if we are a computed width, if some value is known, use that
680                if turtle.bound_right_bottom.x != std::f32::NEG_INFINITY {
681                    return turtle.bound_right_bottom.x - turtle.origin.x + turtle.layout.padding.r
682                }
683            }
684            return nan_val
685        }
686        0.
687    }
688    
689    fn _get_height_left(&self, abs: bool, abs_size: f32) -> f32 {
690        if !abs {
691            self.get_height_left()
692        }
693        else {
694            abs_size
695        }
696    }
697    
698    pub fn get_height_left(&self) -> f32 {
699        if let Some(turtle) = self.turtles.last() {
700            let nan_val = max_zero_keep_nan(turtle.height - turtle.height_used - (turtle.pos.y - turtle.origin.y));
701            if nan_val.is_nan() { // if we are a computed height, if some value is known, use that
702                if turtle.bound_right_bottom.y != std::f32::NEG_INFINITY {
703                    return turtle.bound_right_bottom.y - turtle.origin.y
704                }
705            }
706            return nan_val
707        }
708        0.
709    }
710    
711    fn _get_height_total(&self, abs: bool, abs_size: f32) -> f32 {
712        if !abs {
713            self.get_height_total()
714        }
715        else {
716            abs_size
717        }
718    }
719    
720    pub fn get_height_total(&self) -> f32 {
721        if let Some(turtle) = self.turtles.last() {
722            let nan_val = max_zero_keep_nan(turtle.height /*- (turtle.layout.padding.t + turtle.layout.padding.b)*/);
723            if nan_val.is_nan() { // if we are a computed height, if some value is known, use that
724                if turtle.bound_right_bottom.y != std::f32::NEG_INFINITY {
725                    return turtle.bound_right_bottom.y - turtle.origin.y + turtle.layout.padding.b
726                }
727            }
728            return nan_val
729        }
730        0.
731    }
732    
733    pub fn is_height_computed(&self) -> bool {
734        if let Some(turtle) = self.turtles.last() {
735            if let Height::Compute = turtle.layout.walk.height {
736                return true
737            }
738        }
739        false
740    }
741    
742    pub fn is_width_computed(&self) -> bool {
743        if let Some(turtle) = self.turtles.last() {
744            if let Width::Compute = turtle.layout.walk.width {
745                return true
746            }
747        }
748        false
749    }
750    
751}
752/*
753thread_local!(pub static debug_pts_store: RefCell<Vec<(f32,f32,i32,String)>> = RefCell::new(Vec::new()));
754pub fn debug_pt(x:f32, y:f32, color:i32, s:&str){
755    debug_pts_store.with(|c|{
756        let mut store = c.borrow_mut();
757        store.push((x,y,color,s.to_string()));
758    })
759}
760
761
762        debug_pts_store.with(|c|{
763            let mut store = c.borrow_mut();
764            for (x,y,col,s) in store.iter(){
765                self.debug_qd.color = match col{
766                    0=>color("red"),
767                    1=>color("green"),
768                    2=>color("blue"),
769                    _=>color("yellow")
770                };
771                self.debug_qd.draw_abs(cx, false, *x, *y,2.0,2.0);
772                if s.len() != 0{
773                    self.debug_tx.draw_text(cx, Fixed(*x), Fixed(*y), s);
774                }
775            }
776            store.truncate(0);
777        })*/
778
779
780#[derive(Copy, Clone, Debug)]
781pub enum Width {
782    Fill,
783    Fix(f32),
784    Compute,
785    ComputeFill,
786    FillPad(f32),
787    FillScale(f32),
788    FillScalePad(f32, f32),
789    Scale(f32),
790    ScalePad(f32, f32),
791}
792
793#[derive(Copy, Clone, Debug)]
794pub enum Height {
795    Fill,
796    Fix(f32),
797    Compute,
798    ComputeFill,
799    FillPad(f32),
800    FillScale(f32),
801    FillScalePad(f32, f32),
802    Scale(f32),
803    ScalePad(f32, f32),
804}
805
806impl Default for Width {
807    fn default() -> Self {
808        Width::Fill
809    }
810}
811
812
813impl Default for Height {
814    fn default() -> Self {
815        Height::Fill
816    }
817}
818
819
820impl Width {
821    pub fn fixed(&self) -> f32 {
822        match self {
823            Width::Fix(v) => *v,
824            _ => 0.
825        }
826    }
827    
828    pub fn eval_width(&self, cx: &Cx, margin: Margin, abs: bool, abs_pos: f32) -> (f32,f32) {
829        match self {
830            Width::Compute => (std::f32::NAN,0.),
831            Width::ComputeFill => (std::f32::NAN,cx._get_width_left(abs, abs_pos) - (margin.l + margin.r)),
832            Width::Fix(v) => (max_zero_keep_nan(*v),0.),
833            Width::Fill => (max_zero_keep_nan(cx._get_width_left(abs, abs_pos) - (margin.l + margin.r)),0.),
834            Width::FillPad(p) => (max_zero_keep_nan(cx._get_width_left(abs, abs_pos) - p - (margin.l + margin.r)),0.),
835            Width::FillScale(s) => (max_zero_keep_nan(cx._get_width_left(abs, abs_pos) * s - (margin.l + margin.r)),0.),
836            Width::FillScalePad(s, p) => (max_zero_keep_nan(cx._get_width_left(abs, abs_pos) * s - p - (margin.l + margin.r)),0.),
837            Width::Scale(s) => (max_zero_keep_nan(cx._get_width_total(abs, abs_pos) * s - (margin.l + margin.r)),0.),
838            Width::ScalePad(s, p) => (max_zero_keep_nan(cx._get_width_total(abs, abs_pos) * s - p - (margin.l + margin.r)),0.),
839        }
840    }
841}
842
843impl Height {
844    pub fn fixed(&self) -> f32 {
845        match self {
846            Height::Fix(v) => *v,
847            _ => 0.
848        }
849    }
850    pub fn eval_height(&self, cx: &Cx, margin: Margin, abs: bool, abs_pos: f32) -> (f32,f32) {
851        match self {
852            Height::Compute => (std::f32::NAN,0.),
853            Height::ComputeFill => (std::f32::NAN,cx._get_height_left(abs, abs_pos) - (margin.t + margin.b)),
854            Height::Fix(v) => (max_zero_keep_nan(*v),0.),
855            Height::Fill => (max_zero_keep_nan(cx._get_height_left(abs, abs_pos) - (margin.t + margin.b)),0.),
856            Height::FillPad(p) => (max_zero_keep_nan(cx._get_height_left(abs, abs_pos) - p - (margin.t + margin.b)),0.),
857            Height::FillScale(s) => (max_zero_keep_nan(cx._get_height_left(abs, abs_pos) * s - (margin.t + margin.b)),0.),
858            Height::FillScalePad(s, p) => (max_zero_keep_nan(cx._get_height_left(abs, abs_pos) * s - p - (margin.t + margin.b)),0.),
859            Height::Scale(s) => (max_zero_keep_nan(cx._get_height_total(abs, abs_pos) * s - (margin.t + margin.b)),0.),
860            Height::ScalePad(s, p) => (max_zero_keep_nan(cx._get_height_total(abs, abs_pos) * s - p - (margin.t + margin.b)),0.),
861        }
862    }
863}
864
865#[derive(Clone, Copy, Default, Debug)]
866pub struct Align {
867    pub fx: f32,
868    pub fy: f32
869}
870
871impl Align {
872    pub fn left_top() -> Align {Align {fx: 0., fy: 0.}}
873    pub fn center_top() -> Align {Align {fx: 0.5, fy: 0.0}}
874    pub fn right_top() -> Align {Align {fx: 1.0, fy: 0.0}}
875    pub fn left_center() -> Align {Align {fx: 0.0, fy: 0.5}}
876    pub fn center() -> Align {Align {fx: 0.5, fy: 0.5}}
877    pub fn right_center() -> Align {Align {fx: 1.0, fy: 0.5}}
878    pub fn left_bottom() -> Align {Align {fx: 0., fy: 1.0}}
879    pub fn center_bottom() -> Align {Align {fx: 0.5, fy: 1.0}}
880    pub fn right_bottom() -> Align {Align {fx: 1.0, fy: 1.0}}
881}
882
883#[derive(Clone, Copy, Default, Debug)]
884pub struct Margin {
885    pub l: f32,
886    pub t: f32,
887    pub r: f32,
888    pub b: f32
889}
890
891impl Margin {
892    pub fn zero() -> Margin {
893        Margin {l: 0.0, t: 0.0, r: 0.0, b: 0.0}
894    }
895    
896    pub fn all(v: f32) -> Margin {
897        Margin {l: v, t: v, r: v, b: v}
898    }
899}
900
901impl Rect {
902    pub fn contains_with_margin(&self, x: f32, y: f32, margin: &Option<Margin>) -> bool {
903        if let Some(margin) = margin {
904            return x >= self.x - margin.l && x <= self.x + self.w + margin.r &&
905            y >= self.y - margin.t && y <= self.y + self.h + margin.b;
906        }
907        else {
908            return self.contains(x, y);
909        }
910        
911    }
912}
913
914#[derive(Clone, Copy, Default, Debug)]
915pub struct Padding {
916    pub l: f32,
917    pub t: f32,
918    pub r: f32,
919    pub b: f32
920}
921
922impl Padding {
923    pub fn zero() -> Padding {
924        Padding {l: 0.0, t: 0.0, r: 0.0, b: 0.0}
925    }
926    pub fn all(v: f32) -> Padding {
927        Padding {l: v, t: v, r: v, b: v}
928    }
929}
930
931
932#[derive(Copy, Clone, Debug)]
933pub enum Direction {
934    Left,
935    Right,
936    Up,
937    Down
938}
939
940impl Default for Direction {
941    fn default() -> Self {
942        Direction::Right
943    }
944}
945
946#[derive(Copy, Clone, SerRon, DeRon)]
947pub enum Axis {
948    Horizontal,
949    Vertical
950}
951
952impl Default for Axis {
953    fn default() -> Self {
954        Axis::Horizontal
955    }
956}
957
958#[derive(Copy, Clone, Debug)]
959pub enum LineWrap {
960    None,
961    NewLine,
962    MaxSize(f32)
963}
964impl Default for LineWrap {
965    fn default() -> Self {
966        LineWrap::None
967    }
968}
969
970#[derive(Copy, Clone, Default, Debug)]
971pub struct Layout {
972    pub padding: Padding,
973    pub align: Align,
974    pub direction: Direction,
975    pub line_wrap: LineWrap,
976    pub new_line_padding: f32,
977    pub abs_origin: Option<Vec2>,
978    pub abs_size: Option<Vec2>,
979    pub walk: Walk,
980}
981
982#[derive(Copy, Clone, Default, Debug)]
983pub struct Walk {
984    pub margin: Margin,
985    pub width: Width,
986    pub height: Height,
987}
988
989impl Walk {
990    pub fn wh(w: Width, h: Height) -> Self {
991        Self {
992            width: w,
993            height: h,
994            margin: Margin::zero(),
995        }
996    }
997}
998
999impl Layout {
1000    pub fn abs_origin_zero() -> Self {
1001        Layout {
1002            abs_origin: Some(Vec2::default()),
1003            ..Default::default()
1004        }
1005    }
1006}
1007
1008#[derive(Clone, Default, Debug)]
1009pub struct Turtle {
1010    pub align_list_x: usize,
1011    pub align_list_y: usize,
1012    pub pos: Vec2,
1013    pub origin: Vec2,
1014    pub bound_left_top: Vec2,
1015    pub bound_right_bottom: Vec2,
1016    pub width: f32,
1017    pub height: f32,
1018    pub min_width: f32,
1019    pub min_height: f32,
1020    pub abs_size: Vec2,
1021    pub width_used: f32,
1022    pub height_used: f32,
1023    pub biggest: f32,
1024    pub layout: Layout,
1025    pub guard_area: Area
1026}
1027
1028pub fn max_zero_keep_nan(v: f32) -> f32 {
1029    if v.is_nan() {
1030        v
1031    }
1032    else {
1033        f32::max(v, 0.0)
1034    }
1035}