Type: discard queued font wheel turns after text selection changes

This commit is contained in:
dotCooCoo
2026-11-03 16:03:40 +07:00
committed by bflatastic
parent a9048ddfab
commit 84db6a54a0
3 changed files with 50 additions and 5 deletions
+11 -4
View File
@@ -11,7 +11,7 @@
//! filters as the remaining families. In the Character and Properties panels and the Control bar,
//! the mouse wheel turned over the closed field applies the next family (or previous one) to the
//! selected text, one undo step per notch. The menu's order at the first turn stays fixed until the
//! pointer leaves the field; steps stop at its first and last family.
//! pointer leaves the field or the text selection changes; steps stop at its first and last family.
use std::collections::HashMap;
use std::sync::Arc;
@@ -287,7 +287,7 @@ struct MenuState {
/// The height of the search field and filters last frame.
header: f32,
/// The families the wheel steps through, listed at its first notch over the closed field and
/// kept while the pointer stays on it: each step makes its family a recent one, which would
/// kept for that text selection while the pointer stays on it: each step makes its family a recent one, which would
/// otherwise move it in the order.
wheel_order: Option<Arc<[String]>>,
wheel_target: Option<WheelTarget>,
@@ -451,15 +451,22 @@ fn wheel_fields_id(viewport: egui::ViewportId) -> egui::Id {
egui::Id::unique("font-wheel-fields").with(viewport)
}
#[derive(Clone, Copy, PartialEq)]
#[derive(Clone, PartialEq)]
struct WheelTarget {
input: crate::state::InputGeneration,
workspace: u64,
selection: Vec<vectorcraft_doc::NodeId>,
range: Option<(vectorcraft_doc::NodeId, usize, usize)>,
}
/// Pending turns and field rectangles belong to their input target and visible workspace.
pub(crate) fn sync_wheel_target(app: &VectorcraftApp, ctx: &egui::Context, viewport: egui::ViewportId) {
let target = WheelTarget { input: app.input_generation(), workspace: app.ui.docking_generation };
let target = WheelTarget {
input: app.input_generation(),
workspace: app.ui.docking_generation,
selection: app.session.active().map_or_else(Vec::new, |document| document.selection.objects.clone()),
range: crate::panels::character::text_editing(app),
};
let key = wheel_fields_id(viewport);
let (previous, mut fields) = ctx.data(|d| (d.get_temp::<WheelTarget>(key.with("target")), d.get_temp::<WheelFields>(key)));
if previous.is_some_and(|old| old != target) {
+38
View File
@@ -22,6 +22,44 @@ fn deselecting_type_leaves_the_missing_control_fields_wheel_available() {
assert!(control_wheel_available_after("select.set", json!({"ids": []})));
}
#[test]
fn a_queued_font_wheel_does_not_restyle_a_new_object_selection() {
assert_queued_wheel_discarded_after_target_change(false);
}
#[test]
fn a_queued_font_wheel_does_not_restyle_a_new_character_range() {
assert_queued_wheel_discarded_after_target_change(true);
}
fn assert_queued_wheel_discarded_after_target_change(range: bool) {
let (ctx, mut app) = selected_type_with_control();
let first = app.session.active().unwrap().selection.objects.first().unwrap().0;
let second = app.run("text.create", json!({"x": 30, "y": 120, "text": "Other text", "font": "Inter"})).unwrap()["id"].clone();
app.run("select.set", json!({"ids": [first]})).unwrap();
if range {
app.select_tool("type");
app.session.set_tool_option("textSelection", &json!({"editing": first, "anchor": 0, "caret": 2}));
}
let mut texts = Vec::new();
for _ in 0..8 {
texts = frame(&mut app, &ctx, vec![]);
}
let point = texts.iter().find(|(t, p)| t == "Inter" && p.y < 150.0).unwrap().1;
let mut raw = wheel_input(point);
app.raw_input_hook(&ctx, &mut raw);
assert!(!raw.events.iter().any(|e| matches!(e, Event::MouseWheel { .. })), "the original field captured its turn");
if range {
app.session.set_tool_option("textSelection", &json!({"editing": first, "anchor": 2, "caret": 4}));
} else {
app.run("select.set", json!({"ids": [second]})).unwrap();
}
let before = app.session.active().unwrap().history.undo.len();
let mut out = ctx.run_ui(raw, |ui| app.ui(ui));
out.textures_delta.clear();
assert_eq!(app.session.active().unwrap().history.undo.len(), before, "the captured turn cannot restyle the new target");
}
#[test]
fn artboard_controls_leave_replaced_font_fields_wheel_available() {
let (ctx, mut app) = selected_type_with_control();
File diff suppressed because one or more lines are too long