mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Properties: test that typed transforms turn about the reference point (#4285)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use egui::{Event, PointerButton, Pos2, Rect, vec2};
|
||||
use serde_json::Value;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
use super::*;
|
||||
@@ -1445,3 +1446,114 @@ fn one_effect_has_a_delete_icon_in_appearance() {
|
||||
app.run("select.set", json!({"ids": [id, other]})).unwrap();
|
||||
assert!(!shows(&tall(&mut app, &ctx, vec![]), &name), "two objects");
|
||||
}
|
||||
|
||||
/// A rectangle at 100, 100, 100 × 60 pt, selected, with `then` run on it, and a context that
|
||||
/// builds the accessibility tree (to find the locator's squares).
|
||||
fn reference_setup(then: &[(&str, Value)]) -> (VectorcraftApp, egui::Context) {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 300, "height": 300})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 100, "y": 100, "width": 100, "height": 60})).unwrap();
|
||||
for (id, p) in then {
|
||||
app.run(id, p.clone()).unwrap();
|
||||
}
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
ctx.enable_accesskit();
|
||||
(app, ctx)
|
||||
}
|
||||
|
||||
/// Clicks the topmost control of `draw` that screen readers call `name`.
|
||||
fn click_named(app: &mut VectorcraftApp, ctx: &egui::Context, draw: fn(&mut VectorcraftApp, &mut Ui), name: &str) {
|
||||
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| draw(app, ui));
|
||||
out.textures_delta.clear();
|
||||
let nodes = &out.platform_output.accesskit_update.as_ref().expect("an accessibility tree").nodes;
|
||||
let b = nodes
|
||||
.iter()
|
||||
.filter(|(_, n)| n.label() == Some(name))
|
||||
.filter_map(|(_, n)| n.bounds())
|
||||
.min_by(|a, b| a.y0.total_cmp(&b.y0))
|
||||
.unwrap_or_else(|| panic!("no {name:?}"));
|
||||
let at = egui::pos2(((b.x0 + b.x1) / 2.0) as f32, ((b.y0 + b.y1) / 2.0) as f32);
|
||||
crate::panels::tests_appearance::click(ctx, app, at, draw);
|
||||
}
|
||||
|
||||
/// Types `value` over the topmost field of `draw` that shows `shown`, and presses Enter.
|
||||
fn type_over(app: &mut VectorcraftApp, ctx: &egui::Context, draw: fn(&mut VectorcraftApp, &mut Ui), shown: &str, value: &str) {
|
||||
use crate::panels::tests_appearance::{click, frame_events, text_rect};
|
||||
let texts = frame_events(ctx, app, vec![], draw);
|
||||
click(ctx, app, text_rect(&texts, shown).center(), draw);
|
||||
let key = |key, modifiers| Event::Key { key, physical_key: None, pressed: true, repeat: false, modifiers };
|
||||
frame_events(ctx, app, vec![key(egui::Key::A, egui::Modifiers::COMMAND)], draw);
|
||||
frame_events(ctx, app, vec![Event::Text(value.into())], draw);
|
||||
frame_events(ctx, app, vec![key(egui::Key::Enter, egui::Modifiers::NONE)], draw);
|
||||
frame_events(ctx, app, vec![], draw);
|
||||
}
|
||||
|
||||
/// The Properties panel with the Transform panel below it, as when both are open.
|
||||
fn properties_and_transform(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
show(app, ui);
|
||||
ui.separator();
|
||||
crate::panels::transform::show(app, ui);
|
||||
}
|
||||
|
||||
/// A panel (or panels) drawn in a test frame.
|
||||
type Draw = fn(&mut VectorcraftApp, &mut Ui);
|
||||
|
||||
fn near(a: vectorcraft_geom::Point, b: vectorcraft_geom::Point) -> bool {
|
||||
(a - b).hypot() < 1e-6
|
||||
}
|
||||
|
||||
/// #4285: with the locator's top-left square chosen, a rotation typed in the Properties panel's
|
||||
/// Transform section turns the selection about the box's top-left corner, as the Transform panel
|
||||
/// does: on a plain rectangle, on one already turned 30°, and with the Transform panel open too.
|
||||
#[test]
|
||||
fn a_typed_rotation_turns_about_the_reference_point() {
|
||||
// (case, the angle the rectangle starts at, the panels, the angle typed)
|
||||
let cases: [(&str, f64, Draw, &str); 4] = [
|
||||
("section", 0.0, transform_section, "45"),
|
||||
("panel", 0.0, show, "45"),
|
||||
("turned 30°", 30.0, transform_section, "60"),
|
||||
("with the Transform panel", 0.0, properties_and_transform, "45"),
|
||||
];
|
||||
for (case, start, draw, value) in cases {
|
||||
let (mut app, ctx) = reference_setup(&[("object.rotate", json!({"angle": start}))]);
|
||||
let shown = format!("{start}°");
|
||||
click_named(&mut app, &ctx, draw, "Top Left");
|
||||
assert_eq!(ctx.data(|d| d.get_temp::<usize>(egui::Id::unique("refpt"))), Some(0), "{case}: the locator's choice");
|
||||
let corner = app.selection_box().unwrap().reference_point(0);
|
||||
type_over(&mut app, &ctx, draw, &shown, value);
|
||||
let bx = app.selection_box().unwrap();
|
||||
assert!((bx.angle - value.parse::<f64>().unwrap()).abs() < 1e-6, "{case}: turned to {}", bx.angle);
|
||||
assert!(near(bx.reference_point(0), corner), "{case}: the top-left corner {corner:?} moved to {:?}", bx.reference_point(0));
|
||||
}
|
||||
}
|
||||
|
||||
/// #4285: the flip buttons mirror the selection about the chosen reference point: with the
|
||||
/// top-left square, across the box's left edge (left-right) or top edge (top-bottom).
|
||||
#[test]
|
||||
fn the_flip_buttons_mirror_about_the_reference_point() {
|
||||
for (button, want) in [("Flip Along Horizontal Axis", (0.0, 100.0, 100.0, 160.0)), ("Flip Along Vertical Axis", (100.0, 40.0, 200.0, 100.0))] {
|
||||
let (mut app, ctx) = reference_setup(&[]);
|
||||
click_named(&mut app, &ctx, transform_section, "Top Left");
|
||||
click_named(&mut app, &ctx, transform_section, button);
|
||||
let r = app.selection_box().unwrap().rect;
|
||||
let got = (r.x0, r.y0, r.x1, r.y1);
|
||||
assert!([got.0 - want.0, got.1 - want.1, got.2 - want.2, got.3 - want.3].iter().all(|d| d.abs() < 1e-6), "{button}: {got:?}, want {want:?}");
|
||||
}
|
||||
}
|
||||
|
||||
/// With Use Registration Point for Symbol on, one symbol instance turns about its symbol's
|
||||
/// registration point (here the bottom-right one) whatever the locator shows: the locator
|
||||
/// follows the registration point.
|
||||
#[test]
|
||||
fn a_symbol_instance_turns_about_its_registration_point() {
|
||||
let (mut app, ctx) = reference_setup(&[]);
|
||||
let id = app.run("symbol.new", json!({"name": "Corner", "registration": 8})).unwrap()["id"].clone();
|
||||
app.run("select.set", json!({"ids": [id]})).unwrap();
|
||||
app.session.prefs.use_registration_point_for_symbol = true;
|
||||
click_named(&mut app, &ctx, transform_section, "Top Left");
|
||||
type_over(&mut app, &ctx, transform_section, "0°", "45");
|
||||
let bx = app.selection_box().unwrap();
|
||||
assert!((bx.angle - 45.0).abs() < 1e-6, "turned to {}", bx.angle);
|
||||
assert!(near(bx.reference_point(8), vectorcraft_geom::Point::new(200.0, 160.0)), "the registration point moved to {:?}", bx.reference_point(8));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user