mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-24 09:46:59 -06:00
Type: text.create and text.createInPath return the new type's bounds (#2413)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
bflatastic
co-authored by
Claude Sonnet 5.5
parent
d3751cc07b
commit
4f30e52368
@@ -74,7 +74,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Create Text",
|
||||
[],
|
||||
None,
|
||||
"{x, y, text, vertical?: bool = false, size?: pt, font?: family, style?, color?, area?: {width, height, fit?: none|autoHeight|shrinkText, fitMinPercent?}, placeholder?: bool (fill it with placeholder text instead, as the Type tools do with prefs placeholderText), justify?: \"auto\" (default: the start of each paragraph's direction)|\"left\"|\"center\"|\"right\"|\"justifyAll\" (point type: aligned on x, so \"center\" centres each line on the point and \"right\" ends it there; text.setStyle changes it later), leadingModel?: \"romanBaseline\"|\"emBoxTop\", charAlign?: \"romanBaseline\"|\"emBoxTop\"|\"emBoxCenter\"|\"emBoxBottom\"|\"icfTop\"|\"icfBottom\" (default: emBoxTop and emBoxCenter while the interface is in Japanese, else romanBaseline)} → {id, overflow?: [id] (area type whose text doesn't fit its frame: the lines past it aren't drawn; left out when it fits)}; new area type gets Auto Size (fit autoHeight) when the autoSizeAreaType preference is on",
|
||||
"{x, y, text, vertical?: bool = false, size?: pt, font?: family, style?, color?, area?: {width, height, fit?: none|autoHeight|shrinkText, fitMinPercent?}, placeholder?: bool (fill it with placeholder text instead, as the Type tools do with prefs placeholderText), justify?: \"auto\" (default: the start of each paragraph's direction)|\"left\"|\"center\"|\"right\"|\"justifyAll\" (point type: aligned on x, so \"center\" centres each line on the point and \"right\" ends it there; text.setStyle changes it later), leadingModel?: \"romanBaseline\"|\"emBoxTop\", charAlign?: \"romanBaseline\"|\"emBoxTop\"|\"emBoxCenter\"|\"emBoxBottom\"|\"icfTop\"|\"icfBottom\" (default: emBoxTop and emBoxCenter while the interface is in Japanese, else romanBaseline)} → {id, bounds: {x, y, width, height} (the new type's box as document.inspect reports it, after justify), overflow?: [id] (area type whose text doesn't fit its frame: the lines past it aren't drawn; left out when it fits)}; new area type gets Auto Size (fit autoHeight) when the autoSizeAreaType preference is on",
|
||||
has_doc,
|
||||
text_create
|
||||
),
|
||||
@@ -512,6 +512,7 @@ fn text_create(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
let made = add_node(s, "Type", NodeKind::Text(Box::new(t)), Appearance::default(), None)?;
|
||||
let id = made.get("id").and_then(Value::as_u64).map(NodeId);
|
||||
let made = super::typecmd::with_bounds(s, id, made);
|
||||
Ok(super::typecmd::with_overflow(s, id.as_slice(), made))
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Area / Path Type",
|
||||
[],
|
||||
None,
|
||||
"{path: id, mode: \"area\"|\"onPath\", text?: \"\", vertical?: bool = false, at?: [x, y] (on-path start: nearest point), size?, font?, style?, color? (as text.create), fit?: none|autoHeight|shrinkText, fitMinPercent? (area: as text.areaOptions; default autoHeight when the autoSizeAreaType preference is on), placeholder?: bool (placeholder text instead, as text.create), justify?, leadingModel?, charAlign? (as text.create)} turn a path into an area-type frame or a type-on-a-path baseline (the path's paint is dropped) → {id, overflow?: [id] (the text doesn't fit; left out when it fits)}",
|
||||
"{path: id, mode: \"area\"|\"onPath\", text?: \"\", vertical?: bool = false, at?: [x, y] (on-path start: nearest point), size?, font?, style?, color? (as text.create), fit?: none|autoHeight|shrinkText, fitMinPercent? (area: as text.areaOptions; default autoHeight when the autoSizeAreaType preference is on), placeholder?: bool (placeholder text instead, as text.create), justify?, leadingModel?, charAlign? (as text.create)} turn a path into an area-type frame or a type-on-a-path baseline (the path's paint is dropped) → {id, bounds: {x, y, width, height} (as text.create), overflow?: [id] (the text doesn't fit; left out when it fits)}",
|
||||
has_doc,
|
||||
create_in_path
|
||||
),
|
||||
@@ -609,7 +609,8 @@ fn create_in_path(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
sel.set([id]);
|
||||
Ok(id)
|
||||
})?;
|
||||
Ok(super::typecmd::with_overflow(s, &[id], json!({"id": id.0})))
|
||||
let made = super::typecmd::with_bounds(s, Some(id), json!({"id": id.0}));
|
||||
Ok(super::typecmd::with_overflow(s, &[id], made))
|
||||
}
|
||||
|
||||
/// What `type.step` changes.
|
||||
|
||||
@@ -269,6 +269,17 @@ pub(crate) fn with_overflow(s: &Session, ids: &[NodeId], mut result: Value) -> V
|
||||
result
|
||||
}
|
||||
|
||||
/// Adds `bounds` to the result of a command that made the type object `id`: the same
|
||||
/// `{x, y, width, height}` box `document.inspect` reports for it (after `justify`, so a script can
|
||||
/// place the new type without a helper object); `null` when it has none.
|
||||
pub(crate) fn with_bounds(s: &Session, id: Option<NodeId>, mut result: Value) -> Value {
|
||||
let Ok(st) = s.doc() else { return result };
|
||||
let symbols = st.doc.symbol_boxes();
|
||||
let b = id.and_then(|i| st.doc.node(i)).and_then(|n| n.geometric_bounds_with(&symbols));
|
||||
result["bounds"] = crate::inspect::rect_json(b);
|
||||
result
|
||||
}
|
||||
|
||||
/// The warning that Print gives for `ids`, area type and type on a path whose text doesn't fit
|
||||
/// (as [`with_overflow`] finds it): the text past the end of their frames doesn't print. It names
|
||||
/// the first 20 ids and counts the rest; `None` when `ids` is empty.
|
||||
@@ -783,6 +794,30 @@ fn reshape_area(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
mod area_tests {
|
||||
use super::*;
|
||||
|
||||
/// #2413: `text.create` and `text.createInPath` return the new type's `bounds`, the box
|
||||
/// `document.inspect` reports for it, so a script can place it without a helper object.
|
||||
#[test]
|
||||
fn text_create_returns_the_inspect_bounds() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 600, "height": 400})).unwrap();
|
||||
let inspected = |s: &mut Session, id: &Value| {
|
||||
let doc = s.execute("document.inspect", &json!({})).unwrap();
|
||||
fn find(v: &Value, id: &Value) -> Option<Value> {
|
||||
if &v["id"] == id {
|
||||
return Some(v["bounds"].clone());
|
||||
}
|
||||
v.get("children").or_else(|| v.get("layers"))?.as_array()?.iter().find_map(|c| find(c, id))
|
||||
}
|
||||
find(&doc, id).unwrap()
|
||||
};
|
||||
let made = s.execute("text.create", &json!({"x": 300, "y": 100, "text": "Hello", "size": 24, "justify": "center"})).unwrap();
|
||||
assert!(made["bounds"]["width"].as_f64().unwrap() > 10.0, "{made}");
|
||||
assert_eq!(made["bounds"], inspected(&mut s, &made["id"]));
|
||||
let rect = s.execute("shape.rectangle", &json!({"x": 10, "y": 200, "width": 100, "height": 50})).unwrap();
|
||||
let area = s.execute("text.createInPath", &json!({"path": rect["id"], "mode": "area", "text": "Boxed"})).unwrap();
|
||||
assert_eq!(area["bounds"], inspected(&mut s, &area["id"]));
|
||||
}
|
||||
|
||||
/// #1748: `text.create` takes `justify`; point type then lines up on its point (centred on
|
||||
/// it, or ending there), and `text.setStyle` moves it the same way afterwards.
|
||||
#[test]
|
||||
|
||||
@@ -6,7 +6,7 @@ use vectorcraft_doc::{ArrowAlign, Document, LineCap, LineJoin, Node, NodeKind, S
|
||||
|
||||
use crate::Session;
|
||||
|
||||
fn rect_json(r: Option<vectorcraft_geom::Rect>) -> Value {
|
||||
pub(crate) fn rect_json(r: Option<vectorcraft_geom::Rect>) -> Value {
|
||||
match r {
|
||||
Some(r) => json!({ "x": r.x0, "y": r.y0, "width": r.width(), "height": r.height() }),
|
||||
None => Value::Null,
|
||||
|
||||
+2
-1
@@ -3165,7 +3165,8 @@ Text that doesn't fit is drawn up to the last line that does, so a one-line head
|
||||
draws nothing. The commands that make type or change what it holds say so in their own reply: `text.create`,
|
||||
`text.createInPath`, `text.setText` and `text.setStyle` add `overflow: [id…]` (the area type and type on a path among
|
||||
the objects they made or changed whose text doesn't fit), and the `add_text` tool adds `overflow: true`. The key is
|
||||
left out when everything fits. The warnings of `file.print` and `print.preview` give the ids of the objects that print
|
||||
left out when everything fits. `text.create` and `text.createInPath` also return `bounds: {x, y, width, height}`, the new type's box
|
||||
as `document.inspect` reports it (after `justify`), so a script can place the type without a helper object (#2413). The warnings of `file.print` and `print.preview` give the ids of the objects that print
|
||||
and whose text doesn't fit: the first 20 ids, then how many more.
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user