Merge pull request #4225 from ncic/pencil-live-preview

Pencil: Live preview option in Pencil Tool Options
This commit is contained in:
BFlat
2026-10-26 12:59:05 +07:00
committed by GitHub
29 changed files with 141 additions and 11 deletions
+26
View File
@@ -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");
}
+61 -2
View File
@@ -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<Overlay> {
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.
+3 -3
View File
@@ -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"]),
+26 -5
View File
@@ -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);
+1
View File
@@ -5483,3 +5483,4 @@ axis Y Axis Y ос
Polar Grid Tool Options Опции на инструмента „Полярна мрежа“
Fill Line Запълване на линия
Scale Presets Пресети за мащаб
Live preview Преглед на живо
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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 Ζωντανή προεπισκόπηση
Can't render this file because it contains an unexpected character in line 2070 and column 19.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 3839 and column 19.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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 ライブプレビュー
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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 ცოცხალი წინასწარი ნახვა
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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 실시간 미리 보기
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -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 Просмотр в реальном времени
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -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 Перегляд у реальному часі
Can't render this file because it contains an unexpected character in line 749 and column 40.
+1
View File
@@ -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
Can't render this file because it contains an unexpected character in line 4053 and column 10.
+1
View File
@@ -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 实时预览
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -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 即時預覽
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1 -1
View File
@@ -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",