mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 04:07:12 -06:00
Part of #812: more tools open their options on a double-click
Double-clicking a tool's button (or tool.options {tool}) now also opens:
- Artboard tool: Artboard Options of the active artboard (the Artboard
tool's while it is in use, else the window's);
- the graph tools: Graph Type for the selected graph (an error without
one), the dialog Object > Graph > Type opens;
- the Symbolism tools: Symbolism Tools Options with the brush the eight
tools share (Diameter, Intensity, Symbol Set Density);
- Magic Wand: the Magic Wand panel.
The Object > Graph dialogs' opener is shared with the toolbar. The new
labels are translated in the eight complete catalogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ae27d899b8
commit
990d869897
+2
-2
File diff suppressed because one or more lines are too long
@@ -16,11 +16,16 @@ pub fn create(id: &str) -> Option<Box<dyn Tool>> {
|
||||
"flare" => Box::new(FlareTool::default()),
|
||||
"reshape" => Box::new(ReshapeTool::default()),
|
||||
"shaper" => Box::new(ShaperTool::default()),
|
||||
id if vectorcraft_doc::GraphKind::parse(id).is_some() && id.ends_with("Graph") => Box::new(GraphTool::new(id)),
|
||||
id if is_graph_tool(id) => Box::new(GraphTool::new(id)),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether `id` is one of the graph tools (`columnGraph`, `pieGraph`…).
|
||||
pub fn is_graph_tool(id: &str) -> bool {
|
||||
id.ends_with("Graph") && vectorcraft_doc::GraphKind::parse(id).is_some()
|
||||
}
|
||||
|
||||
/// The Flare Tool Options as `shape.flare` takes them: (key, default, least, most). The diameter
|
||||
/// and the rings' path length are in points, the counts whole, the direction in degrees, the
|
||||
/// rest percentages.
|
||||
|
||||
@@ -14,7 +14,8 @@ type Row = (&'static [&'static str], Option<&'static str>, &'static [&'static st
|
||||
/// The Liquify tools.
|
||||
pub const LIQUIFY: &[&str] = &["warp", "twirl", "pucker", "bloat", "scallop", "crystallize", "wrinkle"];
|
||||
|
||||
const SYMBOLISM: &[&str] =
|
||||
/// The Symbolism tools.
|
||||
pub const SYMBOLISM: &[&str] =
|
||||
&["symbolSprayer", "symbolShifter", "symbolScruncher", "symbolSizer", "symbolSpinner", "symbolStainer", "symbolScreener", "symbolStyler"];
|
||||
|
||||
const ROWS: &[Row] = &[
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! Artboard Options (double-click with the Artboard tool): the artboard's fields, sizes parsed
|
||||
//! with units.
|
||||
//! Artboard Options (double-click with the Artboard tool, or double-click the tool): the
|
||||
//! artboard's fields, sizes parsed with units.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
|
||||
@@ -9,6 +9,25 @@ use crate::state::Dialog;
|
||||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec { heading: |_| tl!("Artboard Options").into(), confirm, ..DialogSpec::FORM };
|
||||
|
||||
/// The dialog kind of Artboard Options.
|
||||
pub const KIND: &str = "artboardOptions";
|
||||
|
||||
/// Open Artboard Options for the active artboard: the Artboard tool's while it is in use, else the
|
||||
/// window's.
|
||||
pub fn open(app: &mut VectorcraftApp) -> Result<Value, String> {
|
||||
let index = if app.session.tool_id() == "artboard" {
|
||||
app.session.tool_options()["active"].as_u64().and_then(|i| usize::try_from(i).ok()).unwrap_or(0)
|
||||
} else {
|
||||
app.view().map_or(0, |v| v.artboard)
|
||||
};
|
||||
let st = app.session.active().ok_or("no document")?;
|
||||
let a = st.doc.artboards.get(index).ok_or("no artboard")?;
|
||||
let r = a.rect;
|
||||
let fields = json!({"index": index, "name": a.name, "x": r.x0, "y": r.y0, "width": r.width(), "height": r.height()});
|
||||
app.ui.dialog = Some(Dialog::new(KIND, fields));
|
||||
Ok(json!({ "dialog": KIND }))
|
||||
}
|
||||
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
|
||||
let mut p = Value::Object(d.fields.clone());
|
||||
for k in ["x", "y", "width", "height"] {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
mod about;
|
||||
mod all_tools;
|
||||
mod artboard_options;
|
||||
pub mod artboard_options;
|
||||
pub mod blend_options;
|
||||
pub mod brush_options;
|
||||
pub mod color_balance;
|
||||
@@ -79,6 +79,7 @@ pub mod spot_colors;
|
||||
pub(crate) mod svg_options;
|
||||
pub mod swatch_conflict;
|
||||
pub mod swatch_options;
|
||||
pub mod symbolism_options;
|
||||
mod text_export;
|
||||
pub mod text_import;
|
||||
mod tiff_bmp_tga;
|
||||
@@ -205,7 +206,7 @@ registry! {
|
||||
Transform: ["move", "rotate", "scale", "reflect", "shear"] => transform::SPEC,
|
||||
PathOp: ["average", "offsetPath", "simplify", "splitIntoGrid"] => path_ops::SPEC,
|
||||
DocumentSetup: ["documentSetup"] => document_setup::SPEC,
|
||||
ArtboardOptions: ["artboardOptions"] => artboard_options::SPEC,
|
||||
ArtboardOptions: [artboard_options::KIND] => artboard_options::SPEC,
|
||||
AllTools: ["allTools"] => all_tools::SPEC,
|
||||
ExportForScreens: ["exportForScreens"] => export_for_screens::SPEC,
|
||||
Recolor: [recolor::KIND] => recolor::SPEC,
|
||||
@@ -278,6 +279,7 @@ registry! {
|
||||
LiquifyOptions: [liquify::KIND] => liquify::SPEC,
|
||||
FreehandOptions: [freehand::KIND] => freehand::SPEC,
|
||||
FlareOptions: [flare_options::KIND] => flare_options::SPEC,
|
||||
SymbolismOptions: [symbolism_options::KIND] => symbolism_options::SPEC,
|
||||
PerspectiveGridPresets: [perspective_presets::KIND] => perspective_presets::SPEC,
|
||||
PerspectiveGridOptions: [perspective_options::KIND] => perspective_options::SPEC,
|
||||
BlendOptions: [blend_options::KIND] => blend_options::SPEC,
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
//! Symbolism Tools Options (double-click a Symbolism tool): the brush the eight tools share, which
|
||||
//! lasts across tool switches and is saved with the preferences. OK sets it with
|
||||
//! `tool.setOption {tool, values}`.
|
||||
//!
|
||||
//! Fields: `tool`; `diameter` (points), `intensity` and `density` (Symbol Set Density), 1–10.
|
||||
|
||||
use serde_json::{Map, Value, json};
|
||||
|
||||
use super::{DialogResult, DialogSpec, form, run_and_close};
|
||||
use crate::state::Dialog;
|
||||
use crate::{VectorcraftApp, widgets};
|
||||
|
||||
/// The dialog kind of the Symbolism Tools Options.
|
||||
pub const KIND: &str = "symbolismOptions";
|
||||
|
||||
pub(super) const SPEC: DialogSpec =
|
||||
DialogSpec { heading: |_| tl!("Symbolism Tools Options").into(), body, confirm, min_width: 320.0, ..DialogSpec::FORM };
|
||||
|
||||
/// The options the dialog edits.
|
||||
const KEYS: [&str; 3] = ["diameter", "intensity", "density"];
|
||||
|
||||
/// Width of the label column.
|
||||
const LABEL_W: f32 = 130.0;
|
||||
|
||||
/// Open the options of Symbolism tool `tool` (one of `vectorcraft_tools::settings::SYMBOLISM`)
|
||||
/// with their current values.
|
||||
pub fn open(app: &mut VectorcraftApp, tool: &str) -> Value {
|
||||
let o = app.session.tool_options_of(tool);
|
||||
let mut fields: Map<String, Value> = KEYS.iter().filter_map(|key| Some((key.to_string(), o.get(*key)?.clone()))).collect();
|
||||
fields.insert("tool".into(), json!(tool));
|
||||
app.ui.dialog = Some(Dialog { kind: KIND.into(), fields });
|
||||
json!({ "dialog": KIND })
|
||||
}
|
||||
|
||||
fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
|
||||
let unit = app.session.general_unit();
|
||||
widgets::label_row(ui, tl!("Diameter:"), LABEL_W, |ui| {
|
||||
form::length(ui, d, "diameter", unit, 100.0);
|
||||
});
|
||||
for (key, label) in [("intensity", tl!("Intensity:")), ("density", tl!("Symbol Set Density:"))] {
|
||||
widgets::label_row(ui, label, LABEL_W, |ui| {
|
||||
if let Some(v) = widgets::range_field(ui, ("symbolism", key), d.f64(key, 5.0), 1.0..=10.0, "", 0, 100.0) {
|
||||
d.fields.insert(key.into(), json!(v));
|
||||
}
|
||||
});
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// OK: set the brush the Symbolism tools share (the tool keeps it within its ranges).
|
||||
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> DialogResult {
|
||||
let values: Map<String, Value> = KEYS.iter().filter(|k| d.fields.contains_key(**k)).map(|k| (k.to_string(), json!(d.f64(k, 0.0)))).collect();
|
||||
run_and_close(app, "tool.setOption", json!({ "tool": d.str("tool"), "values": values }))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use vectorcraft_engine::Session;
|
||||
|
||||
#[test]
|
||||
fn the_symbolism_tools_share_the_options_ok_sets() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
app.run("tool.options", json!({"tool": "symbolSizer"})).unwrap();
|
||||
let text = crate::tests_labels::painted_text(&mut app, |app, ui| super::super::show(app, ui.ctx()));
|
||||
for label in ["Symbolism Tools Options", "Diameter:", "Intensity:", "Symbol Set Density:"] {
|
||||
assert!(text.contains(label), "{label} in {text}");
|
||||
}
|
||||
let d = app.ui.dialog.as_mut().unwrap();
|
||||
assert_eq!((d.str("tool"), d.f64("diameter", 0.0), d.f64("intensity", 0.0)), ("symbolSizer".to_string(), 80.0, 5.0));
|
||||
for (k, v) in [("diameter", json!(120)), ("intensity", json!(9)), ("density", json!(2))] {
|
||||
d.fields.insert(k.into(), v);
|
||||
}
|
||||
super::super::confirm(&mut app).unwrap();
|
||||
assert!(app.ui.dialog.is_none());
|
||||
// The Sprayer has them too: one brush for the eight tools.
|
||||
let o = app.session.tool_options_of("symbolSprayer");
|
||||
assert_eq!((o["diameter"].as_f64(), o["intensity"].as_f64(), o["density"].as_f64()), (Some(120.0), Some(9.0), Some(2.0)));
|
||||
}
|
||||
}
|
||||
@@ -1002,6 +1002,8 @@
|
||||
Path: Trazado:
|
||||
Largest: Más grande:
|
||||
Direction: Dirección:
|
||||
Symbolism Tools Options Opciones de las herramientas de símbolos
|
||||
Symbol Set Density: Densidad del conjunto de símbolos:
|
||||
Flat Image Imagen acoplada
|
||||
Flat Logo Logotipo plano
|
||||
Flatness: Lisura:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1001,6 +1001,8 @@
|
||||
Path: Tracé :
|
||||
Largest: Le plus grand :
|
||||
Direction: Direction :
|
||||
Symbolism Tools Options Options des outils de symbole
|
||||
Symbol Set Density: Densité du jeu de symboles :
|
||||
Flat Image Image aplatie
|
||||
Flat Logo Logo en aplats
|
||||
Flatness: Planéité :
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1001,6 +1001,8 @@
|
||||
Path: Tracciato:
|
||||
Largest: Più grande:
|
||||
Direction: Direzione:
|
||||
Symbolism Tools Options Opzioni strumenti simboli
|
||||
Symbol Set Density: Densità del set di simboli:
|
||||
Flat Image Immagine appiattita
|
||||
Flat Logo Logo piatto
|
||||
Flatness: Planarità:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2871,6 +2871,8 @@
|
||||
Path: パス:
|
||||
Largest: 最大:
|
||||
Direction: 方向:
|
||||
Symbolism Tools Options シンボルツールオプション
|
||||
Symbol Set Density: シンボルセットの密度:
|
||||
Flattener presets 分割・統合プリセット
|
||||
Focal Fill 焦点の塗り
|
||||
Focal Stroke 焦点の線
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -999,6 +999,8 @@
|
||||
Path: Контур:
|
||||
Largest: Самое большое:
|
||||
Direction: Направление:
|
||||
Symbolism Tools Options Параметры инструментов символов
|
||||
Symbol Set Density: Плотность набора символов:
|
||||
Flat Image Плоское изображение
|
||||
Flat Logo Плоский логотип
|
||||
Flatness: Плоскостность:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -995,6 +995,8 @@
|
||||
Path: Контур:
|
||||
Largest: Найбільше:
|
||||
Direction: Напрямок:
|
||||
Symbolism Tools Options Параметри інструментів символів
|
||||
Symbol Set Density: Щільність набору символів:
|
||||
Flat Image Плоске зображення
|
||||
Flat Logo Плоский логотип
|
||||
Flatness: Площинність:
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 634 and column 40.
|
@@ -979,6 +979,8 @@
|
||||
Path: 路径:
|
||||
Largest: 最大:
|
||||
Direction: 方向:
|
||||
Symbolism Tools Options 符号工具选项
|
||||
Symbol Set Density: 符号组密度:
|
||||
Flat Image 平面图像
|
||||
Flatness: 平滑度:
|
||||
Flatten Transparency 拼合透明度
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -1002,6 +1002,8 @@
|
||||
Path: 路徑:
|
||||
Largest: 最大:
|
||||
Direction: 方向:
|
||||
Symbolism Tools Options 符號工具選項
|
||||
Symbol Set Density: 符號組密度:
|
||||
Flat Image 平面影像
|
||||
Flatness: 平滑度:
|
||||
Flatten Artwork 將圖稿平面化
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -353,7 +353,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 → the Gradient panel (window.panel), 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), 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, closeWithin and editWithin (px, 0 is off), size (pt); OK runs tool.setOption {tool, 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; OK runs artboard.setProps), 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), 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, closeWithin and editWithin (px, 0 is off), size (pt); OK runs tool.setOption {tool, values})",
|
||||
),
|
||||
(
|
||||
"ui.colorGuideOptions",
|
||||
@@ -2964,6 +2964,14 @@ fn to_field(ctx: &egui::Context, field: egui::Id, e: egui::Event) {
|
||||
}
|
||||
|
||||
/// Invoke a menu/command id with UI side effects (dialogs for "…" commands that need input).
|
||||
/// Object › Graph › Type… or Data… (`id`): its dialog, with the selected graph's current values
|
||||
/// (the command's query).
|
||||
pub(crate) fn graph_dialog(app: &mut VectorcraftApp, id: &str) -> Result<Value, String> {
|
||||
let v = app.session.execute(id, &json!({})).map_err(|e| e.to_string())?;
|
||||
let (label, fields) = if id == "graph.setData" { ("Graph Data", json!({"csv": v["csv"]})) } else { ("Graph Type", v) };
|
||||
app.run("ui.paramDialog", json!({"command": id, "label": label, "params": fields})).map(|_| json!({ "dialog": "command" }))
|
||||
}
|
||||
|
||||
pub fn invoke(app: &mut VectorcraftApp, id: &str, p: Value) {
|
||||
if waits_for_ime(app, id) {
|
||||
return;
|
||||
@@ -3007,12 +3015,8 @@ pub fn invoke(app: &mut VectorcraftApp, id: &str, p: Value) {
|
||||
}
|
||||
// Object → Graph → Type… / Data…: dialogs with the selected graph's current values.
|
||||
if matches!(id, "graph.setType" | "graph.setData") && p.as_object().is_none_or(|o| o.is_empty()) {
|
||||
match app.session.execute(id, &json!({})) {
|
||||
Ok(v) => {
|
||||
let (label, fields) = if id == "graph.setData" { ("Graph Data", json!({"csv": v["csv"]})) } else { ("Graph Type", v) };
|
||||
let _ = app.run("ui.paramDialog", json!({"command": id, "label": label, "params": fields}));
|
||||
}
|
||||
Err(e) => app.status(e.to_string()),
|
||||
if let Err(e) = graph_dialog(app, id) {
|
||||
app.status(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -312,8 +312,10 @@ pub fn open_options(app: &mut VectorcraftApp, tool: &str) -> Result<serde_json::
|
||||
crate::menus::invoke(app, &id, json!({}));
|
||||
Ok(json!({ "dialog": dialog }))
|
||||
}
|
||||
"gradient" if app.ui.open_panel.as_deref() == Some("gradient") => Ok(json!({ "open": "gradient" })),
|
||||
"gradient" => app.run("window.panel", json!({ "panel": "gradient" })),
|
||||
// The Gradient and Magic Wand tools: their panels.
|
||||
"gradient" | "magicWand" if app.ui.open_panel.as_deref() == Some(tool) => Ok(json!({ "open": tool })),
|
||||
"gradient" | "magicWand" => app.run("window.panel", json!({ "panel": tool })),
|
||||
"artboard" => crate::dialogs::artboard_options::open(app),
|
||||
"eyedropper" => {
|
||||
crate::dialogs::eyedropper::open(app);
|
||||
Ok(json!({ "dialog": crate::dialogs::eyedropper::KIND }))
|
||||
@@ -334,6 +336,10 @@ pub fn open_options(app: &mut VectorcraftApp, tool: &str) -> Result<serde_json::
|
||||
_ if vectorcraft_tools::settings::LIQUIFY.contains(&tool) => crate::dialogs::liquify::open(app, tool),
|
||||
// The freehand tools: their Tool Options (Fidelity, fill, the tolerances, the brush size).
|
||||
_ if crate::dialogs::freehand::TOOLS.contains(&tool) => crate::dialogs::freehand::open(app, tool),
|
||||
// The Symbolism tools: the brush they share.
|
||||
_ if vectorcraft_tools::settings::SYMBOLISM.contains(&tool) => Ok(crate::dialogs::symbolism_options::open(app, tool)),
|
||||
// The graph tools: Graph Type for the selected graph.
|
||||
_ if vectorcraft_tools::extra::is_graph_tool(tool) => crate::menus::graph_dialog(app, "graph.setType"),
|
||||
_ if vectorcraft_tools::tool_info(tool).is_none() => Err(format!("unknown tool `{tool}`")),
|
||||
_ => Err(format!("the {tool} tool has no options")),
|
||||
}
|
||||
@@ -817,6 +823,38 @@ pub(crate) mod tests {
|
||||
assert_eq!(frame_in(&mut app, &ctx, 8.0, vec![], 500.0)[0], tools[0]);
|
||||
}
|
||||
|
||||
/// #812: double-clicking the Artboard tool opens Artboard Options of the active artboard, a
|
||||
/// graph tool Graph Type for the selected graph, and the Magic Wand tool its panel.
|
||||
#[test]
|
||||
fn more_tools_open_their_options() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 300, "height": 200})).unwrap();
|
||||
// Without the Artboard tool, the window's artboard.
|
||||
assert_eq!(open_options(&mut app, "artboard").unwrap()["dialog"], "artboardOptions");
|
||||
let d = app.ui.dialog.take().unwrap();
|
||||
assert_eq!((d.f64("index", 9.0), d.f64("width", 0.0), d.str("name")), (0.0, 300.0, "Artboard 1".to_string()));
|
||||
// With it, its active artboard.
|
||||
app.run("artboard.new", json!({"x": 400, "y": 0, "width": 100, "height": 50})).unwrap();
|
||||
app.run("tool.select", json!({"tool": "artboard"})).unwrap();
|
||||
app.run("tool.setOption", json!({"key": "active", "value": 1})).unwrap();
|
||||
open_options(&mut app, "artboard").unwrap();
|
||||
let d = app.ui.dialog.take().unwrap();
|
||||
assert_eq!((d.f64("index", 9.0), d.f64("x", 0.0), d.f64("width", 0.0)), (1.0, 400.0, 100.0));
|
||||
// Graph Type needs a selected graph.
|
||||
app.run("select.set", json!({"ids": []})).unwrap();
|
||||
assert!(open_options(&mut app, "pieGraph").is_err());
|
||||
assert!(app.ui.dialog.is_none());
|
||||
app.run("graph.create", json!({"type": "column", "x": 0, "y": 0, "width": 200, "height": 150})).unwrap();
|
||||
assert_eq!(open_options(&mut app, "pieGraph").unwrap()["dialog"], "command");
|
||||
let d = app.ui.dialog.take().unwrap();
|
||||
assert_eq!((d.str("__command"), d.str("type")), ("graph.setType".to_string(), "column".to_string()));
|
||||
// The Magic Wand panel opens, and stays open on a second double-click.
|
||||
open_options(&mut app, "magicWand").unwrap();
|
||||
assert_eq!(app.ui.open_panel.as_deref(), Some("magicWand"));
|
||||
assert_eq!(open_options(&mut app, "magicWand").unwrap()["open"], "magicWand");
|
||||
assert_eq!(app.ui.open_panel.as_deref(), Some("magicWand"));
|
||||
}
|
||||
|
||||
/// A double-click at `at`, `time` seconds in (a second apart from the last).
|
||||
fn double_click(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64, at: Pos2) {
|
||||
let b = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() };
|
||||
|
||||
+5
-1
@@ -2697,7 +2697,11 @@ desktop app. The Flare tool's are its Flare Tool Options (dialog `flareOptions`)
|
||||
`opacity`, `brightness`, `growth`, `fuzziness`, `longest`, `rayFuzziness` and `largest` (%), `rays`, `rings`,
|
||||
`direction` (°), and `raysOn` and `ringsOn` (the Rays and Rings checkboxes; off, the flare has none). OK keeps them
|
||||
(`tool.setOption {tool: "flare", values}`) and the next flare dragged out uses them; a click with the tool opens the
|
||||
same dialog, and its OK also draws a flare there.
|
||||
same dialog, and its OK also draws a flare there. The Artboard tool's are the active artboard's Artboard Options
|
||||
(dialog `artboardOptions`; the Artboard tool's active artboard while it is in use, else the window's), a graph tool's
|
||||
the selected graph's Graph Type (an error without one), a Symbolism tool's the Symbolism Tools Options (dialog
|
||||
`symbolismOptions`: `diameter` in points, `intensity` and `density` 1–10, one brush for the eight tools) and the
|
||||
Magic Wand tool's its panel.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"tool.setOption","params":{"tool":"flare","values":{"rays":24,"ringsOn":false}}}}
|
||||
|
||||
Reference in New Issue
Block a user