Skip to content

Commit af404fe

Browse files
authored
Persist Area positions again (emilk#4749)
* Reverts emilk#4577 We again persist area positions, but not sizes.
1 parent 0fc79ae commit af404fe

File tree

6 files changed

+102
-64
lines changed

6 files changed

+102
-64
lines changed

crates/eframe/src/web/web_logger.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ impl log::Log for WebLogger {
3838
}
3939

4040
fn log(&self, record: &log::Record<'_>) {
41+
#![allow(clippy::match_same_arms)]
42+
4143
if !self.enabled(record.metadata()) {
4244
return;
4345
}

crates/egui/src/containers/area.rs

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,44 @@ use crate::*;
88
///
99
/// Areas back [`crate::Window`]s and other floating containers,
1010
/// 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.
1611
#[derive(Clone, Copy, Debug)]
12+
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
1713
pub struct AreaState {
1814
/// Last known position of the pivot.
19-
pub pivot_pos: Pos2,
15+
pub pivot_pos: Option<Pos2>,
2016

2117
/// The anchor point of the area, i.e. where on the area the [`Self::pivot_pos`] refers to.
2218
pub pivot: Align2,
2319

2420
/// 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>,
2628

2729
/// If false, clicks goes straight through to what is behind us. Useful for tooltips etc.
2830
pub interactable: bool,
2931

3032
/// At what time was this area first shown?
3133
///
3234
/// 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+
}
3449
}
3550

3651
impl AreaState {
@@ -42,23 +57,27 @@ impl AreaState {
4257

4358
/// The left top positions of the area.
4459
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();
4562
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,
4865
)
4966
}
5067

5168
/// Move the left top positions of the area.
5269
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+
));
5775
}
5876

5977
/// Where the area is on screen.
6078
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)
6281
}
6382
}
6483

@@ -371,16 +390,28 @@ impl Area {
371390

372391
let layer_id = LayerId::new(order, id);
373392

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,
378401
});
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);
382406
}
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+
384415
// during the sizing pass we will use this as the max size
385416
let mut size = default_size;
386417

@@ -396,28 +427,20 @@ impl Area {
396427
size = size.at_most(constrain_rect.size());
397428
}
398429

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
406431
});
407-
state.pivot_pos = new_pos.unwrap_or(state.pivot_pos);
408-
state.interactable = interactable;
409432

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
411434
let visible_last_frame = ctx.memory(|mem| mem.areas().visible_last_frame(&layer_id));
412435

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));
415438
}
416439

417440
if let Some((anchor, offset)) = anchor {
418441
state.set_left_top_pos(
419442
anchor
420-
.align_size_within_rect(state.size, constrain_rect)
443+
.align_size_within_rect(size, constrain_rect)
421444
.left_top()
422445
+ offset,
423446
);
@@ -446,7 +469,9 @@ impl Area {
446469
});
447470

448471
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+
}
450475
}
451476

452477
if (move_response.dragged() || move_response.clicked())
@@ -481,7 +506,7 @@ impl Area {
481506
enabled,
482507
constrain,
483508
constrain_rect,
484-
sizing_pass: is_new,
509+
sizing_pass,
485510
fade_in,
486511
}
487512
}
@@ -505,7 +530,7 @@ impl Prepared {
505530
}
506531

507532
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();
509534

510535
let clip_rect = self.constrain_rect; // Don't paint outside our bounds
511536

@@ -519,13 +544,14 @@ impl Prepared {
519544
);
520545

521546
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+
}
529555
}
530556
}
531557

@@ -545,10 +571,11 @@ impl Prepared {
545571
layer_id,
546572
mut state,
547573
move_response: mut response,
574+
sizing_pass,
548575
..
549576
} = self;
550577

551-
state.size = content_ui.min_size();
578+
state.size = Some(content_ui.min_size());
552579

553580
// Make sure we report back the correct size.
554581
// Very important after the initial sizing pass, when the initial estimate of the size is way off.
@@ -558,6 +585,11 @@ impl Prepared {
558585

559586
ctx.memory_mut(|m| m.areas_mut().set_state(layer_id, state));
560587

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+
561593
response
562594
}
563595
}
@@ -571,12 +603,13 @@ fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool {
571603
}
572604
}
573605

