diff --git a/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index d66ea18445..12e5a31dce 100644 --- a/crates/mcp/src/ui_catalog.rs +++ b/crates/mcp/src/ui_catalog.rs @@ -731,6 +731,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{key, value} | {values: {key: value…}}, tool?: id (default: the active tool) → the tool's options (`{}` reads them). The options a tool keeps (Liquify brush and tool options, Mirror & Cut, Puppet Warp, drawing tools…) last across tool switches and are saved with the preferences; another tool's are stored for when it is chosen", ), + ( + "tool.toggleTouchMode", + "Switch Selection and Drawing Modes", + "", + "{} from the Selection, Direct Selection, Group Selection, Magic Wand, Lasso or Perspective Selection tool, choose the drawing tool chosen last in this window (none yet: the Pencil tool); from any other tool, choose the Selection tool, remembering the tool left as the drawing tool unless it is the Hand, Rotate View, Zoom or Print Tiling tool → {tool}. A two-finger tap on the canvas runs it in the Touch workspace (Enable Touch Gestures on)", + ), ( "effect.dialog", "Effect…", diff --git a/crates/ui-egui/src/app/commands.rs b/crates/ui-egui/src/app/commands.rs index c418f1950c..1d2493385e 100644 --- a/crates/ui-egui/src/app/commands.rs +++ b/crates/ui-egui/src/app/commands.rs @@ -416,6 +416,10 @@ impl VectorcraftApp { self.pen_end.forget_tool(); self.held_tool.clear(); self.activate_tool(id, true); + let now = self.session.tool_id(); + if crate::touch::is_drawing_tool(now) { + self.ui.touch_drawing_tool = Some(now.to_string()); + } } /// Go back to tool `id` after a tool that stood in for it (the pen's eraser end, [`stylus`]): diff --git a/crates/ui-egui/src/canvas/tests.rs b/crates/ui-egui/src/canvas/tests.rs index 04a9f2e4c8..cfe8b02a03 100644 --- a/crates/ui-egui/src/canvas/tests.rs +++ b/crates/ui-egui/src/canvas/tests.rs @@ -966,6 +966,88 @@ fn two_finger_tap_undoes_and_three_finger_tap_redoes() { assert_eq!(count(&app), 2, "off: the first finger draws as the pointer, and nothing is undone"); } +/// In the Touch workspace a two-finger tap switches between the Selection tool and the drawing tool +/// chosen last, and undoes nothing; three fingers still redo. Outside it, two fingers undo again. +#[test] +fn two_finger_tap_in_touch_switches_selection_and_drawing_modes() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap(); + app.session.execute("shape.rectangle", &json!({"x": 50, "y": 50, "width": 40, "height": 40})).unwrap(); + crate::workspaces::switch(&mut app.ui, crate::workspaces::TOUCH).unwrap(); + app.select_tool("rectangle"); + let ctx = egui::Context::default(); + frame(&mut app, &ctx, vec![]); + let count = |app: &VectorcraftApp| app.session.active().unwrap().doc.layers[0].children().unwrap().len(); + 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 }; + let button = |pos, pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + let tap = |app: &mut VectorcraftApp, fingers: u64| { + let at = |n: u64| c + vec2(n as f32 * 50.0, 0.0); + frame(app, &ctx, vec![touch(0, egui::TouchPhase::Start, at(0)), egui::Event::PointerMoved(at(0)), button(at(0), true)]); + frame(app, &ctx, vec![touch(0, egui::TouchPhase::Move, at(0) + vec2(6.0, 6.0)), egui::Event::PointerMoved(at(0) + vec2(6.0, 6.0))]); + frame(app, &ctx, (1..fingers).map(|n| touch(n, egui::TouchPhase::Start, at(n))).collect()); + frame(app, &ctx, vec![touch(0, egui::TouchPhase::End, at(0)), button(at(0) + vec2(6.0, 6.0), false), egui::Event::PointerGone]); + frame(app, &ctx, (1..fingers).map(|n| touch(n, egui::TouchPhase::End, at(n))).collect()); + frame(app, &ctx, vec![]); + }; + tap(&mut app, 2); + assert_eq!((app.session.tool_id(), count(&app)), ("selection", 1), "selection mode, nothing undone or drawn"); + tap(&mut app, 2); + assert_eq!((app.session.tool_id(), count(&app)), ("rectangle", 1), "back to the drawing tool"); + // A tool chosen by hand becomes the drawing tool a switch returns to. + app.select_tool("ellipse"); + tap(&mut app, 2); + tap(&mut app, 2); + assert_eq!((app.session.tool_id(), count(&app)), ("ellipse", 1)); + // The switches added no undo step: undo still takes the rectangle away, and three fingers redo it. + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(count(&app), 0); + tap(&mut app, 3); + assert_eq!(count(&app), 1, "three fingers redo in Touch too"); + // Presentation Mode runs neither. + app.ui.screen_mode = 3; + frame(&mut app, &ctx, vec![]); + tap(&mut app, 2); + assert_eq!(app.session.tool_id(), "ellipse", "Presentation Mode: no switch"); + app.ui.screen_mode = 0; + // Enable Touch Gestures off: no switch. + app.session.prefs.touch_gestures = false; + app.select_tool("selection"); + frame(&mut app, &ctx, vec![]); + tap(&mut app, 2); + assert_eq!(app.session.tool_id(), "selection", "off: no switch"); + app.session.prefs.touch_gestures = true; + // Another workspace: two fingers undo again and switch no tool. + crate::workspaces::switch(&mut app.ui, crate::workspaces::ESSENTIALS).unwrap(); + app.select_tool("rectangle"); + frame(&mut app, &ctx, vec![]); + let before = count(&app); + tap(&mut app, 2); + assert_eq!(app.session.tool_id(), "rectangle"); + assert_eq!(count(&app), before - 1, "Essentials: undone"); +} + +/// Switch Selection and Drawing Modes from the command: Pencil when no drawing tool was chosen yet, +/// a view tool is left for Selection without becoming the drawing tool, and every selection tool +/// switches to drawing mode. +#[test] +fn switch_selection_and_drawing_modes_command() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap(); + assert!(app.ui.touch_drawing_tool.is_none()); + app.select_tool("selection"); + let r = app.run(crate::touch::TOGGLE_MODE, json!({})).unwrap(); + assert_eq!((r, app.session.tool_id()), (json!({"tool": "pencil"}), "pencil"), "no drawing tool chosen yet: Pencil"); + app.select_tool("hand"); + assert_eq!(app.run(crate::touch::TOGGLE_MODE, json!({})).unwrap(), json!({"tool": "selection"})); + assert_eq!(app.run(crate::touch::TOGGLE_MODE, json!({})).unwrap(), json!({"tool": "pencil"}), "the Hand tool is no drawing tool"); + app.select_tool("pen"); + for id in ["directSelection", "groupSelection", "magicWand", "lasso"] { + app.select_tool(id); + assert_eq!(app.run(crate::touch::TOGGLE_MODE, json!({})).unwrap(), json!({"tool": "pen"}), "{id}"); + } +} + /// The Artboard tool's label holds the artboard's name (never translated); the tools' own /// labels are interface text. #[test] diff --git a/crates/ui-egui/src/canvas/view.rs b/crates/ui-egui/src/canvas/view.rs index dae1060502..0304e28c17 100644 --- a/crates/ui-egui/src/canvas/view.rs +++ b/crates/ui-egui/src/canvas/view.rs @@ -532,7 +532,8 @@ pub(super) fn wheel( /// Enable Touch Gestures: this frame's finger contacts on the canvas ([`crate::touch`]). A second /// finger drops the press the first one began (its tool interaction rolled back; the rest of the -/// gesture presses nothing), and a tap of two fingers undoes, one of three redoes. +/// gesture presses nothing), and a tap of two fingers undoes, one of three redoes. In the Touch +/// workspace a tap of two fingers switches between selection and drawing modes instead. pub(super) fn touch_gestures(app: &mut VectorcraftApp, ui: &Ui, rect: egui::Rect, view: vectorcraft_engine::ViewInfo) { if !app.session.prefs.touch_gestures { return; @@ -564,12 +565,12 @@ pub(super) fn touch_gestures(app: &mut VectorcraftApp, ui: &Ui, rect: egui::Rect drop_press(app, ui, view); ui.data_mut(|d| d.insert_temp(drag_id(), Drag::Gesture)); } - Some(crate::touch::Touch::Tap(n)) => tap = crate::touch::tap_command(n), + Some(crate::touch::Touch::Tap(n)) => tap = crate::touch::tap_command(n, app.ui.workspace == crate::workspaces::TOUCH), None => {} } } ui.data_mut(|d| d.insert_temp(key, taps)); - // In Presentation Mode a tap of two or three fingers undoes or redoes nothing. + // In Presentation Mode a tap of two or three fingers runs nothing. if let Some(cmd) = tap && app.ui.screen_mode < 3 { diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 8edde76eb3..5859da1fdc 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -1015,6 +1015,7 @@ font-technology Variable Променливи Enable Rubber Band for Разрешаване на ластик за Enable text access for screen readers Разрешаване на достъп до текст за екранни четци Enable Touch Gestures Разрешаване на докосващи жестове + Switch Selection and Drawing Modes Превключване между режим на селекция и рисуване Enable Touch Workspace Разрешаване на работно пространство за докосване Encoding Кодиране Encoding: Кодиране: diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index c9c207eaa2..19e9c66f5d 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -3732,6 +3732,7 @@ theme Appearance Izgled Enable Missing Glyph Protection Omogući zaštitu od nedostajućih glifova Enable Rubber Band for Omogući gumenu traku za Enable Touch Gestures Omogući pokrete dodirom + Switch Selection and Drawing Modes Prebaci između načina odabira i crtanja Enable Touch Workspace Omogući radni prostor za dodir English: UK Engleski: UK Every (minutes) Svakih (minuta) diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 0399e6c04a..7015eb9009 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1478,6 +1478,7 @@ antiAlias Sharp Nítid Enable Rubber Band for Activa la goma elàstica per a Enable text access for screen readers Activa l’accés al text per als lectors de pantalla Enable Touch Gestures Activa els gestos tàctils + Switch Selection and Drawing Modes Canvia entre els modes de selecció i de dibuix Enable Touch Workspace Activa l’espai de treball tàctil Encoding Codificació Encoding and writing the file… S’està codificant i escrivint el fitxer… diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index e62070329c..c473ff570b 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3786,6 +3786,7 @@ theme Appearance Motiv Enable Missing Glyph Protection Povolit ochranu chybějících glyfů Enable Rubber Band for Povolit gumovou čáru pro Enable Touch Gestures Povolit dotyková gesta + Switch Selection and Drawing Modes Přepnout mezi režimem výběru a kreslení Enable Touch Workspace Povolit dotykový pracovní prostor English: UK Angličtina: Spojené království Every (minutes) Každých (minut) diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 47a78ef612..5eb2e59ee5 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -3519,6 +3519,7 @@ theme Appearance Udseende Enable Missing Glyph Protection Aktivér beskyttelse mod manglende glyffer Enable Rubber Band for Aktivér gummibånd for Enable Touch Gestures Aktivér berøringsbevægelser + Switch Selection and Drawing Modes Skift mellem markerings- og tegnetilstand Enable Touch Workspace Aktivér touch-arbejdsområde English: UK Engelsk: UK Every (minutes) Hvert (minut) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index af4489a637..e10996ca88 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1017,6 +1017,7 @@ font-technology Variable Variabel Enable Rubber Band for Gummiband aktivieren für Enable text access for screen readers Textzugriff für Bildschirmlesegeräte zulassen Enable Touch Gestures Touch-Gesten aktivieren + Switch Selection and Drawing Modes Zwischen Auswahl- und Zeichenmodus wechseln Enable Touch Workspace Touch-Arbeitsbereich aktivieren Encoding Kodierung Encoding: Kodierung: diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 6ad6ff11c0..c55732bb3a 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -985,6 +985,7 @@ font-technology Variable Μεταβλητές Enable Rubber Band for Ενεργοποίηση δυναμικής προεπισκόπησης γραμμής για Enable text access for screen readers Ενεργοποίηση πρόσβασης κειμένου για αναγνώστες οθόνης Enable Touch Gestures Ενεργοποίηση χειρονομιών αφής + Switch Selection and Drawing Modes Εναλλαγή λειτουργιών επιλογής και σχεδίασης Enable Touch Workspace Ενεργοποίηση χώρου εργασίας αφής Encoding Κωδικοποίηση Encoding: Κωδικοποίηση: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 41f92e14f7..d9db5c5856 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1038,6 +1038,7 @@ font-technology Variable Variables Enable Rubber Band for Activar banda elástica para Enable text access for screen readers Permitir el acceso al texto para lectores de pantalla Enable Touch Gestures Activar gestos táctiles + Switch Selection and Drawing Modes Cambiar entre modos de selección y dibujo Enable Touch Workspace Activar espacio de trabajo táctil Encoding Codificación Encoding: Codificación: diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index fd38382f63..6e1a01f775 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -3724,6 +3724,7 @@ theme Appearance Ulkoasu Enable Missing Glyph Protection Ota puuttuvien merkkien suojaus käyttöön Enable Rubber Band for Ota kumilankavalinta käyttöön kohteelle Enable Touch Gestures Ota kosketuseleet käyttöön + Switch Selection and Drawing Modes Vaihda valinta- ja piirtotilan välillä Enable Touch Workspace Ota kosketustyötila käyttöön English: UK Englanti: UK Every (minutes) Välein (minuuttia) diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index dcbe436fc3..34d42d353b 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1033,6 +1033,7 @@ font-technology Variable Variables Enable Rubber Band for Activer l’élastique pour Enable text access for screen readers Autoriser l’accès au texte pour les lecteurs d’écran Enable Touch Gestures Activer les gestes tactiles + Switch Selection and Drawing Modes Basculer entre les modes sélection et dessin Enable Touch Workspace Activer l’espace de travail tactile Encoding Codage Encoding: Codage : diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 9b7a15bbad..f9a563fe66 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -1629,6 +1629,7 @@ antiAlias Sharp शार्प Enable Rubber Band for के लिए रबर बैंड सक्षम करें Enable text access for screen readers स्क्रीन रीडर के लिए टेक्स्ट एक्सेस सक्षम करें Enable Touch Gestures टच जेस्चर ऑन करें + Switch Selection and Drawing Modes चयन और ड्रॉइंग मोड के बीच बदलें Enable Touch Workspace टच वर्कस्पेस सक्षम करें Enclosing Lasso लासो संलग्न करना Enclosing Lasso Tool लासो टूल संलग्न करना diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 6832a86707..9e9b48602e 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1314,6 +1314,7 @@ font-technology Variable Változó Enable Rubber Band for Gumiszalag-előnézet ehhez: Enable text access for screen readers Szöveghozzáférés engedélyezése képernyőolvasóknak Enable Touch Gestures Érintési kézmozdulatok + Switch Selection and Drawing Modes Váltás a kijelölési és a rajzolási mód között Enable Touch Workspace Érintőképernyős munkaterület Encoding Kódolás Encoding: Kódolás: diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 45e05b9622..17b852af67 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -3519,6 +3519,7 @@ theme Appearance Tampilan Enable Missing Glyph Protection Aktifkan perlindungan glyph yang hilang Enable Rubber Band for Aktifkan rubber band untuk Enable Touch Gestures Aktifkan gestur sentuh + Switch Selection and Drawing Modes Beralih antara mode seleksi dan menggambar Enable Touch Workspace Aktifkan ruang kerja sentuh English: UK Inggris: UK Every (minutes) Setiap (menit) diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 57f141f880..cf8d7489f0 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1047,6 +1047,7 @@ font-technology Variable Variabili Enable Rubber Band for Attiva elastico per Enable text access for screen readers Consenti l'accesso al testo per le utilità di lettura dello schermo Enable Touch Gestures Attiva gesti touch + Switch Selection and Drawing Modes Passa tra modalità selezione e disegno Enable Touch Workspace Attiva area di lavoro touch Encoding Codifica Encoding: Codifica: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 69b55c9121..ea9b587940 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1635,6 +1635,7 @@ antiAlias Sharp シャープ Enable Opacity Mask 不透明マスクを有効にする Enable Rubber Band for ラバーバンドを有効にする対象 Enable Touch Gestures タッチジェスチャーを有効にする + Switch Selection and Drawing Modes 選択モードと描画モードを切り替え Enable Touch Workspace タッチワークスペースを有効にする Enable copying of text, images and other content テキスト、イメージ、その他のコンテンツのコピーを許可 Enable in-menu font previews メニュー内でフォントプレビューを有効にする diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 1cbb2d1bf1..a4cf6a3ff7 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -3363,6 +3363,7 @@ theme Appearance იერსახე Enable Missing Glyph Protection გამოტოვებული გლიფებისგან დაცვის ჩართვა Enable Rubber Band for რეზინის ზოლის ჩართვა: Enable Touch Gestures შეხების ჟესტების ჩართვა + Switch Selection and Drawing Modes მონიშვნისა და ხატვის რეჟიმების გადართვა Enable Touch Workspace სენსორული სამუშაო სივრცის ჩართვა English: UK ინგლისური: ბრიტანეთი Every (minutes) ყოველ (წუთში) diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 6135f8d5ec..ad6b72b74f 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -1625,6 +1625,7 @@ antiAlias Sharp 날카롭게 Enable Opacity Mask 불투명 마스크 사용 Enable Rubber Band for 러버 밴드 사용 대상 Enable Touch Gestures 터치 제스처 사용 + Switch Selection and Drawing Modes 선택 모드와 그리기 모드 전환 Enable Touch Workspace 터치 작업 영역 사용 Enable copying of text, images and other content 텍스트, 이미지 및 기타 콘텐츠 복사 허용 Enable in-menu font previews 메뉴에서 글꼴 미리 보기 사용 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 327aee902c..d5c570175d 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -3533,6 +3533,7 @@ theme Appearance Vormgeving Enable Missing Glyph Protection Beveiliging tegen ontbrekende glyphs inschakelen Enable Rubber Band for Rubberband inschakelen voor Enable Touch Gestures Touchgebaren inschakelen + Switch Selection and Drawing Modes Wisselen tussen selectie- en tekenmodus Enable Touch Workspace Touch-werkruimte inschakelen English: UK Engels: VK Every (minutes) Elke (minuten) diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index f277d51d65..1b04841927 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3663,6 +3663,7 @@ theme Appearance Wygląd Enable Missing Glyph Protection Włącz ochronę przed brakującymi glifami Enable Rubber Band for Włącz gumkę podglądu dla Enable Touch Gestures Włącz gesty dotykowe + Switch Selection and Drawing Modes Przełącz tryby zaznaczania i rysowania Enable Touch Workspace Włącz dotykowy obszar roboczy English: UK Angielski: Wielka Brytania Every (minutes) Co (minuty) diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index afd10a5fbc..770c271250 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -929,6 +929,7 @@ antiAlias Sharp Nítido Enable Opacity Mask Habilitar Máscara de Opacidade Enable Rubber Band for Habilitar Elástico para Enable Touch Gestures Habilitar Gestos de Toque + Switch Selection and Drawing Modes Alternar Modos de Seleção e Desenho Enable Touch Workspace Habilitar Espaço de Trabalho de Toque Enable copying of text, images and other content Habilitar cópia de texto, imagens e outro conteúdo Enable in-menu font previews Habilitar visualizações de fonte no menu diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 2dca232079..61fba56e56 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1036,6 +1036,7 @@ font-technology Variable Вариативные Enable Rubber Band for Включить «резиновую полосу» для Enable text access for screen readers Разрешить доступ к тексту для программ чтения с экрана Enable Touch Gestures Включить сенсорные жесты + Switch Selection and Drawing Modes Переключить режимы выделения и рисования Enable Touch Workspace Включить сенсорную рабочую среду Encoding Кодировка Encoding: Кодировка: diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index e47c4e8662..79b5032e1e 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -1017,6 +1017,7 @@ font-technology Variable Variabla Enable Rubber Band for Aktivera gummiband för Enable text access for screen readers Ge skärmläsare åtkomst till text Enable Touch Gestures Aktivera pekgester + Switch Selection and Drawing Modes Växla mellan markerings- och ritläge Enable Touch Workspace Aktivera arbetslayout för pekskärm Encoding Kodning Encoding: Kodning: diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 5983ec4bfe..3f8d2d82a9 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1009,6 +1009,7 @@ font-technology Variable Değişken Enable Rubber Band for Lastik Bandı Etkinleştir Enable text access for screen readers Ekran okuyucular için metin erişimini etkinleştirin Enable Touch Gestures Dokunma Hareketlerini Etkinleştir + Switch Selection and Drawing Modes Seçim ve Çizim Modları Arasında Geçiş Yap Enable Touch Workspace Dokunmatik Çalışma Alanını Etkinleştir Encoding Kodlama Encoding: Kodlama: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 4c9326af72..c5c50b5cc8 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1041,6 +1041,7 @@ font-technology Variable Варіативні Enable Rubber Band for Включити «гумову смугу» для Enable text access for screen readers Дозволити доступ до тексту для програм читання з екрану Enable Touch Gestures Увімкнути сенсорні жести + Switch Selection and Drawing Modes Перемкнути режими виділення та малювання Enable Touch Workspace Включити сенсорне робоче середовище Encoding Кодування Encoding: Кодування: diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index fef0b8ff90..bf559ddb3b 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1204,6 +1204,7 @@ font-technology Variable Biến thiên Enable Rubber Band for Bật vùng chọn kéo cho Enable text access for screen readers Bật truy cập văn bản cho trình đọc màn hình Enable Touch Gestures Bật cử chỉ cảm ứng + Switch Selection and Drawing Modes Chuyển giữa chế độ chọn và vẽ Enable Touch Workspace Bật không gian làm việc cảm ứng Encoding Mã hóa Encoding: Mã hóa: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index a0d941ada8..8c5b8cc3ff 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -979,6 +979,7 @@ antiAlias Sharp 锐利 Enable Rubber Band for 为这些工具启用橡皮筋预览 Enable text access for screen readers 启用屏幕阅读器的文字访问 Enable Touch Gestures 启用触控手势 + Switch Selection and Drawing Modes 切换选择与绘图模式 Enable Touch Workspace 启用触控工作区 Encoding 编码 Encoding: 编码: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index b724e57351..d06caf147a 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -1009,6 +1009,7 @@ font-technology Variable 可變 Enable Rubber Band for 啟用橡皮筋預覽,適用於 Enable text access for screen readers 啟用螢幕閱讀器的文字存取 Enable Touch Gestures 啟用觸控手勢 + Switch Selection and Drawing Modes 切換選取與繪圖模式 Enable Touch Workspace 啟用觸控工作區 Encoding 編碼 Encoding: 編碼: diff --git a/crates/ui-egui/src/menus/run.rs b/crates/ui-egui/src/menus/run.rs index 964be00d61..54315665bf 100644 --- a/crates/ui-egui/src/menus/run.rs +++ b/crates/ui-egui/src/menus/run.rs @@ -684,6 +684,7 @@ fn run_ui_part6(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option Err(format!("unknown tool `{t}`")), None => Err("missing `tool`".into()), }, + crate::touch::TOGGLE_MODE => Ok(crate::touch::toggle_mode(app)), crate::addons::pathtools::corner::COMMAND => crate::addons::pathtools::corner::open(app), crate::addons::pathtools::shared_edge::COMMAND => crate::addons::pathtools::shared_edge::open(app), crate::addons::arrange::ZOOM => crate::addons::arrange::zoom(app), diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 08c1bbb4c6..e85818083e 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -732,6 +732,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "", "{key, value} | {values: {key: value…}}, tool?: id (default: the active tool) → the tool's options (`{}` reads them). The options a tool keeps (Liquify brush and tool options, Mirror & Cut, Puppet Warp, drawing tools…) last across tool switches and are saved with the preferences; another tool's are stored for when it is chosen", ), + ( + crate::touch::TOGGLE_MODE, + "Switch Selection and Drawing Modes", + "", + "{} from the Selection, Direct Selection, Group Selection, Magic Wand, Lasso or Perspective Selection tool, choose the drawing tool chosen last in this window (none yet: the Pencil tool); from any other tool, choose the Selection tool, remembering the tool left as the drawing tool unless it is the Hand, Rotate View, Zoom or Print Tiling tool → {tool}. A two-finger tap on the canvas runs it in the Touch workspace (Enable Touch Gestures on)", + ), ( "effect.dialog", "Effect…", diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index f2728fe994..f84b8f53b0 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -565,6 +565,10 @@ pub struct UiState { pub flyout: Option, /// Last tool shown for each toolbar group (flyout selection sticks). pub group_tool: Vec, + /// The drawing tool last chosen in this window, which a two-finger tap in the Touch workspace + /// returns to from the Selection tool ([`crate::touch::toggle_mode`]). Not saved. + #[serde(skip)] + pub touch_drawing_tool: Option, pub status: String, /// The Replace Font request behind the status bar's warning ([`ReplaceFontWarning`]). Not saved. #[serde(skip)] @@ -1129,6 +1133,7 @@ impl Default for UiState { answering_control: false, flyout: None, group_tool: vectorcraft_tools::TOOL_GROUPS.iter().map(|g| g[0].id.to_string()).collect(), + touch_drawing_tool: None, status: String::new(), replace_font_warning: None, status_notes: None, diff --git a/crates/ui-egui/src/touch.rs b/crates/ui-egui/src/touch.rs index 247a6c5f0a..7cd1a4ad76 100644 --- a/crates/ui-egui/src/touch.rs +++ b/crates/ui-egui/src/touch.rs @@ -1,5 +1,6 @@ //! Touch gestures on the canvas (Preferences › Devices › Enable Touch Gestures, #585): a tap with -//! two fingers undoes, one with three redoes. +//! two fingers undoes, one with three redoes. In the Touch workspace a tap with two fingers +//! switches between selection and drawing modes instead ([`TOGGLE_MODE`]). //! //! egui hands every finger's contact over as [`egui::Event::Touch`] (winit reads them from the //! system: `WM_POINTER` on Windows), and makes the first finger the mouse pointer too. So the first @@ -121,15 +122,52 @@ impl Taps { } } -/// The command a tap of `fingers` runs: two undo, three redo. -pub(crate) fn tap_command(fingers: usize) -> Option<&'static str> { +/// The command a tap of `fingers` runs: two undo, three redo. In the Touch workspace (`touch_workspace`) +/// two switch between the Selection tool and the last drawing tool ([`TOGGLE_MODE`]). +pub(crate) fn tap_command(fingers: usize, touch_workspace: bool) -> Option<&'static str> { match fingers { + 2 if touch_workspace => Some(TOGGLE_MODE), 2 => Some("edit.undo"), 3 => Some("edit.redo"), _ => None, } } +/// Switch between selection and drawing modes: the Selection tool, and back to the drawing tool +/// chosen last. +pub const TOGGLE_MODE: &str = "tool.toggleTouchMode"; + +/// The drawing tool a switch out of selection mode chooses when none has been chosen in this window. +pub const FIRST_DRAWING_TOOL: &str = "pencil"; + +/// A tool of selection mode: one that selects objects or parts of them. +fn selects(id: &str) -> bool { + vectorcraft_tools::catalog::is_selection_tool(id) || matches!(id, "magicWand" | "lasso" | "perspectiveSelection") +} + +/// A tool of drawing mode: any tool but those of selection mode and those that only move, turn or +/// zoom the view. +pub(crate) fn is_drawing_tool(id: &str) -> bool { + !selects(id) && !matches!(id, "hand" | "rotateView" | "zoom" | "printTiling") +} + +/// [`TOGGLE_MODE`]: from a tool of selection mode, choose the drawing tool chosen last (none yet: +/// [`FIRST_DRAWING_TOOL`]); from any other tool, keep it as the drawing tool and choose the +/// Selection tool. → `{tool}`, the tool now active. +pub(crate) fn toggle_mode(app: &mut crate::VectorcraftApp) -> serde_json::Value { + let now = app.session.tool_id().to_string(); + let next = if selects(&now) { + app.ui.touch_drawing_tool.clone().unwrap_or_else(|| FIRST_DRAWING_TOOL.to_string()) + } else { + if is_drawing_tool(&now) { + app.ui.touch_drawing_tool = Some(now); + } + "selection".to_string() + }; + app.select_tool(&next); + serde_json::json!({"tool": app.session.tool_id()}) +} + #[cfg(test)] mod tests { use super::*; @@ -155,7 +193,20 @@ mod tests { } let ends: Vec<_> = [4, 5, 6].into_iter().map(|n| t.feed(id(n), TouchPhase::End, p, 1.2)).collect(); assert_eq!(ends, [None, None, Some(Touch::Tap(3))]); - assert_eq!((tap_command(2), tap_command(3), tap_command(1)), (Some("edit.undo"), Some("edit.redo"), None)); + assert_eq!((tap_command(2, false), tap_command(3, false), tap_command(1, false)), (Some("edit.undo"), Some("edit.redo"), None)); + assert_eq!((tap_command(2, true), tap_command(3, true), tap_command(1, true)), (Some(TOGGLE_MODE), Some("edit.redo"), None)); + } + + #[test] + fn drawing_tools_are_neither_selection_nor_view_tools() { + for id in ["pencil", "pen", "rectangle", "paintbrush", "type", "eraser", "shapeBuilder"] { + assert!(is_drawing_tool(id), "{id}"); + } + let selecting = ["selection", "directSelection", "groupSelection", "magicWand", "lasso", "perspectiveSelection"]; + for id in selecting.into_iter().chain(["hand", "rotateView", "zoom", "printTiling"]) { + assert!(!is_drawing_tool(id), "{id}"); + } + assert!(vectorcraft_tools::tool_info(FIRST_DRAWING_TOOL).is_some_and(|t| is_drawing_tool(t.id))); } #[test] diff --git a/docs/hardware-parity.md b/docs/hardware-parity.md index 31a218eb6c..38adb78090 100644 --- a/docs/hardware-parity.md +++ b/docs/hardware-parity.md @@ -24,7 +24,7 @@ It reads the pen's tilt and barrel rotation on macOS and on Linux under X11 and | Pen pressure | yes (Wacom, Windows Ink, macOS tablet events) | yes, read from AppKit's tablet events before the window gets them (winit reports none), each move of a stroke with its own pressure, mouse event coalescing off (#852; not yet tried on a Mac) | yes (pointer force via `WM_POINTER`) | under X11 and XWayland (Preferences › Performance › Linux Display Server, #1151, #2028; Automatic uses XWayland when a pen is attached on a Wayland session, #4967), read from XInput2 raw events (#764); **no** on native Wayland, where winit reports none and the pen may not reach the window (#491) | browser pointer events, with coalesced events; Apple Pencil on iPad (hover, palm rejection: fingers navigate after a pen contact until reload) | partial: 2–4 h for Wayland (winit 0.31), plus a check on a Mac | | Pen tilt, bearing, barrel rotation (6D Art Pen) | yes, drives brushes | read from AppKit's tablet events (tilt, and the barrel rotation of a pen that reports one); Calligraphic and Scatter brushes vary with them (#764; not yet tried on a Mac) | with Windows Ink, read from the pen's `POINTER_PEN_INFO` (tilt and barrel rotation where its pen mask says the pen reports them); Calligraphic and Scatter brushes vary with them (#372; not yet tried with a pen) | under X11 and XWayland, read from XInput2 raw events; Calligraphic and Scatter brushes vary with them (#764); **no** on native Wayland | no | partial: native Wayland and the web read none, and Pattern and Bristle brushes do not use them (an Art brush's Width follows them); 3–6 h (#372) | | Pen eraser end | yes | the eraser end (AppKit's tablet proximity events) selects the Eraser tool and the tip brings back the tool used before, as on Linux (#852; not yet tried on a Mac) | with Windows Ink, an inverted pen or its eraser button (`PEN_FLAG_INVERTED`, `PEN_FLAG_ERASER` in the pen's pointer messages) selects the Eraser tool and the tip brings back the tool used before, as on Linux (not yet tried with a pen) | under X11 and XWayland, the eraser end selects the Eraser tool and the tip brings back the tool used before (#764); **no** on native Wayland | a press of the eraser end (Pointer Events eraser button, `button` 5) selects the Eraser tool, and that press erases; a press of the tip brings back the tool used before; Pointer Events report no end while the pen hovers | partial | -| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch, scroll and rotate | pinch, pan (a second finger cancels the first finger's tool press, #4198); 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 | +| Touch gestures (pinch zoom, two-finger pan, rotate view, Touch workspace) | yes (Windows touch, Surface) | trackpad pinch, scroll and rotate | pinch, pan (a second finger cancels the first finger's tool press, #4198); two-finger tap undo, three-finger redo (#585); in the Touch workspace a two-finger tap switches between the Selection tool and the last drawing tool | 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) ; macOS cursors use aligned 1×/2× native image representations (#2292), covered by headless construction tests; mixed-display transitions and physical cursor sizing remain unverified, no additional hardware validated | | 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) | diff --git a/docs/mcp.md b/docs/mcp.md index 20e37222c6..eb600c8109 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -5459,6 +5459,15 @@ point deltas from an unnamed source in the desktop app, or line deltas that are trackpad gesture holds Alt, it zooms until it ends, also after Alt is let go. The control channel's `ui.wheel` with `cmd: true` turns the wheel with Cmd/Ctrl held. +With Preferences › Devices › Enable Touch Gestures on, a two-finger tap on the canvas undoes and a three-finger tap +redoes. In the Touch workspace a two-finger tap runs `tool.toggleTouchMode` (Switch Selection and Drawing Modes, `{}`, +desktop app) instead and undoes nothing ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/touch-workspace-in-illustrator.html)). +From the Selection, Direct Selection, Group Selection, Magic Wand, Lasso or Perspective Selection tool it chooses the +drawing tool chosen last in the window, or the Pencil tool when none has been chosen yet; from any other tool it +chooses the Selection tool, and the tool left becomes the drawing tool unless it is the Hand, Rotate View, Zoom or Print +Tiling tool. It returns `{tool}` and adds no undo step. Pencil as the first drawing tool and the list of selection tools +are VectorCraft's own rules. A tap in Presentation Mode runs nothing. + `gridlineEvery` (72 pt by default) and `gridSubdivisions` (8 by default) set the document grid that View › Show Grid draws, and View › Snap to Grid snaps to its subdivisions. Changing either sets the grid of every open document. `file.new` gives a new document that grid, and `document.open`, `file.newFromTemplate` and `file.revert` give it to diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 30b1e883d7..6b64ab0ae5 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1149,6 +1149,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur With the view rotated, an Escape that has nothing else to end resets the rotation, as Reset Rotate View does, with or without a selection, which stays selected. An Escape that ends something else ends only that, and the next Escape resets the view: to reset the view while another tool or feature is in use, Escape is pressed twice ([Rotate canvas view](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/start-a-new-file/rotate-canvas-view.html)). Under VectorCraft's own rule, the things an Escape ends first are isolation mode, pattern editing, Global Edit, a tool flyout, a full screen mode, Presentation Mode, a path being drawn with the Pen, Smart Pen or Curvature tool, the Blend tool's first pick, the Artboard tool, a tool's drag, a menu, a dialog, a text field and a drag of the ruler origin, and the second press may come any time later, after clicks or other keys. With the Rotate View tool, the view resets before a full screen mode ends; with another tool, Escape leaves a full screen mode first, and Shift+Escape, which does not leave it, resets the view. An Escape does not reset the view while About, a progress window or an Appearance row's popup is open, while a pointer button is held, or in the frame a button is let go. It used to reset the view only as the second of two Escape presses less than a second apart. 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). On a touch screen a second finger cancels the tool press the first finger began, in the frame it lands or later: the Pencil's stroke or a Selection move is rolled back with no undo entry, and a finger that lands while another is down presses nothing, so a pinch or two-finger pan only zooms and pans (#4198; canvas test `a_second_finger_cancels_the_tool_gesture_the_first_began`). + In the Touch workspace a two-finger tap switches between the Selection tool and the drawing tool chosen last (the Pencil tool when none has been chosen yet) and undoes nothing; outside Touch it still undoes, and a three-finger tap still redoes ([the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/touch-workspace-in-illustrator.html); `tool.toggleTouchMode`; canvas test `two_finger_tap_in_touch_switches_selection_and_drawing_modes`). A tool press dropped by a second finger, a long touch or a canceled touch cancels the tool's drag as Escape does: the tool gets back the options it had when the press began (the Polygon tool's sides, set with ↑ and ↓) and they are not stored, a press held still opens no size dialog, a Selection or Artboard tool marquee selects nothing, and the Pen goes on drawing its path without the dropped anchor (canvas tests `a_canceled_touch_drops_the_drag_without_keeping_its_options` and `long_touch_drops_a_tool_press_without_committing_art`; engine tests in `tests::toolsettings`). 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:**