mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-27 20:36:48 -06:00
TEXTEDIT's prompt no longer reads "or or". Undo reverts the last edit made in this TEXTEDIT, and Mode switches between Multiple (keep selecting) and Single (end after one edit). At the command line and in scripts, MTEXT takes one paragraph per input line until an empty line, instead of ending after the first line and running the next one as a command. The Text Editor window sends its contents and then Enter, so it still ends MTEXT. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a6b728b0f7
commit
ca882b38e2
@@ -1315,6 +1315,9 @@ impl Interactive for TextM {
|
||||
struct MTextM {
|
||||
first: Option<Vec2>,
|
||||
second: Option<Vec2>,
|
||||
/// Paragraphs typed so far: one per input line, an empty line ends the text (as on
|
||||
/// AutoCAD's command line and in scripts).
|
||||
lines: Vec<String>,
|
||||
}
|
||||
|
||||
impl Interactive for MTextM {
|
||||
@@ -1327,14 +1330,19 @@ impl Interactive for MTextM {
|
||||
(Some(a), None) => Prompt::new("Specify opposite corner", Accept::POINT)
|
||||
.kw(&["Height", "Justify", "Line spacing", "Rotation", "Style", "Width", "Columns"])
|
||||
.base(a),
|
||||
_ => Prompt::new("Enter text (use \\P for new paragraphs)", Accept::TEXT),
|
||||
_ if self.lines.is_empty() => Prompt::new("Enter text (use \\P for new paragraphs)", Accept::TEXT),
|
||||
_ => Prompt::new("Enter next line of text (empty line to finish)", Accept::TEXT),
|
||||
}
|
||||
}
|
||||
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
|
||||
match (self.first, self.second, i) {
|
||||
(None, _, Input::Point(p)) => self.first = Some(p),
|
||||
(Some(_), None, Input::Point(p)) => self.second = Some(p),
|
||||
(Some(a), Some(b), Input::Text(t)) => {
|
||||
(Some(_), Some(_), Input::Text(t)) if !t.is_empty() => self.lines.push(t),
|
||||
(Some(a), Some(b), Input::Text(_) | Input::Enter) => {
|
||||
if self.lines.is_empty() {
|
||||
return Ok(Step::Done);
|
||||
}
|
||||
let d = s.doc()?;
|
||||
let h = d.header.f64("TEXTSIZE", 0.2);
|
||||
let style = d.header.str("TEXTSTYLE", "Standard");
|
||||
@@ -1346,7 +1354,7 @@ impl Interactive for MTextM {
|
||||
attach: 1,
|
||||
rotation: 0.0,
|
||||
style,
|
||||
contents: t,
|
||||
contents: self.lines.join("\\P"),
|
||||
line_spacing: 1.0,
|
||||
}))?;
|
||||
return Ok(Step::Done);
|
||||
|
||||
@@ -2269,6 +2269,12 @@ fn run_textedit(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
#[derive(Default)]
|
||||
struct TextEditM {
|
||||
h: Option<(Handle, String)>,
|
||||
/// Edits made in this run, newest last: each object as it was before (the Undo option).
|
||||
edits: Vec<(Handle, EntityKind)>,
|
||||
/// Single mode: end after one edit (the Mode option; Multiple by default).
|
||||
single: bool,
|
||||
/// Asking for the edit mode.
|
||||
mode: bool,
|
||||
}
|
||||
|
||||
impl Interactive for TextEditM {
|
||||
@@ -2276,12 +2282,26 @@ impl Interactive for TextEditM {
|
||||
"TEXTEDIT"
|
||||
}
|
||||
fn prompt(&self, _s: &Session) -> Prompt {
|
||||
if self.mode {
|
||||
let current = if self.single { "Single" } else { "Multiple" };
|
||||
return Prompt::new("Enter a text edit mode option", curves::KW).kw(&["Single", "Multiple"]).default(current);
|
||||
}
|
||||
match &self.h {
|
||||
None => Prompt::new("Select an annotation object or", Accept::POINT).kw(&["Undo", "Mode"]),
|
||||
None => Prompt::new("Select an annotation object", Accept::POINT).kw(&["Undo", "Mode"]),
|
||||
Some((_, old)) => Prompt::new("Enter new text", Accept::TEXT).default(old.chars().take(40).collect::<String>()),
|
||||
}
|
||||
}
|
||||
fn input(&mut self, s: &mut Session, i: Input) -> Result<Step> {
|
||||
if self.mode {
|
||||
match i {
|
||||
Input::Keyword(k) if k == "Single" => self.single = true,
|
||||
Input::Keyword(k) if k == "Multiple" => self.single = false,
|
||||
Input::Enter => {}
|
||||
_ => return Err(other("Enter Single or Multiple.")),
|
||||
}
|
||||
self.mode = false;
|
||||
return Ok(Step::Continue);
|
||||
}
|
||||
match (&self.h, i) {
|
||||
(None, Input::Point(p)) => {
|
||||
let h = curves::pick_at(s, p).ok_or_else(|| other("*Invalid selection*"))?;
|
||||
@@ -2290,16 +2310,29 @@ impl Interactive for TextEditM {
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
(Some((h, _)), Input::Text(t) | Input::Keyword(t)) => {
|
||||
set_text(s, *h, &t)?;
|
||||
let h = *h;
|
||||
let before = curves::entity(s, h)?.kind;
|
||||
set_text(s, h, &t)?;
|
||||
self.edits.push((h, before));
|
||||
self.h = None;
|
||||
Ok(Step::Continue)
|
||||
Ok(if self.single { Step::Done } else { Step::Continue })
|
||||
}
|
||||
(Some(_), Input::Enter) => {
|
||||
self.h = None;
|
||||
Ok(Step::Continue)
|
||||
Ok(if self.single { Step::Done } else { Step::Continue })
|
||||
}
|
||||
(None, Input::Enter) => Ok(Step::Done),
|
||||
(None, Input::Keyword(_)) => Ok(Step::Continue),
|
||||
(None, Input::Keyword(k)) if k == "Undo" => {
|
||||
match self.edits.pop() {
|
||||
Some((h, before)) => set_kind(s, h, before)?,
|
||||
None => s.echo("Everything has been undone"),
|
||||
}
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
(None, Input::Keyword(k)) if k == "Mode" => {
|
||||
self.mode = true;
|
||||
Ok(Step::Continue)
|
||||
}
|
||||
_ => Ok(Step::Continue),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//! MTEXT contents from scripts and the command line; TEXTEDIT's prompt and options.
|
||||
|
||||
use cadcraft_engine::Session;
|
||||
use cadcraft_engine::doc::{EntityKind, Space};
|
||||
|
||||
fn kinds(s: &Session) -> Vec<EntityKind> {
|
||||
s.doc().ok().and_then(|d| d.space(&Space::Model)).map(|st| st.iter().map(|e| e.kind.clone()).collect()).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn mtexts(s: &Session) -> Vec<String> {
|
||||
kinds(s)
|
||||
.into_iter()
|
||||
.filter_map(|k| match k {
|
||||
EntityKind::MText(t) => Some(t.contents),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn texts(s: &Session) -> Vec<String> {
|
||||
kinds(s)
|
||||
.into_iter()
|
||||
.filter_map(|k| match k {
|
||||
EntityKind::Text(t) => Some(t.value),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn script_mtext_takes_lines_until_an_empty_line() {
|
||||
let mut s = Session::new();
|
||||
s.script("MTEXT 0,10 20,0\nFirst line\nSecond line\n\nLINE 0,0 5,5\n\n").unwrap();
|
||||
assert!(s.running.is_none(), "{}", s.prompt_text());
|
||||
assert_eq!(mtexts(&s), vec!["First line\\PSecond line".to_string()]);
|
||||
assert_eq!(kinds(&s).len(), 2, "{:?}", kinds(&s));
|
||||
|
||||
// Typed at the command line: spaces stay in the text, Enter on an empty line ends it.
|
||||
let mut s = Session::new();
|
||||
s.cmdline("MTEXT 0,10 20,0").unwrap();
|
||||
s.cmdline("Hello world").unwrap();
|
||||
assert!(s.running.is_some());
|
||||
s.cmdline("").unwrap();
|
||||
assert!(s.running.is_none(), "{}", s.prompt_text());
|
||||
assert_eq!(mtexts(&s), vec!["Hello world".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn textedit_prompt_mode_and_undo() {
|
||||
let mut s = Session::new();
|
||||
s.cmdline("TEXT 0,0 1 0 old").unwrap();
|
||||
s.cmdline("").unwrap();
|
||||
s.cmdline("TEXTEDIT").unwrap();
|
||||
let p = s.prompt_text();
|
||||
assert!(!p.contains("or or"), "{p}");
|
||||
// Edit, then Undo restores the old text.
|
||||
s.cmdline("0.5,0.5").unwrap();
|
||||
s.cmdline("new").unwrap();
|
||||
assert_eq!(texts(&s), vec!["new".to_string()]);
|
||||
s.cmdline("u").unwrap();
|
||||
assert_eq!(texts(&s), vec!["old".to_string()]);
|
||||
// Mode Single: the command ends after one edit.
|
||||
s.cmdline("m").unwrap();
|
||||
s.cmdline("s").unwrap();
|
||||
s.cmdline("0.5,0.5").unwrap();
|
||||
s.cmdline("once").unwrap();
|
||||
assert!(s.running.is_none(), "{}", s.prompt_text());
|
||||
assert_eq!(texts(&s), vec!["once".to_string()]);
|
||||
}
|
||||
@@ -388,7 +388,13 @@ fn mtext_editor(app: &mut CadApp, ctx: &egui::Context) {
|
||||
ctx.data_mut(|d| d.insert_temp(id, buf));
|
||||
if let Some(t) = submit {
|
||||
ctx.data_mut(|d| d.remove::<String>(id));
|
||||
let _ = app.session.input(cadcraft_engine::Input::Text(t));
|
||||
// The editor holds the whole contents: one input, then Enter ends MTEXT's text entry.
|
||||
if !t.is_empty() {
|
||||
let _ = app.session.input(cadcraft_engine::Input::Text(t));
|
||||
}
|
||||
if app.session.running.as_ref().is_some_and(|r| r.id == "mtext") {
|
||||
let _ = app.session.input(cadcraft_engine::Input::Enter);
|
||||
}
|
||||
} else if cancel {
|
||||
ctx.data_mut(|d| d.remove::<String>(id));
|
||||
app.session.cancel();
|
||||
|
||||
@@ -612,7 +612,7 @@
|
||||
Select a line. Виберіть відрізок.
|
||||
Select a polyline. Виберіть полілінію.
|
||||
Select a text, mtext, attribute definition or dimension. Виберіть текст, багаторядковий текст, визначення атрибута або розмір.
|
||||
Select an annotation object or Виберіть об'єкт анотації або
|
||||
Select an annotation object Виберіть об'єкт анотації
|
||||
Select an arc or circle Виберіть дугу або коло
|
||||
Select an arc. Виберіть дугу.
|
||||
Select an object Виберіть об'єкт
|
||||
|
||||
|
Reference in New Issue
Block a user