diff --git a/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index 1175779777..342feb568c 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 b70648cc9a..3828d9ba2d 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -1018,6 +1018,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 75596c0015..81fee3a079 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -3739,6 +3739,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 945dcf5d11..02ae0f4a09 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1481,6 +1481,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 9c29a3068b..7e3794e8a1 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3793,6 +3793,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 f674ecce29..f7011e23bf 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -3526,6 +3526,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 7e2a636d2a..4174537f87 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1020,6 +1020,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 ec8ca831b2..bde2fbee78 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -988,6 +988,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 fc2a9290b8..ff28d9dfc5 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1041,6 +1041,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 35e5c8b2b9..bb74188ed8 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -3731,6 +3731,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 82391dff60..09c9c35b0d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1036,6 +1036,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 2a6f9a84df..1afed73418 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -1632,6 +1632,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 50551e2063..af6f586880 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1317,6 +1317,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 a80ba4a759..e0ba31ecd5 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -3526,6 +3526,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 a61a82a0b2..ce0bc58ce5 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1050,6 +1050,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 0eab273ce5..1ecce4618a 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -1641,6 +1641,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 60b8da3c43..3d6425c373 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -3370,6 +3370,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 c94aa99e91..fb87488d64 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -1631,6 +1631,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 44c3fb209c..b3388fa82a 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -3540,6 +3540,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 5c4179c782..0729e35c79 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3670,6 +3670,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 2302dbcd5c..36f778dbd5 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -931,6 +931,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 6c60695805..442a9612d4 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1039,6 +1039,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 88d15715e7..86b9ba6580 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -1020,6 +1020,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 2c86a6cc50..ef8773c31b 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1012,6 +1012,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 732351cce6..0bccbd8cb6 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1044,6 +1044,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 5bc28166eb..4f084029db 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1207,6 +1207,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 10b313f84a..b04e65a65b 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -982,6 +982,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 2d4570d10c..be62fde8a7 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -1012,6 +1012,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 8eeb714eb4..efa4e95ac4 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 8e76cfed5d..20a2c58314 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 da66dc962d..2dd65a1b99 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -5549,6 +5549,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 146c10a013..3df453c354 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1160,6 +1160,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:**