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:
Hakan Seven 2026-06-26 22:51:29 +03:00
commit bddc050b3e
39 changed files with 26678 additions and 26117 deletions

673
src/app/commands/draw.rs Normal file
View file

@ -0,0 +1,673 @@
use super::*;
impl OpenCADStudio {
pub(super) fn dispatch_draw(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
match cmd {
// ── Draw commands ──────────────────────────────────────────────
"LINE" | "L" => {
use crate::modules::draw::draw::line::LineCommand;
let new_cmd = LineCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"MLINE" | "ML" => {
use crate::modules::draw::draw::mline::MlineCommand;
let style = self.tabs[i].scene.document.header.multiline_style.clone();
let cmd_obj = MlineCommand::with_style(style);
self.command_line.push_info(&cmd_obj.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
}
cmd if cmd == "WIPEOUT" || cmd == "WO" || cmd.starts_with("WIPEOUT ") => {
use crate::modules::draw::draw::wipeout::WipeoutCommand;
let args = cmd
.split_once(' ')
.map(|(_, r)| r.trim().to_uppercase())
.unwrap_or_default();
let wo_cmd = if args == "P" || args == "POLYGONAL" {
WipeoutCommand::new_polygonal()
} else {
WipeoutCommand::new_rectangular()
};
self.command_line.push_info(&wo_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(wo_cmd));
}
cmd if cmd == "IMAGE" || cmd == "IMAGEATTACH" || cmd == "IM" => {
return Some(Task::done(Message::ImagePick));
}
"REVCLOUD" => {
use crate::modules::draw::draw::revcloud::RevCloudCommand;
let cmd = RevCloudCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
"ATTDEF" => {
use crate::modules::draw::draw::attdef::AttdefCommand;
let cmd = AttdefCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
// ATTEDIT — list or edit attribute values on selected Insert entities.
// Usage:
// ATTEDIT — list all attributes on selected Insert(s)
// ATTEDIT <tag> <v> — set the value of attribute <tag> to <v>
cmd if cmd == "ATTEDIT" || cmd.starts_with("ATTEDIT ") => {
let rest = cmd.trim_start_matches("ATTEDIT").trim();
let parts: Vec<&str> = rest.splitn(2, char::is_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("ATTEDIT: select an Insert entity first.");
} else {
let mut found_any = false;
for sh in &selected_handles {
if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i]
.scene
.document
.entities()
.find(|e| e.common().handle == *sh)
{
found_any = true;
if rest.is_empty() {
// List attributes.
if ins.attributes.is_empty() {
self.command_line.push_output(&format!(
" Insert {:x}: no attributes.",
sh.value()
));
} else {
for attr in &ins.attributes {
self.command_line.push_output(&format!(
" [{tag}] = {val}",
tag = attr.tag,
val = attr.get_value()
));
}
}
}
}
}
if !found_any {
self.command_line
.push_error("ATTEDIT: no Insert entities in selection.");
}
// If tag + value supplied, mutate attributes.
if parts.len() == 2 && !parts[0].is_empty() {
let tag_up = parts[0].to_uppercase();
let new_val = parts[1];
let mut changed = 0usize;
self.push_undo_snapshot(i, "ATTEDIT");
for sh in &selected_handles {
if let Some(acadrust::EntityType::Insert(ins)) = self.tabs[i]
.scene
.document
.entities_mut()
.find(|e| e.common().handle == *sh)
{
for attr in &mut ins.attributes {
if attr.tag.to_uppercase() == tag_up {
attr.set_value(new_val);
changed += 1;
}
}
}
}
if changed > 0 {
self.tabs[i].dirty = true;
self.command_line.push_output(&format!(
"ATTEDIT: updated {changed} attribute(s) [{tag_up}] = {new_val}."
));
} else {
self.command_line.push_error(&format!(
"ATTEDIT: tag '{tag_up}' not found in selection."
));
}
}
}
}
// ATTDISP — control attribute display visibility.
// ATTDISP ON — make all AttributeDefinitions visible
// ATTDISP OFF — make all AttributeDefinitions invisible
// ATTDISP NORMAL — restore: show only those without the invisible flag
cmd if cmd == "ATTDISP" || cmd.starts_with("ATTDISP ") => {
let sub = cmd.split_whitespace().nth(1).unwrap_or("").to_uppercase();
match sub.as_str() {
"ON" | "OFF" | "NORMAL" => {
self.push_undo_snapshot(i, "ATTDISP");
let mut count = 0usize;
for entity in self.tabs[i].scene.document.entities_mut() {
if let acadrust::EntityType::AttributeDefinition(ad) = entity {
match sub.as_str() {
"ON" => {
ad.flags.invisible = false;
count += 1;
}
"OFF" => {
ad.flags.invisible = true;
count += 1;
}
"NORMAL" => { /* leave existing flags — they are already the "normal" state */
}
_ => {}
}
}
}
self.tabs[i].dirty = true;
self.command_line.push_output(&format!(
"ATTDISP {sub}: {count} attribute definition(s) updated."
));
}
_ => {
self.command_line
.push_info("Usage: ATTDISP ON | OFF | NORMAL");
}
}
}
"DONUT" | "DO" => {
use crate::modules::draw::draw::donut::DonutCommand;
let cmd = DonutCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
"CIRCLE" | "C" => {
use crate::modules::draw::draw::circle::CircleCommand;
let new_cmd = CircleCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"CIRCLE_CD" => {
use crate::modules::draw::draw::circle::CircleCDCommand;
let new_cmd = CircleCDCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"CIRCLE_2P" => {
use crate::modules::draw::draw::circle::Circle2PCommand;
let new_cmd = Circle2PCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"CIRCLE_3P" => {
use crate::modules::draw::draw::circle::Circle3PCommand;
let new_cmd = Circle3PCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"CIRCLE_TTR" => {
use crate::modules::draw::draw::circle::CircleTTRCommand;
let new_cmd = CircleTTRCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.pre_cmd_tangent = Some(self.snapper.is_on(crate::snap::SnapType::Tangent));
self.snapper.enabled.insert(crate::snap::SnapType::Tangent);
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"CIRCLE_TTT" => {
use crate::modules::draw::draw::circle::CircleTTTCommand;
let new_cmd = CircleTTTCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.pre_cmd_tangent = Some(self.snapper.is_on(crate::snap::SnapType::Tangent));
self.snapper.enabled.insert(crate::snap::SnapType::Tangent);
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC" | "A" => {
use crate::modules::draw::draw::arc::ArcCommand;
let new_cmd = ArcCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_3P" => {
use crate::modules::draw::draw::arc::Arc3PCommand;
let new_cmd = Arc3PCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SCE" => {
use crate::modules::draw::draw::arc::ArcSCECommand;
let new_cmd = ArcSCECommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SCA" => {
use crate::modules::draw::draw::arc::ArcSCACommand;
let new_cmd = ArcSCACommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SCL" => {
use crate::modules::draw::draw::arc::ArcSCLCommand;
let new_cmd = ArcSCLCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SEA" => {
use crate::modules::draw::draw::arc::ArcSEACommand;
let new_cmd = ArcSEACommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SER" => {
use crate::modules::draw::draw::arc::ArcSERCommand;
let new_cmd = ArcSERCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_SED" => {
use crate::modules::draw::draw::arc::ArcSEDCommand;
let new_cmd = ArcSEDCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_CSA" => {
use crate::modules::draw::draw::arc::ArcCSACommand;
let new_cmd = ArcCSACommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ARC_CSL" => {
use crate::modules::draw::draw::arc::ArcCSLCommand;
let new_cmd = ArcCSLCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"RECT" | "RECTANG" | "REC" => {
use crate::modules::draw::draw::shapes::RectCommand;
let new_cmd = RectCommand::new();
self.command_line.push_info(&new_cmd.prompt());
if self.ortho_mode {
self.rect_suppressed_ortho = true;
self.ortho_mode = false;
}
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"RECT_ROT" => {
use crate::modules::draw::draw::shapes::RectRotCommand;
let new_cmd = RectRotCommand::new();
self.command_line.push_info(&new_cmd.prompt());
if self.ortho_mode {
self.rect_suppressed_ortho = true;
self.ortho_mode = false;
}
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"RECT_CEN" => {
use crate::modules::draw::draw::shapes::RectCenCommand;
let new_cmd = RectCenCommand::new();
self.command_line.push_info(&new_cmd.prompt());
if self.ortho_mode {
self.rect_suppressed_ortho = true;
self.ortho_mode = false;
}
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"POLY" | "POLYGON" | "POL" => {
use crate::modules::draw::draw::shapes::PolyCommand;
let new_cmd = PolyCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"POLY_C" => {
use crate::modules::draw::draw::shapes::PolyCCommand;
let new_cmd = PolyCCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"POLY_E" => {
use crate::modules::draw::draw::shapes::PolyECommand;
let new_cmd = PolyECommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"PLINE" | "PL" => {
use crate::modules::draw::draw::polyline::PlineCommand;
let new_cmd = PlineCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
// ── Modify commands ────────────────────────────────────────────
"MOVE" | "M" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("MOVE");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::translate::MoveCommand;
let wires = self.tabs[i].scene.wire_models_for(&handles);
let new_cmd = MoveCommand::new(handles, wires);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"COPY" | "CO" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("COPY");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::copy::CopyCommand;
let wires = self.tabs[i].scene.wire_models_for(&handles);
let new_cmd = CopyCommand::new(handles, wires);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"ROTATE" | "RO" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("ROTATE");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::rotate::RotateCommand;
let wires = self.tabs[i].scene.wire_models_for(&handles);
let new_cmd = RotateCommand::new(handles, wires);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"TORIENT" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("TORIENT");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::torient::TorientCommand;
let entities: Vec<_> = handles
.iter()
.filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e)))
.collect();
let cam_rot = self.tabs[i].scene.camera.borrow().rotation;
let right = cam_rot * glam::Vec3::X;
let view_twist = right.y.atan2(right.x) as f64;
let new_cmd = TorientCommand::new(entities, view_twist);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"POINT" | "PO" => {
use crate::modules::draw::draw::point::PointCommand;
let new_cmd = PointCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"RAY" => {
use crate::modules::draw::draw::ray::RayCommand;
let new_cmd = RayCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"XLINE" | "XL" | "CONSTRUCTIONLINE" => {
use crate::modules::draw::draw::ray::XLineCommand;
let new_cmd = XLineCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"HATCH" | "H" => {
use crate::modules::draw::draw::hatch::HatchCommand;
let outlines = self.tabs[i].scene.closed_outlines();
let new_cmd = HatchCommand::new(outlines);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"HATCHEDIT" | "HE" => {
use crate::modules::draw::draw::hatchedit::HatcheditCommand;
// If a single hatch is already selected, skip the pick step.
let sel = self.tabs[i].scene.selected_entities();
if sel.len() == 1 {
let (h, _) = sel[0];
if let Some(model) = self.tabs[i].scene.hatches.get(&h).cloned() {
let cmd = HatcheditCommand::with_handle(
h,
model.name.clone(),
model.scale,
model.angle_offset,
);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
self.command_line
.push_error("HATCHEDIT: selected entity is not a hatch.");
}
} else {
let cmd = HatcheditCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
}
"GRADIENT" => {
use crate::modules::draw::draw::hatch::GradientCommand;
let outlines = self.tabs[i].scene.closed_outlines();
let new_cmd = GradientCommand::new(outlines);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"BOUNDARY" => {
use crate::modules::draw::draw::hatch::BoundaryCommand;
let outlines = self.tabs[i].scene.closed_outlines();
let new_cmd = BoundaryCommand::new(outlines);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ELLIPSE" | "EL" => {
use crate::modules::draw::draw::ellipse::EllipseCommand;
let new_cmd = EllipseCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ELLIPSE_AXIS" => {
use crate::modules::draw::draw::ellipse::EllipseAxisCommand;
let new_cmd = EllipseAxisCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"ELLIPSE_ARC" => {
use crate::modules::draw::draw::ellipse::EllipseArcCommand;
let new_cmd = EllipseArcCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"SPLINE" | "SPL" => {
use crate::modules::draw::draw::spline::SplineCommand;
let new_cmd = SplineCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"SCALE" | "SC" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("SCALE");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::scale::ScaleCommand;
let wires = self.tabs[i].scene.wire_models_for(&handles);
let new_cmd = ScaleCommand::new(handles, wires);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"MIRROR" | "MI" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("MIRROR");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
use crate::modules::draw::modify::mirror::MirrorCommand;
let wires = self.tabs[i].scene.wire_models_for(&handles);
let new_cmd = MirrorCommand::new(handles, wires);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
}
"ERASE" | "E" => {
let handles: Vec<_> = self.tabs[i]
.scene
.selected_entities()
.into_iter()
.map(|(h, _)| h)
.collect();
if handles.is_empty() {
use crate::modules::draw::select::SelectObjectsCommand;
let cmd = SelectObjectsCommand::new("ERASE");
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
} else {
let n = handles.len();
self.push_undo_snapshot(i, "ERASE");
self.tabs[i].scene.erase_entities(&handles);
self.tabs[i].dirty = true;
self.refresh_properties();
self.command_line
.push_output(&format!("{n} object(s) erased."));
}
}
// ── Model commands (3D primitives) ─────────────────────────────
"BOX" | "WEDGE" | "CYLINDER" | "CONE" | "SPHERE" | "TORUS" => {
use crate::modules::model::primitive_cmd::PrimitiveCommand;
let new_cmd = PrimitiveCommand::new(cmd);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
// ── Design commands (solid booleans) ───────────────────────────
"UNION" | "SUBTRACT" | "INTERSECT" => {
use crate::modules::model::boolean_cmd::BoolOp;
if let Some(op) = BoolOp::from_id(cmd) {
return Some(self.solid_boolean(op));
}
}
// ── Annotate commands ──────────────────────────────────────────
"TEXT" | "T" | "DT" => {
use crate::modules::annotate::text::TextCommand;
let new_cmd = TextCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"DDEDIT" | "ED" => {
use crate::modules::annotate::ddedit::DdeditCommand;
// A single text entity already selected opens its in-place
// editor directly; otherwise prompt for a pick.
let sel = self.tabs[i].scene.selected_entities();
let editable = (sel.len() == 1).then(|| sel[0].0).filter(|h| {
self.tabs[i].scene.document.get_entity(*h).is_some_and(|e| {
super::super::text_inline::read_text_field(e).is_some()
|| matches!(e, acadrust::EntityType::Leader(_))
})
});
if let Some(h) = editable {
return Some(self.begin_text_edit(h));
}
if sel.len() == 1 {
self.command_line
.push_error("DDEDIT: selected entity is not text.");
} else {
let cmd = DdeditCommand::new();
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
}
"MTEXT" | "MT" => {
use crate::modules::annotate::mtext::MTextCommand;
let new_cmd = MTextCommand::new();
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"TEXTEDIT" | "TEDIT" => {
use crate::modules::annotate::textedit::TexteditCommand;
let mode_str = if self.texteditmode { "Single" } else { "Multiple" };
self.command_line.push_output(&format!("Current settings: Edit mode = {}", mode_str));
let new_cmd = TexteditCommand::new(self.texteditmode);
self.command_line.push_info(&new_cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
}
"TEXTEDITMODE" => {
use crate::modules::annotate::textedit::TexteditmodeCommand;
let cmd = TexteditmodeCommand::new(self.texteditmode);
self.command_line.push_info(&cmd.prompt());
self.tabs[i].active_cmd = Some(Box::new(cmd));
}
_ => return None,
}
Some(self.finish_dispatch(cmd))
}
}