mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-07 12:13:20 +00:00
test(ui): verify floating frame compatibility and resize constraints
This commit is contained in:
@@ -34,6 +34,19 @@ only. `FloatingFrame::resize_axes` defaults to both axes and preserves an enable
|
||||
axis at corners when the other is disabled. A host with its own title bar can
|
||||
disable the separate top gutter through `reserve_top_resize_band(false)`.
|
||||
|
||||
Compatibility and target-size audit: before these hooks, resizable frames already
|
||||
reserved a 4 pt top band. That remains the default; `false` is the new host opt-out.
|
||||
The unchanged stock edge hit bands are 4 pt thick; corners are at most 12 pt.
|
||||
They do **not** meet a 24-by-24 logical-pixel target at 1x. This PR preserves
|
||||
those hit rectangles and does not claim accessibility compliance. Focused
|
||||
resize handles also support arrow keys (8 pt steps). Enlarging pointer targets
|
||||
needs separate overlap, title-bar and viewport-edge acceptance checks.
|
||||
|
||||
`chrome::compatibility_tests::defaults_preserve_pre_host_hook_frame_geometry`
|
||||
locks down the pre-hook hit rectangles/defaults. `host_hooks_tests` exercises all
|
||||
four axis settings with actual corner and side pointer drags, minimum/maximum
|
||||
sizes, the viewport edge, and top-gutter opt-out geometry.
|
||||
|
||||
Exhaustive `DockStyle` struct literals need `dock_gap: None`; constructors such
|
||||
as `from_ui` and `from_visuals` supply the compatible default.
|
||||
|
||||
|
||||
@@ -520,3 +520,33 @@ pub fn live_move(ctx: &Context, bar: &Response, rect: Rect, bounds: Rect) -> Opt
|
||||
forget();
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod compatibility_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn defaults_preserve_pre_host_hook_frame_geometry() {
|
||||
let rect = Rect::from_min_size(pos2(100.0, 100.0), vec2(300.0, 200.0));
|
||||
let frame = FloatingFrame::new(Id::unique("defaults"), rect);
|
||||
assert_eq!(frame.resize_axes, [true, true]);
|
||||
assert!(frame.reserve_top_resize_band);
|
||||
assert!(frame.resizable);
|
||||
// Literal pre-PR hit rectangles: preserve these rather than silently enlarging them.
|
||||
let expected = [
|
||||
[388.0, 288.0, 400.0, 300.0],
|
||||
[100.0, 112.0, 104.0, 288.0],
|
||||
[396.0, 112.0, 400.0, 288.0],
|
||||
[112.0, 296.0, 388.0, 300.0],
|
||||
[100.0, 288.0, 112.0, 300.0],
|
||||
[112.0, 100.0, 388.0, 104.0],
|
||||
[100.0, 100.0, 112.0, 104.0],
|
||||
[388.0, 100.0, 400.0, 104.0],
|
||||
];
|
||||
for ((_, hit, _), [x0, y0, x1, y1]) in handles(rect).into_iter().zip(expected) {
|
||||
assert_eq!(hit, Rect::from_min_max(pos2(x0, y0), pos2(x1, y1)));
|
||||
// Audit result: legacy targets do NOT meet a 24-by-24 logical-pixel target.
|
||||
assert!(hit.width() < 24.0 || hit.height() < 24.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,3 +363,63 @@ fn owned_minimized_groups_skip_the_trailing_stock_target() {
|
||||
assert_eq!(group_drop, !owned, "owned={owned}: {:?}", out.actions);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn disabled_edge_axes_ignore_pointer_drags_and_enabled_edges_obey_limits() {
|
||||
for axes in [[true, true], [true, false], [false, true], [false, false]] {
|
||||
for horizontal in [true, false] {
|
||||
for grow in [true, false] {
|
||||
let ctx = Context::default();
|
||||
let rect = Rect::from_min_size(pos2(400.0, 300.0), vec2(300.0, 200.0));
|
||||
let bounds = Rect::from_min_max(pos2(0.0, 0.0), pos2(720.0, 520.0));
|
||||
let frame = |events| {
|
||||
let mut resized = None;
|
||||
ctx.run_ui(input(events), |ui| {
|
||||
let style = chrome::ChromeStyle::from_ui(ui);
|
||||
resized = chrome::FloatingFrame::new(Id::unique("edge-axis"), rect)
|
||||
.resize_axes(axes)
|
||||
.bounds(bounds)
|
||||
.limits(PanelLimits { min: vec2(160.0, 120.0), max: vec2(310.0, 210.0) })
|
||||
.show(ui.ctx(), &style, |_, _| ())
|
||||
.resized;
|
||||
})
|
||||
.drop_without_applying_deltas();
|
||||
resized
|
||||
};
|
||||
frame(vec![]);
|
||||
frame(vec![]);
|
||||
let from = if horizontal { pos2(rect.right() - 2.0, rect.center().y) } else { pos2(rect.center().x, rect.bottom() - 2.0) };
|
||||
frame(vec![Event::PointerMoved(from), button(from, true)]);
|
||||
let delta = if horizontal { vec2(10.0, 0.0) } else { vec2(0.0, 10.0) };
|
||||
frame(vec![Event::PointerMoved(from + delta)]);
|
||||
let result = frame(vec![Event::PointerMoved(from + delta * if grow { 100.0 } else { -100.0 })]);
|
||||
let enabled = if horizontal { axes[0] } else { axes[1] };
|
||||
let expected = if horizontal { vec2(if grow { 310.0 } else { 160.0 }, 200.0) } else { vec2(300.0, if grow { 210.0 } else { 120.0 }) };
|
||||
assert_eq!(result, enabled.then(|| Rect::from_min_size(rect.min, expected)), "{axes:?} horizontal={horizontal} grow={grow}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frame_at_viewport_edge_cannot_resize_beyond_it() {
|
||||
let ctx = Context::default();
|
||||
let rect = Rect::from_min_max(pos2(400.0, 300.0), pos2(800.0, 600.0));
|
||||
let frame = |events| {
|
||||
let mut result = None;
|
||||
ctx.run_ui(input(events), |ui| {
|
||||
let style = chrome::ChromeStyle::from_ui(ui);
|
||||
let response = chrome::FloatingFrame::new(Id::unique("viewport-edge"), rect).show(ui.ctx(), &style, |_, _| ());
|
||||
assert_eq!(response.rect, rect);
|
||||
result = response.resized;
|
||||
})
|
||||
.drop_without_applying_deltas();
|
||||
result
|
||||
};
|
||||
frame(vec![]);
|
||||
frame(vec![]);
|
||||
let from = rect.max - vec2(2.0, 2.0);
|
||||
frame(vec![Event::PointerMoved(from), button(from, true)]);
|
||||
frame(vec![Event::PointerMoved(from + vec2(10.0, 10.0))]);
|
||||
assert_eq!(frame(vec![Event::PointerMoved(from + vec2(100.0, 100.0))]), None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user