Merge pull request #4252 from nrq6993-a11y/stream/vc-feat-g1-blob-brush-scrub-length

G1: Blob Brush drag shows scrub path length when Measurement Labels on
This commit is contained in:
BFlat
2026-10-26 18:48:45 +07:00
committed by GitHub
6 changed files with 31 additions and 5 deletions
+3
View File
@@ -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:?}");
+24 -1
View File
@@ -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.
@@ -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]
+1 -1
View File
@@ -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"
)});
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -1341,7 +1341,7 @@ impl VectorcraftApp {
let scale = f64::from(ctx.pixels_per_point());
let pixels = |points: f32, what: &str| -> Result<usize, String> {
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)
+1 -1
View File
@@ -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;
}