mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Swatches: Ctrl/Cmd+Shift-click on New Swatch makes a global process color
Ctrl/Cmd+Shift-clicking New Swatch in the Swatches panel opened New Swatch for a spot color, as a Ctrl/Cmd-click does. It now opens the dialog for a global process color, and with Alt also held it saves one at once. `ui.newSwatch` takes `global`. An Alt+Ctrl/Cmd-click with a gradient or pattern as the active paint failed with "spot, mode and group apply to solid colours only". It now saves a gradient or pattern swatch, as a Ctrl/Cmd-click does through the dialog.
This commit is contained in:
@@ -21,8 +21,9 @@ pub const KIND: &str = "newSwatch";
|
||||
|
||||
pub(super) const SPEC: DialogSpec = DialogSpec { heading: |_| tl!("New Swatch").into(), body, confirm, min_width: 340.0, ..DialogSpec::FORM };
|
||||
|
||||
/// Open New Swatch for the active paint: a spot colour with `spot`, going into colour `group`.
|
||||
pub fn open(app: &mut VectorcraftApp, spot: bool, group: Option<&str>) -> Result<Value, String> {
|
||||
/// Open New Swatch for the active paint, going into color `group`. A color opens as a spot color
|
||||
/// with `spot`, and as a global process color with `global` alone.
|
||||
pub fn open(app: &mut VectorcraftApp, spot: bool, global: bool, group: Option<&str>) -> Result<Value, String> {
|
||||
let paint = crate::panels::active_paint(app);
|
||||
let name = app.session.active().ok_or("no document open")?.doc.new_swatch_name(&paint);
|
||||
let mut fields = match &paint {
|
||||
@@ -30,7 +31,7 @@ pub fn open(app: &mut VectorcraftApp, spot: bool, group: Option<&str>) -> Result
|
||||
json!({"__paint": crate::panels::paint_params(&paint), "group": group.unwrap_or_default()})
|
||||
}
|
||||
Paint::Solid { color, .. } => {
|
||||
json!({"color": color, "mode": mode_id(Mode::of(color)), "spot": spot, "global": spot, "group": group.unwrap_or_default()})
|
||||
json!({"color": color, "mode": mode_id(Mode::of(color)), "spot": spot, "global": spot || global, "group": group.unwrap_or_default()})
|
||||
}
|
||||
Paint::None => return Err("pick a colour, gradient or pattern to save as a swatch".into()),
|
||||
other => json!({"__paint": crate::panels::paint_params(other)}),
|
||||
@@ -92,7 +93,7 @@ mod tests {
|
||||
app.run("file.new", json!({"width": 100, "height": 100})).unwrap();
|
||||
app.run("swatch.new", json!({"name": "Ink", "color": "#cc0066", "spot": true})).unwrap();
|
||||
app.run("paint.setFill", json!({"swatch": "Ink", "tint": 40})).unwrap();
|
||||
open(&mut app, false, None).unwrap();
|
||||
open(&mut app, false, false, None).unwrap();
|
||||
let d = app.ui.dialog.clone().unwrap();
|
||||
assert_eq!(params(&d), json!({"name": "Ink 40%", "swatch": "Ink", "tint": 40.0}));
|
||||
run_and_close(&mut app, "swatch.new", params(&d)).unwrap();
|
||||
|
||||
@@ -401,7 +401,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
"ui.newSwatch",
|
||||
"New Swatch…",
|
||||
"",
|
||||
"{spot?, group?: colour group name} open New Swatch for the active fill or stroke (dialog `newSwatch`, engine: swatch.new)",
|
||||
"{spot?, global?, group?: colour group name} open New Swatch for the active fill or stroke (dialog `newSwatch`, engine: swatch.new); a color opens as a spot color with `spot`, and as a global process color with `global` alone",
|
||||
),
|
||||
(
|
||||
"ui.newColorGroup",
|
||||
@@ -1345,7 +1345,10 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
|
||||
Some(name) => crate::dialogs::swatch_options::open(app, &name),
|
||||
None => Err("missing `name`".into()),
|
||||
},
|
||||
"ui.newSwatch" => crate::dialogs::new_swatch::open(app, p.get("spot").and_then(Value::as_bool).unwrap_or(false), s("group").as_deref()),
|
||||
"ui.newSwatch" => {
|
||||
let on = |k: &str| p.get(k).and_then(Value::as_bool).unwrap_or(false);
|
||||
crate::dialogs::new_swatch::open(app, on("spot"), on("global"), s("group").as_deref())
|
||||
}
|
||||
"ui.newColorGroup" => {
|
||||
let names = p.get("swatches").and_then(Value::as_array).map(|a| a.iter().filter_map(Value::as_str).map(str::to_string).collect());
|
||||
crate::dialogs::new_color_group::open(app, names.unwrap_or_default())
|
||||
|
||||
@@ -887,10 +887,10 @@ fn bottom(app: &mut VectorcraftApp, ui: &mut Ui, sel: &[String]) {
|
||||
if widgets::icon_button(ui, "dc-folder", tl!("New Color Group"), false, 24.0).clicked() {
|
||||
app.run("ui.newColorGroup", json!({"swatches": deletable(sel)})).ok();
|
||||
}
|
||||
// Alt-click skips the dialog; Ctrl/Cmd-click makes a spot colour.
|
||||
// Alt-click skips the dialog; Ctrl/Cmd-click makes a spot color, and Ctrl/Cmd+Shift-click
|
||||
// makes a global process color.
|
||||
if widgets::icon_button(ui, "dc-new-item", tl!("New Swatch"), false, 24.0).clicked() {
|
||||
let m = ui.input(|i| i.modifiers);
|
||||
new_swatch(app, sel, m.command, m.alt);
|
||||
new_swatch(app, sel, ui.input(|i| i.modifiers));
|
||||
}
|
||||
// Alt-click deletes without asking.
|
||||
let del = deletable(sel);
|
||||
@@ -906,17 +906,22 @@ fn target_group(app: &VectorcraftApp, sel: &[String]) -> Option<String> {
|
||||
group_of(&app.session.active()?.doc, sel.last()?)
|
||||
}
|
||||
|
||||
/// New Swatch from the active paint (a colour goes into the selected colour group): opens the
|
||||
/// dialog, or saves at once with `now` (Alt-click). `spot` (Cmd/Ctrl-click) makes a spot colour.
|
||||
fn new_swatch(app: &mut VectorcraftApp, sel: &[String], spot: bool, now: bool) {
|
||||
/// New Swatch from the active paint (a color goes into the selected color group), for a click
|
||||
/// with modifiers `m`: a plain click opens the dialog and an Alt-click saves at once. Cmd/Ctrl
|
||||
/// makes a color a spot color, and Cmd/Ctrl+Shift makes it a global process color.
|
||||
fn new_swatch(app: &mut VectorcraftApp, sel: &[String], m: egui::Modifiers) {
|
||||
let paint = active_paint(app);
|
||||
let group = paint.color().and_then(|_| target_group(app, sel));
|
||||
if !now {
|
||||
app.run("ui.newSwatch", json!({"spot": spot, "group": group})).ok();
|
||||
let (spot, global) = (m.command && !m.shift, m.command && m.shift);
|
||||
if !m.alt {
|
||||
app.run("ui.newSwatch", json!({"spot": spot, "global": global, "group": group})).ok();
|
||||
return;
|
||||
}
|
||||
let mut params = super::paint_params(&paint);
|
||||
params["spot"] = json!(spot);
|
||||
if paint.color().is_some() {
|
||||
params["spot"] = json!(spot);
|
||||
params["global"] = json!(global);
|
||||
}
|
||||
if let Some(g) = group {
|
||||
params["group"] = json!(g);
|
||||
}
|
||||
@@ -929,7 +934,7 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let del = deletable(&selected);
|
||||
let opts = editable(app, &selected);
|
||||
if menu_item(ui, tl!("New Swatch…"), true, false) {
|
||||
new_swatch(app, &selected, false, false);
|
||||
new_swatch(app, &selected, egui::Modifiers::NONE);
|
||||
}
|
||||
if menu_item(ui, tl!("New Color Group…"), true, false) {
|
||||
app.run("ui.newColorGroup", json!({"swatches": del})).ok();
|
||||
@@ -1344,7 +1349,7 @@ mod tests {
|
||||
let mut app = app();
|
||||
let ctx = context();
|
||||
app.run("paint.setFill", json!({"color": "#ff8000"})).unwrap();
|
||||
new_swatch(&mut app, &["Bright Red".into()], true, false);
|
||||
new_swatch(&mut app, &["Bright Red".into()], Modifiers::COMMAND);
|
||||
let d = app.ui.dialog.as_ref().expect("New Swatch opens its dialog");
|
||||
assert_eq!(
|
||||
(d.kind.as_str(), d.str("name").as_str(), d.str("group").as_str()),
|
||||
@@ -1358,9 +1363,9 @@ mod tests {
|
||||
assert!(w.spot && doc.swatch_group_of(&w.name).is_some_and(|g| doc.swatch_groups[g].name == "Brights"));
|
||||
// Alt-click saves at once; a gradient takes only a name and stays out of colour groups.
|
||||
app.run("paint.setFill", json!({"swatch": "Sunset"})).unwrap();
|
||||
new_swatch(&mut app, &["Brights".into()], false, true);
|
||||
new_swatch(&mut app, &["Brights".into()], Modifiers::ALT);
|
||||
assert!(app.ui.dialog.is_none() && app.session.doc().unwrap().doc.swatch("New Gradient Swatch 1").is_some());
|
||||
new_swatch(&mut app, &[], false, false);
|
||||
new_swatch(&mut app, &[], Modifiers::NONE);
|
||||
let d = app.ui.dialog.as_ref().unwrap();
|
||||
assert_eq!((d.str("name").as_str(), d.fields.contains_key("__paint")), ("New Gradient Swatch 2", true));
|
||||
dialog_frame(&mut app, &ctx);
|
||||
@@ -1368,6 +1373,37 @@ mod tests {
|
||||
assert!(app.session.doc().unwrap().doc.swatch("New Gradient Swatch 2").is_some_and(|w| matches!(w.paint, Paint::Gradient(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cmd_shift_click_on_new_swatch_makes_a_global_process_color() {
|
||||
let mut app = app();
|
||||
let ctx = context();
|
||||
let global_process = Modifiers::COMMAND | Modifiers::SHIFT;
|
||||
app.run("paint.setFill", json!({"color": "#2a6fb0"})).unwrap();
|
||||
new_swatch(&mut app, &[], global_process);
|
||||
let d = app.ui.dialog.as_ref().expect("New Swatch opens its dialog");
|
||||
assert!(d.bool("global") && !d.bool("spot"), "Cmd/Ctrl+Shift-click opens a global process color");
|
||||
let name = d.str("name");
|
||||
dialog_frame(&mut app, &ctx);
|
||||
crate::dialogs::confirm(&mut app).unwrap();
|
||||
let w = app.session.doc().unwrap().doc.swatch(&name).cloned().expect("created");
|
||||
assert!(w.global && !w.spot);
|
||||
// With Alt also held it saves at once.
|
||||
app.run("paint.setFill", json!({"color": "#00a050"})).unwrap();
|
||||
new_swatch(&mut app, &[], global_process | Modifiers::ALT);
|
||||
assert!(app.ui.dialog.is_none());
|
||||
let w = app.session.doc().unwrap().doc.swatches.last().cloned().unwrap();
|
||||
assert_eq!(w.paint.color().map(|c| c.to_hex()), Some("#00a050".into()));
|
||||
assert!(w.global && !w.spot);
|
||||
// A gradient saves as a gradient swatch whether or not Cmd/Ctrl and Shift are held.
|
||||
app.run("paint.setFill", json!({"swatch": "Sunset"})).unwrap();
|
||||
new_swatch(&mut app, &[], Modifiers::COMMAND | Modifiers::ALT);
|
||||
new_swatch(&mut app, &[], global_process | Modifiers::ALT);
|
||||
let doc = &app.session.doc().unwrap().doc;
|
||||
for n in ["New Gradient Swatch 1", "New Gradient Swatch 2"] {
|
||||
assert!(doc.swatch(n).is_some_and(|w| matches!(w.paint, Paint::Gradient(_)) && !w.global && !w.spot), "{n}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_color_group_dialog_makes_a_group_from_swatches_or_artwork() {
|
||||
let mut app = app();
|
||||
|
||||
@@ -132,7 +132,7 @@ swatch of its kind in the Swatches panel with Alt held replaces it (`swatch.edit
|
||||
Confirmations: deleting swatches from the Swatches panel opens a `confirm` dialog (fields `message`, `detail`);
|
||||
`ui.dialog.confirm` runs the command it asks about (here `swatch.delete`) and `ui.dialog.cancel` drops it.
|
||||
|
||||
New swatches and colour groups: `ui.newSwatch {spot?, group?}` opens the `newSwatch` dialog prefilled from the active
|
||||
New swatches and colour groups: `ui.newSwatch {spot?, global?, group?}` opens the `newSwatch` dialog prefilled from the active
|
||||
fill or stroke (fields as in `swatchOptions` plus `group`; a gradient or pattern only takes `name`) and
|
||||
`ui.newColorGroup {swatches?}` opens `newColorGroup` (fields `name`, `fromArtwork`, `toGlobal`, `includeTints`,
|
||||
`swatches`); `ui.dialog.confirm` runs `swatch.new` / `swatch.newGroup`.
|
||||
|
||||
@@ -468,6 +468,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
Duplicate Artboards, the Artboard tool's Alt-drag and Paste give the new artboard an id that no other artboard has, also when an artboard's id is the largest possible value (#1718). Deleting the copy or the original then leaves the other's guides.
|
||||
- **Editing type:** double-clicking type with a selection tool switches to the Type tool with the caret where it was clicked.
|
||||
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection). Held Space pans and Cmd+Space zooms (Cmd+Alt+Space out) whatever the tool.
|
||||
- **Swatches panel clicks:** Ctrl/Cmd-clicking New Swatch makes a spot color, and Ctrl/Cmd+Shift-clicking it makes a global process color.
|
||||
- **Panel drag and drop:** art dragged off the canvas onto the Symbols panel becomes a symbol, onto the Swatches panel a pattern swatch and onto the Brushes panel an Art brush; a symbol dragged onto the canvas places an instance where it is dropped, and a brush dragged onto a path is applied to it.
|
||||
- **Tool double-clicks:** double-clicking a tool's button opens its options: Hand fits the artboard in the window, Zoom shows 100 %, Rotate, Scale, Reflect and Shear open their Object › Transform dialogs, the Selection, Direct Selection and Group Selection tools the Move dialog (Enter opens these seven tools' dialogs too), Gradient, Eyedropper, Blend and the Liquify tools their panels or options, the Pencil, Paintbrush, Smooth, Blob Brush and Eraser tools their Tool Options (Fidelity, fill, the tolerances, the brush size), and (#812) the Flare tool its Flare Tool Options, the Artboard tool the active artboard's Artboard Options, the graph tools Graph Type for the selected graph, the Symbolism tools the Symbolism Tools Options (Diameter, Intensity, Symbol Set Density) and the Magic Wand tool its panel.
|
||||
- **Ruler guides as layer objects:** a guide goes on the current layer, shows, hides, locks and is deleted with it, is listed in the Layers panel (its row selects it) and moves to another layer (`guide.setLayer`, or its row dragged onto that layer's rows); guides on a hidden or locked layer can't be selected, moved or deleted, Duplicate copies a layer's guides and Merge Selected and Flatten Artwork keep them on the merged layer; guides from files saved before guides had layers go on the top visible, unlocked layer when the document opens. Not done: guides stacked among the art of their layer (they are listed above it), a guide row's own eye and lock. Lock Guides also locks the objects made into guides: they can't be selected, moved or deleted (#1543); they are drawn in the guide colour and style of Preferences, and a dragged selection snaps to ruler guides with Smart Guides on (#1583, fixes #1534). The Transform panel shows a selected ruler guide's X or Y, and typing a value moves it (#1659, part of #1597). This is not established by the documentation we could find: the public help pages on guides, rulers and the Transform, Control and Properties panels (read again on 2026-10-14) describe selecting and dragging unlocked guides and the panels' X and Y for a selection, not a guide's position in a panel, and the one public statement of it is a user's [community answer](https://community.adobe.com/questions-652/adding-a-new-custom-guide-785586), which is not documentation. It is kept as a convenience pending the owner's decision, with no parity claim and nothing counted towards a score.
|
||||
|
||||
Reference in New Issue
Block a user