mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-02 16:29:34 -06:00
M5: an open overlay follows the layer that opened it when the view changes
Review follow-up. The overlay kept the screen rect its trigger had when it opened, so after a resize, a size option change or a pan a trigger-relative (or manual) overlay stayed where the trigger used to be. It now keeps that rect in page units of the presented frame and places it again on every layout. The dropdown test switches to Fill while the menu is open. The anchor's is_screen gets a doc comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Albert Nimtz
co-authored by
Claude Opus 5.5
parent
545cd630fd
commit
76804506dc
@@ -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
|
||||
}
|
||||
|
||||
@@ -67,8 +67,9 @@ pub struct OverlayState {
|
||||
pub position: OverlayPosition,
|
||||
pub close_on_outside: bool,
|
||||
pub background: Option<Color>,
|
||||
/// Screen rect of the layer that opened it (manual position).
|
||||
pub anchor: Option<Rect>,
|
||||
/// 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<figcraft_geom::Rect>,
|
||||
/// 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<Rect>) -> Option<figcraft_geom::Rect> {
|
||||
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<Rect>, 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<Layout> {
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user