mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 02:07:09 -06:00
No behaviour change: every command keeps its id, label, menu, shortcut and params doc. - Engine: swatch.* -> cmd/swatch.rs, graphicStyle.* -> cmd/style.rs, appearance.* (incl. copyFrom) -> cmd/appearance.rs, stroke.set/setAdvanced -> cmd/stroke.rs, paint.editGradient/setGradientGeom -> cmd/gradient.rs. Shared helpers in cmd/mod.rs: leaf_targets/paint_targets (replacing three copies) and unique_name (swatch and graphic style naming). - UI: dialogs.rs -> dialogs/ with one file per dialog and a DialogSpec registry (one DialogKind row per dialog); shared field widgets in dialogs/form.rs. Headless frame tests draw and confirm every dialog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
73 lines
3.0 KiB
Rust
73 lines
3.0 KiB
Rust
//! The Graphic Styles panel: apply, new, delete and duplicate.
|
|
|
|
use serde_json::{Value, json};
|
|
|
|
use super::*;
|
|
use crate::EngineError;
|
|
|
|
pub fn specs() -> Vec<CommandSpec> {
|
|
vec![
|
|
cmd!("graphicStyle.apply", "Apply Graphic Style", ["Window", "Graphic Styles"], None, "{name}", has_selection, style_apply),
|
|
cmd!("graphicStyle.new", "New Graphic Style", ["Window", "Graphic Styles"], None, "{name?} from the selection", has_selection, style_new),
|
|
cmd!("graphicStyle.delete", "Delete Graphic Style", ["Window", "Graphic Styles"], None, "{name}", has_doc, style_delete),
|
|
cmd!("graphicStyle.duplicate", "Duplicate Graphic Style", ["Window", "Graphic Styles"], None, "{name}", has_doc, style_duplicate),
|
|
]
|
|
}
|
|
|
|
fn style_apply(s: &mut Session, p: &Value) -> Result<Value> {
|
|
let name = str_param(p, "name").ok_or_else(|| bad("graphicStyle.apply", "missing name"))?;
|
|
let ap = s
|
|
.doc()?
|
|
.doc
|
|
.graphic_styles
|
|
.iter()
|
|
.find(|g| g.name == name)
|
|
.map(|g| g.appearance.clone())
|
|
.ok_or_else(|| EngineError::Other(format!("no graphic style `{name}`")))?;
|
|
let ids = paint_targets(s, p)?;
|
|
s.edit("Apply Graphic Style", |d, _| {
|
|
for id in &ids {
|
|
if let Some(n) = d.node_mut(*id) {
|
|
n.appearance = ap.clone();
|
|
}
|
|
}
|
|
Ok(())
|
|
})?;
|
|
ok()
|
|
}
|
|
|
|
fn style_new(s: &mut Session, p: &Value) -> Result<Value> {
|
|
let st = s.doc()?;
|
|
let id = *st.selection.objects.first().unwrap();
|
|
let ap = st.doc.node(id).map(|n| n.appearance.clone()).unwrap_or_default();
|
|
let name = str_param(p, "name").map(str::to_string).unwrap_or_else(|| format!("Graphic Style {}", st.doc.graphic_styles.len() + 1));
|
|
s.edit("New Graphic Style", |d, _| {
|
|
d.graphic_styles.push(vectorcraft_doc::GraphicStyle { name: name.clone(), appearance: ap });
|
|
Ok(())
|
|
})?;
|
|
Ok(json!({ "name": name }))
|
|
}
|
|
|
|
fn style_delete(s: &mut Session, p: &Value) -> Result<Value> {
|
|
let name = str_param(p, "name").ok_or_else(|| bad("graphicStyle.delete", "missing name"))?.to_string();
|
|
s.edit("Delete Graphic Style", |d, _| {
|
|
let before = d.graphic_styles.len();
|
|
d.graphic_styles.retain(|g| g.name != name);
|
|
if d.graphic_styles.len() == before { Err(EngineError::Other(format!("no graphic style `{name}`"))) } else { Ok(()) }
|
|
})?;
|
|
ok()
|
|
}
|
|
|
|
fn style_duplicate(s: &mut Session, p: &Value) -> Result<Value> {
|
|
let name = str_param(p, "name").ok_or_else(|| bad("graphicStyle.duplicate", "missing name"))?.to_string();
|
|
let new = s.edit("Duplicate Graphic Style", |d, _| {
|
|
let pos = d.graphic_styles.iter().position(|g| g.name == name).ok_or_else(|| EngineError::Other(format!("no graphic style `{name}`")))?;
|
|
let mut g = d.graphic_styles[pos].clone();
|
|
let nm = unique_name(&format!("{name} copy"), |n| d.graphic_styles.iter().any(|x| x.name == n));
|
|
g.name = nm.clone();
|
|
d.graphic_styles.insert(pos + 1, g);
|
|
Ok(nm)
|
|
})?;
|
|
Ok(json!({"name": new}))
|
|
}
|