mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 21:57:12 -06:00
Type: Change Case, Smart Punctuation and other commands that change nothing record no undo step
These commands edited the document even when nothing changed, so the call recorded an undo step, emptied Redo and marked the document modified: - Change Case to the case the text has, Smart Punctuation on type with nothing left to change or on a selection with no type in it, and Find and Replace when every match is replaced with the text it is. All three rewrite the text runs through edit_runs, which now edits only when a run changes. - Convert To Area Type with no point type among the objects, and Convert To Point Type with no area type among them. - Type on a Path Options given the values the type has, without Flip: OK in the dialog with nothing changed, or a Type on a Path effect the type has. - Fill with Placeholder Text on type that holds its placeholder text already. - type.insert with an empty text on type that ends with a run of text. These commands now return without editing the document. The replies have the same form as before. A call that changes something still records one undo step. One line in the evidence list of docs/target-app-parity.md names every command above except type.insert.
This commit is contained in:
@@ -132,9 +132,10 @@ fn ids_json(ids: &[NodeId]) -> Value {
|
||||
json!({ "ids": ids.iter().map(|i| i.0).collect::<Vec<_>>() })
|
||||
}
|
||||
|
||||
/// Rewrite every run of the given text objects with `f(run text, state)`.
|
||||
/// Rewrite every run of the given text objects with `f(run text, state)`. When no run changes, the
|
||||
/// document and its undo history stay as they are.
|
||||
fn edit_runs<S: Default>(s: &mut Session, label: &str, ids: &[NodeId], f: impl Fn(&str, &mut S) -> String) -> Result<usize> {
|
||||
s.edit(label, |d, _| {
|
||||
super::overprint::edit_counted(s, label, |d| {
|
||||
let mut changed = 0;
|
||||
for id in ids {
|
||||
let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
@@ -260,6 +261,11 @@ fn smart_punctuation(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
|
||||
fn to_area(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ids = texts(s, p, "type.convertToAreaType")?;
|
||||
// With no point type among the objects, the document and its undo history stay as they are.
|
||||
let d = &s.doc()?.doc;
|
||||
if !ids.iter().any(|id| matches!(d.node(*id).map(|n| &n.kind), Some(NodeKind::Text(t)) if matches!(t.kind, TextKind::Point))) {
|
||||
return Ok(ids_json(&ids));
|
||||
}
|
||||
s.edit("Convert To Area Type", |d, _| {
|
||||
for id in &ids {
|
||||
let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
@@ -302,6 +308,11 @@ fn locate(runs: &[TextRun], byte: usize) -> Option<(usize, usize)> {
|
||||
|
||||
fn to_point(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ids = texts(s, p, "type.convertToPointType")?;
|
||||
// With no area type among the objects, the document and its undo history stay as they are.
|
||||
let d = &s.doc()?.doc;
|
||||
if !ids.iter().any(|id| matches!(d.node(*id).map(|n| &n.kind), Some(NodeKind::Text(t)) if matches!(t.kind, TextKind::Area { .. }))) {
|
||||
return Ok(ids_json(&ids));
|
||||
}
|
||||
s.edit("Convert To Point Type", |d, _| {
|
||||
for id in &ids {
|
||||
let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
@@ -418,6 +429,22 @@ fn path_options(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
None => None,
|
||||
};
|
||||
let spacing = p.get("spacing").and_then(Value::as_f64).filter(|x| x.is_finite()).map(|x| x.clamp(-1000.0, 1000.0));
|
||||
// Without a flip, when every object already has every value given, the document and its undo
|
||||
// history stay as they are.
|
||||
let has_values = |t: &TextObject| match &t.kind {
|
||||
TextKind::OnPath { start: st, end: en, .. } => {
|
||||
start.is_none_or(|v| v == *st)
|
||||
&& end.is_none_or(|v| v == *en)
|
||||
&& effect.is_none_or(|e| t.path_effect == e)
|
||||
&& align.is_none_or(|a| t.path_align == a)
|
||||
&& spacing.is_none_or(|v| t.path_spacing == v)
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
let d = &s.doc()?.doc;
|
||||
if !flip && ids.iter().all(|id| path_text(d.node(*id)).is_none_or(has_values)) {
|
||||
return options(s);
|
||||
}
|
||||
s.edit("Type on a Path Options", |d, _| {
|
||||
for id in &ids {
|
||||
let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
@@ -457,13 +484,18 @@ start as rough drafts like this one. Keep going until the frame is full and the
|
||||
|
||||
fn fill_placeholder(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let ids = texts(s, p, "type.fillPlaceholder")?;
|
||||
s.edit("Fill with Placeholder Text", |d, _| {
|
||||
// When filling leaves every object as it was, the document and its undo history stay as they
|
||||
// are.
|
||||
super::overprint::edit_counted(s, "Fill with Placeholder Text", |d| {
|
||||
let mut changed = 0;
|
||||
for id in &ids {
|
||||
if let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) {
|
||||
let before = t.clone();
|
||||
fill_with_placeholder(t);
|
||||
changed += usize::from(*t != before);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
Ok(changed)
|
||||
})?;
|
||||
Ok(ids_json(&ids))
|
||||
}
|
||||
@@ -539,6 +571,16 @@ fn insert_char(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
_ => return Err(bad(C, "give `char` or `text`")),
|
||||
};
|
||||
let ids = texts(s, p, C)?;
|
||||
// When `text` is empty and every object ends with a run of text, the document and its undo
|
||||
// history stay as they are.
|
||||
let d = &s.doc()?.doc;
|
||||
let ends_with_text = |id: &NodeId| match d.node(*id).map(|n| &n.kind) {
|
||||
Some(NodeKind::Text(t)) => t.runs.last().is_some_and(|r| r.inline.is_none()),
|
||||
_ => false,
|
||||
};
|
||||
if text.is_empty() && ids.iter().all(ends_with_text) {
|
||||
return Ok(ids_json(&ids));
|
||||
}
|
||||
s.edit("Typing", |d, _| {
|
||||
for id in &ids {
|
||||
let Some(NodeKind::Text(t)) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
|
||||
Reference in New Issue
Block a user