mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-29 04:46:50 -06:00
M3: reorder paints, effects and layout grids in the Design panel; grid and instance property variable bindings (#81)
Fill, stroke, effect and layout grid rows drag to reorder by their grip (a handle shows over the
row, an insertion line where it lands, the row's label follows the pointer like a dragged layer;
Escape cancels; lists that differ across the selection have nothing to drag). New commands
`fill.move`, `stroke.move` and `grid.move {ids?, from, to}` join `effect.move` (lists bottom first,
one undo step; items keep everything, linked list styles are unlinked).
Variable bindings on list items follow their items when a list is reordered or shrinks:
paint colours bind per paint (`fills/<i>/color`, `strokes/<i>/color`; `fill`/`stroke` stay the
first paint's), so a moved bound paint keeps its variable and its neighbours are never recoloured.
`fill.remove`, `stroke.remove` and `grid.remove` refuse negative indices.
Layout grids get settings in the Design panel (size; count, type, width or height, margin or
offset, gutter; colour) whose numbers bind to number variables (`layoutGrids/<i>/<field>`),
re-applied on variable and mode changes. Instance boolean and text properties bind to boolean and
string variables (`componentProperties/<name>`, from the instance's properties panel), kept across
instance rebuilds, following property renames and dropped on delete, reset and detach.
Inspect names the variable of each paint.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -215,37 +215,8 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
spec!("effect.remove", "Remove effect", "{ids?, index}", true, |s, p| {
|
||||
let i = req_index("effect.remove", p, "index")?;
|
||||
for id in req_targets("effect.remove", s, p)? {
|
||||
let n = s.doc_mut()?.get_mut(id)?;
|
||||
if i < n.effects.len() {
|
||||
n.effects.remove(i);
|
||||
// Its bindings go with it; later effects' move down one.
|
||||
super::styles::remap_effect_bindings(n, |k| match k.cmp(&i) {
|
||||
std::cmp::Ordering::Less => Some(k),
|
||||
std::cmp::Ordering::Equal => None,
|
||||
std::cmp::Ordering::Greater => Some(k - 1),
|
||||
});
|
||||
}
|
||||
}
|
||||
ok()
|
||||
}),
|
||||
spec!("effect.move", "Move effect", "{ids?, from, to} (indices in the effect list, bottom first)", true, |s, p| {
|
||||
let from = req_index("effect.move", p, "from")?;
|
||||
let to = req_index("effect.move", p, "to")?;
|
||||
for id in req_targets("effect.move", s, p)? {
|
||||
let n = s.doc_mut()?.get_mut(id)?;
|
||||
if from >= n.effects.len() || to >= n.effects.len() || from == to {
|
||||
continue;
|
||||
}
|
||||
let e = n.effects.remove(from);
|
||||
n.effects.insert(to, e);
|
||||
// Bindings follow their effects.
|
||||
super::styles::remap_effect_bindings(n, |k| {
|
||||
if k == from {
|
||||
return Some(to);
|
||||
}
|
||||
let k = if k > from { k - 1 } else { k };
|
||||
Some(if k >= to { k + 1 } else { k })
|
||||
});
|
||||
// Its bindings go with it; later effects' move down one.
|
||||
super::lists::remove_item(s.doc_mut()?.get_mut(id)?, super::lists::BoundList::Effects, i);
|
||||
}
|
||||
ok()
|
||||
}),
|
||||
@@ -268,12 +239,9 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
ok()
|
||||
}),
|
||||
spec!("grid.remove", "Remove layout grid", "{ids?, index}", true, |s, p| {
|
||||
let i = req_num("grid.remove", p, "index")? as usize;
|
||||
let i = req_index("grid.remove", p, "index")?;
|
||||
for id in req_targets("grid.remove", s, p)? {
|
||||
let n = s.doc_mut()?.get_mut(id)?;
|
||||
if i < n.grids.len() {
|
||||
n.grids.remove(i);
|
||||
}
|
||||
super::lists::remove_item(s.doc_mut()?.get_mut(id)?, super::lists::BoundList::Grids, i);
|
||||
}
|
||||
ok()
|
||||
}),
|
||||
@@ -980,6 +948,7 @@ fn reparent(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
fn paint_list(s: &mut Session, p: &Value, fills: bool, add: bool) -> Result<Value> {
|
||||
let cmd = if fills { "fill" } else { "stroke" };
|
||||
let ids = req_targets(cmd, s, p)?;
|
||||
let index = if add || get(p, "index").is_none() { None } else { Some(req_index(cmd, p, "index")?) };
|
||||
let doc = s.doc_mut()?;
|
||||
for i in ids {
|
||||
let n = doc.get_mut(i)?;
|
||||
@@ -994,17 +963,9 @@ fn paint_list(s: &mut Session, p: &Value, fills: bool, add: bool) -> Result<Valu
|
||||
};
|
||||
list.push(paint);
|
||||
} else {
|
||||
let idx = num(p, "index").map(|x| x as usize).unwrap_or(list.len().saturating_sub(1));
|
||||
if idx < list.len() {
|
||||
list.remove(idx);
|
||||
// A binding on the first paint's colour belongs to the paint just removed.
|
||||
if idx == 0 {
|
||||
let keys: &[&str] = if fills { &["fill", "fills", "fills/0", "fills/0/color"] } else { &["stroke", "strokes/0/color"] };
|
||||
for k in keys {
|
||||
n.bound.remove(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
let idx = index.unwrap_or(list.len().saturating_sub(1));
|
||||
// The paint's colour binding goes with it; later paints' move down one.
|
||||
super::lists::remove_item(n, if fills { super::lists::BoundList::Fills } else { super::lists::BoundList::Strokes }, idx);
|
||||
}
|
||||
}
|
||||
ok()
|
||||
|
||||
Reference in New Issue
Block a user