Rotate View: a two-finger rotation on a Mac trackpad turns the view about the pointer

A two-finger rotation on a Mac trackpad did not turn the view. With
the new General preference Trackpad Gesture to Rotate View
(`trackpadRotateView`, on by default) and the pointer over the canvas,
the rotation turns the view about the pointer, keeping the document
point under it in place. The view does not turn until the rotation
reaches 5°; it then turns by the whole rotation and follows each later
rotation event. A rotation ends 0.25 s after its last event. A rotation
in the same frame as a pinch zooms and turns about the same point.

egui reports the gesture as `Event::Rotate`: egui-winit makes it from
winit's macOS rotation gesture, and eframe makes it from Safari's
gesture events in the web build. Rotation events that arrive while
fingers are on a touch screen do not turn the view. With the
preference off, the gesture does not turn the view.

Docs: docs/mcp.md, docs/target-app-parity.md, docs/hardware-parity.md.
This commit is contained in:
dotCooCoo
2026-10-17 16:38:42 +07:00
committed by bflatastic
parent c5405479b9
commit 4285ccdda3
28 changed files with 187 additions and 3 deletions
+1
View File
@@ -138,6 +138,7 @@ pub const PREF_SPECS: &[PrefSpec] = &[
p!("scaleStrokes", "General", "Options", "Scale Strokes & Effects", bool),
p!("zoomWithMouseWheel", "General", "Options", "Zoom with Mouse Wheel", bool),
p!("scrubNumericFields", "General", "Options", "Scrub Numeric Fields by Dragging", bool),
p!("trackpadRotateView", "General", "Options", "Trackpad Gesture to Rotate View", bool),
// Selection & Anchor Display
p!("selectionTolerance", "Selection & Anchor Display", "Selection", "Tolerance", num(1.0, 8.0, "px")),
p!("objectSelectionByPathOnly", "Selection & Anchor Display", "Selection", "Object Selection by Path Only", bool),
+3
View File
@@ -737,6 +737,8 @@ pub struct Prefs {
pub zoom_with_mouse_wheel: bool,
/// A horizontal drag on a numeric field or its label steps its value (#400).
pub scrub_numeric_fields: bool,
/// A two-finger rotation on a Mac trackpad turns the view about the pointer once it reaches 5°.
pub trackpad_rotate_view: bool,
/// Offset for Paste / duplicate (Illustrator pastes to the view centre; we offset by this).
pub paste_offset: f64,
// Selection & Anchor Display
@@ -992,6 +994,7 @@ impl Default for Prefs {
scale_strokes: false,
zoom_with_mouse_wheel: false,
scrub_numeric_fields: true,
trackpad_rotate_view: true,
paste_offset: 10.0,
selection_tolerance: 3.0,
object_selection_by_path_only: false,
+156 -1
View File
@@ -709,7 +709,7 @@ fn handle_input(app: &mut VectorcraftApp, ui: &Ui, resp: &egui::Response, rect:
});
// Zoom around the pointer, and scroll ([`wheel`]): fingers pinching on a touch screen may also
// slide.
// slide. Then turn the view about the pointer for a trackpad rotation ([`trackpad_turn`]).
if resp.hovered() {
if (factor - 1.0).abs() > 1e-6
&& let (Some(p), Some(vm)) = (hover, app.view_mut())
@@ -722,6 +722,12 @@ fn handle_input(app: &mut VectorcraftApp, ui: &Ui, resp: &egui::Response, rect:
let d = Xf { rect, zoom: vm.zoom, center: vm.center, rot: vm.rotation.to_radians() }.delta_to_doc(scroll);
vm.center -= d;
}
if let Some(p) = hover
&& let Some(deg) = trackpad_turn(ui, app.session.prefs.trackpad_rotate_view)
&& let Some(vm) = app.view_mut()
{
rotate_about(vm, rect, p, deg);
}
}
// In Presentation Mode a click anywhere goes to the next artboard. The tool receives no press,
@@ -981,6 +987,65 @@ fn zoom_about(vm: &mut View, rect: egui::Rect, p: Pos2, zoom: f64) {
vm.center += before - Xf::new(rect, vm).to_doc(p);
}
/// Turn view `vm` of the canvas `rect` by `degrees` clockwise, keeping the document point under
/// screen point `p` where it is.
fn rotate_about(vm: &mut View, rect: egui::Rect, p: Pos2, degrees: f64) {
let before = Xf::new(rect, vm).to_doc(p);
vm.rotation = vectorcraft_geom::normalize_deg(vm.rotation + degrees);
vm.center += before - Xf::new(rect, vm).to_doc(p);
}
/// How far a trackpad rotation turns before it turns the view, in degrees.
const ROTATE_DEAD_ZONE: f64 = 5.0;
/// How long after its last rotation event a trackpad rotation ends, in seconds.
const ROTATE_GESTURE_END: f64 = 0.25;
/// A trackpad rotation under way ([`trackpad_turn`]).
#[derive(Clone, Copy, Debug)]
struct TrackpadTurn {
/// Degrees it has turned so far, clockwise.
total: f64,
/// When its last rotation event came (egui's input time, in seconds).
last: f64,
/// It has reached [`ROTATE_DEAD_ZONE`] and turns the view.
turning: bool,
}
/// General › Trackpad Gesture to Rotate View (`on`): the degrees, clockwise, that a two-finger
/// rotation on a Mac trackpad turns the view this frame. egui sends the rotation as
/// [`egui::Event::Rotate`], from winit on macOS and from Safari's gesture events in the web build.
/// A rotation does not turn the view until it reaches [`ROTATE_DEAD_ZONE`]; it then turns the view
/// by all of it, and by each later event as it comes. It ends [`ROTATE_GESTURE_END`] after its last
/// event. Rotation events that come while fingers are on a touch screen do not turn the view.
fn trackpad_turn(ui: &Ui, on: bool) -> Option<f64> {
if !on {
return None;
}
let (deg, now) = ui.input(|i| {
let rad: f32 =
if i.multi_touch().is_some() { 0.0 } else { i.events.iter().map(|e| if let egui::Event::Rotate(r) = e { *r } else { 0.0 }).sum() };
(f64::from(rad).to_degrees(), i.time)
});
if deg == 0.0 || !deg.is_finite() {
return None;
}
let id = egui::Id::new("canvas-trackpad-turn");
let kept: Option<TrackpadTurn> = ui.data(|d| d.get_temp(id));
let mut t = kept.filter(|t| now - t.last <= ROTATE_GESTURE_END).unwrap_or(TrackpadTurn { total: 0.0, last: now, turning: false });
t.total += deg;
t.last = now;
let turn = if t.turning {
Some(deg)
} else if t.total.abs() >= ROTATE_DEAD_ZONE {
t.turning = true;
Some(t.total)
} else {
None
};
ui.data_mut(|d| d.insert_temp(id, t));
turn
}
/// One scroll bar along its axis, in screen points: the span it scrolls over (`lo..hi`) and the
/// part of it the canvas shows (`v0..v1`).
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -5111,6 +5176,96 @@ mod tests {
}
}
/// A canvas frame at input time `t` (seconds) with `events`, for the trackpad rotation tests.
fn timed_frame(app: &mut VectorcraftApp, ctx: &egui::Context, t: f64, events: Vec<egui::Event>) {
let raw = egui::RawInput {
screen_rect: Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0))),
time: Some(t),
events,
..Default::default()
};
let mut out = ctx.run_ui(raw, |ui| show(app, ui));
out.textures_delta.clear();
}
/// General › Trackpad Gesture to Rotate View, on by default: a two-finger rotation on a Mac
/// trackpad (egui's `Event::Rotate`, clockwise radians) turns the view about the pointer, also
/// in the frame of a pinch, which zooms about the same point. With the preference off, the view
/// does not turn.
#[test]
fn a_trackpad_rotation_turns_the_view_about_the_pointer() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
let ctx = egui::Context::default();
timed_frame(&mut app, &ctx, 0.0, vec![]);
let rect = app.canvas_rect.unwrap();
let at = rect.center() + vec2(60.0, 40.0);
timed_frame(&mut app, &ctx, 0.1, vec![egui::Event::PointerMoved(at)]);
let under = |app: &VectorcraftApp| Xf::new(rect, app.view().unwrap()).to_doc(at);
let before = (*app.view().unwrap(), under(&app));
timed_frame(&mut app, &ctx, 0.2, vec![egui::Event::Rotate(0.25)]);
let after = *app.view().unwrap();
assert!((after.rotation - before.0.rotation - 0.25f64.to_degrees()).abs() < 1e-4, "turned by 14.32°: {}", after.rotation);
assert!((after.zoom - before.0.zoom).abs() < 1e-12, "no zoom");
assert!(under(&app).distance(before.1) < 1e-6, "the point under the pointer stays: {:?}", under(&app));
// A pinch and a rotation in one frame, in a new gesture.
let before = (*app.view().unwrap(), under(&app));
timed_frame(&mut app, &ctx, 1.0, vec![egui::Event::Zoom(1.25), egui::Event::Rotate(0.25)]);
let after = *app.view().unwrap();
assert!((after.zoom / before.0.zoom - 1.25).abs() < 1e-6, "the pinch zooms: {}", after.zoom / before.0.zoom);
assert!((after.rotation - before.0.rotation - 0.25f64.to_degrees()).abs() < 1e-4, "and the rotation turns: {}", after.rotation);
assert!(under(&app).distance(before.1) < 1e-6, "about the same point: {:?}", under(&app));
app.session.execute("prefs.set", &json!({"key": "trackpadRotateView", "value": false})).unwrap();
let before = *app.view().unwrap();
timed_frame(&mut app, &ctx, 2.0, vec![egui::Event::Rotate(0.25)]);
timed_frame(&mut app, &ctx, 2.1, vec![egui::Event::Rotate(0.25)]);
let after = *app.view().unwrap();
assert!(after.rotation == before.rotation && after.center == before.center, "off: the view does not turn ({})", after.rotation);
}
/// A trackpad rotation turns the view only once it reaches 5°, so a pinch that twists a little
/// only zooms. It then turns the view by all of it and follows each later rotation event, until
/// a quarter of a second passes without one.
#[test]
fn a_trackpad_rotation_turns_nothing_until_it_reaches_five_degrees() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
let ctx = egui::Context::default();
timed_frame(&mut app, &ctx, 0.0, vec![]);
let at = app.canvas_rect.unwrap().center() + vec2(60.0, 40.0);
timed_frame(&mut app, &ctx, 0.1, vec![egui::Event::PointerMoved(at)]);
let rotate = |deg: f32| egui::Event::Rotate(deg.to_radians());
let zoom = app.view().unwrap().zoom;
timed_frame(&mut app, &ctx, 1.0, vec![egui::Event::Zoom(1.25), rotate(1.0)]);
assert!(app.view().unwrap().zoom > zoom * 1.2, "the pinch zooms");
timed_frame(&mut app, &ctx, 1.1, vec![egui::Event::Zoom(1.25), rotate(1.0)]);
timed_frame(&mut app, &ctx, 1.2, vec![egui::Event::Zoom(1.25), rotate(1.0)]);
assert_eq!(app.view().unwrap().rotation, 0.0, "3° in all does not turn the view");
timed_frame(&mut app, &ctx, 1.3, vec![rotate(3.0)]);
assert!((app.view().unwrap().rotation - 6.0).abs() < 1e-4, "6° in all turns by 6°: {}", app.view().unwrap().rotation);
timed_frame(&mut app, &ctx, 1.4, vec![rotate(-1.0)]);
assert!((app.view().unwrap().rotation - 5.0).abs() < 1e-4, "then each event turns it: {}", app.view().unwrap().rotation);
timed_frame(&mut app, &ctx, 2.0, vec![rotate(3.0)]);
assert!((app.view().unwrap().rotation - 5.0).abs() < 1e-4, "a new rotation starts under 5° again: {}", app.view().unwrap().rotation);
}
/// Rotation events that come while two fingers are on a touch screen (Safari on an iPad sends
/// them with the touches) do not turn the view.
#[test]
fn a_rotation_with_fingers_on_a_touch_screen_turns_nothing() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
let ctx = egui::Context::default();
timed_frame(&mut app, &ctx, 0.0, vec![]);
let c = app.canvas_rect.unwrap().center();
let touch = |n: u64, phase, pos| egui::Event::Touch { device_id: egui::TouchDeviceId(1), id: egui::TouchId(n), phase, pos, force: None };
timed_frame(&mut app, &ctx, 0.1, vec![egui::Event::PointerMoved(c - vec2(40.0, 0.0))]);
let before = app.view().unwrap().rotation;
let start = vec![touch(1, egui::TouchPhase::Start, c - vec2(40.0, 0.0)), touch(2, egui::TouchPhase::Start, c + vec2(40.0, 0.0))];
timed_frame(&mut app, &ctx, 0.2, [start, vec![egui::Event::Rotate(0.25)]].concat());
assert_eq!(app.view().unwrap().rotation, before, "the view does not turn");
}
/// General › Display Print Size at 100% Zoom (#394): `view.actualSize` is one document point per
/// screen point with it off, and 96 screen points per document inch with it on.
#[test]
+1
View File
@@ -2488,6 +2488,7 @@
Rotate 90° CW Завъртане на 90° по часовниковата стрелка
Rotate Tool Инструмент „Завъртане“
Rotate View Tool Инструмент „Завъртане на изглед“
Trackpad Gesture to Rotate View Завъртане на изгледа с жест на тракпада
Rotate… Завъртане…
Rotation Завъртане
Roughen… Грапавост…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1227,6 +1227,7 @@ lighting Light Světlo
Reshape Tool Nástroj Přetvarování
Rotate Tool Nástroj Otočení
Rotate View Tool Nástroj Otočení zobrazení
Trackpad Gesture to Rotate View Otáčet zobrazení gestem na trackpadu
Rotate View Otočení zobrazení
Rounded Rectangle Tool Nástroj Zaoblený obdélník
Scale Tool Nástroj Změna velikosti
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1152,6 +1152,7 @@ lighting Light Lys
Reshape Tool Omformningsværktøj
Rotate Tool Roteringsværktøj
Rotate View Tool Visningsroteringsværktøj
Trackpad Gesture to Rotate View Rotér visning med bevægelse på pegefeltet
Rotate View Rotér visning
Rounded Rectangle Tool Afrundet rektangelværktøj
Scale Tool Skaleringsværktøj
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2500,6 +2500,7 @@ mark-style Japanese Japanisch
Rotate 90° CW Um 90° im Uhrzeigersinn drehen
Rotate Tool Drehen-Werkzeug
Rotate View Tool Ansichtdrehung-Werkzeug
Trackpad Gesture to Rotate View Ansicht mit Trackpad-Geste drehen
Rotate… Drehen…
Rotation Drehung
Roughen… Aufrauen…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1670,6 +1670,7 @@
Roman Hanging Punctuation Κρεμαστή στίξη όρθιας γραφής
Rotate Περιστροφή
Rotate View Tool Εργαλείο περιστροφής προβολής (Rotate View Tool)
Trackpad Gesture to Rotate View Περιστροφή προβολής με χειρονομία στην επιφάνεια αφής
Rotate… Περιστροφή…
Rotation Περιστροφή
Rows: Γραμμές:
1 # Ελληνικά (el) — VectorCraft
1670
1671
1672
1673
1674
1675
1676
+1
View File
@@ -2534,6 +2534,7 @@
Rotate 90° CW Rotar 90° en sentido horario
Rotate Tool Herramienta Rotar
Rotate View Tool Herramienta Rotar vista
Trackpad Gesture to Rotate View Rotar vista con un gesto del trackpad
Rotate… Rotar…
Rotation Rotación
Roughen… Hacer áspero…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1203,6 +1203,7 @@ lighting Light Valo
Reshape Tool Uudelleenmuotoilutyökalu
Rotate Tool Kiertotyökalu
Rotate View Tool Näkymän kiertotyökalu
Trackpad Gesture to Rotate View Kierrä näkymää ohjauslevyn eleellä
Rotate View Kierrä näkymää
Rounded Rectangle Tool Pyöristetyn suorakulmion työkalu
Scale Tool Skaalaustyökalu
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2531,6 +2531,7 @@
Rotate 90° CW Rotation de 90° horaire
Rotate Tool Outil Rotation
Rotate View Tool Outil Faire pivoter l’affichage
Trackpad Gesture to Rotate View Faire pivoter l’affichage avec un geste du trackpad
Rotate… Rotation…
Rotation Rotation
Roughen… Rugosité…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -3095,6 +3095,7 @@ mark-style Japanese Japán
Rotate Tool Elforgatás
Rotate View Nézet elforgatása
Rotate View Tool Nézet elforgatása
Trackpad Gesture to Rotate View Nézet elforgatása kézmozdulattal az érintőpadon
Rotate… Elforgatás…
Rotation Elforgatás
Rotation relative to: Elforgatás viszonyítási alapja:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1152,6 +1152,7 @@ lighting Light Cahaya
Reshape Tool Alat pembentuk ulang
Rotate Tool Alat putar
Rotate View Tool Alat putar tampilan
Trackpad Gesture to Rotate View Putar tampilan dengan gestur trackpad
Rotate View Putar tampilan
Rounded Rectangle Tool Alat persegi panjang bulat
Scale Tool Alat skala
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1152
1153
1154
1155
1156
1157
1158
+1
View File
@@ -2551,6 +2551,7 @@ mark-style Japanese Giapponese
Rotate 90° CW Ruota di 90° in senso orario
Rotate Tool Strumento Ruota
Rotate View Tool Strumento Ruota vista
Trackpad Gesture to Rotate View Ruota vista con un gesto sul trackpad
Rotate… Ruota…
Rotation Rotazione
Roughen… Irruvidisci…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2482,6 +2482,7 @@ mark-style Japanese 日本式
Rotate 回転
Rotate Tool 回転ツール
Rotate View Tool 回転ビューツール
Trackpad Gesture to Rotate View トラックパッドのジェスチャーでビューを回転
Rotation 回転
Round Cap 丸型線端
Round Join ラウンド結合
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2476,6 +2476,7 @@ mark-style Japanese 일본식
Rotate 회전
Rotate Tool 회전 도구
Rotate View Tool 보기 회전 도구
Trackpad Gesture to Rotate View 트랙패드 제스처로 보기 회전
Rotation 회전
Round Cap 둥근 끝
Round Join 둥근 연결
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1152,6 +1152,7 @@ lighting Light Licht
Reshape Tool Hervormen-gereedschap
Rotate Tool Draaien-gereedschap
Rotate View Tool Weergave draaien-gereedschap
Trackpad Gesture to Rotate View Weergave draaien met trackpadgebaar
Rotate View Weergave draaien
Rounded Rectangle Tool Afgeronde rechthoek-gereedschap
Scale Tool Schalen-gereedschap
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1149,6 +1149,7 @@ lighting Light Światło
Reshape Tool Narzędzie Zmiana kształtu
Rotate Tool Narzędzie Obrót
Rotate View Tool Narzędzie Obrót widoku
Trackpad Gesture to Rotate View Obracaj widok gestem na gładziku
Rounded Rectangle Tool Narzędzie Zaokrąglony prostokąt
Scale Tool Narzędzie Skalowanie
Scallop Tool Narzędzie Ząbkowanie
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2094,6 +2094,7 @@
Rotate Girar
Rotate Tool Ferramenta Girar
Rotate View Tool Ferramenta Girar Visualização
Trackpad Gesture to Rotate View Girar Visualização com Gesto do Trackpad
Rotate… Girar…
Rotation Rotação
Roughen… Enrugar…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -2547,6 +2547,7 @@ mark-style Japanese Японский
Rotate 90° CW Повернуть на 90° по часовой стрелке
Rotate Tool Инструмент «Поворот»
Rotate View Tool Инструмент «Поворот вида»
Trackpad Gesture to Rotate View Поворачивать вид жестом трекпада
Rotate… Повернуть…
Rotation Поворот
Roughen… Огрубление…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2488,6 +2488,7 @@
Rotate 90° CW Rotera 90° medurs
Rotate Tool Rotationsverktyg
Rotate View Tool Verktyget Rotera vy
Trackpad Gesture to Rotate View Rotera vyn med en gest på styrplattan
Rotate… Rotera…
Rotation Rotation
Roughen… Ojämn…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -2543,6 +2543,7 @@ mark-style Japanese Японський
Rotate 90° CW Повернути на 90° за годинниковою стрілкою
Rotate Tool Інструмент «Поворот»
Rotate View Tool Інструмент «Поворот виду»
Trackpad Gesture to Rotate View Повертати вид жестом трекпада
Rotate… Повернути…
Rotation Поворот
Roughen… Надати нерівності…
Can't render this file because it contains an unexpected character in line 723 and column 40.
+1
View File
@@ -2834,6 +2834,7 @@ mark-style Japanese Kiểu Nhật
Rotate 90° CW Xoay 90° theo chiều kim đồng hồ
Rotate Tool Công cụ Xoay
Rotate View Tool Công cụ Xoay chế độ xem
Trackpad Gesture to Rotate View Xoay chế độ xem bằng cử chỉ trên bàn di chuột
Rotate… Xoay…
Rotation Góc xoay
Rotation relative to: Xoay tương đối với:
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
2834
2835
2836
2837
2838
2839
2840
+1
View File
@@ -2441,6 +2441,7 @@ mark-style Japanese 日式
Rotate 90° CW 顺时针旋转 90°
Rotate Tool 旋转工具
Rotate View Tool 旋转视图工具
Trackpad Gesture to Rotate View 使用触控板手势旋转视图
Rotate… 旋转…
Rotation 旋转
Roughen… 粗糙效果…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2525,6 +2525,7 @@ mark-style Japanese 日式
Rotate 90° CW 順時針旋轉 90°
Rotate Tool 旋轉工具
Rotate View Tool 旋轉檢視工具
Trackpad Gesture to Rotate View 使用觸控式軌跡板手勢旋轉檢視
Rotate… 旋轉…
Rotation 旋轉
Roughen… 粗糙效果…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1 -1
View File
@@ -22,7 +22,7 @@ on huge documents. Pen input is pressure-only and Windows-first.
| Pen pressure | yes (Wacom, Windows Ink, macOS tablet events) | **no** (winit reports none, #852) | yes (pointer force via `WM_POINTER`) | X11 only via XWayland (Preferences › Performance › Linux Display Server, #1151); none on Wayland (#491, #764) | browser pointer events, with coalesced events; Apple Pencil on iPad (hover, palm rejection: fingers navigate after a pen contact until reload) | partial: 4–8 h for macOS and Wayland (winit 0.31) |
| Pen tilt, bearing, barrel rotation (6D Art Pen) | yes, drives brushes | no | no | no | no | missing: 4–8 h after the input layer (#372) |
| Pen eraser end | yes | no | no | no | no | missing |
| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch and scroll | pinch, pan; two-finger tap undo, three-finger redo (#585) | same where winit reports touch | eframe until the first pen contact, then Pointer Events (pen and fingers told apart); long press opens the context menu | partial: touch workspace and rotate gesture missing, 3–5 h |
| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch, scroll and rotate | pinch, pan; two-finger tap undo, three-finger redo (#585) | same where winit reports touch | eframe until the first pen contact, then Pointer Events (pen and fingers told apart); long press opens the context menu | partial: touch workspace and touch-screen rotation missing, 3–5 h |
| Trackpad (smooth scroll, pinch) | yes | yes | precision touchpads | yes | yes | done |
| HiDPI / Retina, per-monitor scale | yes | yes | yes | yes | yes | done; a window dragged between monitors of different scale can still grow on Windows (#1459, open); the custom title bar's window on Windows stays truly borderless so the pointer lines up with what is drawn (#1791, #1758; awaiting the reporter's confirmation) |
| Multiple monitors: panels and document windows on another screen | yes | no (one OS window) | no | no | — | missing: 6–10 h (with multiple windows, gap G7) |
+3 -1
View File
@@ -2123,7 +2123,9 @@ The Selection & Anchor Display and General preferences apply to `pointer_gesture
The preferences for the view need the desktop app (`vectorcraft-cli mcp --connect`): `zoomWithMouseWheel` (the
wheel zooms about the pointer, Shift-wheel scrolls up and down, Cmd/Ctrl-wheel sideways; the control channel's
`ui.wheel` turns the wheel), `zoomToSelection` (on: Zoom In and Zoom Out centre the selection), `showToolTips`,
`ui.wheel` turns the wheel), `trackpadRotateView` (on by default: a two-finger rotation on a Mac trackpad, in the
macOS app or in Safari, turns the view about the pointer once it reaches 5°; with it off, the gesture does not turn
the view), `zoomToSelection` (on: Zoom In and Zoom Out centre the selection), `showToolTips`,
`anchorSize` (1–7), `gridColor`, `gridStyle`, `gridsInBack`, `guideColor`, `guideStyle`, `showPixelGrid` (on by
default: in View › Pixel Preview at 600% zoom and above, a line at every document pixel over the art),
`recentFontsCount`, `antiAliasedArtwork` (on by default; off, the canvas draws the art with hard edges — a pixel is
+1
View File
@@ -561,6 +561,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
View › Rotate View turns the view to the status bar's preset angles (`view.rotate {angle}`, which takes any angle). View › Rotate View to Selection turns the view to the angle of the selected objects' bounding box, so the box is square to the window (`view.rotateToSelection`).
When the view angle changes (a Rotate View drag, the status bar's angle menu, Reset Rotate View or a saved view) and the desktop app renders the frame at the new angle on the render worker, the canvas draws the last frame turned to the new angle until that frame is ready. It used to draw no art until then.
With the view rotated, a second Escape less than a second after the first resets the rotation, as Reset Rotate View does. The first Escape may end something else, such as isolation mode or a path being drawn. The second resets the view only when it has nothing else to end, and an Escape that closes a menu does not count. After a click, or a press of another key that is not a modifier (Shift, Control, Alt/Option or Command), the next Escape counts as the first. With the Rotate View tool chosen, one Escape resets the view.
A two-finger rotation on a Mac trackpad turns the view about the pointer once it reaches 5° (Preferences › General › Trackpad Gesture to Rotate View, on by default).
The status bar navigator's field shows the active artboard's number and name, and its drop-down lists every artboard by number and name, as the Artboards panel does. Choosing an artboard in the list makes it the active artboard and fits it in the window (`view.goToArtboard`). The list opens on the active artboard's row and lays out only the rows in view and at most one more.
- **Geometry and effects:**
- **Open-path anchor Delete:** Delete/Backspace removes selected anchors and their adjoining segments, leaving drawable runs as separate open subpaths of the original object. Surviving curves and appearance stay intact; one Undo restores the document and selection, and a second Delete removes the selected remainder. Explicit Remove Anchor Points still reconnects/refits. Four core regressions, one full-frame Delete/Backspace regression and three existing removal/segment controls pass, covering these distinctions, multiple deletions, transformed geometry and mixed whole-object selection. Behaviour references: the legacy public [Illustrator reference guide, Add or delete an anchor point](https://helpx.adobe.com/content/dam/help/en/pdf/illustrator_reference.pdf) distinguishes keyboard deletion from dedicated removal; the current [Add or remove anchor points](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-paths/add-or-remove-anchor-points.html) describes the dedicated tools. Other G11 gaps and scores/hours remain unchanged.