forked from 1b-agent/opencadstudio-upstream
Merge pull request #698 from gianlucafiore/fix/697
Fix TCOUNT numbering and add placement options
This commit is contained in:
commit
7ef5b431e1
2 files changed
+230
-19
No files matched your search
+69
-19
@@ -425,22 +425,44 @@ impl OpenCADStudio {
|
||||
// TCOUNT [start] — prefix selected single-line text with sequential
|
||||
// numbers in reading order (top-to-bottom, then left-to-right).
|
||||
"TCOUNT" => {
|
||||
use crate::command::SelectThenValueCommand;
|
||||
use crate::command::TCountCommand;
|
||||
|
||||
let has_sel = !self.tabs[i].scene.selected_entities().is_empty();
|
||||
let c = SelectThenValueCommand::new(
|
||||
"TCOUNT",
|
||||
"TCOUNT starting number (Enter for 1):",
|
||||
has_sel,
|
||||
);
|
||||
let c = TCountCommand::new(has_sel);
|
||||
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
cmd if cmd.starts_with("TCOUNT ") => {
|
||||
let start: i64 = cmd
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
let mut args = cmd.split_whitespace().skip(1);
|
||||
|
||||
let start: i64 = args
|
||||
.next()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(1);
|
||||
|
||||
let increment: i64 = args
|
||||
.next()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(1);
|
||||
|
||||
let placement = args
|
||||
.next()
|
||||
.unwrap_or("O")
|
||||
.to_uppercase();
|
||||
|
||||
let placement = match placement.as_str() {
|
||||
"O" | "OVERWRITE" => "O",
|
||||
"P" | "PREFIX" => "P",
|
||||
"S" | "SUFFIX" => "S",
|
||||
_ => {
|
||||
self.command_line.push_error(
|
||||
"TCOUNT: placement must be Overwrite, Prefix, or Suffix.",
|
||||
);
|
||||
return Some(Task::none());
|
||||
}
|
||||
};
|
||||
|
||||
let mut texts: Vec<(acadrust::Handle, f64, f64)> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
@@ -452,19 +474,28 @@ impl OpenCADStudio {
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
if texts.is_empty() {
|
||||
self.command_line
|
||||
.push_error(crate::t!("TCOUNT: select single-line text first.").as_ref());
|
||||
.push_error("TCOUNT: select single-line text first.");
|
||||
return Some(Task::none());
|
||||
}
|
||||
// Reading order: higher Y first, then smaller X.
|
||||
|
||||
// Keep the existing reading order:
|
||||
// higher Y first, then smaller X.
|
||||
texts.sort_by(|a, b| {
|
||||
b.2.partial_cmp(&a.2)
|
||||
.unwrap_or(std::cmp::Ordering::Equal)
|
||||
.then(a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
|
||||
.then(
|
||||
a.1.partial_cmp(&b.1)
|
||||
.unwrap_or(std::cmp::Ordering::Equal),
|
||||
)
|
||||
});
|
||||
|
||||
self.push_undo_snapshot(i, "TCOUNT");
|
||||
|
||||
let mut num = start;
|
||||
|
||||
for (h, _, _) in &texts {
|
||||
if let Some(acadrust::EntityType::Text(t)) = self.tabs[i]
|
||||
.scene
|
||||
@@ -472,20 +503,39 @@ impl OpenCADStudio {
|
||||
.entities_mut()
|
||||
.find(|e| e.common().handle == *h)
|
||||
{
|
||||
t.value = format!("{num} {}", t.value);
|
||||
num += 1;
|
||||
t.value = match placement {
|
||||
"P" => format!("{num} {}", t.value),
|
||||
"S" => format!("{} {num}", t.value),
|
||||
_ => num.to_string(),
|
||||
};
|
||||
|
||||
num += increment;
|
||||
}
|
||||
}
|
||||
|
||||
self.tabs[i].dirty = true;
|
||||
|
||||
let changes: Vec<_> = texts
|
||||
.iter()
|
||||
.map(|(handle, _, _)| (*handle, crate::scene::ChangeKind::Modified))
|
||||
.map(|(handle, _, _)| {
|
||||
(*handle, crate::scene::ChangeKind::Modified)
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.tabs[i].scene.bump_entities(&changes);
|
||||
self.command_line.push_output(crate::tf!(
|
||||
"TCOUNT: numbered {} text object(s) from {start}.",
|
||||
texts.len()
|
||||
).as_ref());
|
||||
|
||||
let placement_name = match placement {
|
||||
"P" => "Prefix",
|
||||
"S" => "Suffix",
|
||||
_ => "Overwrite",
|
||||
};
|
||||
|
||||
self.command_line.push_output(&format!(
|
||||
"TCOUNT: numbered {} text object(s) from {} by {} ({placement_name}).",
|
||||
texts.len(),
|
||||
start,
|
||||
increment,
|
||||
));
|
||||
}
|
||||
|
||||
"MLEADER" => {
|
||||
|
||||
+161
@@ -873,7 +873,168 @@ impl CadCommand for SelectThenValueCommand {
|
||||
CmdResult::Dispatch(format!("{} ", self.name))
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum TCountStep {
|
||||
Start,
|
||||
Increment,
|
||||
Placement,
|
||||
}
|
||||
|
||||
/// Interactive front-end for TCOUNT.
|
||||
///
|
||||
/// After gathering the text selection it asks for:
|
||||
/// 1. starting number,
|
||||
/// 2. increment,
|
||||
/// 3. placement mode (Overwrite / Prefix / Suffix).
|
||||
///
|
||||
/// The actual entity modification remains in the inline TCOUNT handler.
|
||||
pub struct TCountCommand {
|
||||
gathering: bool,
|
||||
selected: Vec<Handle>,
|
||||
step: TCountStep,
|
||||
start: i64,
|
||||
increment: i64,
|
||||
}
|
||||
|
||||
impl TCountCommand {
|
||||
pub fn new(has_selection: bool) -> Self {
|
||||
Self {
|
||||
gathering: !has_selection,
|
||||
selected: Vec::new(),
|
||||
step: TCountStep::Start,
|
||||
start: 1,
|
||||
increment: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch(&self, placement: &str) -> CmdResult {
|
||||
CmdResult::Dispatch(format!(
|
||||
"TCOUNT {} {} {}",
|
||||
self.start, self.increment, placement
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl CadCommand for TCountCommand {
|
||||
fn name(&self) -> &'static str {
|
||||
"TCOUNT"
|
||||
}
|
||||
|
||||
fn prompt(&self) -> String {
|
||||
if self.gathering {
|
||||
return "TCOUNT select text objects, then press Enter:".to_string();
|
||||
}
|
||||
|
||||
match self.step {
|
||||
TCountStep::Start => {
|
||||
format!("TCOUNT starting number <{}>:", self.start)
|
||||
}
|
||||
TCountStep::Increment => {
|
||||
format!("TCOUNT increment <{}>:", self.increment)
|
||||
}
|
||||
TCountStep::Placement => {
|
||||
"TCOUNT placement [Overwrite/Prefix/Suffix] <Overwrite>:".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn options(&self) -> Vec<CmdOption> {
|
||||
if self.gathering || self.step != TCountStep::Placement {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
vec![
|
||||
CmdOption::new("Overwrite", "O"),
|
||||
CmdOption::new("Prefix", "P"),
|
||||
CmdOption::new("Suffix", "S"),
|
||||
]
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
match self.step {
|
||||
TCountStep::Start => {
|
||||
if let Ok(value) = t.parse::<i64>() {
|
||||
self.start = value;
|
||||
self.step = TCountStep::Increment;
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
|
||||
TCountStep::Increment => {
|
||||
if let Ok(value) = t.parse::<i64>() {
|
||||
self.increment = value;
|
||||
self.step = TCountStep::Placement;
|
||||
}
|
||||
Some(CmdResult::NeedPoint)
|
||||
}
|
||||
|
||||
TCountStep::Placement => {
|
||||
let placement = match t.to_uppercase().as_str() {
|
||||
"O" | "OVERWRITE" => "O",
|
||||
"P" | "PREFIX" => "P",
|
||||
"S" | "SUFFIX" => "S",
|
||||
_ => return Some(CmdResult::NeedPoint),
|
||||
};
|
||||
|
||||
Some(self.dispatch(placement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
match self.step {
|
||||
TCountStep::Start => {
|
||||
self.start = 1;
|
||||
self.step = TCountStep::Increment;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
TCountStep::Increment => {
|
||||
self.increment = 1;
|
||||
self.step = TCountStep::Placement;
|
||||
CmdResult::NeedPoint
|
||||
}
|
||||
|
||||
TCountStep::Placement => self.dispatch("O"),
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Generic front-end for a two-value command that operates on the current
|
||||
/// selection (POLYSOLID width + height on a selected polyline). Gathers a
|
||||
/// selection first when none is set, prompts for two values, and dispatches
|
||||
|
||||
Reference in new issue
Block a user