feat(commands): step the selection-first text commands (bucket C)

Add SelectThenValueCommand (gather selection, then one value) and convert:
- SelectThenValueCommand: HYPERLINK (url), ARCTEXT (text), TEXTFIT (width),
  SCALETEXT (factor), TCOUNT (start), TEXTMASK (Enter to apply).
- SelectThenKeywordCommand: JUSTIFYTEXT (Left/Center/Right/Middle/Aligned/Fit),
  TCASE (Upper/Lower/Sentence/Title).

Each bare form gathers a selection when none is set, prompts, and delegates to
the inline handler. OBJECTSCALE already opens its dialog; TABLE CELL takes three
values (row/col/text) and stays typeable — TABLE bare already builds a table
interactively.
This commit is contained in:
Hakan Seven committed 2026-07-16 14:37:10 +03:00
1 parent cf5362aeab
commit 208e617868
4 files changed
+185 -8

No files matched your search

+78
View File
@@ -595,6 +595,84 @@ impl CadCommand for SelectThenKeywordCommand {
}
}
/// Generic interactive front-end for a single-value command that operates on
/// the current selection (HYPERLINK url, ARCTEXT text, TEXTFIT width, TCASE
/// aside…). Gathers a selection first when none is set (Enter confirms), then
/// prompts for one value and dispatches `<name> <value>` to the inline handler,
/// which reads the still-selected set. A bare Enter on the value step dispatches
/// `<name>` alone — for commands whose value is optional (TCOUNT, TEXTMASK).
pub struct SelectThenValueCommand {
name: &'static str,
value_prompt: &'static str,
gathering: bool,
selected: Vec<Handle>,
}
impl SelectThenValueCommand {
pub fn new(name: &'static str, value_prompt: &'static str, has_selection: bool) -> Self {
Self {
name,
value_prompt,
gathering: !has_selection,
selected: Vec::new(),
}
}
}
impl CadCommand for SelectThenValueCommand {
fn name(&self) -> &'static str {
self.name
}
fn prompt(&self) -> String {
if self.gathering {
format!("{} select objects, then press Enter:", self.name)
} else {
self.value_prompt.to_string()
}
}
fn wants_text_input(&self) -> bool {
!self.gathering
}
fn is_selection_gathering(&self) -> bool {
self.gathering
}
fn on_selection_complete(&mut self, handles: Vec<Handle>) -> CmdResult {
self.selected = handles;
CmdResult::NeedPoint
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
if self.gathering {
return None;
}
let t = text.trim();
if t.is_empty() {
return None;
}
Some(CmdResult::Dispatch(format!("{} {t}", self.name)))
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
CmdResult::NeedPoint
}
fn on_enter(&mut self) -> CmdResult {
if self.gathering {
if self.selected.is_empty() {
return CmdResult::Cancel;
}
self.gathering = false;
return CmdResult::NeedPoint;
}
// Value step, nothing typed: the no-argument form (optional value).
CmdResult::Dispatch(format!("{} ", self.name))
}
}
// ── Result token ──────────────────────────────────────────────────────────
/// Returned by every `CadCommand` method to tell main.rs what to do.