574-
fn automatic_area_position(ctx: &Context) -> Pos2 {
606+
fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 {
575607
let mut existing: Vec<Rect> = ctx.memory(|mem| {
576608
mem.areas()
577609
.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())
580613
.collect()
581614
});
582615
existing.sort_by_key(|r| r.left().round() as i32);

crates/egui/src/containers/combo_box.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,12 @@ fn combo_box_dyn<'c, R>(
296296

297297
let is_popup_open = ui.memory(|m| m.is_popup_open(popup_id));
298298

299-
let popup_height = ui.memory(|m| m.areas().get(popup_id).map_or(100.0, |state| state.size.y));
299+
let popup_height = ui.memory(|m| {
300+
m.areas()
301+
.get(popup_id)
302+
.and_then(|state| state.size)
303+
.map_or(100.0, |size| size.y)
304+
});
300305

301306
let above_or_below =
302307
if ui.next_widget_position().y + ui.spacing().interact_size.y + popup_height

crates/egui/src/containers/popup.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ fn show_tooltip_at_avoid_dyn<'c, R>(
118118
});
119119

120120
let tooltip_area_id = tooltip_id(widget_id, state.tooltip_count);
121-
let expected_tooltip_size =
122-
AreaState::load(ctx, tooltip_area_id).map_or(vec2(64.0, 32.0), |area| area.size);
121+
let expected_tooltip_size = AreaState::load(ctx, tooltip_area_id)
122+
.and_then(|area| area.size)
123+
.unwrap_or(vec2(64.0, 32.0));
123124

124125
let screen_rect = ctx.screen_rect();
125126

crates/egui/src/context.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -495,11 +495,11 @@ impl ContextImpl {
495495
self.memory.areas_mut().set_state(
496496
LayerId::background(),
497497
AreaState {
498-
pivot_pos: screen_rect.left_top(),
498+
pivot_pos: Some(screen_rect.left_top()),
499499
pivot: Align2::LEFT_TOP,
500-
size: screen_rect.size(),
500+
size: Some(screen_rect.size()),
501501
interactable: true,
502-
last_became_visible_at: f64::NEG_INFINITY,
502+
last_became_visible_at: None,
503503
},
504504
);
505505

@@ -2209,7 +2209,7 @@ impl Context {
22092209
pub fn used_rect(&self) -> Rect {
22102210
self.write(|ctx| {
22112211
let mut used = ctx.viewport().frame_state.used_by_panels;
2212-
for window in ctx.memory.areas().visible_windows() {
2212+
for (_id, window) in ctx.memory.areas().visible_windows() {
22132213
used = used.union(window.rect());
22142214
}
22152215
used

crates/egui/src/memory.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -934,9 +934,6 @@ impl Memory {
934934
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
935935
#[cfg_attr(feature = "serde", serde(default))]
936936
pub struct Areas {
937-
/// Area state is intentionally NOT persisted between sessions,
938-
/// so that a bad tooltip or menu size won't be remembered forever.
939-
#[cfg_attr(feature = "serde", serde(skip))]
940937
areas: IdMap<area::AreaState>,
941938

942939
/// Back-to-front. Top is last.
@@ -1030,12 +1027,12 @@ impl Areas {
10301027
.collect()
10311028
}
10321029

1033-
pub(crate) fn visible_windows(&self) -> Vec<&area::AreaState> {
1030+
pub(crate) fn visible_windows(&self) -> impl Iterator<Item = (LayerId, &area::AreaState)> {
10341031
self.visible_layer_ids()
1035-
.iter()
1032+
.into_iter()
10361033
.filter(|layer| layer.order == crate::Order::Middle)
1037-
.filter_map(|layer| self.get(layer.id))
1038-
.collect()
1034+
.filter(|&layer| !self.is_sublayer(&layer))
1035+
.filter_map(|layer| Some((layer, self.get(layer.id)?)))
10391036
}
10401037

10411038
pub fn move_to_top(&mut self, layer_id: LayerId) {

0 commit comments

Comments
 (0)