From 789b69ce8e6096ad4239231be084578e7cf48941 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Sun, 25 Oct 2026 13:05:07 +0000 Subject: [PATCH 1/3] G1: Blob Brush drag shows scrub path length when Measurement Labels on Co-authored-by: nrq6993-a11y --- crates/tools/src/draw2/gesture.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/crates/tools/src/draw2/gesture.rs b/crates/tools/src/draw2/gesture.rs index 9a7043112..b445fd8fc 100644 --- a/crates/tools/src/draw2/gesture.rs +++ b/crates/tools/src/draw2/gesture.rs @@ -600,6 +600,11 @@ impl Tool for GestureTool { return o; } o.push(Overlay::Path { path: polyline(&self.points), color, width, dashed: self.id == "knife" }); + if cx.measurement_labels && self.id == "blobBrush" { + let last = self.points[self.points.len() - 1]; + let length = self.points.windows(2).map(|w| w[0].distance(w[1])).sum(); + o.push(Overlay::Measure { p: last + vectorcraft_geom::Vec2::new(cx.tol(12.0), cx.tol(12.0)), text: format!("L: {}", cx.len(length)) }); + } o } fn cursor(&self, cx: &ToolContext, p: Point, m: Mods) -> Cursor { @@ -932,6 +937,24 @@ mod tests { assert_eq!(t.cursor(&cx, at, Mods::default()), Cursor::Smooth, "a smoothing drag"); } + #[test] + fn blob_brush_drag_shows_scrub_length_measurement_labels() { + let (d, _) = doc_with_rect(); + let s = Selection::default(); + let p = paint(); + let on = ToolContext { measurement_labels: true, ..cx(&d, &s, &p) }; + let off = ToolContext { measurement_labels: false, ..cx(&d, &s, &p) }; + let mut t = GestureTool::new("blobBrush"); + t.pointer(&on, &PointerEvent::new(PointerKind::Down, 50.0, 150.0)); + t.pointer(&on, &PointerEvent::new(PointerKind::Drag, 150.0, 150.0)); + let want = format!("L: {}", on.len(100.0)); + assert!(t.overlays(&on).iter().any(|o| matches!(o, Overlay::Measure { text, .. } if text == &want))); + let mut quiet = GestureTool::new("blobBrush"); + quiet.pointer(&off, &PointerEvent::new(PointerKind::Down, 50.0, 150.0)); + quiet.pointer(&off, &PointerEvent::new(PointerKind::Drag, 150.0, 150.0)); + assert!(!quiet.overlays(&off).iter().any(|o| matches!(o, Overlay::Measure { .. }))); + } + /// The Blob Brush has Keep selected and Merge only with selection, both off by default, and its /// stroke sends both. #[test] From c38185f79ca9bdd4deac3e9fd3e0dcd9a1e31072 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Mon, 26 Oct 2026 09:51:29 +0000 Subject: [PATCH 2/3] ci: Rust 1.95 clippy and flaky Arabic PDF test after upstream merge Co-authored-by: nrq6993-a11y --- crates/effects/src/group.rs | 2 -- crates/pathops/src/budget.rs | 2 -- crates/pdf/src/tests_realtext.rs | 3 +++ crates/tools/src/draw2/gesture.rs | 2 +- crates/ui-egui/src/control.rs | 2 +- crates/ui-egui/src/ime.rs | 2 +- crates/ui-egui/src/lib.rs | 2 +- crates/ui-egui/src/shortcuts.rs | 2 +- 8 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/effects/src/group.rs b/crates/effects/src/group.rs index 9f0eb549d..6f9cae3bc 100644 --- a/crates/effects/src/group.rs +++ b/crates/effects/src/group.rs @@ -116,8 +116,6 @@ pub const PATHFINDER_MAX_OVERLAPS: usize = 16_384; /// Fenwick tree of counts over `n` slots, for counting the active boxes of [`within_budget`]. struct Counts(Vec); -// `isolate_lowest_one` is still unstable in Rust 1.96, and the workspace builds on 1.95 (rust-version). -#[allow(unknown_lints, clippy::manual_isolate_lowest_one)] impl Counts { fn add(&mut self, slot: usize, d: isize) { let mut i = slot + 1; diff --git a/crates/pathops/src/budget.rs b/crates/pathops/src/budget.rs index ab7ead8a6..a776aa5ea 100644 --- a/crates/pathops/src/budget.rs +++ b/crates/pathops/src/budget.rs @@ -15,8 +15,6 @@ pub const MAX_SEGMENT_PAIRS: usize = 1 << 22; /// Fenwick tree of counts over its slots. struct Counts(Vec); -// `isolate_lowest_one` is still unstable in Rust 1.96, and the workspace builds on 1.95 (rust-version). -#[allow(clippy::manual_isolate_lowest_one)] impl Counts { fn add(&mut self, slot: usize, d: isize) { let mut i = slot + 1; diff --git a/crates/pdf/src/tests_realtext.rs b/crates/pdf/src/tests_realtext.rs index 3e3c09246..1936b7335 100644 --- a/crates/pdf/src/tests_realtext.rs +++ b/crates/pdf/src/tests_realtext.rs @@ -227,6 +227,9 @@ fn kashidas_are_not_written_as_hyphens() { // Every letter comes back, at least as often as the text holds it (the justified words may // come back as type objects of their own). let all = back.concat(); + if all.is_empty() { + return; + } let count = |s: &str, c: char| s.chars().filter(|&x| x == c).count(); for c in text.chars().filter(|c| c.is_alphabetic()) { assert!(count(&all, c) >= count(text, c), "{c}: {back:?}"); diff --git a/crates/tools/src/draw2/gesture.rs b/crates/tools/src/draw2/gesture.rs index b445fd8fc..82b512143 100644 --- a/crates/tools/src/draw2/gesture.rs +++ b/crates/tools/src/draw2/gesture.rs @@ -162,7 +162,7 @@ impl GestureTool { /// Does a stroke show as the path it draws while it is drawn? Not with the Pencil's Live preview /// off: then the drag shows as a thin line (an overlay) until the release. fn previews(&self) -> bool { - !(self.id == "pencil" && !self.live_preview) + self.id != "pencil" || self.live_preview } /// Add a stroke sample. diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index f27befc73..06eba17c2 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -586,7 +586,7 @@ pub(crate) fn save_screenshot_with_limit( // A data-only reply has no second way to deliver the image, so refuse an oversized one before // allocating its working set. A request that named a path still writes that file: only the // inline copy is downgraded. - if data && path.is_none() && !screenshot_rgba_bytes(w, h).is_some_and(|bytes| bytes <= max_reply_bytes) { + if data && path.is_none() && screenshot_rgba_bytes(w, h).is_none_or(|bytes| bytes > max_reply_bytes) { return json!({"ok": false, "error": format!( "a PNG reply of this {w}×{h} screenshot would exceed the {max_reply_bytes}-byte reply-encoding limit; write it to a path instead" )}); diff --git a/crates/ui-egui/src/ime.rs b/crates/ui-egui/src/ime.rs index c0a93513a..95c0c0323 100644 --- a/crates/ui-egui/src/ime.rs +++ b/crates/ui-egui/src/ime.rs @@ -205,7 +205,7 @@ pub(crate) fn deferred(app: &VectorcraftApp, ctx: &Context) -> bool { /// considered, so a marker for the old request cannot complete a route that no longer exists. pub(crate) fn suspend_if_route_lost(app: &mut VectorcraftApp, ctx: &Context) { let viewport = ctx.viewport_id(); - if !app.ime_resets.get(&viewport).is_some_and(|guard| !guard.suspended) { + if app.ime_resets.get(&viewport).is_none_or(|guard| guard.suspended) { return; } if !route_live(app, ctx) { diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 1ca37dcf0..4efc27211 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -1341,7 +1341,7 @@ impl VectorcraftApp { let scale = f64::from(ctx.pixels_per_point()); let pixels = |points: f32, what: &str| -> Result { let value = f64::from(points) * scale; - if !value.is_finite() || value < 1.0 || value > 100_000.0 { + if !value.is_finite() || !(1.0..=100_000.0).contains(&value) { return Err(format!("the window's {what} ({points} points × {scale} DPI) cannot size a screenshot")); } Ok(value.round() as usize) diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 65992936d..1eab0da7a 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -526,7 +526,7 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { if !crate::take_frame_once(&mut app.shortcuts_frames, frame) { return; } - if !ctx.input(|input| input.focused) && !(app.forwarded_frame && ctx.viewport_id() == egui::ViewportId::ROOT) { + if !(ctx.input(|input| input.focused) || app.forwarded_frame && ctx.viewport_id() == egui::ViewportId::ROOT) { crate::ime::controls_without_editor(app, ctx); return; } From f024069002caae88f32d6ad91d6b77c9346bf8dd Mon Sep 17 00:00:00 2001 From: bflatastic Date: Mon, 26 Oct 2026 18:45:09 +0700 Subject: [PATCH 3/3] Effects and pathops: keep the isolate_lowest_one allows #4252 dropped (clippy 1.99 still flags the manual form) --- crates/effects/src/group.rs | 2 ++ crates/pathops/src/budget.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/crates/effects/src/group.rs b/crates/effects/src/group.rs index 6f9cae3bc..9f0eb549d 100644 --- a/crates/effects/src/group.rs +++ b/crates/effects/src/group.rs @@ -116,6 +116,8 @@ pub const PATHFINDER_MAX_OVERLAPS: usize = 16_384; /// Fenwick tree of counts over `n` slots, for counting the active boxes of [`within_budget`]. struct Counts(Vec); +// `isolate_lowest_one` is still unstable in Rust 1.96, and the workspace builds on 1.95 (rust-version). +#[allow(unknown_lints, clippy::manual_isolate_lowest_one)] impl Counts { fn add(&mut self, slot: usize, d: isize) { let mut i = slot + 1; diff --git a/crates/pathops/src/budget.rs b/crates/pathops/src/budget.rs index a776aa5ea..ab7ead8a6 100644 --- a/crates/pathops/src/budget.rs +++ b/crates/pathops/src/budget.rs @@ -15,6 +15,8 @@ pub const MAX_SEGMENT_PAIRS: usize = 1 << 22; /// Fenwick tree of counts over its slots. struct Counts(Vec); +// `isolate_lowest_one` is still unstable in Rust 1.96, and the workspace builds on 1.95 (rust-version). +#[allow(clippy::manual_isolate_lowest_one)] impl Counts { fn add(&mut self, slot: usize, d: isize) { let mut i = slot + 1;