diff --git a/crates/doc/src/proto.rs b/crates/doc/src/proto.rs index d3830bfb3..b4808011f 100644 --- a/crates/doc/src/proto.rs +++ b/crates/doc/src/proto.rs @@ -517,6 +517,7 @@ pub enum OverlayAnchor { } impl OverlayAnchor { + /// The default (the screen places the overlay), which files and JSON leave out. pub fn is_screen(&self) -> bool { *self == OverlayAnchor::Screen } diff --git a/crates/ui-egui/src/present.rs b/crates/ui-egui/src/present.rs index 1b483a2c2..13f4c53bf 100644 --- a/crates/ui-egui/src/present.rs +++ b/crates/ui-egui/src/present.rs @@ -67,8 +67,9 @@ pub struct OverlayState { pub position: OverlayPosition, pub close_on_outside: bool, pub background: Option, - /// Screen rect of the layer that opened it (manual position). - pub anchor: Option, + /// The layer that opened it, in page units of the presented frame (so it follows the frame + /// when the window resizes or the view pans). + pub anchor: Option, /// Manual position: the overlay's top-left from the anchor's (page units). pub offset: Option<[f64; 2]>, pub anchor_to: OverlayAnchor, @@ -790,6 +791,13 @@ impl Presenter { error } + /// Screen rect `r` in page units of the frame shown last. + fn page_rect(&self, r: Option) -> Option { + let (r, (_, place, _)) = (r?, self.shown?); + let (a, b) = (place.page(r.min), place.page(r.max)); + Some(figcraft_geom::Rect::new(a.x, a.y, b.x, b.y)) + } + fn perform(&mut self, a: &Action, node: NodeId, tr: Transition, dur: f64, easing: Easing, rect: Option, now: f64) -> bool { self.perform_at(a, node, tr, dur, easing, rect, now, 0) } @@ -833,6 +841,7 @@ impl Presenter { true } Action::OpenOverlay { dest, position, close_on_outside, background, offset, anchor } => { + let at = self.page_rect(rect); if !doc.contains(*dest) { return false; } @@ -842,7 +851,7 @@ impl Presenter { position: *position, close_on_outside: *close_on_outside, background: *background, - anchor: rect, + anchor: at, offset: *offset, anchor_to: *anchor, opened: now, @@ -854,6 +863,7 @@ impl Presenter { true } Action::SwapOverlay { dest } => { + let at = self.page_rect(rect); if !doc.contains(*dest) { return false; } @@ -871,7 +881,7 @@ impl Presenter { position: OverlayPosition::Center, close_on_outside: true, background: None, - anchor: rect, + anchor: at, offset: None, anchor_to: OverlayAnchor::Screen, opened: now, @@ -1647,7 +1657,7 @@ fn layout(p: &mut Presenter, area: Rect, chrome: Chrome) -> Option { let ob = disp.world_bounds(o.id)?; let size = vec2((ob.width() * scale) as f32, (ob.height() * scale) as f32); let offset = o.offset.map(|[x, y]| vec2((x * scale) as f32, (y * scale) as f32)); - let r = anchored_overlay_rect(o.position, o.anchor_to, screen, size, o.anchor, offset); + let r = anchored_overlay_rect(o.position, o.anchor_to, screen, size, o.anchor.map(|a| place.rect(a)), offset); Some((o.id, Place { page_min: (ob.x0, ob.y0), origin: r.min, scale }, r)) }; let overlays = p.overlays.iter().filter_map(place_overlay).collect(); diff --git a/crates/ui-egui/src/present_overlay_anchor_tests.rs b/crates/ui-egui/src/present_overlay_anchor_tests.rs index 1cb3e310b..16d16b2db 100644 --- a/crates/ui-egui/src/present_overlay_anchor_tests.rs +++ b/crates/ui-egui/src/present_overlay_anchor_tests.rs @@ -64,12 +64,24 @@ fn a_dropdown_opens_right_under_its_button() { let mut u = Harness::new(s); u.run("proto.present", json!({"start": a})); u.frames(2, Vec::new); + let scale = |u: &mut Harness, scale: &str| { + u.run("proto.presentOptions", json!({"scale": scale})); + u.frames(2, Vec::new); + }; + scale(&mut u, "fill"); + let filled = hotspot(&u, hot); + scale(&mut u, "fit"); let h = hotspot(&u, hot); + assert_ne!(filled, h, "fill shows the frame at another size"); u.frame(vec![Event::PointerMoved(h.center())]); u.frame(vec![button(h.center(), true)]); u.frame(vec![button(h.center(), false)]); u.frames(2, Vec::new); - let got = hotspot(&u, item).min; - let want = pos2(h.min.x, h.max.y); - assert!((got - want).length() < 0.01, "{got:?} vs {want:?}"); + let under = |u: &Harness, b: Rect| { + let (got, want) = (hotspot(u, item).min, pos2(b.min.x, b.max.y)); + assert!((got - want).length() < 0.01, "{got:?} vs {want:?}"); + }; + under(&u, h); + scale(&mut u, "fill"); + under(&u, filled); }