mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-31 21:27:00 -06:00
Direct Selection: a marquee selects area type's frame anchors
Squashed from #4435 onto main.
This commit is contained in:
@@ -96,7 +96,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Reshape Type Area",
|
||||
[],
|
||||
None,
|
||||
"{id, anchors: [[subpath, anchor]…], dx, dy} move anchors of area type's frame (the type area) by dx, dy points, with their handles; the text reflows at its size, through its thread too (what Direct Selection does dragging a frame corner or edge)",
|
||||
"{id, anchors: [[subpath, anchor]…], dx, dy} or {items: [{id, anchors}…], dx, dy} move anchors of area type's frame (the type area) by dx, dy points, with their handles; the text reflows at its size, through its thread too (what Direct Selection does dragging a frame corner or edge, or the frame anchors its marquee selected, and the arrow keys nudging those); with items, every listed frame moves in one step",
|
||||
has_doc,
|
||||
reshape_area
|
||||
),
|
||||
@@ -1063,22 +1063,45 @@ fn area_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
fn reshape_area(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "text.reshapeArea";
|
||||
let id = id_param(p, "id").ok_or_else(|| bad(C, "missing id"))?;
|
||||
let mut refs = super::select::checked_refs(p.get("anchors"), C)?;
|
||||
refs.sort_unstable();
|
||||
refs.dedup();
|
||||
if refs.is_empty() {
|
||||
return Err(bad(C, "missing anchors [[subpath, anchor]…]"));
|
||||
// One area type (`id`, `anchors`), or several (`items`: [{id, anchors}…]).
|
||||
let raw: Vec<&Value> = match p.get("items") {
|
||||
Some(items) => {
|
||||
if p.get("id").is_some() || p.get("anchors").is_some() {
|
||||
return Err(bad(C, "give `id` and `anchors`, or `items`, not both"));
|
||||
}
|
||||
let items = items.as_array().ok_or_else(|| bad(C, "`items` must be an array of {id, anchors}"))?;
|
||||
if items.is_empty() {
|
||||
return Err(bad(C, "`items` is empty"));
|
||||
}
|
||||
items.iter().collect()
|
||||
}
|
||||
None => vec![p],
|
||||
};
|
||||
let mut items = Vec::with_capacity(raw.len());
|
||||
for it in raw {
|
||||
let id = id_param(it, "id").ok_or_else(|| bad(C, "missing id"))?;
|
||||
let mut refs = super::select::checked_refs(it.get("anchors"), C)?;
|
||||
refs.sort_unstable();
|
||||
refs.dedup();
|
||||
if refs.is_empty() {
|
||||
return Err(bad(C, "missing anchors [[subpath, anchor]…]"));
|
||||
}
|
||||
if items.iter().any(|(other, _)| *other == id) {
|
||||
return Err(bad(C, format!("area type {} is named twice", id.0)));
|
||||
}
|
||||
let node = s.doc()?.doc.node(id);
|
||||
if area_text(node).is_none() || node.is_some_and(|n| n.extras().perspective.is_some()) {
|
||||
return Err(bad(C, "not area type (text in a frame) outside perspective"));
|
||||
}
|
||||
items.push((id, refs));
|
||||
}
|
||||
let d = Vec2::new(f64_req(p, "dx", C)?, f64_req(p, "dy", C)?);
|
||||
let node = s.doc()?.doc.node(id);
|
||||
if area_text(node).is_none() || node.is_some_and(|n| n.extras().perspective.is_some()) {
|
||||
return Err(bad(C, "not area type (text in a frame) outside perspective"));
|
||||
}
|
||||
s.edit("Reshape Type Area", |doc, _| {
|
||||
let Some(NodeKind::Text(t)) = doc.node_mut(id).map(|n| &mut n.kind) else { return Err(EngineError::NoNode(id)) };
|
||||
if !reshape_area_with(t, |t| t.move_area_anchors(&refs, d)) {
|
||||
return Err(bad(C, "no such frame anchor, or the frame would reach past the canvas"));
|
||||
for (id, refs) in &items {
|
||||
let Some(NodeKind::Text(t)) = doc.node_mut(*id).map(|n| &mut n.kind) else { return Err(EngineError::NoNode(*id)) };
|
||||
if !reshape_area_with(t, |t| t.move_area_anchors(refs, d)) {
|
||||
return Err(bad(C, "no such frame anchor, or the frame would reach past the canvas"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
Reference in New Issue
Block a user