diff --git a/crates/engine/src/tests_draw2.rs b/crates/engine/src/tests_draw2.rs index f712891d5..ffb72a346 100644 --- a/crates/engine/src/tests_draw2.rs +++ b/crates/engine/src/tests_draw2.rs @@ -2771,3 +2771,29 @@ fn pencil_redraws_a_selected_compound_path() { assert_eq!(s.doc().unwrap().selection.objects, compound); assert!(members(&s)[1] != before[1], "the hole is redrawn"); } + +/// With the Pencil's Live preview off, nothing is added to the document while a filled stroke is +/// drawn; the release draws the path with its fill, as one undo step. +#[test] +fn pencil_with_live_preview_off_draws_the_filled_path_on_release() { + let mut s = session(); + s.execute("select.none", &json!({})).unwrap(); + s.execute("paint.setFill", &json!({"color": "#ff0000"})).unwrap(); + s.set_tool_option_cmd(&json!({"tool": "pencil", "values": {"livePreview": false, "fill": true}})).unwrap(); + assert_eq!(s.tool_options_of("pencil")["livePreview"], json!(false)); + let v = view(); + s.select_tool("pencil", v).unwrap(); + let count = |s: &Session| s.doc().unwrap().doc.layers[0].children().map_or(0, |c| c.len()); + let (before, undo) = (count(&s), s.doc().unwrap().history.undo.len()); + s.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 100.0), v).unwrap(); + for (x, y) in [(160.0, 110.0), (190.0, 170.0), (120.0, 190.0), (105.0, 120.0)] { + s.pointer(&PointerEvent::new(PointerKind::Drag, x, y), v).unwrap(); + assert_eq!(count(&s), before, "nothing drawn while the stroke is drawn"); + } + s.pointer(&PointerEvent::new(PointerKind::Up, 105.0, 120.0), v).unwrap(); + assert_eq!(count(&s), before + 1, "the release draws the path"); + let doc = &s.doc().unwrap().doc; + let n = doc.layers[0].children().unwrap().last().unwrap(); + assert!(n.path_data().is_some() && !n.appearance.fill_paint().is_none(), "{:?}", n.appearance); + assert_eq!(s.doc().unwrap().history.undo.len(), undo + 1, "one undo step"); +} diff --git a/crates/tools/src/draw2/gesture.rs b/crates/tools/src/draw2/gesture.rs index 2bf687510..e506799e7 100644 --- a/crates/tools/src/draw2/gesture.rs +++ b/crates/tools/src/draw2/gesture.rs @@ -58,6 +58,10 @@ pub struct GestureTool { /// "Alt key toggles to Smooth Tool": holding Alt with the Pencil tool smooths selected paths /// instead of closing the path (Pencil only, off by default). pub alt_smooth: bool, + /// "Live preview" (Pencil only, on by default): the stroke shows with its fill and stroke as it + /// is drawn. Off, only a thin line follows the pen while drawing, and the path takes its fill + /// and stroke on release. + pub live_preview: bool, /// "Live curve fitting": the Pencil's preview fits the stroke while it is drawn; off, the /// preview shows the points joined by straight segments and the release fits them (Pencil /// only, on by default). @@ -131,6 +135,7 @@ impl GestureTool { keep_selected: id != "blobBrush", // Off, as Pencil Tool Options ships it: Alt keeps drawing a closed path until it is on. alt_smooth: false, + live_preview: true, live_fit: true, smoothing: false, straight: false, @@ -154,6 +159,12 @@ impl GestureTool { matches!(self.id, "pencil" | "paintbrush") } + /// Does a stroke show as the path it draws while it is drawn? Not with the Pencil's Live preview + /// off: then the drag shows as a thin line (an overlay) until the release. + fn previews(&self) -> bool { + !(self.id == "pencil" && !self.live_preview) + } + /// Add a stroke sample. fn push(&mut self, p: Point, pressure: f32) { let raw = self.shift_next.take(); @@ -469,7 +480,8 @@ impl Tool for GestureTool { self.began = true; out.push(Action::Begin(self.label().into())); } - if crate::previews_at(self.points.len()) { + // With the Pencil's Live preview off a thin line shows instead (`overlays`). + if self.previews() && crate::previews_at(self.points.len()) { self.previewed = self.points.len(); let mut v = self.freehand_params(false); if !self.live_fit { @@ -567,7 +579,8 @@ impl Tool for GestureTool { } fn overlays(&self, cx: &ToolContext) -> Vec { let mut o = self.snap.guides().to_vec(); - if !self.active || self.points.len() < 2 || self.began { + // A path being drawn shows itself, except with the Pencil's Live preview off. + if !self.active || self.points.len() < 2 || (self.began && self.previews()) { return o; } let width = match self.id { @@ -627,6 +640,7 @@ impl Tool for GestureTool { "fidelity": self.fidelity, "fill": self.fill, "altSmooth": self.alt_smooth, + "livePreview": self.live_preview, "liveFit": self.live_fit, "keepSelected": self.keep_selected, "editWithin": self.edit_within, @@ -656,6 +670,7 @@ impl Tool for GestureTool { "roundness" => self.roundness = v.as_f64().unwrap_or(self.roundness).clamp(1.0, 100.0), "fill" => self.fill = v.as_bool().unwrap_or(self.fill), "altSmooth" => self.alt_smooth = v.as_bool().unwrap_or(self.alt_smooth), + "livePreview" if self.id == "pencil" => self.live_preview = v.as_bool().unwrap_or(self.live_preview), "liveFit" if self.id == "pencil" => self.live_fit = v.as_bool().unwrap_or(self.live_fit), "keepSelected" => self.keep_selected = v.as_bool().unwrap_or(self.keep_selected), "mergeOnlySelection" => self.merge_only_selection = v.as_bool().unwrap_or(self.merge_only_selection), @@ -812,6 +827,50 @@ mod tests { } } + /// With the Pencil's Live preview off, a stroke previews nothing while it is drawn: a thin line + /// follows the pen (an overlay), and the release draws the path with its fill and stroke. On (the + /// default), every step previews the path and no line is shown. The Paintbrush has no such option. + #[test] + fn pencil_with_live_preview_off_shows_a_thin_line_until_the_release() { + let (d, _) = doc_with_rect(); + let s = Selection::default(); + let p = paint(); + let cx = cx(&d, &s, &p); + let previews = |acts: &[Action]| acts.iter().filter(|a| matches!(a, Action::Preview(..))).count(); + let line = |t: &GestureTool| t.overlays(&cx).iter().any(|o| matches!(o, Overlay::Path { width, .. } if *width == 1.0)); + let pts = [(0.0, 0.0), (10.0, 5.0), (20.0, 0.0), (30.0, 5.0)]; + for live in [true, false] { + let mut t = GestureTool::new("pencil"); + assert_eq!(t.options()["livePreview"], json!(true), "on by default"); + t.set_option("livePreview", &json!(live)); + assert_eq!(t.options()["livePreview"], json!(live)); + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, pts[0].0, pts[0].1)); + let mut during = vec![]; + for &(x, y) in &pts[1..] { + during.extend(t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, x, y))); + } + assert!(matches!(during.first(), Some(Action::Begin(_))), "live {live}: {during:?}"); + assert_eq!(previews(&during), if live { 3 } else { 0 }, "live {live}: {during:?}"); + assert_eq!(line(&t), !live, "live {live}: the thin line shows only with Live preview off"); + let up = t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 30.0, 5.0)); + assert!(matches!(up.as_slice(), [Action::Preview(c, _), Action::Commit] if c == "path.freehand"), "live {live}: {up:?}"); + assert!(!line(&t), "live {live}: no line after the release"); + } + // Escape mid-stroke with Live preview off cancels the stroke it began. + let mut t = GestureTool::new("pencil"); + t.set_option("livePreview", &json!(false)); + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 0.0, 0.0)); + t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 10.0, 5.0)); + assert_eq!(t.key(&cx, ToolKey::Escape, Mods::default()), vec![Action::Cancel]); + // The Paintbrush keeps previewing: Live preview isn't one of its options. + let mut b = GestureTool::new("paintbrush"); + assert!(b.options().get("livePreview").is_none()); + b.set_option("livePreview", &json!(false)); + b.pointer(&cx, &PointerEvent::new(PointerKind::Down, 0.0, 0.0)); + let acts = b.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 10.0, 5.0)); + assert_eq!(previews(&acts), 1, "{acts:?}"); + } + /// Holding Alt shows the Paintbrush's closing cursor before the press, and the Pencil's and the /// Paintbrush's while drawing (before a press, Alt gives the Pencil its straight-line cursor); a release /// with Alt still held closes the path, and Alt let go before the release leaves it open. diff --git a/crates/tools/src/settings.rs b/crates/tools/src/settings.rs index 633380904..878fc607e 100644 --- a/crates/tools/src/settings.rs +++ b/crates/tools/src/settings.rs @@ -35,9 +35,9 @@ const ROWS: &[Row] = &[ // Line Segment Tool Options: the Length and Angle a click's dialog opens with, Fill Line. (&["lineSegment"], None, &["length", "angle", "fillLine"]), (&["pencil", "paintbrush"], None, &["fidelity", "fill", "keepSelected", "editWithin", "closeWithin"]), - // Pencil Tool Options' Alt key toggles to Smooth Tool and Live curve fitting (the Paintbrush - // has neither option). - (&["pencil"], None, &["altSmooth", "liveFit"]), + // Pencil Tool Options' Alt key toggles to Smooth Tool, Live preview and Live curve fitting (the + // Paintbrush has none of them). + (&["pencil"], None, &["altSmooth", "livePreview", "liveFit"]), (&["smooth"], None, &["fidelity"]), (&["blobBrush", "eraser"], None, &["size"]), (&["blobBrush"], None, &["keepSelected", "mergeOnlySelection"]), diff --git a/crates/ui-egui/src/dialogs/freehand.rs b/crates/ui-egui/src/dialogs/freehand.rs index 1c6896e3e..7c6e8f3c0 100644 --- a/crates/ui-egui/src/dialogs/freehand.rs +++ b/crates/ui-egui/src/dialogs/freehand.rs @@ -6,7 +6,9 @@ //! Fields: `tool`; `fidelity` (points; Pencil, Paintbrush and Smooth), `fill` (Pencil and //! Paintbrush), `keepSelected` (Pencil, Paintbrush and Blob Brush), `mergeOnlySelection` (Blob //! Brush), `closeWithin` and `editWithin` (screen pixels, 0 turns it off; Pencil and Paintbrush), -//! `angle` (degrees) and `roundness` (percent; Eraser) and `size` (points; Blob Brush and Eraser). +//! `angle` (degrees) and `roundness` (percent; Eraser), `livePreview` (Pencil: the stroke shows +//! with its fill and stroke while it is drawn, or as a thin line until the release) and `size` +//! (points; Blob Brush and Eraser). use serde_json::{Map, Value, json}; @@ -25,8 +27,20 @@ pub(super) const SPEC: DialogSpec = DialogSpec { heading, body, confirm, min_wid pub const TOOLS: [&str; 5] = ["pencil", "paintbrush", "smooth", "blobBrush", "eraser"]; /// The options the dialog edits, in the order it shows them (a tool has those it reports). -const KEYS: [&str; 11] = - ["fidelity", "angle", "roundness", "size", "fill", "altSmooth", "keepSelected", "mergeOnlySelection", "closeWithin", "editWithin", "liveFit"]; +const KEYS: [&str; 12] = [ + "fidelity", + "angle", + "roundness", + "size", + "fill", + "altSmooth", + "keepSelected", + "mergeOnlySelection", + "closeWithin", + "editWithin", + "livePreview", + "liveFit", +]; /// Width of the dialog's rows: every number field ends at its right edge. const CONTENT_W: f32 = 330.0; @@ -190,8 +204,14 @@ fn body(_app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { if d.fields.contains_key("editWithin") { number(ui, d, ("editWithin", tl!("Edit selected paths within:")), "px", 0, (0.0, 100.0)); } - if d.fields.contains_key("liveFit") { + if d.fields.contains_key("livePreview") { ui.add_space(6.0); + check(ui, d, "livePreview", tl!("Live preview")); + } + if d.fields.contains_key("liveFit") { + if !d.fields.contains_key("livePreview") { + ui.add_space(6.0); + } check(ui, d, "liveFit", tl!("Live curve fitting")); } false @@ -283,6 +303,7 @@ mod tests { "Alt key toggles to Smooth Tool", "Close paths when ends are within:", "Edit selected paths within:", + "Live preview", ] { assert!(text.contains(label), "{label} in {text}"); } @@ -310,7 +331,7 @@ mod tests { assert_eq!((d.f64("fidelity", 0.0), d.bool("fill")), (1.5, false)); let pb_text = painted(&mut app); assert!(pb_text.contains("Paintbrush Tool Options")); - assert!(!pb_text.contains("Alt key toggles to Smooth Tool")); + assert!(!pb_text.contains("Alt key toggles to Smooth Tool") && !pb_text.contains("Live preview"), "{pb_text}"); // Smooth has Fidelity only, the Eraser Size only, and the Blob Brush Size, Keep selected and Merge only with selection. app.run("tool.options", json!({"tool": "smooth"})).unwrap(); let text = painted(&mut app); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 68a1bc3c9..de8a69f1e 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5483,3 +5483,4 @@ axis Y Axis Y ос Polar Grid Tool Options Опции на инструмента „Полярна мрежа“ Fill Line Запълване на линия Scale Presets Пресети за мащаб + Live preview Преглед на живо diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 4b22a988f..3db8cb460 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5561,3 +5561,4 @@ axis Y Axis osa Y Scale Presets Přednastavení měřítka @msg a {width}×{height} screenshot's byte count overflows počet bajtů snímku {width}×{height} přeteče @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot okno {what} ({points} bodů × {scale} DPI) nemůže určit velikost snímku + Live preview Živý náhled diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 4102c5085..ee84f2d81 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5499,3 +5499,4 @@ axis Y Axis Y-akse Polar Grid Tool Options Indstillinger for polært gitterværktøj Fill Line Udfyld linje Scale Presets Skaleringsforudindstillinger + Live preview Direkte forhåndsvisning diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 46bc42641..c68f76ee6 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5143,3 +5143,4 @@ axis Y Axis Y-Achse Scale Presets Skalierungsvorgaben @msg a {width}×{height} screenshot's byte count overflows die Byteanzahl eines {width}×{height}-Screenshots läuft über @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot die {what} des Fensters ({points} Punkte × {scale} DPI) kann keinen Screenshot bemessen + Live preview Live-Vorschau diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 8a20b9b2d..d74f3289d 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5513,3 +5513,4 @@ axis Y Axis Άξονας Y Scale Presets Προεπιλογές κλίμακας @msg a {width}×{height} screenshot's byte count overflows το πλήθος byte ενός στιγμιότυπου {width}×{height} υπερχειλίζει @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot το {what} του παραθύρου ({points} σημεία × {scale} DPI) δεν μπορεί να καθορίσει μέγεθος στιγμιότυπου + Live preview Ζωντανή προεπισκόπηση diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 01f8091a0..d0e28ac50 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5503,3 +5503,4 @@ axis Y Axis Eje Y Scale Presets Ajustes predefinidos de escala @msg a {width}×{height} screenshot's byte count overflows el número de bytes de una captura de {width}×{height} se desborda @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot el {what} de la ventana ({points} puntos × {scale} DPI) no puede dimensionar una captura + Live preview Vista previa en directo diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 4f9592be7..65a282bf7 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5592,3 +5592,4 @@ axis Y Axis Y-akseli Scale Presets Skaalauksen esiasetukset @msg a {width}×{height} screenshot's byte count overflows {width}×{height} kuvakaappauksen tavumäärä ylittää rajan @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot ikkunan {what} ({points} pistettä × {scale} DPI) ei voi määrittää kuvakaappauksen kokoa + Live preview Reaaliaikainen esikatselu diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index cb6b7d2fa..f63f46f5d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5560,3 +5560,4 @@ axis Y Axis Axe Y Scale Presets Préréglages de mise à l'échelle @msg a {width}×{height} screenshot's byte count overflows le nombre d'octets d'une capture {width}×{height} dépasse la limite @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot la {what} de la fenêtre ({points} points × {scale} DPI) ne permet pas de dimensionner une capture + Live preview Aperçu en direct diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 338568215..29328c4ee 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5547,3 +5547,4 @@ axis Y Axis Y tengely Scale Presets Méretezési beállítások @msg a {width}×{height} screenshot's byte count overflows a(z) {width}×{height} képernyőkép bájtszáma túlcsordul @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot az ablak {what} ({points} pont × {scale} DPI) nem tud képernyőkép-méretet adni + Live preview Élő előnézet diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 420a480c5..6e911b413 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5499,3 +5499,4 @@ axis Y Axis Sumbu Y Polar Grid Tool Options Opsi alat kisi polar Fill Line Isi garis Scale Presets Preset skala + Live preview Pratinjau langsung diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 1aa64038c..deb1590f5 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5177,3 +5177,4 @@ axis Y Axis Asse Y Scale Presets Preimpostazioni di scala @msg a {width}×{height} screenshot's byte count overflows il numero di byte di uno screenshot {width}×{height} va in overflow @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot la {what} della finestra ({points} punti × {scale} DPI) non può dimensionare uno screenshot + Live preview Anteprima dal vivo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index a626a1495..ab7aefc0d 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5239,3 +5239,4 @@ axis Y Axis Y 軸 Scale Presets 拡大縮小のプリセット @msg a {width}×{height} screenshot's byte count overflows {width}×{height} のスクリーンショットのバイト数がオーバーフローします @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot ウィンドウの {what}({points} ポイント × {scale} DPI)ではスクリーンショットのサイズを決められません + Live preview ライブプレビュー diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 9dfe36829..c3665f79d 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5477,3 +5477,4 @@ axis Y Axis Y ღერძი Scale Presets მასშტაბის წინასწარ დაყენებული მნიშვნელობები @msg a {width}×{height} screenshot's byte count overflows {width}×{height} ეკრანის სურათის ბაიტების რაოდენობა გადაივსო @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot ფანჯრის {what} ({points} პუნქტი × {scale} DPI) ეკრანის სურათის ზომას ვერ განსაზღვრავს + Live preview ცოცხალი წინასწარი ნახვა diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index e63f40ae3..54dd30cd4 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5140,3 +5140,4 @@ axis Y Axis Y축 Scale Presets 크기 조절 사전 설정 @msg a {width}×{height} screenshot's byte count overflows {width}×{height} 스크린샷의 바이트 수가 넘칩니다 @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot 창의 {what}({points}포인트 × {scale} DPI)로는 스크린샷 크기를 정할 수 없습니다 + Live preview 실시간 미리 보기 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index b7d4a8b13..c761d2009 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5543,3 +5543,4 @@ axis Y Axis Y-as Polar Grid Tool Options Opties polair-rastergereedschap Fill Line Lijn vullen Scale Presets Schaalvoorinstellingen + Live preview Live voorbeeld diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 5b74b91e9..e0cdfee37 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5413,3 +5413,4 @@ axis Y Axis Oś Y Scale Presets Ustawienia wstępne skali @msg a {width}×{height} screenshot's byte count overflows liczba bajtów zrzutu ekranu {width}×{height} przekracza limit @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot {what} okna ({points} punktów × {scale} DPI) nie pozwala ustalić rozmiaru zrzutu ekranu + Live preview Podgląd na żywo diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index f251ba0c9..0e2ced7f6 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5478,3 +5478,4 @@ axis Y Axis Eixo Y Scale Presets Predefinições de escala @msg a {width}×{height} screenshot's byte count overflows a contagem de bytes de uma captura {width}×{height} transborda @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot o {what} da janela ({points} pontos × {scale} DPI) não pode dimensionar uma captura + Live preview Visualização ao vivo diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 1523725d4..d9860d046 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5173,3 +5173,4 @@ axis Y Axis Ось Y Scale Presets Наборы масштаба @msg a {width}×{height} screenshot's byte count overflows количество байтов снимка {width}×{height} переполняется @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot {what} окна ({points} пунктов × {scale} DPI) не позволяет задать размер снимка + Live preview Просмотр в реальном времени diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index cb9adb174..56776874e 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5585,3 +5585,4 @@ axis Y Axis Y-axel Scale Presets Skalningsförval @msg a {width}×{height} screenshot's byte count overflows byteantalet för en skärmbild på {width}×{height} överstiger gränsen @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot fönstrets {what} ({points} punkter × {scale} DPI) kan inte bestämma en skärmbilds storlek + Live preview Direkt förhandsgranskning diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 2b2ba1b90..dd03926c8 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5518,3 +5518,4 @@ axis Y Axis Y ekseni Scale Presets Ölçek ön ayarları @msg a {width}×{height} screenshot's byte count overflows {width}×{height} ekran görüntüsünün bayt sayısı taşıyor @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot pencerenin {what} ({points} nokta × {scale} DPI) bir ekran görüntüsünü boyutlandıramaz + Live preview Canlı önizleme diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index c14fd0f8c..f29bd25f1 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5157,3 +5157,4 @@ axis Y Axis Вісь Y Scale Presets Набори масштабу @msg a {width}×{height} screenshot's byte count overflows кількість байтів знімка {width}×{height} переповнюється @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot {what} вікна ({points} пунктів × {scale} DPI) не дозволяє задати розмір знімка + Live preview Перегляд у реальному часі diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index fd9a3f65a..4d35cd04d 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5157,3 +5157,4 @@ axis Y Axis Trục Y Scale Presets Cài đặt sẵn tỉ lệ @msg a {width}×{height} screenshot's byte count overflows số byte của ảnh chụp {width}×{height} bị tràn @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot {what} của cửa sổ ({points} điểm × {scale} DPI) không thể định cỡ ảnh chụp + Live preview Xem trước trực tiếp diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 7999d6ccb..e650d1436 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5400,3 +5400,4 @@ axis Y Axis Y 轴 Scale Presets 缩放预设 @msg a {width}×{height} screenshot's byte count overflows {width}×{height} 截图的字节数溢出 @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot 窗口的{what}({points} 点 × {scale} DPI)无法确定截图大小 + Live preview 实时预览 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 2916e7c9e..13b3df32d 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5196,3 +5196,4 @@ axis Y Axis Y 軸 Scale Presets 縮放預設集 @msg a {width}×{height} screenshot's byte count overflows {width}×{height} 螢幕擷取畫面的位元組數溢出 @msg the window's {what} ({points} points × {scale} DPI) cannot size a screenshot 視窗的{what}({points} 點 × {scale} DPI)無法決定螢幕擷取畫面的大小 + Live preview 即時預覽 diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 621b845e8..bc0965803 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -621,7 +621,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "tool.options", "Tool Options…", "", - "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x and y, draws one there with them), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (pt), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values})", + "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x and y, draws one there with them), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (pt), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), livePreview (pencil, on: off draws a thin line while the stroke is drawn, and the path's fill and stroke on release), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values})", ), ( "ui.colorGuideOptions",