Files
effectcraft/crates/ui-egui/tests/it/ui_control_modifiers.rs
T
skylartaylor 9ee853715d M0.37: migrate EffectCraft to egui 0.37
Reconcile main 991904888a and preserve the reviewed lifecycle, IME, rendering and lockfile changes.

Formatting, strict lint, layers, assets and WASM passed. Full CI retained 3,961 passes, 14 baseline-matching GPU failures and 31 ignored executions. The local review commit has explicit approval to retain these documented failures; native/browser acceptance and paired app performance remain open.
2026-10-25 02:01:16 +01:00

84 lines
3.8 KiB
Rust

//! Control-channel clicks and drags hold their modifiers for the whole gesture: egui takes
//! `i.modifiers` from `ModifiersChanged` events, not from the pointer events, and synthetic
//! events arrive one per frame, so a Shift-click must hold Shift from its press to its release.
use effectcraft_engine::Session;
use effectcraft_ui_egui::EffectcraftApp;
use effectcraft_ui_egui::control::{self, ControlRequest};
use serde_json::{Value, json};
/// Feed the queued synthetic input frame by frame; per frame: (has a press/release, the
/// modifiers egui holds in that frame, which only `ModifiersChanged` events change).
fn frames(app: &mut EffectcraftApp) -> Vec<(bool, egui::Modifiers)> {
let mut out = vec![];
let mut held = egui::Modifiers::NONE;
for _ in 0..64 {
let mut raw = egui::RawInput::default();
app.feed_synthetic(&mut raw);
if raw.events.is_empty() {
break;
}
// The harness feeds the batch itself; a real UI pass would apply it now.
app.mark_synthetic_applied();
let mut button = false;
for e in &raw.events {
match e {
egui::Event::ModifiersChanged(m) => held = *m,
egui::Event::PointerButton { .. } => button = true,
_ => {}
}
}
out.push((button, held));
}
out
}
fn send(app: &mut EffectcraftApp, method: &str, params: Value) {
let ctx = egui::Context::default();
let (req, _rx) = ControlRequest::new(method, params);
let now = app.now();
let _ = control::handle(app, &ctx, &req, now);
}
#[test]
fn control_gestures_reject_excessive_counts_without_queue_mutation() {
let mut app = EffectcraftApp::new(Session::default());
send(&mut app, "ui.click", json!({"x": 10, "y": 10, "count": u64::MAX}));
assert!(app.take_synthetic_input().is_empty(), "an oversized click must not partially queue input");
send(&mut app, "ui.drag", json!({"from": {"x": 10, "y": 10}, "to": {"x": 90, "y": 40}, "steps": u64::MAX}));
assert!(app.take_synthetic_input().is_empty(), "an oversized drag must not partially queue input");
send(&mut app, "ui.click", json!({"x": 10, "y": 10, "count": 512}));
send(&mut app, "ui.click", json!({"x": 10, "y": 10, "count": 512}));
assert_eq!(app.take_synthetic_input().len(), 2054, "two maximum accepted clicks fit under the aggregate cap");
for _ in 0..4 {
send(&mut app, "ui.click", json!({"x": 10, "y": 10, "count": 512}));
}
assert_eq!(app.take_synthetic_input().len(), 3081, "the pending event limit is enforced before requests can exceed it");
}
#[test]
fn shift_click_holds_shift_from_press_to_release() {
let mut app = EffectcraftApp::new(Session::default());
send(&mut app, "ui.click", json!({"x": 10, "y": 10, "modifiers": {"shift": true}}));
let f = frames(&mut app);
let buttons: Vec<_> = f.iter().filter(|(b, _)| *b).collect();
assert_eq!(buttons.len(), 2, "press and release: {f:?}");
assert!(buttons.iter().all(|(_, m)| m.shift), "Shift on the press and the release: {f:?}");
// Afterwards the modifiers are released again.
send(&mut app, "ui.click", json!({"x": 10, "y": 10}));
assert!(frames(&mut app).iter().all(|(_, m)| !m.shift), "a plain click after it has no Shift");
}
#[test]
fn shift_drag_holds_shift_through_the_moves() {
let mut app = EffectcraftApp::new(Session::default());
send(&mut app, "ui.drag", json!({"from": {"x": 10, "y": 10}, "to": {"x": 90, "y": 40}, "steps": 4, "modifiers": {"shift": true}}));
let f = frames(&mut app);
let first = f.iter().position(|(b, _)| *b).expect("press");
let last = f.iter().rposition(|(b, _)| *b).expect("release");
assert!(last > first + 1, "moves between press and release: {f:?}");
assert!(f[first..=last].iter().all(|(_, m)| m.shift), "Shift held for the whole drag: {f:?}");
}