mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 13:47:21 -06:00
Step 3 of the Rectangular Grid tool and Arc tool help pages (https://helpx.adobe.com/illustrator/using/tool-techniques/rectangular-grid-tool.html, https://helpx.adobe.com/illustrator/using/tool-techniques/arc-tool.html) and the Tools page (https://helpx.adobe.com/illustrator/using/tools.html) say a double-click on the tool opens its Tool Options. For these four tools the double-click (tool.options, and Enter once #2891 lands) said the tool had no options (#2691). The Tool Options are the tool's click dialog in dialogs/shapes.rs, opened without a place to draw (shapes::open_options; a `__toolOptions` field marks it), as the review of #2979 asked: no copied form code. Its OK runs tool.setOption and draws nothing; Cancel changes nothing. The grids' dialogs are titled Rectangular Grid / Polar Grid Tool Options there. The options are what the next drag draws with and what a click's dialog opens at: - Line Segment: Length and Angle (now kept by the tool and used by the click dialog) and Fill Line (new: shape.line {fill: true} gives the line the current fill; the click dialog has it too); - Arc: lengths, Reference Point, Type, Base Along, Slope, Fill Arc; - the grids: dividers, skews and switches, plus the Default Size and Reference Point, now kept by the tool (width, height, reference) for the click dialog; a drag leaves them out of its command. The grids' Reference Point is drawn only as the 9-point locator (it also showed as a plain number field). New strings (Rectangular Grid Tool Options, Polar Grid Tool Options, Fill Line) have rows in all 24 catalogs. Verification: toolbar::tests::double_clicking_the_line_arc_and_grid_tools_opens_their_tool_options double-clicks each tool's toolbar button: the dialog and its title, Cancel leaves the next drag as it was, OK draws nothing, the next drag uses the option and a click's dialog opens at it (a grid at its Default Size); line_segment_options_and_fill_line, grid_default_size_is_an_option_the_drag_leaves_out, fill_line_gives_a_new_line_the_current_fill. Grid dialog tests now expect the stored Default Size. Live: double-clicking the Rectangular Grid tool in the release build opens Rectangular Grid Tool Options; OK with 2 and 3 dividers, then a drag, draws a 3 × 4 grid.
170 lines
7.5 KiB
Rust
170 lines
7.5 KiB
Rust
//! Dialogs a tool click opens: shape sizes, transform values, graph size and data, flare options,
|
||
//! artboard options and the gradient stop popover.
|
||
|
||
use serde_json::{Value, json};
|
||
|
||
use super::shapes::grid_pin_to_anchor;
|
||
use crate::VectorcraftApp;
|
||
use crate::state::Dialog;
|
||
|
||
/// A click with a shape tool opens its size dialog.
|
||
pub fn open_tool_dialog(app: &mut VectorcraftApp, kind: &str, p: Value) {
|
||
let x = p.get("x").and_then(Value::as_f64).unwrap_or(0.0);
|
||
let y = p.get("y").and_then(Value::as_f64).unwrap_or(0.0);
|
||
let alt = p.get("alt").and_then(Value::as_bool).unwrap_or(false);
|
||
let d = match kind {
|
||
"rectangle" | "ellipse" => Dialog::new(kind, json!({"x": x, "y": y, "alt": alt, "width": "100 pt", "height": "100 pt"})),
|
||
"roundedRectangle" => Dialog::new(kind, json!({"x": x, "y": y, "alt": alt, "width": "100 pt", "height": "100 pt", "radius": "12 pt"})),
|
||
"polygon" => Dialog::new(kind, json!({"x": x, "y": y, "radius": "50 pt", "sides": app.session.tool_options_of(kind)["sides"]})),
|
||
"star" => {
|
||
Dialog::new(kind, json!({"x": x, "y": y, "radius1": "50 pt", "radius2": "25 pt", "points": app.session.tool_options_of(kind)["points"]}))
|
||
}
|
||
// The Line Segment Tool Options: Length, Angle and Fill Line as the tool keeps them.
|
||
"lineSegment" => {
|
||
let o = app.session.tool_options_of(kind);
|
||
let fill = o.get("fillLine").and_then(Value::as_bool).unwrap_or(false);
|
||
Dialog::new(
|
||
kind,
|
||
json!({"x": x, "y": y, "length": pt(o.get("length")), "angle": o.get("angle").cloned().unwrap_or(json!(0)), "fillLine": fill}),
|
||
)
|
||
}
|
||
"spiral" => {
|
||
let options = app.session.tool_options_of(kind);
|
||
Dialog::new(
|
||
kind,
|
||
json!({
|
||
"x": x,
|
||
"y": y,
|
||
"radius": "50 pt",
|
||
"decay": options.get("decay").cloned().unwrap_or(json!(80.0)),
|
||
"segments": options.get("segments").cloned().unwrap_or(json!(10)),
|
||
"clockwise": options.get("clockwise").cloned().unwrap_or(json!(true)),
|
||
}),
|
||
)
|
||
}
|
||
"arc" => {
|
||
let opts = app.session.tool_options_of(kind);
|
||
let closed = opts.get("closed").and_then(Value::as_bool).unwrap_or(false);
|
||
let mut d = Dialog::new(
|
||
kind,
|
||
json!({
|
||
"__anchorX": x,
|
||
"__anchorY": y,
|
||
"x": x,
|
||
"y": y,
|
||
"lengthX": opts.get("lengthX").cloned().unwrap_or(json!("100 pt")),
|
||
"lengthY": opts.get("lengthY").cloned().unwrap_or(json!("100 pt")),
|
||
"reference": opts.get("reference").cloned().unwrap_or(json!(0)),
|
||
"arcType": if closed { "closed" } else { "open" },
|
||
"baseAlong": opts.get("baseAlong").cloned().unwrap_or(json!("x")),
|
||
"slope": opts.get("slope").cloned().unwrap_or(json!(50)),
|
||
"fillArc": opts.get("fillArc").and_then(Value::as_bool).unwrap_or(false),
|
||
}),
|
||
);
|
||
super::shapes::arc_pin_to_anchor(&mut d);
|
||
d
|
||
}
|
||
// The grids: the tool's stored options (Default Size, Reference Point, divider counts and
|
||
// skews, switches).
|
||
"rectangularGrid" | "polarGrid" => {
|
||
let mut obj = match app.session.tool_options_of(kind) {
|
||
Value::Object(o) => o,
|
||
_ => serde_json::Map::new(),
|
||
};
|
||
obj.insert("__anchorX".into(), json!(x));
|
||
obj.insert("__anchorY".into(), json!(y));
|
||
// The Default Size in points, as typed lengths.
|
||
let (w, h) = (pt(obj.get("width")), pt(obj.get("height")));
|
||
obj.insert("width".into(), w);
|
||
obj.insert("height".into(), h);
|
||
obj.entry("reference").or_insert(json!(0));
|
||
let mut d = Dialog::new(kind, Value::Object(obj));
|
||
grid_pin_to_anchor(&mut d);
|
||
d
|
||
}
|
||
// Graph tool click: the graph's size.
|
||
"graph" => Dialog::new(
|
||
"command",
|
||
json!({"__command": "graph.create", "__label": "Graph", "type": p.get("type").cloned().unwrap_or(json!("column")), "x": x, "y": y, "width": 200, "height": 150}),
|
||
),
|
||
"graphData" => {
|
||
// After drawing a graph: Graph Data, its worksheet.
|
||
if let Err(e) = super::graph_data::open(app) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
// A Flare tool click: the Flare Tool Options, with the tool's values; OK draws a flare there.
|
||
"flare" => {
|
||
super::flare_options::open(app, Some(vectorcraft_geom::Point::new(x, y)));
|
||
return;
|
||
}
|
||
"rotate" | "reflect" | "scale" | "shear" | "artboardOptions" => {
|
||
let mut base = match kind {
|
||
"rotate" => json!({"angle": 0}),
|
||
"reflect" => json!({"axis": "vertical", "angle": 90}),
|
||
"scale" => json!({"sx": 100, "sy": 100, "uniform": true}),
|
||
"shear" => json!({"angle": 0, "axis": "horizontal"}),
|
||
_ => json!({}),
|
||
};
|
||
if let (Some(b), Some(o)) = (base.as_object_mut(), p.as_object()) {
|
||
for (k, v) in o {
|
||
b.insert(k.clone(), v.clone());
|
||
}
|
||
}
|
||
Dialog::new(kind, base)
|
||
}
|
||
// Double-clicking a stop on the gradient annotator: its popover, next to the stop's chip.
|
||
"gradientStop" => Dialog::new(kind, json!({"index": p.get("index").cloned().unwrap_or(json!(0)), "x": x, "y": y, "tab": "color"})),
|
||
// Brush Options › Key Color eyedropper: update the open dialog, do not replace it.
|
||
"brushKeyColor" => {
|
||
if let Some(c) = p.get("color") {
|
||
super::brush_art_options::apply_key_color_sample(app, c);
|
||
}
|
||
return;
|
||
}
|
||
// Double-clicking a width point with the Width tool: Width Point Edit.
|
||
super::width_point::KIND => {
|
||
if let Err(e) = super::width_point::open(app, &p) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
// The Blend tool's double-click and Alt-click: Blend Options.
|
||
super::blend_options::KIND => {
|
||
if let Err(e) = super::blend_options::open(app) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
// Double-clicking a plane widget of the perspective grid: that plane's options.
|
||
super::perspective_plane::KIND => {
|
||
if let Err(e) = super::perspective_plane::open(app, &p) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
// Double-clicking a Live Corners widget: Corners.
|
||
super::corners::KIND => {
|
||
if let Err(e) = super::corners::open(app, &p) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
// Double-clicking a slice with the Slice Selection tool: Slice Options.
|
||
super::slices::OPTIONS => {
|
||
if let Err(e) = super::slices::open_options(app) {
|
||
app.status(e);
|
||
}
|
||
return;
|
||
}
|
||
_ => return,
|
||
};
|
||
app.ui.dialog = Some(d);
|
||
}
|
||
|
||
/// A stored length option (points) as a dialog's typed length, `"N pt"`; 100 pt when unset.
|
||
fn pt(v: Option<&Value>) -> Value {
|
||
json!(format!("{} pt", v.and_then(Value::as_f64).filter(|n| n.is_finite()).unwrap_or(100.0)))
|
||
}
|