forked from 1b-agent/opencadstudio-upstream
refactor: split four oversized files into submodule trees
Break up the four giant files (~29k lines total) into focused submodule
directories, one concern per file. Pure mechanical moves — behavior is
unchanged.
- app/update.rs (9650) -> app/update/{mod,viewport,command,style,file,
dynamic,dialog,util}. The 384-arm update_inner match stays in mod.rs
with thin delegating arms; the 66 fattest arm bodies move to topic
methods. Arm set preserved exactly (384 -> 384).
- scene/mod.rs (9017) -> scene/{mod,entity,tess,hittest,layout,paper,
mspace,project,selection,modify,group_layer,preview}. The impl Scene
body is split across files via inherent-impl-per-file; struct, ctor,
fields and load-time helpers stay in the root. fn count 123 -> 123.
- app/commands.rs (6082) -> app/commands/* grouped by command family.
The 233-arm dispatch match becomes source-ordered per-family
dispatch_* handlers, preserving first-match precedence (233 -> 233).
- app/view.rs (4686) -> app/view/{mod,overlay,modal,viewcube,controls}.
Largest file drops from 9650 to 3343. Builds clean; app launches and
loads plugins with no panic.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
2baabdf4a0
commit
bddc050b3e
39 files changed
+26674
-26113
No files matched your search
@@ -0,0 +1,638 @@
|
||||
use super::*;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_styleprops(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
// ── LINETYPE management ───────────────────────────────────────
|
||||
cmd if cmd == "LINETYPE"
|
||||
|| cmd == "LT"
|
||||
|| cmd.starts_with("LINETYPE ")
|
||||
|| cmd.starts_with("LT ") =>
|
||||
{
|
||||
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
||||
let parts: Vec<&str> = raw_rest.split_whitespace().collect();
|
||||
let sub = parts.get(0).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||
match sub.as_str() {
|
||||
"" | "LIST" | "?" => {
|
||||
let ltypes: Vec<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.line_types
|
||||
.iter()
|
||||
.map(|lt| format!("{} ({})", lt.name, lt.description))
|
||||
.collect();
|
||||
if ltypes.is_empty() {
|
||||
self.command_line.push_output("No linetypes defined.");
|
||||
} else {
|
||||
self.command_line
|
||||
.push_output(&format!("Linetypes: {}", ltypes.join(", ")));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.command_line.push_info("Usage: LINETYPE LIST");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── PURGE unused definitions ──────────────────────────────────
|
||||
cmd if cmd == "PURGE" || cmd.starts_with("PURGE ") => {
|
||||
let sub = cmd
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.unwrap_or("ALL")
|
||||
.to_uppercase();
|
||||
let all = sub == "ALL" || sub.is_empty();
|
||||
|
||||
// Collect names in use (immutable borrows — done in their own scope)
|
||||
let used_layers: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|e| {
|
||||
let name = &e.common().layer;
|
||||
if name.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(name.clone())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let used_text_styles: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|e| match e {
|
||||
acadrust::EntityType::Text(t) => Some(t.style.clone()),
|
||||
acadrust::EntityType::MText(t) => Some(t.style.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
let used_linetypes: rustc_hash::FxHashSet<String> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter_map(|e| {
|
||||
let lt = &e.common().linetype;
|
||||
if lt.is_empty() || lt == "ByLayer" || lt == "ByBlock" {
|
||||
None
|
||||
} else {
|
||||
Some(lt.clone())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Build removal lists (still immutable)
|
||||
let layer_remove: Vec<String> = if all || sub == "LAYERS" {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.layers
|
||||
.iter()
|
||||
.filter(|l| l.name != "0" && !used_layers.contains(&l.name))
|
||||
.map(|l| l.name.clone())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let style_remove: Vec<String> = if all || sub == "TEXTSTYLES" || sub == "STYLES" {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.text_styles
|
||||
.iter()
|
||||
.filter(|s| s.name != "Standard" && !used_text_styles.contains(&s.name))
|
||||
.map(|s| s.name.clone())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
let lt_remove: Vec<String> = if all || sub == "LINETYPES" || sub == "LT" {
|
||||
let standard = ["Continuous", "ByLayer", "ByBlock"];
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.line_types
|
||||
.iter()
|
||||
.filter(|lt| {
|
||||
!standard.iter().any(|s| s.eq_ignore_ascii_case(<.name))
|
||||
&& !used_linetypes.contains(<.name)
|
||||
})
|
||||
.map(|lt| lt.name.clone())
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
// Apply removals (mutable)
|
||||
let purged = layer_remove.len() + style_remove.len() + lt_remove.len();
|
||||
for name in &layer_remove {
|
||||
self.tabs[i].scene.document.layers.remove(name);
|
||||
}
|
||||
for name in &style_remove {
|
||||
self.tabs[i].scene.document.text_styles.remove(name);
|
||||
}
|
||||
for name in <_remove {
|
||||
self.tabs[i].scene.document.line_types.remove(name);
|
||||
}
|
||||
|
||||
if purged > 0 {
|
||||
self.push_undo_snapshot(i, "PURGE");
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("PURGE: {} definition(s) removed.", purged));
|
||||
} else {
|
||||
self.command_line.push_output("PURGE: nothing to purge.");
|
||||
}
|
||||
}
|
||||
|
||||
// ── CHPROP — change entity properties from command line ───────
|
||||
cmd if cmd == "CHPROP" || cmd.starts_with("CHPROP ") => {
|
||||
// Usage: CHPROP <property> <value>
|
||||
// Applies to currently selected entities.
|
||||
// Properties: LAYER, COLOR, LINETYPE, LTSCALE
|
||||
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
||||
let prop = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||
let value = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
|
||||
|
||||
if prop.is_empty() {
|
||||
self.command_line.push_info(
|
||||
"Usage: CHPROP <prop> <val> (props: LAYER COLOR LINETYPE LTSCALE)",
|
||||
);
|
||||
} else {
|
||||
let handles: Vec<_> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.into_iter()
|
||||
.map(|(h, _)| h)
|
||||
.collect();
|
||||
if handles.is_empty() {
|
||||
self.command_line
|
||||
.push_error("CHPROP: no entities selected.");
|
||||
} else {
|
||||
// Validate value early to give clear errors
|
||||
let color_val: Option<acadrust::types::Color> = if prop == "COLOR" {
|
||||
value
|
||||
.parse::<i16>()
|
||||
.ok()
|
||||
.map(acadrust::types::Color::from_index)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let ltscale_val: Option<f64> = if prop == "LTSCALE" {
|
||||
value.parse().ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let transparency_val: Option<acadrust::types::Transparency> =
|
||||
if prop == "TRANSPARENCY" {
|
||||
value
|
||||
.parse::<f64>()
|
||||
.ok()
|
||||
.map(acadrust::types::Transparency::from_percent)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if (prop == "COLOR" && color_val.is_none())
|
||||
|| (prop == "LTSCALE" && ltscale_val.is_none())
|
||||
|| (prop == "TRANSPARENCY" && transparency_val.is_none())
|
||||
{
|
||||
self.command_line.push_error(&format!(
|
||||
"CHPROP: invalid value '{}' for {}.",
|
||||
value, prop
|
||||
));
|
||||
} else {
|
||||
let mut changed = 0usize;
|
||||
for handle in &handles {
|
||||
if let Some(entity) =
|
||||
self.tabs[i].scene.document.get_entity_mut(*handle)
|
||||
{
|
||||
let common = entity.common_mut();
|
||||
match prop.as_str() {
|
||||
"LAYER" => {
|
||||
common.layer = value.clone();
|
||||
changed += 1;
|
||||
}
|
||||
"LINETYPE" | "LT" => {
|
||||
common.linetype = value.clone();
|
||||
changed += 1;
|
||||
}
|
||||
"LTSCALE" => {
|
||||
common.linetype_scale = ltscale_val.unwrap();
|
||||
changed += 1;
|
||||
}
|
||||
"COLOR" => {
|
||||
common.color = color_val.unwrap();
|
||||
changed += 1;
|
||||
}
|
||||
"TRANSPARENCY" => {
|
||||
common.transparency = transparency_val.unwrap();
|
||||
changed += 1;
|
||||
}
|
||||
_ => {
|
||||
self.command_line.push_error(&format!(
|
||||
"CHPROP: unknown property '{}'. Use: LAYER COLOR LINETYPE LTSCALE TRANSPARENCY", prop
|
||||
));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed > 0 {
|
||||
self.push_undo_snapshot(i, "CHPROP");
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&format!(
|
||||
"CHPROP: {} entity/entities updated.",
|
||||
changed
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── RENAME table entries ──────────────────────────────────────
|
||||
cmd if cmd == "RENAME" || cmd.starts_with("RENAME ") => {
|
||||
// Usage: RENAME <type> <old_name> <new_name>
|
||||
// Types: LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW
|
||||
let parts: Vec<&str> = cmd.split_whitespace().collect();
|
||||
let type_str = parts.get(1).map(|s| s.to_uppercase()).unwrap_or_default();
|
||||
let old_name = parts.get(2).map(|s| s.trim()).unwrap_or("").to_string();
|
||||
let new_name = parts.get(3).map(|s| s.trim()).unwrap_or("").to_string();
|
||||
|
||||
if type_str.is_empty() || old_name.is_empty() || new_name.is_empty() {
|
||||
self.command_line.push_info(
|
||||
"Usage: RENAME <type> <old> <new> (types: LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW)"
|
||||
);
|
||||
} else {
|
||||
let doc = &mut self.tabs[i].scene.document;
|
||||
let ok = match type_str.as_str() {
|
||||
"LAYER" => {
|
||||
if let Some(l) = doc.layers.get_mut(&old_name) {
|
||||
l.name = new_name.clone();
|
||||
// Update entity references
|
||||
for e in doc.entities_mut() {
|
||||
if e.common().layer == old_name {
|
||||
e.common_mut().layer = new_name.clone();
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"STYLE" | "TEXTSTYLE" => {
|
||||
if let Some(s) = doc.text_styles.get_mut(&old_name) {
|
||||
s.name = new_name.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"DIMSTYLE" => {
|
||||
if let Some(s) = doc.dim_styles.get_mut(&old_name) {
|
||||
s.name = new_name.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"LINETYPE" | "LT" => {
|
||||
if let Some(lt) = doc.line_types.get_mut(&old_name) {
|
||||
lt.name = new_name.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"UCS" => {
|
||||
if let Some(u) = doc.ucss.get_mut(&old_name) {
|
||||
u.name = new_name.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
"VIEW" => {
|
||||
if let Some(v) = doc.views.get_mut(&old_name) {
|
||||
v.name = new_name.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
self.command_line.push_error(&format!("RENAME: unknown type '{}'. Use LAYER BLOCK STYLE DIMSTYLE LINETYPE UCS VIEW", type_str));
|
||||
false
|
||||
}
|
||||
};
|
||||
if ok {
|
||||
self.push_undo_snapshot(i, "RENAME");
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("RENAME: '{}' → '{}'.", old_name, new_name));
|
||||
} else if type_str != "BLOCK" {
|
||||
self.command_line.push_error(&format!(
|
||||
"RENAME: '{}' not found in {}.",
|
||||
old_name, type_str
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── System variable getters/setters ──────────────────────────────────
|
||||
// CLAYER [name] — get or set current layer
|
||||
// TEXTSTYLE [name] — already handled above under STYLE SET
|
||||
// DIMSTYLE [name] — get or set active dim style
|
||||
// LTSCALE [val] — global linetype scale
|
||||
cmd if cmd == "CLAYER" || cmd.starts_with("CLAYER ") => {
|
||||
let name_arg = cmd.trim_start_matches("CLAYER").trim();
|
||||
if name_arg.is_empty() {
|
||||
let cur = &self.tabs[i].scene.document.header.current_layer_name;
|
||||
self.command_line
|
||||
.push_output(&format!("CLAYER = \"{cur}\""));
|
||||
} else {
|
||||
if self.tabs[i].scene.document.layers.contains(name_arg) {
|
||||
self.tabs[i].scene.document.header.current_layer_name =
|
||||
name_arg.to_string();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("CLAYER set to \"{name_arg}\""));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error(&format!("CLAYER: layer '{}' not found.", name_arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
cmd if cmd == "CDIMSTY"
|
||||
|| cmd == "DIMCURRENT"
|
||||
|| cmd.starts_with("CDIMSTY ")
|
||||
|| cmd.starts_with("DIMCURRENT ") =>
|
||||
{
|
||||
let name_arg = cmd.split_whitespace().skip(1).collect::<Vec<_>>().join(" ");
|
||||
if name_arg.is_empty() {
|
||||
let cur = &self.tabs[i].scene.document.header.current_dimstyle_name;
|
||||
self.command_line
|
||||
.push_output(&format!("CDIMSTY = \"{cur}\""));
|
||||
} else {
|
||||
if self.tabs[i].scene.document.dim_styles.contains(&name_arg) {
|
||||
self.tabs[i].scene.document.header.current_dimstyle_name = name_arg.clone();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("Active dim style set to \"{name_arg}\""));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error(&format!("CDIMSTY: dim style '{}' not found.", name_arg));
|
||||
}
|
||||
}
|
||||
}
|
||||
"LTSCALE" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new("LTSCALE", "LTSCALE new global line-type scale:");
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
cmd if cmd.starts_with("LTSCALE ") => {
|
||||
let val_str = cmd.trim_start_matches("LTSCALE").trim();
|
||||
if val_str.is_empty() {
|
||||
let v = self.tabs[i].scene.document.header.linetype_scale;
|
||||
self.command_line.push_output(&format!("LTSCALE = {v:.4}"));
|
||||
} else if let Ok(v) = val_str.parse::<f64>() {
|
||||
if v > 0.0 {
|
||||
self.push_undo_snapshot(i, "LTSCALE");
|
||||
self.tabs[i].scene.document.header.linetype_scale = v;
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("LTSCALE set to {v:.4}"));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error("LTSCALE: value must be positive.");
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error("Usage: LTSCALE [value]");
|
||||
}
|
||||
}
|
||||
"PDMODE" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new(
|
||||
"PDMODE",
|
||||
"PDMODE new value [0=dot 1=none 2=+ 3=x 4=tick; +32 circle +64 square]:",
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
cmd if cmd.starts_with("PDMODE ") => {
|
||||
let val_str = cmd.trim_start_matches("PDMODE").trim();
|
||||
if val_str.is_empty() {
|
||||
let v = self.tabs[i].scene.document.header.point_display_mode;
|
||||
self.command_line.push_output(&format!("PDMODE = {v}"));
|
||||
} else if let Ok(v) = val_str.parse::<i16>() {
|
||||
self.push_undo_snapshot(i, "PDMODE");
|
||||
self.tabs[i].scene.document.header.point_display_mode = v;
|
||||
// Point glyphs are built at tessellation time — rebuild them.
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&format!("PDMODE set to {v}"));
|
||||
} else {
|
||||
self.command_line.push_error(
|
||||
"Usage: PDMODE [value] (0=dot 1=none 2=+ 3=x 4=tick; +32 circle, +64 square)",
|
||||
);
|
||||
}
|
||||
}
|
||||
cmd if cmd.starts_with("TEXTEDITMODE ") => {
|
||||
let val_str = cmd.trim_start_matches("TEXTEDITMODE").trim().to_lowercase();
|
||||
if val_str.is_empty() {
|
||||
let v = if self.texteditmode { 1 } else { 0 };
|
||||
self.command_line.push_output(&format!("TEXTEDITMODE = {v}"));
|
||||
} else if let Some(v) =
|
||||
crate::modules::annotate::textedit::parse_texteditmode(&val_str)
|
||||
{
|
||||
self.texteditmode = v;
|
||||
let n = if v { 1 } else { 0 };
|
||||
self.command_line
|
||||
.push_output(&format!("TEXTEDITMODE set to {n}"));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error("Requires 0 OR 1 OR MULTIPLE OR SINGLE");
|
||||
}
|
||||
}
|
||||
"PDSIZE" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new(
|
||||
"PDSIZE",
|
||||
"PDSIZE new point size (0 = 5% of viewport, <0 = absolute):",
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
cmd if cmd.starts_with("PDSIZE ") => {
|
||||
let val_str = cmd.trim_start_matches("PDSIZE").trim();
|
||||
if val_str.is_empty() {
|
||||
let v = self.tabs[i].scene.document.header.point_display_size;
|
||||
self.command_line.push_output(&format!("PDSIZE = {v:.4}"));
|
||||
} else if let Ok(v) = val_str.parse::<f64>() {
|
||||
self.push_undo_snapshot(i, "PDSIZE");
|
||||
self.tabs[i].scene.document.header.point_display_size = v;
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&format!("PDSIZE set to {v:.4}"));
|
||||
} else {
|
||||
self.command_line.push_error(
|
||||
"Usage: PDSIZE [value] (>0 absolute size, <0 percent of viewport, 0 default)",
|
||||
);
|
||||
}
|
||||
}
|
||||
cmd if cmd == "DDPTYPE" => {
|
||||
// The dialog shows the magnitude; the sign (relative/absolute)
|
||||
// is driven by the radio buttons. A positive PDSIZE is absolute;
|
||||
// zero or negative is relative.
|
||||
let pdsize = self.tabs[i].scene.document.header.point_display_size;
|
||||
self.point_size_relative = pdsize <= 0.0;
|
||||
self.point_size_buf = format!("{}", pdsize.abs());
|
||||
self.active_modal = Some(super::super::ModalKind::PointStyle);
|
||||
}
|
||||
cmd if cmd == "LWDISPLAY" || cmd.starts_with("LWDISPLAY ") => {
|
||||
let val_str = cmd.trim_start_matches("LWDISPLAY").trim();
|
||||
let parsed: Result<Option<bool>, ()> =
|
||||
match val_str.to_ascii_uppercase().as_str() {
|
||||
"" => Ok(None),
|
||||
"ON" | "1" | "TRUE" => Ok(Some(true)),
|
||||
"OFF" | "0" | "FALSE" => Ok(Some(false)),
|
||||
_ => Err(()),
|
||||
};
|
||||
match parsed {
|
||||
Err(_) => self
|
||||
.command_line
|
||||
.push_error("Usage: LWDISPLAY [ON|OFF]"),
|
||||
Ok(Some(v)) => {
|
||||
self.push_undo_snapshot(i, "LWDISPLAY");
|
||||
self.tabs[i].scene.document.header.lineweight_display = v;
|
||||
// No retessellate — the wire shader honours the flag via uniforms.
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&format!(
|
||||
"LWDISPLAY {}",
|
||||
if v { "ON" } else { "OFF" }
|
||||
));
|
||||
}
|
||||
Ok(None) => {
|
||||
let v = self.tabs[i].scene.document.header.lineweight_display;
|
||||
self.command_line.push_output(&format!(
|
||||
"LWDISPLAY = {}",
|
||||
if v { "ON" } else { "OFF" }
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
"CELTSCALE" => {
|
||||
use crate::command::ValuePromptCommand;
|
||||
let c = ValuePromptCommand::new(
|
||||
"CELTSCALE",
|
||||
"CELTSCALE new current-object line-type scale:",
|
||||
);
|
||||
self.command_line.push_info(&c.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(c));
|
||||
}
|
||||
cmd if cmd.starts_with("CELTSCALE ") => {
|
||||
let val_str = cmd.trim_start_matches("CELTSCALE").trim();
|
||||
if val_str.is_empty() {
|
||||
let v = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.header
|
||||
.current_entity_linetype_scale;
|
||||
self.command_line
|
||||
.push_output(&format!("CELTSCALE = {v:.4}"));
|
||||
} else if let Ok(v) = val_str.parse::<f64>() {
|
||||
if v > 0.0 {
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.header
|
||||
.current_entity_linetype_scale = v;
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line
|
||||
.push_output(&format!("CELTSCALE set to {v:.4}"));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error("CELTSCALE: value must be positive.");
|
||||
}
|
||||
} else {
|
||||
self.command_line.push_error("Usage: CELTSCALE [value]");
|
||||
}
|
||||
}
|
||||
|
||||
// ── SCALETEXT — rescale selected Text/MText entities ─────────────────
|
||||
// Usage: SCALETEXT <factor> e.g. SCALETEXT 2
|
||||
// SCALETEXT H <height> set absolute height
|
||||
cmd if cmd == "SCALETEXT" || cmd.starts_with("SCALETEXT ") => {
|
||||
let rest = cmd.trim_start_matches("SCALETEXT").trim();
|
||||
let parts: Vec<&str> = rest.split_whitespace().collect();
|
||||
let selected_handles: Vec<acadrust::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.selected_entities()
|
||||
.iter()
|
||||
.map(|(h, _)| *h)
|
||||
.collect();
|
||||
if selected_handles.is_empty() {
|
||||
self.command_line
|
||||
.push_error("SCALETEXT: select Text/MText entities first.");
|
||||
} else {
|
||||
let (use_absolute, value) = match (
|
||||
parts.first().map(|s| s.to_uppercase()).as_deref(),
|
||||
parts.get(1),
|
||||
) {
|
||||
(Some("H"), Some(v)) => (true, v.parse::<f64>().ok()),
|
||||
(Some(v), None) => (false, v.parse::<f64>().ok()),
|
||||
_ => (false, None),
|
||||
};
|
||||
if let Some(val) = value {
|
||||
if val <= 0.0 {
|
||||
self.command_line
|
||||
.push_error("SCALETEXT: value must be positive.");
|
||||
} else {
|
||||
self.push_undo_snapshot(i, "SCALETEXT");
|
||||
let mut count = 0usize;
|
||||
for sh in &selected_handles {
|
||||
for entity in self.tabs[i].scene.document.entities_mut() {
|
||||
if entity.common().handle != *sh {
|
||||
continue;
|
||||
}
|
||||
match entity {
|
||||
acadrust::EntityType::Text(t) => {
|
||||
t.height =
|
||||
if use_absolute { val } else { t.height * val };
|
||||
count += 1;
|
||||
}
|
||||
acadrust::EntityType::MText(t) => {
|
||||
t.height =
|
||||
if use_absolute { val } else { t.height * val };
|
||||
count += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
self.tabs[i].dirty = true;
|
||||
self.command_line.push_output(&format!(
|
||||
"SCALETEXT: scaled {count} text entity(ies)."
|
||||
));
|
||||
} else {
|
||||
self.command_line
|
||||
.push_error("SCALETEXT: no Text/MText in selection.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.command_line
|
||||
.push_info("Usage: SCALETEXT <factor> or SCALETEXT H <height>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => return None,
|
||||
}
|
||||
Some(self.finish_dispatch(cmd))
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user