mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-10 12:22:22 +00:00
test(ui-egui): assert dock controls are clipped at the panel edge
The narrow-window test intersected each hit rect with the dock and then asserted the intersection lies inside the dock, which always holds. Check each dock control's clipped interact_rect against the dock instead, and add a squeezed-column test where a control's full rect runs past the panel edge, so the clip is actually exercised.
This commit is contained in:
1 parent
03c7afb42c
commit
3652ede689
1 file changed
+76
-23
@@ -1880,17 +1880,15 @@ fn release_only_position_updates_model_and_actual_geometry_on_that_frame() {
|
||||
}
|
||||
}
|
||||
|
||||
/// `dockWidth` stores the requested model width even when a narrow window draws the dock
|
||||
/// narrower: in one frame the model keeps 520, the drawn dock column fits in 364 points and the
|
||||
/// canvas keeps its 200, and a wider window draws the stored width again.
|
||||
#[test]
|
||||
fn a_narrow_window_draws_a_stored_dock_width_narrower_without_losing_it() {
|
||||
/// A dock-edge harness `width` points wide with the Pro single-column toolbox on the right, the
|
||||
/// rail and a stored 520-point dock column, after the frames that draw it.
|
||||
fn narrow_dock_harness(width: f32) -> Harness<'static, PhotocraftApp> {
|
||||
let mut app = app_with_columns(&[400.0], false);
|
||||
app.ui.panels.rail = true;
|
||||
app.ui.panels.toolbar_double = false;
|
||||
app.ui.panels.toolbar_side = crate::state::ToolbarSide::Right;
|
||||
let mut ready = false;
|
||||
let mut h = Harness::builder().with_size(vec2(640.0, 600.0)).with_step_dt(1.0 / 60.0).build_ui_state(
|
||||
let mut h = Harness::builder().with_size(vec2(width, 600.0)).with_step_dt(1.0 / 60.0).build_ui_state(
|
||||
move |ui, app: &mut PhotocraftApp| {
|
||||
if !ready {
|
||||
PhotocraftApp::setup_context(ui.ctx(), crate::theme::ThemeKind::ProMedium);
|
||||
@@ -1910,35 +1908,90 @@ fn a_narrow_window_draws_a_stored_dock_width_narrower_without_losing_it() {
|
||||
let ctx = h.ctx.clone();
|
||||
assert_eq!(control_set(h.state_mut(), &ctx, json!({"dockWidth":520.0}))["ok"], true);
|
||||
h.run_steps(2);
|
||||
let canvas = h.ctx.data(|data| data.get_temp::<egui::Rect>(egui::Id::unique("narrow-canvas-rect")).expect("the canvas rect"));
|
||||
h
|
||||
}
|
||||
|
||||
/// The canvas rectangle [`narrow_dock_harness`] drew on its last frame.
|
||||
fn narrow_canvas(h: &Harness<'static, PhotocraftApp>) -> egui::Rect {
|
||||
h.ctx.data(|data| data.get_temp::<egui::Rect>(egui::Id::unique("narrow-canvas-rect")).expect("the canvas rect"))
|
||||
}
|
||||
|
||||
/// The panel a dock column's control acts on; `None` for floating-window and split controls.
|
||||
fn dock_hit_panel(hit: &craft_ui::docking::Hit<String>) -> Option<&String> {
|
||||
use craft_ui::docking::{Divider, Hit};
|
||||
match hit {
|
||||
Hit::Tab(p)
|
||||
| Hit::DragBar(p)
|
||||
| Hit::Border(p)
|
||||
| Hit::Overflow(p)
|
||||
| Hit::ColumnBar(p)
|
||||
| Hit::Collapse(p)
|
||||
| Hit::StackToggle(p)
|
||||
| Hit::Icon(p)
|
||||
| Hit::IconGrip(p) => Some(p),
|
||||
Hit::Divider(Divider::Column(p) | Divider::Group(p) | Divider::Stack(p)) => Some(p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that every control of the end dock's panels keeps its clipped `interact_rect`, what is
|
||||
/// drawn and clickable, inside `dock`, and returns how many have a full `rect` past it.
|
||||
fn dock_controls_clipped(h: &Harness<'static, PhotocraftApp>, dock: egui::Rect) -> usize {
|
||||
use craft_ui::docking::DockArea;
|
||||
let panels: Vec<&String> = end_columns(h.state()).iter().flat_map(|c| c.groups.iter().flat_map(|g| g.panels.iter())).collect();
|
||||
let area = area_id();
|
||||
let hits = DockArea::<String>::hit_rects(&h.ctx, area);
|
||||
let (mut drawn, mut clipped) = (0, 0);
|
||||
for (hit, rect) in hits.iter().filter(|(hit, _)| dock_hit_panel(hit).is_some_and(|p| panels.contains(&p))) {
|
||||
let Some(response) = h.ctx.read_response(DockArea::<String>::hit_id(area, hit)) else {
|
||||
assert!(!rect.intersect(dock).is_positive(), "{hit:?} at {rect:?} has no response");
|
||||
continue;
|
||||
};
|
||||
let shown = response.interact_rect;
|
||||
assert!(!shown.is_positive() || dock.expand(0.5).contains_rect(shown), "{hit:?}: interactive {shown:?} (full {rect:?}) outside {dock:?}");
|
||||
drawn += usize::from(shown.is_positive());
|
||||
clipped += usize::from(!dock.expand(2.0).contains_rect(*rect));
|
||||
}
|
||||
assert!(drawn > 0, "the dock drew controls: {hits:?}");
|
||||
clipped
|
||||
}
|
||||
|
||||
/// `dockWidth` stores the requested model width even when a narrow window draws the dock
|
||||
/// narrower: in one frame the model keeps 520, the drawn dock column fits in 364 points and the
|
||||
/// canvas keeps its 200, and a wider window draws the stored width again.
|
||||
#[test]
|
||||
fn a_narrow_window_draws_a_stored_dock_width_narrower_without_losing_it() {
|
||||
let mut h = narrow_dock_harness(640.0);
|
||||
let canvas = narrow_canvas(&h);
|
||||
let dock = allocated_dock(&h);
|
||||
let model = dock_width(h.state());
|
||||
assert!((model - 520.0).abs() < 0.5, "the model keeps the request, got {model}");
|
||||
assert!(dock.width() - 36.0 <= 364.5, "the drawn dock column fits beside the toolbox and canvas, got {dock:?}");
|
||||
assert!(canvas.width() >= 199.5, "the canvas keeps its 200-point minimum, got {canvas:?} beside {dock:?}");
|
||||
assert!(!canvas.intersect(dock).is_positive(), "the dock never overlaps the canvas: {canvas:?}, {dock:?}");
|
||||
// `hit_rects` reports each control's full widget rect (`Response::rect`), not its clipped
|
||||
// `interact_rect`, so a squeezed column may report chrome past the panel edge; what is drawn
|
||||
// is that rect inside the panel's clip. No visible part may reach the toolbox, which sits
|
||||
// between the canvas and the dock, or the canvas.
|
||||
let toolbox = egui::Rect::from_x_y_ranges(canvas.right()..=dock.left(), dock.y_range());
|
||||
let hits = craft_ui::docking::DockArea::<String>::hit_rects(&h.ctx, area_id());
|
||||
assert!(hits.iter().any(|(_, rect)| rect.intersect(dock).is_positive()), "the dock drew visible controls: {hits:?}");
|
||||
for (hit, rect) in &hits {
|
||||
let visible = rect.intersect(dock);
|
||||
if !visible.is_positive() {
|
||||
continue;
|
||||
}
|
||||
assert!(dock.contains_rect(visible), "{hit:?}: {visible:?} outside {dock:?}");
|
||||
assert!(!visible.intersect(canvas).is_positive(), "{hit:?}: {visible:?} reaches the canvas {canvas:?}");
|
||||
assert!(!visible.intersect(toolbox).is_positive(), "{hit:?}: {visible:?} reaches the toolbox {toolbox:?}");
|
||||
}
|
||||
// The fitted column lays its chrome out inside the dock, so here nothing needs the clip.
|
||||
dock_controls_clipped(&h, dock);
|
||||
h.set_size(vec2(1280.0, 600.0));
|
||||
h.run_steps(3);
|
||||
assert!((dock_width(h.state()) - 520.0).abs() < 0.5, "growing the window keeps the stored width");
|
||||
assert!((allocated_dock(&h).width() - 520.0 - 36.0).abs() < 1.0, "a wide window draws the stored width again, got {:?}", allocated_dock(&h));
|
||||
}
|
||||
|
||||
/// With no room left for the dock column, its controls' full rects run past the panel edge and
|
||||
/// the panel clip keeps what is drawn and clickable inside the dock.
|
||||
#[test]
|
||||
fn a_squeezed_dock_column_clips_its_controls_at_the_panel_edge() {
|
||||
// The toolbox, the Pro rail (36 points) and the canvas minimum fill the window, which leaves
|
||||
// the dock column only its one-point floor.
|
||||
let h = narrow_dock_harness(crate::panels::toolbar::toolbar_width(true, false) + 36.0 + CANVAS_MIN_WIDTH);
|
||||
let canvas = narrow_canvas(&h);
|
||||
let dock = allocated_dock(&h);
|
||||
assert!(!canvas.intersect(dock).is_positive(), "the dock never overlaps the canvas: {canvas:?}, {dock:?}");
|
||||
// Asserts that some control's full rect overflows the dock while every clipped one stays inside.
|
||||
let clipped = dock_controls_clipped(&h, dock);
|
||||
assert!(clipped > 0, "a squeezed column has chrome wider than the dock that the clip cuts off, in {dock:?}");
|
||||
}
|
||||
|
||||
/// A queued external width cancels a held or just-released dock-edge drag and is drawn at its
|
||||
/// actual width on the first frame that processes it.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user