@@ -8,29 +8,44 @@ use crate::*;
8
8
///
9
9
/// Areas back [`crate::Window`]s and other floating containers,
10
10
/// like tooltips and the popups of [`crate::ComboBox`].
11
- ///
12
- /// Area state is intentionally NOT persisted between sessions,
13
- /// so that a bad tooltip or menu size won't be remembered forever.
14
- /// A resizable [`Window`] remembers the size the user picked using
15
- /// the state in the [`Resize`] container.
16
11
#[ derive( Clone , Copy , Debug ) ]
12
+ #[ cfg_attr( feature = "serde" , derive( serde:: Deserialize , serde:: Serialize ) ) ]
17
13
pub struct AreaState {
18
14
/// Last known position of the pivot.
19
- pub pivot_pos : Pos2 ,
15
+ pub pivot_pos : Option < Pos2 > ,
20
16
21
17
/// The anchor point of the area, i.e. where on the area the [`Self::pivot_pos`] refers to.
22
18
pub pivot : Align2 ,
23
19
24
20
/// Last known size.
25
- pub size : Vec2 ,
21
+ ///
22
+ /// Area size is intentionally NOT persisted between sessions,
23
+ /// so that a bad tooltip or menu size won't be remembered forever.
24
+ /// A resizable [`Window`] remembers the size the user picked using
25
+ /// the state in the [`Resize`] container.
26
+ #[ cfg_attr( feature = "serde" , serde( skip) ) ]
27
+ pub size : Option < Vec2 > ,
26
28
27
29
/// If false, clicks goes straight through to what is behind us. Useful for tooltips etc.
28
30
pub interactable : bool ,
29
31
30
32
/// At what time was this area first shown?
31
33
///
32
34
/// Used to fade in the area.
33
- pub last_became_visible_at : f64 ,
35
+ #[ cfg_attr( feature = "serde" , serde( skip) ) ]
36
+ pub last_became_visible_at : Option < f64 > ,
37
+ }
38
+
39
+ impl Default for AreaState {
40
+ fn default ( ) -> Self {
41
+ Self {
42
+ pivot_pos : None ,
43
+ pivot : Align2 :: LEFT_TOP ,
44
+ size : None ,
45
+ interactable : true ,
46
+ last_became_visible_at : None ,
47
+ }
48
+ }
34
49
}
35
50
36
51
impl AreaState {
@@ -42,23 +57,27 @@ impl AreaState {
42
57
43
58
/// The left top positions of the area.
44
59
pub fn left_top_pos ( & self ) -> Pos2 {
60
+ let pivot_pos = self . pivot_pos . unwrap_or_default ( ) ;
61
+ let size = self . size . unwrap_or_default ( ) ;
45
62
pos2 (
46
- self . pivot_pos . x - self . pivot . x ( ) . to_factor ( ) * self . size . x ,
47
- self . pivot_pos . y - self . pivot . y ( ) . to_factor ( ) * self . size . y ,
63
+ pivot_pos. x - self . pivot . x ( ) . to_factor ( ) * size. x ,
64
+ pivot_pos. y - self . pivot . y ( ) . to_factor ( ) * size. y ,
48
65
)
49
66
}
50
67
51
68
/// Move the left top positions of the area.
52
69
pub fn set_left_top_pos ( & mut self , pos : Pos2 ) {
53
- self . pivot_pos = pos2 (
54
- pos. x + self . pivot . x ( ) . to_factor ( ) * self . size . x ,
55
- pos. y + self . pivot . y ( ) . to_factor ( ) * self . size . y ,
56
- ) ;
70
+ let size = self . size . unwrap_or_default ( ) ;
71
+ self . pivot_pos = Some ( pos2 (
72
+ pos. x + self . pivot . x ( ) . to_factor ( ) * size. x ,
73
+ pos. y + self . pivot . y ( ) . to_factor ( ) * size. y ,
74
+ ) ) ;
57
75
}
58
76
59
77
/// Where the area is on screen.
60
78
pub fn rect ( & self ) -> Rect {
61
- Rect :: from_min_size ( self . left_top_pos ( ) , self . size )
79
+ let size = self . size . unwrap_or_default ( ) ;
80
+ Rect :: from_min_size ( self . left_top_pos ( ) , size)
62
81
}
63
82
}
64
83
@@ -371,16 +390,28 @@ impl Area {
371
390
372
391
let layer_id = LayerId :: new ( order, id) ;
373
392
374
- let state = AreaState :: load ( ctx, id) . map ( |mut state| {
375
- // override the saved state with the correct value
376
- state. pivot = pivot;
377
- state
393
+ let state = AreaState :: load ( ctx, id) ;
394
+ let mut sizing_pass = state. is_none ( ) ;
395
+ let mut state = state. unwrap_or ( AreaState {
396
+ pivot_pos : None ,
397
+ pivot,
398
+ size : None ,
399
+ interactable,
400
+ last_became_visible_at : None ,
378
401
} ) ;
379
- let is_new = state. is_none ( ) ;
380
- if is_new {
381
- ctx. request_repaint ( ) ; // if we don't know the previous size we are likely drawing the area in the wrong place
402
+ state. pivot = pivot;
403
+ state. interactable = interactable;
404
+ if let Some ( new_pos) = new_pos {
405
+ state. pivot_pos = Some ( new_pos) ;
382
406
}
383
- let mut state = state. unwrap_or_else ( || {
407
+ state. pivot_pos . get_or_insert_with ( || {
408
+ default_pos. unwrap_or_else ( || automatic_area_position ( ctx, layer_id) )
409
+ } ) ;
410
+ state. interactable = interactable;
411
+
412
+ let size = * state. size . get_or_insert_with ( || {
413
+ sizing_pass = true ;
414
+
384
415
// during the sizing pass we will use this as the max size
385
416
let mut size = default_size;
386
417
@@ -396,28 +427,20 @@ impl Area {
396
427
size = size. at_most ( constrain_rect. size ( ) ) ;
397
428
}
398
429
399
- AreaState {
400
- pivot_pos : default_pos. unwrap_or_else ( || automatic_area_position ( ctx) ) ,
401
- pivot,
402
- size,
403
- interactable,
404
- last_became_visible_at : ctx. input ( |i| i. time ) ,
405
- }
430
+ size
406
431
} ) ;
407
- state. pivot_pos = new_pos. unwrap_or ( state. pivot_pos ) ;
408
- state. interactable = interactable;
409
432
410
- // TODO(emilk): if last frame was sizing pass, it should be considered invisible for smmother fade-in
433
+ // TODO(emilk): if last frame was sizing pass, it should be considered invisible for smoother fade-in
411
434
let visible_last_frame = ctx. memory ( |mem| mem. areas ( ) . visible_last_frame ( & layer_id) ) ;
412
435
413
- if !visible_last_frame {
414
- state. last_became_visible_at = ctx. input ( |i| i. time ) ;
436
+ if !visible_last_frame || state . last_became_visible_at . is_none ( ) {
437
+ state. last_became_visible_at = Some ( ctx. input ( |i| i. time ) ) ;
415
438
}
416
439
417
440
if let Some ( ( anchor, offset) ) = anchor {
418
441
state. set_left_top_pos (
419
442
anchor
420
- . align_size_within_rect ( state . size , constrain_rect)
443
+ . align_size_within_rect ( size, constrain_rect)
421
444
. left_top ( )
422
445
+ offset,
423
446
) ;
@@ -446,7 +469,9 @@ impl Area {
446
469
} ) ;
447
470
448
471
if movable && move_response. dragged ( ) {
449
- state. pivot_pos += move_response. drag_delta ( ) ;
472
+ if let Some ( pivot_pos) = & mut state. pivot_pos {
473
+ * pivot_pos += move_response. drag_delta ( ) ;
474
+ }
450
475
}
451
476
452
477
if ( move_response. dragged ( ) || move_response. clicked ( ) )
@@ -481,7 +506,7 @@ impl Area {
481
506
enabled,
482
507
constrain,
483
508
constrain_rect,
484
- sizing_pass : is_new ,
509
+ sizing_pass,
485
510
fade_in,
486
511
}
487
512
}
@@ -505,7 +530,7 @@ impl Prepared {
505
530
}
506
531
507
532
pub ( crate ) fn content_ui ( & self , ctx : & Context ) -> Ui {
508
- let max_rect = Rect :: from_min_size ( self . state . left_top_pos ( ) , self . state . size ) ;
533
+ let max_rect = self . state . rect ( ) ;
509
534
510
535
let clip_rect = self . constrain_rect ; // Don't paint outside our bounds
511
536
@@ -519,13 +544,14 @@ impl Prepared {
519
544
) ;
520
545
521
546
if self . fade_in {
522
- let age =
523
- ctx. input ( |i| ( i. time - self . state . last_became_visible_at ) as f32 + i. predicted_dt ) ;
524
- let opacity = crate :: remap_clamp ( age, 0.0 ..=ctx. style ( ) . animation_time , 0.0 ..=1.0 ) ;
525
- let opacity = emath:: easing:: cubic_out ( opacity) ; // slow fade-out = quick fade-in
526
- ui. multiply_opacity ( opacity) ;
527
- if opacity < 1.0 {
528
- ctx. request_repaint ( ) ;
547
+ if let Some ( last_became_visible_at) = self . state . last_became_visible_at {
548
+ let age = ctx. input ( |i| ( i. time - last_became_visible_at) as f32 + i. predicted_dt ) ;
549
+ let opacity = crate :: remap_clamp ( age, 0.0 ..=ctx. style ( ) . animation_time , 0.0 ..=1.0 ) ;
550
+ let opacity = emath:: easing:: cubic_out ( opacity) ; // slow fade-out = quick fade-in
551
+ ui. multiply_opacity ( opacity) ;
552
+ if opacity < 1.0 {
553
+ ctx. request_repaint ( ) ;
554
+ }
529
555
}
530
556
}
531
557
@@ -545,10 +571,11 @@ impl Prepared {
545
571
layer_id,
546
572
mut state,
547
573
move_response : mut response,
574
+ sizing_pass,
548
575
..
549
576
} = self ;
550
577
551
- state. size = content_ui. min_size ( ) ;
578
+ state. size = Some ( content_ui. min_size ( ) ) ;
552
579
553
580
// Make sure we report back the correct size.
554
581
// Very important after the initial sizing pass, when the initial estimate of the size is way off.
@@ -558,6 +585,11 @@ impl Prepared {
558
585
559
586
ctx. memory_mut ( |m| m. areas_mut ( ) . set_state ( layer_id, state) ) ;
560
587
588
+ if sizing_pass {
589
+ // If we didn't know the size, we were likely drawing the area in the wrong place.
590
+ ctx. request_repaint ( ) ;
591
+ }
592
+
561
593
response
562
594
}
563
595
}
@@ -571,12 +603,13 @@ fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool {
571
603
}
572
604
}
573
605
574
- fn automatic_area_position ( ctx : & Context ) -> Pos2 {
606
+ fn automatic_area_position ( ctx : & Context , layer_id : LayerId ) -> Pos2 {
575
607
let mut existing: Vec < Rect > = ctx. memory ( |mem| {
576
608
mem. areas ( )
577
609
. visible_windows ( )
578
- . into_iter ( )
579
- . map ( AreaState :: rect)
610
+ . filter ( |( id, _) | id != & layer_id) // ignore ourselves
611
+ . filter ( |( _, state) | state. pivot_pos . is_some ( ) && state. size . is_some ( ) )
612
+ . map ( |( _, state) | state. rect ( ) )
580
613
. collect ( )
581
614
} ) ;
582
615
existing. sort_by_key ( |r| r. left ( ) . round ( ) as i32 ) ;
0 commit comments