use super::{H7CAD, Message}; use crate::command::CadCommand; use crate::scene::Scene; use iced::Task; use std::path::PathBuf; impl H7CAD { pub(super) fn dispatch_command(&mut self, cmd: &str) -> Task { let i = self.active_tab; // Cancel any running command before starting a new one. if self.tabs[i].active_cmd.is_some() { self.tabs[i].scene.clear_preview_wire(); self.tabs[i].active_cmd = None; } // Reset the last committed point so the first click of the new command // is not constrained by ortho/polar relative to a previous command's endpoint. self.last_point = None; if let Some(path_str) = cmd.strip_prefix("OPEN_RECENT:") { let path = PathBuf::from(path_str); return Task::perform(crate::io::open_path(path), Message::FileOpened); } match cmd { "NEW" => return Task::done(Message::ClearScene), "OPEN" => return Task::done(Message::OpenFile), "SAVE"|"QSAVE" => return Task::done(Message::SaveFile), "SAVEAS" => return Task::done(Message::SaveAs), "UNDO"|"U" => return Task::done(Message::Undo), "REDO" => return Task::done(Message::Redo), "CLEAR"|"CLR" => return Task::done(Message::ClearScene), "WIREFRAME"|"VW" => return Task::done(Message::SetWireframe(true)), "SOLID"|"VS" => return Task::done(Message::SetWireframe(false)), "ORTHO" => return Task::done(Message::SetProjection(true)), "PERSP" => return Task::done(Message::SetProjection(false)), "LAYERS"|"LA" => return Task::done(Message::ToggleLayers), // ── Layer object commands ────────────────────────────────────── "LAYOFF" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("LAYOFF"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let layers: std::collections::HashSet = self.tabs[i].scene .selected_entities().into_iter() .map(|(_, e)| e.common().layer.clone()).collect(); self.push_undo_snapshot(i, "LAYOFF"); for name in &layers { if name == "0" { continue; } if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(name) { dl.turn_off(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("Layer(s) turned off."); } } "LAYFRZ" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("LAYFRZ"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let layers: std::collections::HashSet = self.tabs[i].scene .selected_entities().into_iter() .map(|(_, e)| e.common().layer.clone()).collect(); self.push_undo_snapshot(i, "LAYFRZ"); for name in &layers { if name == "0" { continue; } if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(name) { dl.freeze(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("Layer(s) frozen."); } } "LAYLCK" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("LAYLCK"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let layers: std::collections::HashSet = self.tabs[i].scene .selected_entities().into_iter() .map(|(_, e)| e.common().layer.clone()).collect(); self.push_undo_snapshot(i, "LAYLCK"); for name in &layers { if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(name) { dl.lock(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("Layer(s) locked."); } } "LAYMCUR" => { let entities = self.tabs[i].scene.selected_entities(); if entities.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("LAYMCUR"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let layer = entities[0].1.common().layer.clone(); self.tabs[i].active_layer = layer.clone(); self.ribbon.active_layer = layer.clone(); self.tabs[i].layers.current_layer = layer.clone(); self.command_line.push_info(&format!("Current layer set to \"{layer}\".")); self.sync_ribbon_layers(); } } "LAYON" => { self.push_undo_snapshot(i, "LAYON"); for name in self.tabs[i].scene.document.layers.iter() .map(|l| l.name.clone()).collect::>() { if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(&name) { dl.turn_on(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("All layers turned on."); } "LAYTHW" => { self.push_undo_snapshot(i, "LAYTHW"); for name in self.tabs[i].scene.document.layers.iter() .map(|l| l.name.clone()).collect::>() { if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(&name) { dl.thaw(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("All layers thawed."); } "LAYULK" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("LAYULK"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let layers: std::collections::HashSet = self.tabs[i].scene .selected_entities().into_iter() .map(|(_, e)| e.common().layer.clone()).collect(); self.push_undo_snapshot(i, "LAYULK"); for name in &layers { if let Some(dl) = self.tabs[i].scene.document.layers.get_mut(name) { dl.unlock(); } } self.tabs[i].dirty = true; self.sync_ribbon_layers(); self.command_line.push_info("Layer(s) unlocked."); } } "LAYMATCH"|"LAYMCH" => { use crate::modules::home::layers::match_layer::LayMatchCommand; let dest: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); let cmd = LayMatchCommand::new(dest); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } "MATCHPROP"|"MA" => { use crate::modules::home::properties::match_prop::MatchPropCommand; self.tabs[i].scene.deselect_all(); let cmd = MatchPropCommand::new(); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } "GROUP"|"G" => { let handles: Vec<_> = self.tabs[i] .scene.selected_entities().into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("GROUP"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let auto_name = super::helpers::next_group_auto_name(&self.tabs[i].scene); use crate::modules::home::groups::group::GroupCommand; let cmd = GroupCommand::new(handles, auto_name); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } } "UNGROUP"|"UG" => { let handles: Vec<_> = self.tabs[i] .scene.selected_entities().into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::groups::ungroup::UngroupCommand; let cmd = UngroupCommand::new(); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { self.push_undo_snapshot(i, "UNGROUP"); let count = self.tabs[i].scene.delete_groups_containing(&handles); self.tabs[i].dirty = true; if count > 0 { self.command_line.push_info(&format!("{} group(s) dissolved.", count)); } else { self.command_line.push_info("No groups found for selected objects."); } } } "COPYCLIP"|"CC" => { let handles: Vec<_> = self.tabs[i] .scene.selected_entities().into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("COPYCLIP"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let entities: Vec<_> = handles.iter() .filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned()) .collect(); self.clipboard_centroid = super::helpers::entities_centroid( &self.tabs[i].scene.wire_models_for(&handles), ); self.clipboard = entities; self.command_line.push_info( &format!("{} object(s) copied to clipboard.", self.clipboard.len()), ); } } "CUTCLIP"|"CX" => { let handles: Vec<_> = self.tabs[i] .scene.selected_entities().into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("CUTCLIP"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let entities: Vec<_> = handles.iter() .filter_map(|&h| self.tabs[i].scene.document.get_entity(h).cloned()) .collect(); self.clipboard_centroid = super::helpers::entities_centroid( &self.tabs[i].scene.wire_models_for(&handles), ); let count = entities.len(); self.clipboard = entities; self.push_undo_snapshot(i, "CUTCLIP"); self.tabs[i].scene.erase_entities(&handles); self.tabs[i].scene.deselect_all(); self.tabs[i].dirty = true; self.refresh_properties(); self.command_line.push_info( &format!("{} object(s) cut to clipboard.", count), ); } } "PASTECLIP"|"PC" => { if self.clipboard.is_empty() { self.command_line.push_error("Clipboard is empty."); } else { let wires = self.tabs[i].scene.wires_for_entities(&self.clipboard); use crate::modules::home::clipboard::paste::PasteCommand; let cmd = PasteCommand::new(wires, self.clipboard_centroid); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } } "BLOCK" => { let handles: Vec<_> = self.tabs[i] .scene.selected_entities().into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("BLOCK"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { use crate::modules::insert::create_block::CreateBlockCommand; let cmd = CreateBlockCommand::new(handles); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } } "INSERT" => { let blocks = self.tabs[i].scene.custom_block_names(); if blocks.is_empty() { self.command_line .push_error("No user-defined blocks found in this drawing."); } else { use crate::modules::insert::insert_block::InsertBlockCommand; let cmd = InsertBlockCommand::new(blocks); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } } // ── Draw commands ────────────────────────────────────────────── "LINE"|"L" => { use crate::modules::home::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)); } "CIRCLE"|"C" => { use crate::modules::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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" => { use crate::modules::home::draw::shapes::RectCommand; let new_cmd = RectCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "RECT_ROT" => { use crate::modules::home::draw::shapes::RectRotCommand; let new_cmd = RectRotCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "RECT_CEN" => { use crate::modules::home::draw::shapes::RectCenCommand; let new_cmd = RectCenCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "POLY" => { use crate::modules::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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)); } } "POINT"|"PO" => { use crate::modules::home::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)); } "HATCH"|"H" => { use crate::modules::home::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)); } "GRADIENT" => { use crate::modules::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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::home::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.")); } } // ── 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)); } "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)); } "DIMLINEAR" => { use crate::modules::annotate::linear_dim::LinearDimensionCommand; let new_cmd = LinearDimensionCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "DIMRADIUS" => { use crate::modules::annotate::radius_dim::RadiusDimensionCommand; let new_cmd = RadiusDimensionCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "DIMANGULAR" => { use crate::modules::annotate::angular_dim::AngularDimensionCommand; let new_cmd = AngularDimensionCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "LEADER"|"LE" => { use crate::modules::annotate::leader_cmd::LeaderCommand; let new_cmd = LeaderCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "MLEADER"|"MLD" => { use crate::modules::annotate::mleader_cmd::MLeaderCommand; let new_cmd = MLeaderCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "ZOOM EXTENTS"|"ZOOMEXTENTS"|"ZE" => { self.tabs[i].scene.fit_all(); self.command_line.push_output("Zoom Extents"); } "STRETCH"|"SS" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("STRETCH"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { use crate::modules::home::modify::stretch::StretchCommand; let new_cmd = StretchCommand::new(handles); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } "FILLET"|"F" => { use crate::modules::home::modify::fillet::FilletCommand; let entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e)) }).collect(); let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect(); let new_cmd = FilletCommand::new( crate::modules::home::defaults::get_fillet_radius(), all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "ARRAY"|"AR"|"ARRAYRECT" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("ARRAYRECT"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { use crate::modules::home::modify::array::ArrayRectCommand; let wires = self.tabs[i].scene.wire_models_for(&handles); let new_cmd = ArrayRectCommand::new(handles, wires); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } "ARRAYPOLAR" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("ARRAYPOLAR"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { use crate::modules::home::modify::array::ArrayPolarCommand; let wires = self.tabs[i].scene.wire_models_for(&handles); let new_cmd = ArrayPolarCommand::new(handles, wires); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } "ARRAYPATH" => { let handles: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().map(|(h, _)| h).collect(); if handles.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("ARRAYPATH"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { use crate::modules::home::modify::array::ArrayPathCommand; let wires = self.tabs[i].scene.wire_models_for(&handles); let all_entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned() }).collect(); let new_cmd = ArrayPathCommand::new(handles, wires, all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } "CHAMFER"|"CHA" => { use crate::modules::home::modify::fillet::ChamferCommand; let entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e)) }).collect(); let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect(); let new_cmd = ChamferCommand::new( crate::modules::home::defaults::get_chamfer_dist1(), all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "EXPLODE"|"X" => { use crate::modules::home::modify::explode::explode_entity; let entities: Vec<_> = self.tabs[i].scene.selected_entities() .into_iter().collect(); if entities.is_empty() { use crate::modules::home::select::SelectObjectsCommand; let cmd = SelectObjectsCommand::new("EXPLODE"); self.command_line.push_info(&cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(cmd)); } else { let replacements: Vec<(acadrust::Handle, Vec)> = entities .iter() .filter_map(|(h, e)| { let pieces = explode_entity(e, &self.tabs[i].scene.document); if pieces.is_empty() { None } else { Some((*h, pieces)) } }) .collect(); let exploded = replacements.len(); if exploded > 0 { self.push_undo_snapshot(i, "EXPLODE"); } for (handle, pieces) in replacements { self.tabs[i].scene.erase_entities(&[handle]); for piece in pieces { self.tabs[i].scene.add_entity(piece); } } if exploded > 0 { self.tabs[i].dirty = true; self.refresh_properties(); self.command_line.push_output(&format!("{exploded} object(s) exploded.")); } else { self.command_line.push_info("EXPLODE: no explodable objects selected."); } } } "OFFSET"|"O" => { use crate::modules::home::modify::offset::OffsetCommand; let all_entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned() }).collect(); let new_cmd = OffsetCommand::new( crate::modules::home::defaults::get_offset_dist(), all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "TRIM"|"TR" => { use crate::modules::home::modify::trim::TrimCommand; let entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e)) }).collect(); let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect(); let new_cmd = TrimCommand::new(all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "EXTEND"|"EX" => { use crate::modules::home::modify::trim::ExtendCommand; let entities: Vec<_> = self.tabs[i].scene.entity_wires().iter() .filter_map(|w| { let h = Scene::handle_from_wire_name(&w.name)?; self.tabs[i].scene.document.get_entity(h).cloned().map(|e| (h, e)) }).collect(); let all_entities: Vec<_> = entities.into_iter().map(|(_, e)| e).collect(); let new_cmd = ExtendCommand::new(all_entities); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } "3DORBIT"|"3O" => { self.command_line.push_info("3D Orbit: drag with right mouse button."); } "HELP"|"?" => { self.command_line.push_output( "Draw: LINE CIRCLE ARC PLINE POINT ELLIPSE SPLINE | \ Modify: MOVE COPY ROTATE SCALE MIRROR ERASE | \ Text: TEXT MTEXT | File: OPEN SAVE SAVEAS" ); } "DONATE" => { let _ = open::that("https://patreon.com/HakanSeven12"); self.command_line.push_info("Opening Patreon page..."); } // ── Layout / viewport ────────────────────────────────────────── "MVIEW"|"MV" => { if self.tabs[i].scene.current_layout == "Model" { self.command_line.push_error("MVIEW: switch to a paper space layout first."); } else { use crate::modules::layout::mview::MviewCommand; let new_cmd = MviewCommand::new(); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } // ── MSPACE / PSPACE ─────────────────────────────────────────── "MS"|"MSPACE" => { return Task::done(Message::MspaceCommand); } "PSPACE" => { return Task::done(Message::PspaceCommand); } // ── VPORTS — list viewports in current layout ───────────────── "VPORTS" => { let scene = &self.tabs[i].scene; if scene.current_layout == "Model" { self.command_line.push_error("VPORTS: switch to a paper space layout first."); } else { let layout_block = scene.current_layout_block_handle_pub(); let viewports: Vec<_> = scene.document.entities() .filter_map(|e| { if let acadrust::EntityType::Viewport(vp) = e { if vp.id > 1 && vp.common.owner_handle == layout_block { Some((vp.id, vp.center.clone(), vp.width, vp.height, vp.custom_scale, vp.status.is_on, vp.status.locked)) } else { None } } else { None } }) .collect(); if viewports.is_empty() { self.command_line.push_info("No viewports in current layout. Use MVIEW to create one."); } else { self.command_line.push_output(&format!("{} viewport(s) in layout \"{}\":", viewports.len(), scene.current_layout)); for (id, center, w, h, scale, is_on, locked) in &viewports { let state = match (is_on, locked) { (true, true) => "On, Locked", (true, false) => "On", (false, _) => "Off", }; self.command_line.push_output(&format!( " VP #{id}: {w:.1}×{h:.1} @ ({:.1},{:.1}) scale={scale:.4} [{state}]", center.x, center.y )); } } } } // ── VPLAYER — per-viewport layer freeze/thaw ────────────────── "VPLAYER" => { let scene = &self.tabs[i].scene; if scene.current_layout == "Model" { self.command_line.push_error("VPLAYER: switch to a paper space layout first."); } else if scene.active_viewport.is_none() { self.command_line.push_error("VPLAYER: enter a viewport first (double-click or MS)."); } else { use crate::modules::layout::vplayer::VplayerCommand; let vp_handle = scene.active_viewport.unwrap(); // Collect current frozen layer names for display. let frozen_names: Vec = { if let Some(acadrust::EntityType::Viewport(vp)) = scene.document.get_entity(vp_handle) { vp.frozen_layers.iter().filter_map(|h| { scene.document.layers.iter().find(|l| l.handle == *h).map(|l| l.name.clone()) }).collect() } else { vec![] } }; if frozen_names.is_empty() { self.command_line.push_info("VPLAYER: no frozen layers in active viewport."); } else { self.command_line.push_info(&format!( "VPLAYER: frozen layers: {}", frozen_names.join(", ") )); } let new_cmd = VplayerCommand::new(vp_handle); self.command_line.push_info(&new_cmd.prompt()); self.tabs[i].active_cmd = Some(Box::new(new_cmd)); } } // ── Plot / Page Setup ────────────────────────────────────────── "PRINT"|"PLOT"|"EXPORT" => { return Task::done(Message::PlotExport); } "PAGESETUP" => { if self.tabs[i].scene.current_layout == "Model" { self.command_line.push_error("PAGESETUP: switch to a paper space layout first."); } else { return Task::done(Message::PageSetupOpen); } } _ => self.command_line.push_error(&format!("Unknown command: {cmd}")), } // Focus the command line whenever a command just became active. let i = self.active_tab; if self.tabs[i].active_cmd.is_some() { self.tabs[i].last_cmd = Some(cmd.to_string()); self.focus_cmd_input() } else { Task::none() } } }