feat(alias): centralize command aliases in user-editable ocad.pgp (#286/#288)
Command aliases (L→LINE, CC→COPYCLIP) were scattered across dispatch match arms and CommandRegistration name arrays. Move them all into a single user-editable file: - assets/ocad.pgp: shipped default alias table (ALIAS,*COMMAND format), embedded and copied to config/ocad.pgp on first launch; only that user copy is read after. AA→AREA default closes #286. - resolve_alias() rewrites the leading command token at the top of dispatch_command_inner, so aliases resolve for every path (families, plugins, Repeat menu). Args after the first space are untouched. - Autocomplete hides alias keys and, when the whole input is an alias, surfaces its target command as the top (highlighted) suggestion so the highlight matches what Enter runs (AA→AREA, L→LINE). - ALIASEDIT opens a new editor modal (add/edit/delete rows) with an Apply button; Apply persists to ocad.pgp, closing discards unapplied edits. - Strip alias tokens from all 8 dispatch match-arm files and 71 module CommandRegistration arrays, keeping canonical/synonym/distinct-command names. ocad.pgp is now the sole source: deleting or remapping an alias there takes effect. CUI stays on the keyboard-shortcut panel. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
f5b8a7d993
commit
91ba9f7699
69 changed files with 767 additions and 164 deletions
124
assets/ocad.pgp
Normal file
124
assets/ocad.pgp
Normal file
|
|
@ -0,0 +1,124 @@
|
||||||
|
; OpenCADStudio command aliases.
|
||||||
|
; Format: ALIAS,*COMMAND (lines starting with ';' are comments)
|
||||||
|
; Edit here or via the ALIASEDIT command. One alias per line.
|
||||||
|
;
|
||||||
|
; This file is the single source of truth for command-line aliases. It is
|
||||||
|
; copied to your user config folder on first launch; edit that copy (or use
|
||||||
|
; ALIASEDIT) to add, remap, or remove aliases.
|
||||||
|
|
||||||
|
; ── Draw ──────────────────────────────────────────────────────────────────
|
||||||
|
L, *LINE
|
||||||
|
ML, *MLINE
|
||||||
|
PL, *PLINE
|
||||||
|
A, *ARC
|
||||||
|
C, *CIRCLE
|
||||||
|
DO, *DONUT
|
||||||
|
EL, *ELLIPSE
|
||||||
|
PO, *POINT
|
||||||
|
SPL, *SPLINE
|
||||||
|
H, *HATCH
|
||||||
|
HE, *HATCHEDIT
|
||||||
|
XL, *XLINE
|
||||||
|
POL, *POLYGON
|
||||||
|
REC, *RECT
|
||||||
|
SO, *SOLID
|
||||||
|
WO, *WIPEOUT
|
||||||
|
|
||||||
|
; ── Modify ────────────────────────────────────────────────────────────────
|
||||||
|
M, *MOVE
|
||||||
|
CO, *COPY
|
||||||
|
RO, *ROTATE
|
||||||
|
SC, *SCALE
|
||||||
|
MI, *MIRROR
|
||||||
|
E, *ERASE
|
||||||
|
X, *EXPLODE
|
||||||
|
SS, *STRETCH
|
||||||
|
O, *OFFSET
|
||||||
|
TR, *TRIM
|
||||||
|
EX, *EXTEND
|
||||||
|
F, *FILLET
|
||||||
|
CHA, *CHAMFER
|
||||||
|
J, *JOIN
|
||||||
|
BR, *BREAK
|
||||||
|
BAP, *BREAKATPOINT
|
||||||
|
PE, *PEDIT
|
||||||
|
SPE, *SPLINEDIT
|
||||||
|
LEN, *LENGTHEN
|
||||||
|
AL, *ALIGN
|
||||||
|
AR, *ARRAY
|
||||||
|
MA, *MATCHPROP
|
||||||
|
ATE, *ATTEDIT
|
||||||
|
|
||||||
|
; ── Clipboard ─────────────────────────────────────────────────────────────
|
||||||
|
CC, *COPYCLIP
|
||||||
|
CX, *CUTCLIP
|
||||||
|
PC, *PASTECLIP
|
||||||
|
|
||||||
|
; ── Blocks / references ───────────────────────────────────────────────────
|
||||||
|
XA, *XATTACH
|
||||||
|
XR, *XREF
|
||||||
|
ADC, *ADCENTER
|
||||||
|
|
||||||
|
; ── Layers / groups ───────────────────────────────────────────────────────
|
||||||
|
LA, *LAYERS
|
||||||
|
G, *GROUP
|
||||||
|
UG, *UNGROUP
|
||||||
|
|
||||||
|
; ── Dimensions / annotation ───────────────────────────────────────────────
|
||||||
|
DAL, *DIMALIGNED
|
||||||
|
DAN, *DIMANGULAR
|
||||||
|
DDI, *DIMDIAMETER
|
||||||
|
DLI, *DIMLINEAR
|
||||||
|
DRA, *DIMRADIUS
|
||||||
|
DOR, *DIMORDINATE
|
||||||
|
DBA, *DIMBASELINE
|
||||||
|
DCO, *DIMCONTINUE
|
||||||
|
DED, *DIMEDIT
|
||||||
|
DBR, *DIMBREAK
|
||||||
|
DJL, *DIMJOGLINE
|
||||||
|
DJO, *DIMJOGGED
|
||||||
|
DCE, *DIMCENTER
|
||||||
|
LE, *LEADER
|
||||||
|
QL, *QLEADER
|
||||||
|
MLD, *MLEADER
|
||||||
|
MLA, *MLEADERADD
|
||||||
|
MLR, *MLEADERREMOVE
|
||||||
|
MLAL, *MLEADERALIGN
|
||||||
|
MLC, *MLEADERCOLLECT
|
||||||
|
MT, *MTEXT
|
||||||
|
DT, *TEXT
|
||||||
|
T, *TEXT
|
||||||
|
ED, *DDEDIT
|
||||||
|
TOL, *TOLERANCE
|
||||||
|
|
||||||
|
; ── Inquiry / selection ───────────────────────────────────────────────────
|
||||||
|
AA, *AREA
|
||||||
|
DI, *DIST
|
||||||
|
DIV, *DIVIDE
|
||||||
|
ME, *MEASURE
|
||||||
|
MEA, *MEASUREGEOM
|
||||||
|
LI, *LIST
|
||||||
|
SA, *SELECTALL
|
||||||
|
DE, *DESELECT
|
||||||
|
QS, *QSELECT
|
||||||
|
FI, *FILTER
|
||||||
|
|
||||||
|
; ── View / display ────────────────────────────────────────────────────────
|
||||||
|
P, *PAN
|
||||||
|
U, *UNDO
|
||||||
|
PR, *PROPERTIES
|
||||||
|
UN, *UNITS
|
||||||
|
TP, *TOOLPALETTES
|
||||||
|
SSM, *SHEETSET
|
||||||
|
VW, *WIREFRAME
|
||||||
|
HI, *HIDE
|
||||||
|
3O, *3DORBIT
|
||||||
|
MV, *MVIEW
|
||||||
|
MS, *MSPACE
|
||||||
|
PW, *PLOTWINDOW
|
||||||
|
QP, *QUICKPRINT
|
||||||
|
|
||||||
|
; ── 3D solids ─────────────────────────────────────────────────────────────
|
||||||
|
EXT, *EXTRUDE
|
||||||
|
REV, *REVOLVE
|
||||||
|
INF, *INTERFERE
|
||||||
159
src/app/alias.rs
Normal file
159
src/app/alias.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
//! Command aliases — short abbreviations typed at the command line that expand
|
||||||
|
//! to a full command (e.g. `L` → `LINE`). The alias table is the single source
|
||||||
|
//! of truth: it is loaded from a user-editable `aliases.pgp` file at startup and
|
||||||
|
//! consulted before every command dispatch (see `resolve_alias`), so users can
|
||||||
|
//! add, remap, or delete aliases without touching the app.
|
||||||
|
//!
|
||||||
|
//! File format mirrors the long-standing plain-text `.pgp` convention so it is
|
||||||
|
//! familiar and hand-editable, stored as `ocad.pgp`:
|
||||||
|
//!
|
||||||
|
//! ```text
|
||||||
|
//! ; lines beginning with a semicolon are comments
|
||||||
|
//! L, *LINE
|
||||||
|
//! CC, *COPYCLIP
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! An alias is the token left of the comma; the command is the token right of
|
||||||
|
//! it, with an optional leading `*`. Both are matched case-insensitively and
|
||||||
|
//! stored uppercased. Stored next to the other per-user config under
|
||||||
|
//! `crate::config::config_dir()`, so no serialization crate is pulled in.
|
||||||
|
|
||||||
|
use super::OpenCADStudio;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// The shipped default alias file, embedded at compile time. Its aliases live in
|
||||||
|
/// `assets/ocad.pgp` — not in Rust — so the defaults are just data in the same
|
||||||
|
/// format the user edits. On first launch this text is written verbatim to the
|
||||||
|
/// user's config folder (comments and all) as the starting `ocad.pgp`; only that
|
||||||
|
/// user copy is read thereafter. This embedded copy is the first-run seed /
|
||||||
|
/// factory default.
|
||||||
|
const DEFAULT_ALIASES_PGP: &str = include_str!("../../assets/ocad.pgp");
|
||||||
|
|
||||||
|
/// Path to the user's alias file, `<config>/ocad.pgp`. `None` when the platform
|
||||||
|
/// config base can't be resolved (headless, no `HOME`, wasm).
|
||||||
|
fn alias_file_path() -> Option<PathBuf> {
|
||||||
|
Some(crate::config::config_dir()?.join("ocad.pgp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse `.pgp` text into an `alias → command` map, both uppercased. Skips blank
|
||||||
|
/// lines and `;` comments; tolerates a leading `*` on the command and arbitrary
|
||||||
|
/// whitespace padding.
|
||||||
|
fn parse_pgp(body: &str) -> FxHashMap<String, String> {
|
||||||
|
let mut map = FxHashMap::default();
|
||||||
|
for line in body.lines() {
|
||||||
|
let line = line.trim();
|
||||||
|
if line.is_empty() || line.starts_with(';') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let Some((alias, cmd)) = line.split_once(',') else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let alias = alias.trim().to_uppercase();
|
||||||
|
let cmd = cmd.trim().trim_start_matches('*').trim().to_uppercase();
|
||||||
|
if alias.is_empty() || cmd.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
map.insert(alias, cmd);
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialize an `alias → command` map to `.pgp` text with a header and aligned
|
||||||
|
/// columns, sorted by alias for a stable, diffable file.
|
||||||
|
pub(super) fn to_pgp(map: &FxHashMap<String, String>) -> String {
|
||||||
|
let mut rows: Vec<(&String, &String)> = map.iter().collect();
|
||||||
|
rows.sort_by(|a, b| a.0.cmp(b.0));
|
||||||
|
let mut out = String::from(
|
||||||
|
"; OpenCADStudio command aliases.\n\
|
||||||
|
; Format: ALIAS,*COMMAND (lines starting with ';' are comments)\n\
|
||||||
|
; Edit here or via the ALIASEDIT command. One alias per line.\n\n",
|
||||||
|
);
|
||||||
|
for (alias, cmd) in rows {
|
||||||
|
out.push_str(&format!("{:<10}*{}\n", format!("{alias},"), cmd));
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The built-in defaults, parsed from the embedded `.pgp`. Used when no user
|
||||||
|
/// file exists yet (or can't be reached, e.g. wasm).
|
||||||
|
fn default_map() -> FxHashMap<String, String> {
|
||||||
|
parse_pgp(DEFAULT_ALIASES_PGP)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the alias table at boot. Reads the user's `aliases.pgp`; if it is
|
||||||
|
/// missing, writes the shipped default file verbatim (preserving its comments
|
||||||
|
/// and layout) so the user has a well-formatted starting point to edit, then
|
||||||
|
/// parses it. Falls back to the embedded defaults when the file can't be read
|
||||||
|
/// or written (read-only home, wasm — where it silently no-ops).
|
||||||
|
pub(super) fn load_aliases() -> FxHashMap<String, String> {
|
||||||
|
match alias_file_path() {
|
||||||
|
Some(path) => match std::fs::read_to_string(&path) {
|
||||||
|
Ok(body) => parse_pgp(&body),
|
||||||
|
Err(_) => {
|
||||||
|
// No file yet — copy the shipped default file, best-effort.
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
let _ = std::fs::create_dir_all(dir);
|
||||||
|
}
|
||||||
|
let _ = std::fs::write(&path, DEFAULT_ALIASES_PGP);
|
||||||
|
default_map()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => default_map(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Persist the alias table to `aliases.pgp`. Best-effort; returns `Ok` (no-op)
|
||||||
|
/// when there is no config dir (wasm/headless).
|
||||||
|
pub(super) fn save_map(map: &FxHashMap<String, String>) -> std::io::Result<()> {
|
||||||
|
let Some(path) = alias_file_path() else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
std::fs::create_dir_all(dir)?;
|
||||||
|
}
|
||||||
|
std::fs::write(path, to_pgp(map))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OpenCADStudio {
|
||||||
|
/// Rewrite the leading command token through the alias table, leaving any
|
||||||
|
/// arguments after the first whitespace untouched. Returns `None` when the
|
||||||
|
/// verb is not an alias, so the caller passes the original string through
|
||||||
|
/// unchanged. Keyed uppercase so lowercase aliases from scripts/plugins
|
||||||
|
/// (which bypass the command line's verb-uppercasing) still resolve.
|
||||||
|
pub(super) fn resolve_alias(&self, cmd: &str) -> Option<String> {
|
||||||
|
if self.command_aliases.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (verb, rest) = match cmd.split_once(char::is_whitespace) {
|
||||||
|
Some((v, r)) => (v, Some(r)),
|
||||||
|
None => (cmd, None),
|
||||||
|
};
|
||||||
|
let canon = self.command_aliases.get(&verb.to_uppercase())?;
|
||||||
|
Some(match rest {
|
||||||
|
Some(r) => format!("{canon} {r}"),
|
||||||
|
None => canon.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replace the alias table (from the editor), persist it, and mirror it into
|
||||||
|
/// the command line so autocomplete reflects the edits immediately.
|
||||||
|
pub(super) fn set_command_aliases(&mut self, map: FxHashMap<String, String>) {
|
||||||
|
let _ = save_map(&map);
|
||||||
|
self.command_line.command_aliases = map.clone();
|
||||||
|
self.command_aliases = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Commit the alias-editor working rows to the table (Apply button): build a
|
||||||
|
/// map from the rows, dropping incomplete ones, and persist + sync. Leaves
|
||||||
|
/// the editor open so the user can keep editing.
|
||||||
|
pub(super) fn apply_alias_editor_rows(&mut self) {
|
||||||
|
let map: FxHashMap<String, String> = self
|
||||||
|
.alias_editor_rows
|
||||||
|
.iter()
|
||||||
|
.filter(|(a, c)| !a.trim().is_empty() && !c.trim().is_empty())
|
||||||
|
.map(|(a, c)| (a.trim().to_string(), c.trim().to_string()))
|
||||||
|
.collect();
|
||||||
|
self.set_command_aliases(map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -53,7 +53,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"COPYCLIP" | "CC" => {
|
"COPYCLIP" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -142,7 +142,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"CUTCLIP" | "CX" => {
|
"CUTCLIP" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -179,7 +179,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"PASTECLIP" | "PC" => {
|
"PASTECLIP" => {
|
||||||
if self.clipboard.is_empty() {
|
if self.clipboard.is_empty() {
|
||||||
self.command_line.push_error("Clipboard is empty.");
|
self.command_line.push_error("Clipboard is empty.");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -386,7 +386,7 @@ impl OpenCADStudio {
|
||||||
// ADCENTER / CONTENTBROWSER — report the drawing's named content
|
// ADCENTER / CONTENTBROWSER — report the drawing's named content
|
||||||
// (blocks and layers) from the command line in place of the browser
|
// (blocks and layers) from the command line in place of the browser
|
||||||
// panel.
|
// panel.
|
||||||
"ADCENTER" | "CONTENTBROWSER" | "ADC" => {
|
"ADCENTER" | "CONTENTBROWSER" => {
|
||||||
let blocks = self.tabs[i].scene.custom_block_names();
|
let blocks = self.tabs[i].scene.custom_block_names();
|
||||||
let layers: Vec<String> = self.tabs[i]
|
let layers: Vec<String> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -476,7 +476,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"XATTACH" | "XA" => {
|
"XATTACH" => {
|
||||||
// Launch the file picker; XAttachPickResult will start the command.
|
// Launch the file picker; XAttachPickResult will start the command.
|
||||||
return Some(Task::done(Message::XAttachPick));
|
return Some(Task::done(Message::XAttachPick));
|
||||||
}
|
}
|
||||||
|
|
@ -498,7 +498,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"XREF" | "XR" => {
|
"XREF" => {
|
||||||
// List all xref blocks in the current drawing.
|
// List all xref blocks in the current drawing.
|
||||||
let xrefs: Vec<String> = self.tabs[i]
|
let xrefs: Vec<String> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
|
||||||
|
|
@ -3,42 +3,42 @@ use super::*;
|
||||||
impl OpenCADStudio {
|
impl OpenCADStudio {
|
||||||
pub(super) fn dispatch_dim(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
pub(super) fn dispatch_dim(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||||
match cmd {
|
match cmd {
|
||||||
"DIMALIGNED" | "DAL" => {
|
"DIMALIGNED" => {
|
||||||
use crate::modules::annotate::aligned_dim::AlignedDimensionCommand;
|
use crate::modules::annotate::aligned_dim::AlignedDimensionCommand;
|
||||||
let cmd = AlignedDimensionCommand::new();
|
let cmd = AlignedDimensionCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMDIAMETER" | "DDI" => {
|
"DIMDIAMETER" => {
|
||||||
use crate::modules::annotate::diameter_dim::DiameterDimensionCommand;
|
use crate::modules::annotate::diameter_dim::DiameterDimensionCommand;
|
||||||
let cmd = DiameterDimensionCommand::new();
|
let cmd = DiameterDimensionCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMLINEAR" | "DLI" => {
|
"DIMLINEAR" => {
|
||||||
use crate::modules::annotate::linear_dim::LinearDimensionCommand;
|
use crate::modules::annotate::linear_dim::LinearDimensionCommand;
|
||||||
let new_cmd = LinearDimensionCommand::new();
|
let new_cmd = LinearDimensionCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMRADIUS" | "DRA" => {
|
"DIMRADIUS" => {
|
||||||
use crate::modules::annotate::radius_dim::RadiusDimensionCommand;
|
use crate::modules::annotate::radius_dim::RadiusDimensionCommand;
|
||||||
let new_cmd = RadiusDimensionCommand::new();
|
let new_cmd = RadiusDimensionCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMANGULAR" | "DAN" => {
|
"DIMANGULAR" => {
|
||||||
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
|
use crate::modules::annotate::angular_dim::AngularDimensionCommand;
|
||||||
let new_cmd = AngularDimensionCommand::new();
|
let new_cmd = AngularDimensionCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMORDINATE" | "DOR" => {
|
"DIMORDINATE" => {
|
||||||
use crate::modules::annotate::ordinate_dim::OrdinateDimCommand;
|
use crate::modules::annotate::ordinate_dim::OrdinateDimCommand;
|
||||||
let new_cmd = OrdinateDimCommand::new();
|
let new_cmd = OrdinateDimCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -47,7 +47,7 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// LEADER draws a leader line with an annotation; QLEADER is the same
|
// LEADER draws a leader line with an annotation; QLEADER is the same
|
||||||
// quick leader-plus-annotation operation.
|
// quick leader-plus-annotation operation.
|
||||||
"LEADER" | "LE" | "QLEADER" | "QL" => {
|
"LEADER" | "QLEADER" => {
|
||||||
use crate::modules::annotate::leader_cmd::LeaderCommand;
|
use crate::modules::annotate::leader_cmd::LeaderCommand;
|
||||||
let new_cmd = LeaderCommand::new();
|
let new_cmd = LeaderCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -400,14 +400,14 @@ impl OpenCADStudio {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLEADER" | "MLD" => {
|
"MLEADER" => {
|
||||||
use crate::modules::annotate::mleader_cmd::MLeaderCommand;
|
use crate::modules::annotate::mleader_cmd::MLeaderCommand;
|
||||||
let new_cmd = MLeaderCommand::new();
|
let new_cmd = MLeaderCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"TOLERANCE" | "TOL" => {
|
"TOLERANCE" => {
|
||||||
use crate::modules::annotate::tolerance_cmd::ToleranceCommand;
|
use crate::modules::annotate::tolerance_cmd::ToleranceCommand;
|
||||||
let cmd = ToleranceCommand::new();
|
let cmd = ToleranceCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -421,7 +421,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMCONTINUE" | "DCO" => {
|
"DIMCONTINUE" => {
|
||||||
use crate::modules::annotate::dim_continue::DimContinueCommand;
|
use crate::modules::annotate::dim_continue::DimContinueCommand;
|
||||||
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
||||||
find_last_linear_dim(&self.tabs[i].scene)
|
find_last_linear_dim(&self.tabs[i].scene)
|
||||||
|
|
@ -434,7 +434,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMBASELINE" | "DBA" => {
|
"DIMBASELINE" => {
|
||||||
use crate::modules::annotate::dim_baseline::DimBaselineCommand;
|
use crate::modules::annotate::dim_baseline::DimBaselineCommand;
|
||||||
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
let cmd = if let Some((p1, p2, dp, rot, trot)) =
|
||||||
find_last_linear_dim(&self.tabs[i].scene)
|
find_last_linear_dim(&self.tabs[i].scene)
|
||||||
|
|
@ -548,7 +548,7 @@ impl OpenCADStudio {
|
||||||
return Some(Task::none());
|
return Some(Task::none());
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMEDIT" | "DED" => {
|
"DIMEDIT" => {
|
||||||
use crate::modules::annotate::dimedit::DimEditCommand;
|
use crate::modules::annotate::dimedit::DimEditCommand;
|
||||||
let cmd = DimEditCommand::new();
|
let cmd = DimEditCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -562,7 +562,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMBREAK" | "DBR" => {
|
"DIMBREAK" => {
|
||||||
use crate::modules::annotate::dimbreak::DimBreakCommand;
|
use crate::modules::annotate::dimbreak::DimBreakCommand;
|
||||||
let cmd = DimBreakCommand::new();
|
let cmd = DimBreakCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -576,35 +576,35 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMJOGLINE" | "DJL" => {
|
"DIMJOGLINE" => {
|
||||||
use crate::modules::annotate::dimjogline::DimJogLineCommand;
|
use crate::modules::annotate::dimjogline::DimJogLineCommand;
|
||||||
let cmd = DimJogLineCommand::new();
|
let cmd = DimJogLineCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLEADERADD" | "MLA" => {
|
"MLEADERADD" => {
|
||||||
use crate::modules::annotate::mleader_edit::MLeaderAddCommand;
|
use crate::modules::annotate::mleader_edit::MLeaderAddCommand;
|
||||||
let cmd = MLeaderAddCommand::new();
|
let cmd = MLeaderAddCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLEADERREMOVE" | "MLR" => {
|
"MLEADERREMOVE" => {
|
||||||
use crate::modules::annotate::mleader_edit::MLeaderRemoveCommand;
|
use crate::modules::annotate::mleader_edit::MLeaderRemoveCommand;
|
||||||
let cmd = MLeaderRemoveCommand::new();
|
let cmd = MLeaderRemoveCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLEADERALIGN" | "MLAL" => {
|
"MLEADERALIGN" => {
|
||||||
use crate::modules::annotate::mleader_edit::MLeaderAlignCommand;
|
use crate::modules::annotate::mleader_edit::MLeaderAlignCommand;
|
||||||
let cmd = MLeaderAlignCommand::new();
|
let cmd = MLeaderAlignCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLEADERCOLLECT" | "MLC" => {
|
"MLEADERCOLLECT" => {
|
||||||
use crate::modules::annotate::mleader_edit::MLeaderCollectCommand;
|
use crate::modules::annotate::mleader_edit::MLeaderCollectCommand;
|
||||||
let cmd = MLeaderCollectCommand::new();
|
let cmd = MLeaderCollectCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -647,14 +647,14 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"PLOTWINDOW" | "PW" => {
|
"PLOTWINDOW" => {
|
||||||
use crate::modules::view::plot_window::PlotWindowCommand;
|
use crate::modules::view::plot_window::PlotWindowCommand;
|
||||||
let cmd = PlotWindowCommand::new();
|
let cmd = PlotWindowCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"QUICKPRINT" | "QP" => {
|
"QUICKPRINT" => {
|
||||||
use crate::modules::view::quick_print::QuickPrintCommand;
|
use crate::modules::view::quick_print::QuickPrintCommand;
|
||||||
let cmd = QuickPrintCommand::new();
|
let cmd = QuickPrintCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -671,7 +671,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"STRETCH" | "SS" => {
|
"STRETCH" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -692,7 +692,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"FILLET" | "F" => {
|
"FILLET" => {
|
||||||
use crate::modules::draw::modify::fillet::FilletCommand;
|
use crate::modules::draw::modify::fillet::FilletCommand;
|
||||||
let entities: Vec<_> = self.tabs[i]
|
let entities: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -717,7 +717,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"ARRAY" | "AR" | "ARRAYRECT" => {
|
"ARRAY" | "ARRAYRECT" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -809,7 +809,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"CHAMFER" | "CHA" => {
|
"CHAMFER" => {
|
||||||
use crate::modules::draw::modify::fillet::ChamferCommand;
|
use crate::modules::draw::modify::fillet::ChamferCommand;
|
||||||
let entities: Vec<_> = self.tabs[i]
|
let entities: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -834,7 +834,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"EXPLODE" | "X" => {
|
"EXPLODE" => {
|
||||||
use crate::modules::draw::modify::explode::explode_entity;
|
use crate::modules::draw::modify::explode::explode_entity;
|
||||||
let entities: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
let entities: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
||||||
if entities.is_empty() {
|
if entities.is_empty() {
|
||||||
|
|
@ -876,7 +876,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"OFFSET" | "O" => {
|
"OFFSET" => {
|
||||||
use crate::modules::draw::modify::offset::OffsetCommand;
|
use crate::modules::draw::modify::offset::OffsetCommand;
|
||||||
let all_entities: Vec<_> = self.tabs[i]
|
let all_entities: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -892,7 +892,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"TRIM" | "TR" => {
|
"TRIM" => {
|
||||||
use crate::modules::draw::modify::trim::TrimCommand;
|
use crate::modules::draw::modify::trim::TrimCommand;
|
||||||
let entities: Vec<_> = self.tabs[i]
|
let entities: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -930,7 +930,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"EXTEND" | "EX" => {
|
"EXTEND" => {
|
||||||
use crate::modules::draw::modify::trim::ExtendCommand;
|
use crate::modules::draw::modify::trim::ExtendCommand;
|
||||||
let entities: Vec<_> = self.tabs[i]
|
let entities: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -955,7 +955,7 @@ impl OpenCADStudio {
|
||||||
// DIMJOGGED <DJO> — create a jogged radius dimension on the selected
|
// DIMJOGGED <DJO> — create a jogged radius dimension on the selected
|
||||||
// arc/circle: a standard radius dim carrying an OCS_JOGGED XData marker
|
// arc/circle: a standard radius dim carrying an OCS_JOGGED XData marker
|
||||||
// that the geometry generator renders with a foreshortened zig-zag.
|
// that the geometry generator renders with a foreshortened zig-zag.
|
||||||
"DIMJOGGED" | "DJO" | "DIMJOG" => {
|
"DIMJOGGED" | "DIMJOG" => {
|
||||||
use acadrust::entities::{Dimension, DimensionRadius};
|
use acadrust::entities::{Dimension, DimensionRadius};
|
||||||
use acadrust::types::Vector3;
|
use acadrust::types::Vector3;
|
||||||
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
use acadrust::xdata::{ExtendedDataRecord, XDataValue};
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ impl OpenCADStudio {
|
||||||
match cmd {
|
match cmd {
|
||||||
// Interactive pan: left-drag pans the view until Esc. The only pan
|
// Interactive pan: left-drag pans the view until Esc. The only pan
|
||||||
// path when there is no middle mouse button (trackpad / web).
|
// path when there is no middle mouse button (trackpad / web).
|
||||||
"PAN" | "P" => {
|
"PAN" => {
|
||||||
self.tabs[i].pan_mode = true;
|
self.tabs[i].pan_mode = true;
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_output("PAN: drag with the left mouse button. Press Esc to exit.");
|
.push_output("PAN: drag with the left mouse button. Press Esc to exit.");
|
||||||
|
|
@ -126,7 +126,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── PROPERTIES — toggle Properties panel visibility ──────────────────
|
// ── PROPERTIES — toggle Properties panel visibility ──────────────────
|
||||||
"PROPERTIES" | "PR" | "PROPS" => {
|
"PROPERTIES" | "PROPS" => {
|
||||||
return Some(Task::done(Message::ToggleProperties));
|
return Some(Task::done(Message::ToggleProperties));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,7 +160,7 @@ impl OpenCADStudio {
|
||||||
return Some(Task::done(Message::ToggleSnapPopup));
|
return Some(Task::done(Message::ToggleSnapPopup));
|
||||||
}
|
}
|
||||||
// UNITS — open the drawing-units picker (linear / angular format).
|
// UNITS — open the drawing-units picker (linear / angular format).
|
||||||
"UNITS" | "UN" | "DDUNITS" => {
|
"UNITS" | "DDUNITS" => {
|
||||||
return Some(Task::done(Message::ToggleUnitsPopup));
|
return Some(Task::done(Message::ToggleUnitsPopup));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -174,13 +174,13 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── TOOLPALETTES — not yet implemented ───────────────────────────────
|
// ── TOOLPALETTES — not yet implemented ───────────────────────────────
|
||||||
"TOOLPALETTES" | "TP" => {
|
"TOOLPALETTES" => {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info("TOOLPALETTES: Tool Palettes not yet implemented.");
|
.push_info("TOOLPALETTES: Tool Palettes not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── SHEETSET — not yet implemented ───────────────────────────────────
|
// ── SHEETSET — not yet implemented ───────────────────────────────────
|
||||||
"SHEETSET" | "SSM" => {
|
"SHEETSET" => {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info("SHEETSET: Sheet Set Manager not yet implemented.");
|
.push_info("SHEETSET: Sheet Set Manager not yet implemented.");
|
||||||
}
|
}
|
||||||
|
|
@ -292,7 +292,7 @@ impl OpenCADStudio {
|
||||||
// PRESSPULL on a closed boundary creates a solid by extruding it to a
|
// PRESSPULL on a closed boundary creates a solid by extruding it to a
|
||||||
// height — the same operation as EXTRUDE. THICKEN turns a closed planar
|
// height — the same operation as EXTRUDE. THICKEN turns a closed planar
|
||||||
// profile into a solid of the given thickness, which is also an extrude.
|
// profile into a solid of the given thickness, which is also an extrude.
|
||||||
"EXTRUDE" | "EXT" | "PRESSPULL" | "THICKEN" => {
|
"EXTRUDE" | "PRESSPULL" | "THICKEN" => {
|
||||||
use crate::modules::insert::solid3d_cmds::ExtrudeCommand;
|
use crate::modules::insert::solid3d_cmds::ExtrudeCommand;
|
||||||
// If a single entity is already selected, skip the pick step.
|
// If a single entity is already selected, skip the pick step.
|
||||||
let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
let selected: Vec<_> = self.tabs[i].scene.selected_entities().into_iter().collect();
|
||||||
|
|
@ -311,7 +311,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── REVOLVE ────────────────────────────────────────────────────
|
// ── REVOLVE ────────────────────────────────────────────────────
|
||||||
"REVOLVE" | "REV" => {
|
"REVOLVE" => {
|
||||||
use crate::modules::insert::solid3d_cmds::RevolveCommand;
|
use crate::modules::insert::solid3d_cmds::RevolveCommand;
|
||||||
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
let color = self.tabs[i].scene.layer_color(&self.tabs[i].active_layer);
|
||||||
let cmd = RevolveCommand::new(color);
|
let cmd = RevolveCommand::new(color);
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,14 @@ impl OpenCADStudio {
|
||||||
pub(super) fn dispatch_draw(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
pub(super) fn dispatch_draw(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||||
match cmd {
|
match cmd {
|
||||||
// ── Draw commands ──────────────────────────────────────────────
|
// ── Draw commands ──────────────────────────────────────────────
|
||||||
"LINE" | "L" => {
|
"LINE" => {
|
||||||
use crate::modules::draw::draw::line::LineCommand;
|
use crate::modules::draw::draw::line::LineCommand;
|
||||||
let new_cmd = LineCommand::new();
|
let new_cmd = LineCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MLINE" | "ML" => {
|
"MLINE" => {
|
||||||
use crate::modules::draw::draw::mline::MlineCommand;
|
use crate::modules::draw::draw::mline::MlineCommand;
|
||||||
let style = self.tabs[i].scene.document.header.multiline_style.clone();
|
let style = self.tabs[i].scene.document.header.multiline_style.clone();
|
||||||
let cmd_obj = MlineCommand::with_style(style);
|
let cmd_obj = MlineCommand::with_style(style);
|
||||||
|
|
@ -184,14 +184,14 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"DONUT" | "DO" => {
|
"DONUT" => {
|
||||||
use crate::modules::draw::draw::donut::DonutCommand;
|
use crate::modules::draw::draw::donut::DonutCommand;
|
||||||
let cmd = DonutCommand::new();
|
let cmd = DonutCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"CIRCLE" | "C" => {
|
"CIRCLE" => {
|
||||||
use crate::modules::draw::draw::circle::CircleCommand;
|
use crate::modules::draw::draw::circle::CircleCommand;
|
||||||
let new_cmd = CircleCommand::new();
|
let new_cmd = CircleCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -232,7 +232,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"ARC" | "A" => {
|
"ARC" => {
|
||||||
use crate::modules::draw::draw::arc::ArcCommand;
|
use crate::modules::draw::draw::arc::ArcCommand;
|
||||||
let new_cmd = ArcCommand::new();
|
let new_cmd = ArcCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -318,7 +318,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"RECT" | "RECTANG" | "REC" => {
|
"RECT" | "RECTANG" => {
|
||||||
use crate::modules::draw::draw::shapes::RectCommand;
|
use crate::modules::draw::draw::shapes::RectCommand;
|
||||||
let new_cmd = RectCommand::new();
|
let new_cmd = RectCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -348,7 +348,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
"POLY" | "POLYGON" | "POL" => {
|
"POLY" | "POLYGON" => {
|
||||||
use crate::modules::draw::draw::shapes::PolyCommand;
|
use crate::modules::draw::draw::shapes::PolyCommand;
|
||||||
let new_cmd = PolyCommand::new();
|
let new_cmd = PolyCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -367,7 +367,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"PLINE" | "PL" => {
|
"PLINE" => {
|
||||||
use crate::modules::draw::draw::polyline::PlineCommand;
|
use crate::modules::draw::draw::polyline::PlineCommand;
|
||||||
let new_cmd = PlineCommand::new();
|
let new_cmd = PlineCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -383,7 +383,7 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// 2D filled solid. Reached via SO / SOLID2D — the bare SOLID verb is
|
// 2D filled solid. Reached via SO / SOLID2D — the bare SOLID verb is
|
||||||
// currently the shaded-display toggle (token collision tracked).
|
// currently the shaded-display toggle (token collision tracked).
|
||||||
"SO" | "SOLID" | "SOLID2D" => {
|
"SOLID" | "SOLID2D" => {
|
||||||
use crate::modules::draw::draw::solid2d::Solid2dCommand;
|
use crate::modules::draw::draw::solid2d::Solid2dCommand;
|
||||||
let new_cmd = Solid2dCommand::new();
|
let new_cmd = Solid2dCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -411,7 +411,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIMCENTER" | "DCE" | "CENTERMARK" => {
|
"DIMCENTER" | "CENTERMARK" => {
|
||||||
use crate::modules::draw::draw::dimcenter::DimCenterCommand;
|
use crate::modules::draw::draw::dimcenter::DimCenterCommand;
|
||||||
let new_cmd = DimCenterCommand::new();
|
let new_cmd = DimCenterCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -432,7 +432,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MEASUREGEOM" | "MEA" => {
|
"MEASUREGEOM" => {
|
||||||
use crate::modules::draw::inquiry::measuregeom::MeasureGeomCommand;
|
use crate::modules::draw::inquiry::measuregeom::MeasureGeomCommand;
|
||||||
let new_cmd = MeasureGeomCommand::new();
|
let new_cmd = MeasureGeomCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -442,7 +442,7 @@ impl OpenCADStudio {
|
||||||
// ── Modify commands ────────────────────────────────────────────
|
// ── Modify commands ────────────────────────────────────────────
|
||||||
// MOVE works from picked points, so it already relocates entities
|
// MOVE works from picked points, so it already relocates entities
|
||||||
// in 3D; 3DMOVE is the same operation.
|
// in 3D; 3DMOVE is the same operation.
|
||||||
"MOVE" | "M" | "3DMOVE" => {
|
"MOVE" | "3DMOVE" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -463,7 +463,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"COPY" | "CO" => {
|
"COPY" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -484,7 +484,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"ROTATE" | "RO" => {
|
"ROTATE" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -539,7 +539,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"POINT" | "PO" => {
|
"POINT" => {
|
||||||
use crate::modules::draw::draw::point::PointCommand;
|
use crate::modules::draw::draw::point::PointCommand;
|
||||||
let new_cmd = PointCommand::new();
|
let new_cmd = PointCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -553,14 +553,14 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"XLINE" | "XL" | "CONSTRUCTIONLINE" => {
|
"XLINE" | "CONSTRUCTIONLINE" => {
|
||||||
use crate::modules::draw::draw::ray::XLineCommand;
|
use crate::modules::draw::draw::ray::XLineCommand;
|
||||||
let new_cmd = XLineCommand::new();
|
let new_cmd = XLineCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"HATCH" | "H" => {
|
"HATCH" => {
|
||||||
use crate::modules::draw::draw::hatch::HatchCommand;
|
use crate::modules::draw::draw::hatch::HatchCommand;
|
||||||
let outlines = self.tabs[i].scene.closed_outlines();
|
let outlines = self.tabs[i].scene.closed_outlines();
|
||||||
let new_cmd = HatchCommand::new(outlines);
|
let new_cmd = HatchCommand::new(outlines);
|
||||||
|
|
@ -568,7 +568,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"HATCHEDIT" | "HE" => {
|
"HATCHEDIT" => {
|
||||||
use crate::modules::draw::draw::hatchedit::HatcheditCommand;
|
use crate::modules::draw::draw::hatchedit::HatcheditCommand;
|
||||||
// If a single hatch is already selected, skip the pick step.
|
// If a single hatch is already selected, skip the pick step.
|
||||||
let sel = self.tabs[i].scene.selected_entities();
|
let sel = self.tabs[i].scene.selected_entities();
|
||||||
|
|
@ -610,7 +610,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"ELLIPSE" | "EL" => {
|
"ELLIPSE" => {
|
||||||
use crate::modules::draw::draw::ellipse::EllipseCommand;
|
use crate::modules::draw::draw::ellipse::EllipseCommand;
|
||||||
let new_cmd = EllipseCommand::new();
|
let new_cmd = EllipseCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
@ -631,14 +631,14 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"SPLINE" | "SPL" => {
|
"SPLINE" => {
|
||||||
use crate::modules::draw::draw::spline::SplineCommand;
|
use crate::modules::draw::draw::spline::SplineCommand;
|
||||||
let new_cmd = SplineCommand::new();
|
let new_cmd = SplineCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"SCALE" | "SC" => {
|
"SCALE" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -659,7 +659,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"MIRROR" | "MI" => {
|
"MIRROR" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -682,7 +682,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"ERASE" | "E" => {
|
"ERASE" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -727,7 +727,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERFERE — non-destructive intersect: solid from the overlap.
|
// INTERFERE — non-destructive intersect: solid from the overlap.
|
||||||
"INTERFERE" | "INF" => {
|
"INTERFERE" => {
|
||||||
return Some(self.solid_interfere());
|
return Some(self.solid_interfere());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -963,14 +963,14 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Annotate commands ──────────────────────────────────────────
|
// ── Annotate commands ──────────────────────────────────────────
|
||||||
"TEXT" | "T" | "DT" => {
|
"TEXT" => {
|
||||||
use crate::modules::annotate::text::TextCommand;
|
use crate::modules::annotate::text::TextCommand;
|
||||||
let new_cmd = TextCommand::new();
|
let new_cmd = TextCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
self.tabs[i].active_cmd = Some(Box::new(new_cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DDEDIT" | "ED" => {
|
"DDEDIT" => {
|
||||||
use crate::modules::annotate::ddedit::DdeditCommand;
|
use crate::modules::annotate::ddedit::DdeditCommand;
|
||||||
// A single text entity already selected opens its in-place
|
// A single text entity already selected opens its in-place
|
||||||
// editor directly; otherwise prompt for a pick.
|
// editor directly; otherwise prompt for a pick.
|
||||||
|
|
@ -994,7 +994,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"MTEXT" | "MT" => {
|
"MTEXT" => {
|
||||||
use crate::modules::annotate::mtext::MTextCommand;
|
use crate::modules::annotate::mtext::MTextCommand;
|
||||||
let new_cmd = MTextCommand::new();
|
let new_cmd = MTextCommand::new();
|
||||||
self.command_line.push_info(&new_cmd.prompt());
|
self.command_line.push_info(&new_cmd.prompt());
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"UNDO" | "U" => return Some(Task::done(Message::Undo)),
|
"UNDO" => return Some(Task::done(Message::Undo)),
|
||||||
"REDO" => return Some(Task::done(Message::Redo)),
|
"REDO" => return Some(Task::done(Message::Redo)),
|
||||||
// OOPS — restore the objects removed by the most recent ERASE,
|
// OOPS — restore the objects removed by the most recent ERASE,
|
||||||
// without undoing any work done since.
|
// without undoing any work done since.
|
||||||
|
|
@ -92,7 +92,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"CLEAR" | "CLR" => return Some(Task::done(Message::ClearScene)),
|
"CLEAR" | "CLR" => return Some(Task::done(Message::ClearScene)),
|
||||||
"WIREFRAME" | "VW" => return Some(Task::done(Message::SetWireframe(true))),
|
"WIREFRAME" => return Some(Task::done(Message::SetWireframe(true))),
|
||||||
// Visual-style commands. OCS renders either a wireframe or a shaded
|
// Visual-style commands. OCS renders either a wireframe or a shaded
|
||||||
// view; the named styles map onto the closest of the two and the
|
// view; the named styles map onto the closest of the two and the
|
||||||
// chosen style is reported so the mapping is explicit. (`SOLID` is
|
// chosen style is reported so the mapping is explicit. (`SOLID` is
|
||||||
|
|
@ -309,7 +309,7 @@ impl OpenCADStudio {
|
||||||
"ORTHO" => return Some(Task::done(Message::ToggleOrtho)),
|
"ORTHO" => return Some(Task::done(Message::ToggleOrtho)),
|
||||||
"PARALLEL" => return Some(Task::done(Message::SetProjection(true))),
|
"PARALLEL" => return Some(Task::done(Message::SetProjection(true))),
|
||||||
"PERSP" => return Some(Task::done(Message::SetProjection(false))),
|
"PERSP" => return Some(Task::done(Message::SetProjection(false))),
|
||||||
"LAYERS" | "LA" => return Some(Task::done(Message::ToggleLayers)),
|
"LAYERS" => return Some(Task::done(Message::ToggleLayers)),
|
||||||
|
|
||||||
// SCRIPT <path> — run a command script: each non-blank, non-comment
|
// SCRIPT <path> — run a command script: each non-blank, non-comment
|
||||||
// line is fed through the same command path the `--script` startup
|
// line is fed through the same command path the `--script` startup
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,13 @@ use super::*;
|
||||||
impl OpenCADStudio {
|
impl OpenCADStudio {
|
||||||
pub(super) fn dispatch_inquiry(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
pub(super) fn dispatch_inquiry(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||||
match cmd {
|
match cmd {
|
||||||
"3DORBIT" | "3O" => {
|
"3DORBIT" => {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_info("3D Orbit: drag with right mouse button.");
|
.push_info("3D Orbit: drag with right mouse button.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Selection utilities ───────────────────────────────────────
|
// ── Selection utilities ───────────────────────────────────────
|
||||||
"SELECTALL" | "SA" => {
|
"SELECTALL" => {
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
|
|
@ -28,7 +28,7 @@ impl OpenCADStudio {
|
||||||
self.refresh_properties();
|
self.refresh_properties();
|
||||||
}
|
}
|
||||||
|
|
||||||
"DESELECT" | "DE" | "DESELALL" => {
|
"DESELECT" | "DESELALL" => {
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
self.command_line.push_output("Deselected.");
|
self.command_line.push_output("Deselected.");
|
||||||
self.refresh_properties();
|
self.refresh_properties();
|
||||||
|
|
@ -49,7 +49,7 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
// QSELECT builds a selection set by object type / property; FILTER is
|
// QSELECT builds a selection set by object type / property; FILTER is
|
||||||
// the same criteria-based selection.
|
// the same criteria-based selection.
|
||||||
"QSELECT" | "QS" | "FILTER" | "FI" => {
|
"QSELECT" | "FILTER" => {
|
||||||
return Some(Task::done(Message::QSelectOpen));
|
return Some(Task::done(Message::QSelectOpen));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,7 +75,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── LIST — entity info ────────────────────────────────────────
|
// ── LIST — entity info ────────────────────────────────────────
|
||||||
"LIST" | "LI" => {
|
"LIST" => {
|
||||||
let selected: Vec<_> = self.tabs[i].scene.selected_entities();
|
let selected: Vec<_> = self.tabs[i].scene.selected_entities();
|
||||||
if selected.is_empty() {
|
if selected.is_empty() {
|
||||||
self.command_line
|
self.command_line
|
||||||
|
|
@ -218,35 +218,35 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Break / Join ─────────────────────────────────────────────────
|
// ── Break / Join ─────────────────────────────────────────────────
|
||||||
"JOIN" | "J" => {
|
"JOIN" => {
|
||||||
use crate::modules::draw::modify::join::JoinCommand;
|
use crate::modules::draw::modify::join::JoinCommand;
|
||||||
let cmd = JoinCommand::new();
|
let cmd = JoinCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"BREAK" | "BR" => {
|
"BREAK" => {
|
||||||
use crate::modules::draw::modify::break_cmd::BreakInteractiveCommand;
|
use crate::modules::draw::modify::break_cmd::BreakInteractiveCommand;
|
||||||
let cmd = BreakInteractiveCommand::new();
|
let cmd = BreakInteractiveCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"BREAKATPOINT" | "BAP" => {
|
"BREAKATPOINT" => {
|
||||||
use crate::modules::draw::modify::break_cmd::BreakAtPointCommand;
|
use crate::modules::draw::modify::break_cmd::BreakAtPointCommand;
|
||||||
let cmd = BreakAtPointCommand::new();
|
let cmd = BreakAtPointCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"PEDIT" | "PE" => {
|
"PEDIT" => {
|
||||||
use crate::modules::draw::modify::pedit::PeditCommand;
|
use crate::modules::draw::modify::pedit::PeditCommand;
|
||||||
let cmd_obj = PeditCommand::new();
|
let cmd_obj = PeditCommand::new();
|
||||||
self.command_line.push_info(&cmd_obj.prompt());
|
self.command_line.push_info(&cmd_obj.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
"SPLINEDIT" | "SPE" => {
|
"SPLINEDIT" => {
|
||||||
use crate::modules::draw::modify::splinedit::SplineditCommand;
|
use crate::modules::draw::modify::splinedit::SplineditCommand;
|
||||||
let cmd_obj = SplineditCommand::new();
|
let cmd_obj = SplineditCommand::new();
|
||||||
self.command_line.push_info(&cmd_obj.prompt());
|
self.command_line.push_info(&cmd_obj.prompt());
|
||||||
|
|
@ -257,7 +257,7 @@ impl OpenCADStudio {
|
||||||
// If a single block with attributes is already selected it opens on
|
// If a single block with attributes is already selected it opens on
|
||||||
// that block; otherwise the pick command runs and the editor opens
|
// that block; otherwise the pick command runs and the editor opens
|
||||||
// once a block is chosen (see `command_driver`).
|
// once a block is chosen (see `command_driver`).
|
||||||
"ATTEDIT" | "ATE" => {
|
"ATTEDIT" => {
|
||||||
let selected_attr_insert = {
|
let selected_attr_insert = {
|
||||||
let sel = self.tabs[i].scene.selected_entities();
|
let sel = self.tabs[i].scene.selected_entities();
|
||||||
if sel.len() == 1 {
|
if sel.len() == 1 {
|
||||||
|
|
@ -547,28 +547,28 @@ impl OpenCADStudio {
|
||||||
.push_output("REFCLOSE: Changes discarded.");
|
.push_output("REFCLOSE: Changes discarded.");
|
||||||
}
|
}
|
||||||
|
|
||||||
"ALIGN" | "AL" => {
|
"ALIGN" => {
|
||||||
use crate::modules::draw::modify::align::AlignCommand;
|
use crate::modules::draw::modify::align::AlignCommand;
|
||||||
let cmd = AlignCommand::new();
|
let cmd = AlignCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"LENGTHEN" | "LEN" => {
|
"LENGTHEN" => {
|
||||||
use crate::modules::draw::modify::lengthen::LengthenCommand;
|
use crate::modules::draw::modify::lengthen::LengthenCommand;
|
||||||
let cmd = LengthenCommand::new();
|
let cmd = LengthenCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"DIVIDE" | "DIV" => {
|
"DIVIDE" => {
|
||||||
use crate::modules::draw::inquiry::divide::DivideCommand;
|
use crate::modules::draw::inquiry::divide::DivideCommand;
|
||||||
let cmd = DivideCommand::new();
|
let cmd = DivideCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MEASURE" | "ME" => {
|
"MEASURE" => {
|
||||||
use crate::modules::draw::inquiry::divide::MeasureCommand;
|
use crate::modules::draw::inquiry::divide::MeasureCommand;
|
||||||
let cmd = MeasureCommand::new();
|
let cmd = MeasureCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
@ -576,7 +576,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Inquiry ──────────────────────────────────────────────────────
|
// ── Inquiry ──────────────────────────────────────────────────────
|
||||||
"DIST" | "DI" => {
|
"DIST" => {
|
||||||
use crate::modules::draw::inquiry::dist::DistCommand;
|
use crate::modules::draw::inquiry::dist::DistCommand;
|
||||||
let cmd = DistCommand::new();
|
let cmd = DistCommand::new();
|
||||||
self.command_line.push_info(&cmd.prompt());
|
self.command_line.push_info(&cmd.prompt());
|
||||||
|
|
|
||||||
|
|
@ -510,7 +510,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"MATCHPROP" | "MA" => {
|
"MATCHPROP" => {
|
||||||
use crate::modules::draw::properties::match_prop::MatchPropCommand;
|
use crate::modules::draw::properties::match_prop::MatchPropCommand;
|
||||||
self.tabs[i].scene.deselect_all();
|
self.tabs[i].scene.deselect_all();
|
||||||
let cmd = MatchPropCommand::new();
|
let cmd = MatchPropCommand::new();
|
||||||
|
|
@ -518,7 +518,7 @@ impl OpenCADStudio {
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
"GROUP" | "G" => {
|
"GROUP" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
@ -540,7 +540,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"UNGROUP" | "UG" => {
|
"UNGROUP" => {
|
||||||
let handles: Vec<_> = self.tabs[i]
|
let handles: Vec<_> = self.tabs[i]
|
||||||
.scene
|
.scene
|
||||||
.selected_entities()
|
.selected_entities()
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,12 @@ impl OpenCADStudio {
|
||||||
|
|
||||||
fn dispatch_command_inner(&mut self, cmd: &str, allow_suggest: bool) -> Task<Message> {
|
fn dispatch_command_inner(&mut self, cmd: &str, allow_suggest: bool) -> Task<Message> {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
// Expand a command alias ("L" → "LINE") on the leading token before any
|
||||||
|
// routing, so every path below (Start-tab gate, plugins, all dispatch
|
||||||
|
// families, the Repeat menu) sees the canonical command. Arguments after
|
||||||
|
// the first space are left untouched. A non-alias passes through as-is.
|
||||||
|
let resolved = self.resolve_alias(cmd);
|
||||||
|
let cmd = resolved.as_deref().unwrap_or(cmd);
|
||||||
// Starting a command closes any open ribbon dropdown (e.g. a style
|
// Starting a command closes any open ribbon dropdown (e.g. a style
|
||||||
// combo left open) so it does not stay stuck behind the new tool.
|
// combo left open) so it does not stay stuck behind the new tool.
|
||||||
self.ribbon.close_dropdown();
|
self.ribbon.close_dropdown();
|
||||||
|
|
@ -151,6 +157,7 @@ impl OpenCADStudio {
|
||||||
if let Some(top) = crate::ui::command_line::ranked_matches(
|
if let Some(top) = crate::ui::command_line::ranked_matches(
|
||||||
cmd,
|
cmd,
|
||||||
&self.command_line.dynamic_commands,
|
&self.command_line.dynamic_commands,
|
||||||
|
&self.command_line.command_aliases,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.next()
|
.next()
|
||||||
|
|
|
||||||
|
|
@ -208,10 +208,15 @@ impl OpenCADStudio {
|
||||||
self.command_line.push_info("Opening release notes...");
|
self.command_line.push_info("Opening release notes...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── CUI / ALIASEDIT — customization entry points ───────────────
|
// ── ALIASEDIT — command-alias editor ───────────────────────────
|
||||||
// Command-alias and key-binding editing lives in the keyboard
|
// Opens the command-alias table editor (ocad.pgp). Keyboard
|
||||||
// shortcuts panel, so route the customization verbs there.
|
// key-bindings are edited separately via CUI (below).
|
||||||
"CUI" | "ALIASEDIT" => {
|
"ALIASEDIT" => {
|
||||||
|
return Some(Task::done(Message::AliasEditorOpen));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── CUI — keyboard shortcut / key-binding editor ───────────────
|
||||||
|
"CUI" => {
|
||||||
return Some(Task::done(Message::ShortcutsPanelOpen));
|
return Some(Task::done(Message::ShortcutsPanelOpen));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,7 +385,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Layout / viewport ──────────────────────────────────────────
|
// ── Layout / viewport ──────────────────────────────────────────
|
||||||
"MVIEW" | "MV" => {
|
"MVIEW" => {
|
||||||
if self.tabs[i].scene.current_layout == "Model" {
|
if self.tabs[i].scene.current_layout == "Model" {
|
||||||
self.command_line
|
self.command_line
|
||||||
.push_error("MVIEW: switch to a paper space layout first.");
|
.push_error("MVIEW: switch to a paper space layout first.");
|
||||||
|
|
@ -393,7 +398,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── MSPACE / PSPACE ───────────────────────────────────────────
|
// ── MSPACE / PSPACE ───────────────────────────────────────────
|
||||||
"MS" | "MSPACE" => {
|
"MSPACE" => {
|
||||||
return Some(Task::done(Message::MspaceCommand));
|
return Some(Task::done(Message::MspaceCommand));
|
||||||
}
|
}
|
||||||
"PSPACE" => {
|
"PSPACE" => {
|
||||||
|
|
@ -970,7 +975,7 @@ impl OpenCADStudio {
|
||||||
}
|
}
|
||||||
|
|
||||||
// HIDE — hidden-line view of the active viewport.
|
// HIDE — hidden-line view of the active viewport.
|
||||||
"HIDE" | "HI" => {
|
"HIDE" => {
|
||||||
return Some(Task::done(Message::SetRenderMode(
|
return Some(Task::done(Message::SetRenderMode(
|
||||||
acadrust::entities::ViewportRenderMode::HiddenLine,
|
acadrust::entities::ViewportRenderMode::HiddenLine,
|
||||||
)));
|
)));
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod alias;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod automation;
|
mod automation;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|
@ -629,6 +630,16 @@ pub(super) struct OpenCADStudio {
|
||||||
/// User-defined function-key overrides: "F3" → command string.
|
/// User-defined function-key overrides: "F3" → command string.
|
||||||
shortcut_overrides: rustc_hash::FxHashMap<String, String>,
|
shortcut_overrides: rustc_hash::FxHashMap<String, String>,
|
||||||
|
|
||||||
|
// ── Command Aliases ───────────────────────────────────────────────────
|
||||||
|
/// Command-line aliases: uppercase abbreviation → uppercase command
|
||||||
|
/// ("L" → "LINE"). Loaded from `ocad.pgp` at boot and consulted before
|
||||||
|
/// every dispatch (`resolve_alias`); user-editable via ALIASEDIT. See
|
||||||
|
/// [`alias`].
|
||||||
|
command_aliases: rustc_hash::FxHashMap<String, String>,
|
||||||
|
/// Working buffer for the ALIASEDIT modal: `(alias, command)` rows being
|
||||||
|
/// edited. Seeded from `command_aliases` on open, committed back on close.
|
||||||
|
alias_editor_rows: Vec<(String, String)>,
|
||||||
|
|
||||||
// ── Layout Manager Panel ──────────────────────────────────────────────
|
// ── Layout Manager Panel ──────────────────────────────────────────────
|
||||||
layout_manager_selected: String,
|
layout_manager_selected: String,
|
||||||
layout_manager_rename_buf: String,
|
layout_manager_rename_buf: String,
|
||||||
|
|
@ -1081,6 +1092,7 @@ pub enum ModalKind {
|
||||||
PointStyle,
|
PointStyle,
|
||||||
AttributeEditor,
|
AttributeEditor,
|
||||||
LayerDeleteWarning,
|
LayerDeleteWarning,
|
||||||
|
Aliases,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Identifies a DimStyle field that can be edited in the dialog.
|
/// Identifies a DimStyle field that can be edited in the dialog.
|
||||||
|
|
@ -1628,6 +1640,21 @@ pub enum Message {
|
||||||
ShortcutsPanelOpen,
|
ShortcutsPanelOpen,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
ShortcutsPanelClose,
|
ShortcutsPanelClose,
|
||||||
|
// ── Command Alias Editor (ALIASEDIT) ────────────────────────────────
|
||||||
|
/// Open the command-alias editor modal, seeding rows from the alias table.
|
||||||
|
AliasEditorOpen,
|
||||||
|
/// Live edit of the alias or command in row `idx`.
|
||||||
|
AliasEditorInput {
|
||||||
|
idx: usize,
|
||||||
|
field: crate::ui::window::alias_editor::AliasField,
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
/// Append a blank alias row.
|
||||||
|
AliasEditorAdd,
|
||||||
|
/// Remove alias row `idx`.
|
||||||
|
AliasEditorRemove(usize),
|
||||||
|
/// Commit the edited rows to the alias table (Apply button); stays open.
|
||||||
|
AliasEditorApply,
|
||||||
// ── About window ────────────────────────────────────────────────────
|
// ── About window ────────────────────────────────────────────────────
|
||||||
AboutOpen,
|
AboutOpen,
|
||||||
/// Close whatever in-canvas modal dialog is open (Plan B).
|
/// Close whatever in-canvas modal dialog is open (Plan B).
|
||||||
|
|
@ -2187,6 +2214,9 @@ impl OpenCADStudio {
|
||||||
active_theme: Theme::Dark,
|
active_theme: Theme::Dark,
|
||||||
// Keyboard shortcuts
|
// Keyboard shortcuts
|
||||||
shortcut_overrides: rustc_hash::FxHashMap::default(),
|
shortcut_overrides: rustc_hash::FxHashMap::default(),
|
||||||
|
// Command aliases (populated from ocad.pgp just after construction)
|
||||||
|
command_aliases: rustc_hash::FxHashMap::default(),
|
||||||
|
alias_editor_rows: Vec::new(),
|
||||||
// Layout Manager
|
// Layout Manager
|
||||||
layout_manager_selected: "Model".to_string(),
|
layout_manager_selected: "Model".to_string(),
|
||||||
layout_manager_rename_buf: String::new(),
|
layout_manager_rename_buf: String::new(),
|
||||||
|
|
@ -2328,6 +2358,11 @@ impl OpenCADStudio {
|
||||||
if let Some(s) = settings::UserSettings::load() {
|
if let Some(s) = settings::UserSettings::load() {
|
||||||
app.apply_settings(&s);
|
app.apply_settings(&s);
|
||||||
}
|
}
|
||||||
|
// Load command aliases from ocad.pgp (writes the shipped default file
|
||||||
|
// on first launch). The hide-set keeps aliases out of autocomplete while
|
||||||
|
// their target command still shows.
|
||||||
|
app.command_aliases = alias::load_aliases();
|
||||||
|
app.command_line.command_aliases = app.command_aliases.clone();
|
||||||
// Load external plugin packages from the plugins folder once, then fold
|
// Load external plugin packages from the plugins folder once, then fold
|
||||||
// their ribbon tabs into the ribbon. Skipped under test/wasm.
|
// their ribbon tabs into the ribbon. Skipped under test/wasm.
|
||||||
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
|
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,12 @@ impl OpenCADStudio {
|
||||||
self.attr_editor_selected = 0;
|
self.attr_editor_selected = 0;
|
||||||
self.attr_editor_tab = crate::ui::window::attribute_editor::AttrTab::Attribute;
|
self.attr_editor_tab = crate::ui::window::attribute_editor::AttrTab::Attribute;
|
||||||
}
|
}
|
||||||
|
// Closing (✕) discards edits made since the last Apply — matching the
|
||||||
|
// style editors. Committing happens only through the Apply button.
|
||||||
|
Some(Aliases) => {
|
||||||
|
self.alias_editor_rows.clear();
|
||||||
|
self.ribbon.deactivate_tool_if("ALIASEDIT");
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.active_modal = None;
|
self.active_modal = None;
|
||||||
|
|
@ -2631,6 +2637,52 @@ impl OpenCADStudio {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Command Alias Editor (ALIASEDIT) ──────────────────────────────
|
||||||
|
Message::AliasEditorOpen => {
|
||||||
|
// Seed the working buffer from the current table, sorted by alias
|
||||||
|
// so the list is stable and diffable.
|
||||||
|
let mut rows: Vec<(String, String)> = self
|
||||||
|
.command_aliases
|
||||||
|
.iter()
|
||||||
|
.map(|(a, c)| (a.clone(), c.clone()))
|
||||||
|
.collect();
|
||||||
|
rows.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
|
self.alias_editor_rows = rows;
|
||||||
|
self.active_modal = Some(super::ModalKind::Aliases);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::AliasEditorInput { idx, field, value } => {
|
||||||
|
use crate::ui::window::alias_editor::AliasField;
|
||||||
|
// Aliases and commands are stored uppercase; uppercasing as the
|
||||||
|
// user types keeps display and the committed table consistent.
|
||||||
|
let value = value.to_uppercase();
|
||||||
|
if let Some(rowdata) = self.alias_editor_rows.get_mut(idx) {
|
||||||
|
match field {
|
||||||
|
AliasField::Alias => rowdata.0 = value,
|
||||||
|
AliasField::Command => rowdata.1 = value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::AliasEditorAdd => {
|
||||||
|
self.alias_editor_rows.push((String::new(), String::new()));
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::AliasEditorRemove(idx) => {
|
||||||
|
if idx < self.alias_editor_rows.len() {
|
||||||
|
self.alias_editor_rows.remove(idx);
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::AliasEditorApply => {
|
||||||
|
self.apply_alias_editor_rows();
|
||||||
|
self.command_line.push_info(&format!(
|
||||||
|
"{} alias(es) applied.",
|
||||||
|
self.command_aliases.len()
|
||||||
|
));
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
// ── About window ──────────────────────────────────────────────
|
// ── About window ──────────────────────────────────────────────
|
||||||
Message::AboutOpen => {
|
Message::AboutOpen => {
|
||||||
self.active_modal = Some(super::ModalKind::About);
|
self.active_modal = Some(super::ModalKind::About);
|
||||||
|
|
|
||||||
|
|
@ -1495,6 +1495,7 @@ impl OpenCADStudio {
|
||||||
PointStyle => (360, 470),
|
PointStyle => (360, 470),
|
||||||
AttributeEditor => (640, 500),
|
AttributeEditor => (640, 500),
|
||||||
LayerDeleteWarning => (440, 200),
|
LayerDeleteWarning => (440, 200),
|
||||||
|
Aliases => (480, 520),
|
||||||
};
|
};
|
||||||
Some((w as f32 + EXTRA_W, h as f32 + EXTRA_H))
|
Some((w as f32 + EXTRA_W, h as f32 + EXTRA_H))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ impl OpenCADStudio {
|
||||||
super::super::ModalKind::Shortcuts => {
|
super::super::ModalKind::Shortcuts => {
|
||||||
sized(crate::ui::window::shortcuts::view_window(&self.shortcut_overrides), 720, 520)
|
sized(crate::ui::window::shortcuts::view_window(&self.shortcut_overrides), 720, 520)
|
||||||
}
|
}
|
||||||
|
super::super::ModalKind::Aliases => {
|
||||||
|
sized(crate::ui::window::alias_editor::view_window(&self.alias_editor_rows), 480, 520)
|
||||||
|
}
|
||||||
super::super::ModalKind::PluginManager => sized(
|
super::super::ModalKind::PluginManager => sized(
|
||||||
crate::ui::window::plugin_manager::view_window(
|
crate::ui::window::plugin_manager::view_window(
|
||||||
&self.disabled_plugins,
|
&self.disabled_plugins,
|
||||||
|
|
|
||||||
|
|
@ -257,4 +257,4 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DAL", "DIMALIGNED"] }); // AlignedDimensionCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMALIGNED"] }); // AlignedDimensionCommand
|
||||||
|
|
|
||||||
|
|
@ -179,4 +179,4 @@ fn angular_preview(vertex: Vec3, first: Vec3, second: Vec3, arc_pt: Vec3) -> Vec
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMANGULAR", "DAN"] }); // AngularDimensionCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMANGULAR"] }); // AngularDimensionCommand
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,4 @@ impl CadCommand for DdeditCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DDEDIT", "ED"] });
|
inventory::submit!(crate::command::CommandRegistration { names: &["DDEDIT"] });
|
||||||
|
|
|
||||||
|
|
@ -202,4 +202,4 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DDI", "DIMDIAMETER"] }); // DiameterDimensionCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMDIAMETER"] }); // DiameterDimensionCommand
|
||||||
|
|
|
||||||
|
|
@ -206,4 +206,4 @@ fn v3(p: Vec3) -> Vector3 {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DBA", "DIMBASELINE"] }); // DimBaselineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMBASELINE"] }); // DimBaselineCommand
|
||||||
|
|
|
||||||
|
|
@ -186,4 +186,4 @@ fn v3(p: Vec3) -> Vector3 {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DCO", "DIMCONTINUE"] }); // DimContinueCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMCONTINUE"] }); // DimContinueCommand
|
||||||
|
|
|
||||||
|
|
@ -100,4 +100,4 @@ impl CadCommand for DimBreakCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DBR", "DIMBREAK"] }); // DimBreakCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMBREAK"] }); // DimBreakCommand
|
||||||
|
|
|
||||||
|
|
@ -88,4 +88,4 @@ impl CadCommand for DimEditCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DED", "DIMEDIT"] }); // DimEditCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMEDIT"] }); // DimEditCommand
|
||||||
|
|
|
||||||
|
|
@ -117,4 +117,4 @@ impl CadCommand for DimJogLineCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMJOGLINE", "DJL"] }); // DimJogLineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMJOGLINE"] }); // DimJogLineCommand
|
||||||
|
|
|
||||||
|
|
@ -206,4 +206,4 @@ pub fn arrowhead_wings(tip: Vec3, next: Vec3, size: f32) -> [Vec3; 2] {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["LE", "LEADER"] }); // LeaderCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["LEADER"] }); // LeaderCommand
|
||||||
|
|
|
||||||
|
|
@ -266,4 +266,4 @@ fn linear_text_pos(first: Vec3, second: Vec3, def: Vec3, axis: Vec3) -> Vec3 {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMLINEAR", "DLI"] }); // LinearDimensionCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMLINEAR"] }); // LinearDimensionCommand
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,4 @@ fn arrowhead_wings(tip: Vec3, next: Vec3, size: f32) -> [Vec3; 2] {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MLD", "MLEADER"] }); // MLeaderCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADER"] }); // MLeaderCommand
|
||||||
|
|
|
||||||
|
|
@ -482,7 +482,7 @@ fn _uses_ml_types(_ml: &MultiLeader, _ll: &LeaderLine) {}
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MLA", "MLEADERADD"] }); // MLeaderAddCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERADD"] }); // MLeaderAddCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MLAL", "MLEADERALIGN"] }); // MLeaderAlignCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERALIGN"] }); // MLeaderAlignCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MLC", "MLEADERCOLLECT"] }); // MLeaderCollectCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERCOLLECT"] }); // MLeaderCollectCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERREMOVE", "MLR"] }); // MLeaderRemoveCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERREMOVE"] }); // MLeaderRemoveCommand
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,4 @@ impl CadCommand for MTextCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MT", "MTEXT"] }); // MTextCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MTEXT"] }); // MTextCommand
|
||||||
|
|
|
||||||
|
|
@ -139,4 +139,4 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMORDINATE", "DOR"] }); // OrdinateDimCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMORDINATE"] }); // OrdinateDimCommand
|
||||||
|
|
|
||||||
|
|
@ -207,4 +207,4 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIMRADIUS", "DRA"] }); // RadiusDimensionCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIMRADIUS"] }); // RadiusDimensionCommand
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,4 @@ impl CadCommand for TextCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DT", "T", "TEXT"] }); // TextCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["TEXT"] }); // TextCommand
|
||||||
|
|
|
||||||
|
|
@ -114,4 +114,4 @@ impl CadCommand for ToleranceCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["TOL", "TOLERANCE"] }); // ToleranceCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["TOLERANCE"] }); // ToleranceCommand
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,6 @@ impl CadCommand for PasteCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PASTECLIP", "PC"] }); // PasteCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["PASTECLIP"] }); // PasteCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PASTEORIG"] });
|
inventory::submit!(crate::command::CommandRegistration { names: &["PASTEORIG"] });
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PASTEBLOCK"] });
|
inventory::submit!(crate::command::CommandRegistration { names: &["PASTEBLOCK"] });
|
||||||
|
|
|
||||||
|
|
@ -1392,7 +1392,7 @@ impl CadCommand for ArcContCommand {
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_3P"] }); // Arc3PCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_3P"] }); // Arc3PCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSA"] }); // ArcCSACommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSA"] }); // ArcCSACommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSL"] }); // ArcCSLCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSL"] }); // ArcCSLCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["A", "ARC"] }); // ArcCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC"] }); // ArcCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCA"] }); // ArcSCACommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCA"] }); // ArcSCACommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCE"] }); // ArcSCECommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCE"] }); // ArcSCECommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCL"] }); // ArcSCLCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCL"] }); // ArcSCLCommand
|
||||||
|
|
|
||||||
|
|
@ -991,6 +991,6 @@ impl CadCommand for CircleTTTCommand {
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_2P"] }); // Circle2PCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_2P"] }); // Circle2PCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_3P"] }); // Circle3PCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_3P"] }); // Circle3PCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_CD"] }); // CircleCDCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_CD"] }); // CircleCDCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["C", "CIRCLE"] }); // CircleCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE"] }); // CircleCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTR"] }); // CircleTTRCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTR"] }); // CircleTTRCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTT"] }); // CircleTTTCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTT"] }); // CircleTTTCommand
|
||||||
|
|
|
||||||
|
|
@ -143,4 +143,4 @@ fn make_donut(cx: f64, cy: f64, inner_r: f64, outer_r: f64) -> EntityType {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DO", "DONUT"] }); // DonutCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DONUT"] }); // DonutCommand
|
||||||
|
|
|
||||||
|
|
@ -646,4 +646,4 @@ fn line_wire(from: DVec3, to: DVec3) -> WireModel {
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_ARC"] }); // EllipseArcCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_ARC"] }); // EllipseArcCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_AXIS"] }); // EllipseAxisCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_AXIS"] }); // EllipseAxisCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["EL", "ELLIPSE"] }); // EllipseCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE"] }); // EllipseCommand
|
||||||
|
|
|
||||||
|
|
@ -455,4 +455,4 @@ impl CadCommand for BoundaryCommand {
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["BOUNDARY"] }); // BoundaryCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["BOUNDARY"] }); // BoundaryCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["GRADIENT"] }); // GradientCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["GRADIENT"] }); // GradientCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["H", "HATCH"] }); // HatchCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["HATCH"] }); // HatchCommand
|
||||||
|
|
|
||||||
|
|
@ -164,4 +164,4 @@ impl CadCommand for HatcheditCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["HATCHEDIT", "HE"] }); // HatcheditCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["HATCHEDIT"] }); // HatcheditCommand
|
||||||
|
|
|
||||||
|
|
@ -331,4 +331,4 @@ mod tangent_tests {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["L", "LINE"] }); // LineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["LINE"] }); // LineCommand
|
||||||
|
|
|
||||||
|
|
@ -170,4 +170,4 @@ fn build_mline(pts: &[Vec3], scale: f64, closed: bool, style_name: &str) -> Enti
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ML", "MLINE"] }); // MlineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MLINE"] }); // MlineCommand
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ impl CadCommand for PointCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PO", "POINT"] }); // PointCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["POINT"] }); // PointCommand
|
||||||
// Point display style system variables + dialog.
|
// Point display style system variables + dialog.
|
||||||
inventory::submit!(crate::command::CommandRegistration {
|
inventory::submit!(crate::command::CommandRegistration {
|
||||||
names: &["PDMODE", "PDSIZE", "DDPTYPE"]
|
names: &["PDMODE", "PDSIZE", "DDPTYPE"]
|
||||||
|
|
|
||||||
|
|
@ -351,4 +351,4 @@ impl CadCommand for PlineCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PL", "PLINE"] }); // PlineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["PLINE"] }); // PlineCommand
|
||||||
|
|
|
||||||
|
|
@ -201,4 +201,4 @@ impl CadCommand for XLineCommand {
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CONSTRUCTIONLINE", "XL", "XLINE"] }); // XLineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CONSTRUCTIONLINE", "XLINE"] }); // XLineCommand
|
||||||
|
|
|
||||||
|
|
@ -834,8 +834,8 @@ fn edge_poly_params(a: DVec3, b: DVec3, sides: u32) -> Option<(DVec3, f64, f64)>
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_C"] }); // PolyCCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_C"] }); // PolyCCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["POL", "POLY", "POLYGON"] }); // PolyCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["POLY", "POLYGON"] }); // PolyCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_E"] }); // PolyECommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_E"] }); // PolyECommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_CEN"] }); // RectCenCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_CEN"] }); // RectCenCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["REC", "RECT", "RECTANG"] }); // RectCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["RECT", "RECTANG"] }); // RectCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_ROT"] }); // RectRotCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_ROT"] }); // RectRotCommand
|
||||||
|
|
|
||||||
|
|
@ -190,4 +190,4 @@ impl CadCommand for SplineCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["SPL", "SPLINE"] }); // SplineCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["SPLINE"] }); // SplineCommand
|
||||||
|
|
|
||||||
|
|
@ -191,4 +191,4 @@ fn make_poly_wipeout(pts: &[Vec3]) -> EntityType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["WIPEOUT", "WO"] });
|
inventory::submit!(crate::command::CommandRegistration { names: &["WIPEOUT"] });
|
||||||
|
|
|
||||||
|
|
@ -62,4 +62,4 @@ impl CadCommand for UngroupCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["UG", "UNGROUP"] }); // UngroupCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["UNGROUP"] }); // UngroupCommand
|
||||||
|
|
|
||||||
|
|
@ -90,4 +90,4 @@ impl CadCommand for DistCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DI", "DIST"] }); // DistCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIST"] }); // DistCommand
|
||||||
|
|
|
||||||
|
|
@ -314,5 +314,5 @@ fn arc_span_rad(start: f64, end: f64) -> f64 {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["DIV", "DIVIDE"] }); // DivideCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["DIVIDE"] }); // DivideCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ME", "MEASURE"] }); // MeasureCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MEASURE"] }); // MeasureCommand
|
||||||
|
|
|
||||||
|
|
@ -192,4 +192,4 @@ impl AlignCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["AL", "ALIGN"] }); // AlignCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ALIGN"] }); // AlignCommand
|
||||||
|
|
|
||||||
|
|
@ -62,4 +62,4 @@ impl CadCommand for AtteditCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["ATTEDIT", "ATE"] });
|
inventory::submit!(crate::command::CommandRegistration { names: &["ATTEDIT"] });
|
||||||
|
|
|
||||||
|
|
@ -470,5 +470,5 @@ impl CadCommand for BreakAtPointCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["BAP", "BREAKATPOINT"] }); // BreakAtPointCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["BREAKATPOINT"] }); // BreakAtPointCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["BR", "BREAK"] }); // BreakInteractiveCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["BREAK"] }); // BreakInteractiveCommand
|
||||||
|
|
|
||||||
|
|
@ -1950,5 +1950,5 @@ impl CadCommand for ChamferCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["CHA", "CHAMFER"] }); // ChamferCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["CHAMFER"] }); // ChamferCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["F", "FILLET"] }); // FilletCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["FILLET"] }); // FilletCommand
|
||||||
|
|
|
||||||
|
|
@ -264,4 +264,4 @@ fn is_collinear(verts: &[(DVec3, f64)]) -> bool {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["J", "JOIN"] }); // JoinCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["JOIN"] }); // JoinCommand
|
||||||
|
|
|
||||||
|
|
@ -512,4 +512,4 @@ fn lengthen_spline(spl: &SplineEnt, pick_pt: Vec3, mode: &LenMode) -> Option<Ent
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["LEN", "LENGTHEN"] }); // LengthenCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["LENGTHEN"] }); // LengthenCommand
|
||||||
|
|
|
||||||
|
|
@ -816,7 +816,7 @@ impl CadCommand for OffsetCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["O", "OFFSET"] }); // OffsetCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["OFFSET"] }); // OffsetCommand
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod offset_tests {
|
mod offset_tests {
|
||||||
|
|
|
||||||
|
|
@ -185,4 +185,4 @@ pub fn apply_pedit(entity: &mut EntityType, op: &PeditOp) -> bool {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PE", "PEDIT"] }); // PeditCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["PEDIT"] }); // PeditCommand
|
||||||
|
|
|
||||||
|
|
@ -203,4 +203,4 @@ pub fn apply_spline_op(doc: &mut acadrust::CadDocument, handle: acadrust::Handle
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["SPE", "SPLINEDIT"] }); // SplineditCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["SPLINEDIT"] }); // SplineditCommand
|
||||||
|
|
|
||||||
|
|
@ -2751,8 +2751,8 @@ impl CadCommand for ExtendCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["EX", "EXTEND"] }); // ExtendCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["EXTEND"] }); // ExtendCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["TR", "TRIM"] }); // TrimCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["TRIM"] }); // TrimCommand
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
||||||
|
|
@ -92,4 +92,4 @@ impl CadCommand for MatchPropCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MA", "MATCHPROP"] }); // MatchPropCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MATCHPROP"] }); // MatchPropCommand
|
||||||
|
|
|
||||||
|
|
@ -108,4 +108,4 @@ impl CadCommand for SelectObjectsCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["AR", "ARRAY", "ARRAYPATH", "ARRAYPOLAR", "ARRAYRECT", "BLOCK", "CC", "CO", "COPY", "COPYCLIP", "CUTCLIP", "CX", "E", "ERASE", "EXPLODE", "G", "GROUP", "LAYFRZ", "LAYLCK", "LAYMCUR", "LAYOFF", "LAYULK", "M", "MI", "MIRROR", "MOVE", "RO", "ROTATE", "SC", "SCALE", "SS", "STRETCH", "X"] }); // SelectObjectsCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["ARRAY", "ARRAYPATH", "ARRAYPOLAR", "ARRAYRECT", "BLOCK", "COPY", "COPYCLIP", "CUTCLIP", "ERASE", "EXPLODE", "GROUP", "LAYFRZ", "LAYLCK", "LAYMCUR", "LAYOFF", "LAYULK", "MIRROR", "MOVE", "ROTATE", "SCALE", "STRETCH"] }); // SelectObjectsCommand
|
||||||
|
|
|
||||||
|
|
@ -329,7 +329,7 @@ pub fn empty_solid3d() -> EntityType {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["EXT", "EXTRUDE"] }); // ExtrudeCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["EXTRUDE"] }); // ExtrudeCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["LOFT"] }); // LoftCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["LOFT"] }); // LoftCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["REV", "REVOLVE"] }); // RevolveCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["REVOLVE"] }); // RevolveCommand
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["SWEEP"] }); // SweepCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["SWEEP"] }); // SweepCommand
|
||||||
|
|
|
||||||
|
|
@ -110,4 +110,4 @@ impl CadCommand for MviewCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["MV", "MVIEW"] }); // MviewCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["MVIEW"] }); // MviewCommand
|
||||||
|
|
|
||||||
|
|
@ -81,4 +81,4 @@ impl CadCommand for PlotWindowCommand {
|
||||||
|
|
||||||
|
|
||||||
// ── Autocomplete registry ─────────────────────────────────
|
// ── Autocomplete registry ─────────────────────────────────
|
||||||
inventory::submit!(crate::command::CommandRegistration { names: &["PLOTWINDOW", "PW"] }); // PlotWindowCommand
|
inventory::submit!(crate::command::CommandRegistration { names: &["PLOTWINDOW"] }); // PlotWindowCommand
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ pub struct CommandLine {
|
||||||
/// compile-time command registry, so runtime plugin commands are typeable
|
/// compile-time command registry, so runtime plugin commands are typeable
|
||||||
/// and discoverable — not only reachable via ribbon buttons (#272).
|
/// and discoverable — not only reachable via ribbon buttons (#272).
|
||||||
pub dynamic_commands: Vec<String>,
|
pub dynamic_commands: Vec<String>,
|
||||||
|
/// Command aliases (uppercase `alias → command`), a copy of the app's alias
|
||||||
|
/// table kept here for autocomplete. Aliases are hidden from suggestions
|
||||||
|
/// (a terse `CC` never appears) while their target command (`COPYCLIP`)
|
||||||
|
/// still does; and when the whole input is an alias, its target is surfaced
|
||||||
|
/// as the top suggestion so the highlight matches what Enter runs. (#288)
|
||||||
|
pub command_aliases: rustc_hash::FxHashMap<String, String>,
|
||||||
/// The active command step's prompt, mirrored here so a step change
|
/// The active command step's prompt, mirrored here so a step change
|
||||||
/// can be detected and the pinned (non-fading) history line updated.
|
/// can be detected and the pinned (non-fading) history line updated.
|
||||||
step_prompt: Option<String>,
|
step_prompt: Option<String>,
|
||||||
|
|
@ -302,7 +308,11 @@ impl CommandLine {
|
||||||
/// Autocomplete suggestions for the current input — see
|
/// Autocomplete suggestions for the current input — see
|
||||||
/// [`ranked_matches`]. Includes loaded plugins' commands (#272).
|
/// [`ranked_matches`]. Includes loaded plugins' commands (#272).
|
||||||
pub fn autocomplete_matches(&self) -> Vec<String> {
|
pub fn autocomplete_matches(&self) -> Vec<String> {
|
||||||
ranked_matches(self.input.trim(), &self.dynamic_commands)
|
ranked_matches(
|
||||||
|
self.input.trim(),
|
||||||
|
&self.dynamic_commands,
|
||||||
|
&self.command_aliases,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view<'a>(
|
pub fn view<'a>(
|
||||||
|
|
@ -625,7 +635,18 @@ impl CommandLine {
|
||||||
/// compile-time `inventory` registry — merged with `dynamic`, the command names
|
/// compile-time `inventory` registry — merged with `dynamic`, the command names
|
||||||
/// contributed by loaded plugins (runtime, so they can't be `&'static`; see
|
/// contributed by loaded plugins (runtime, so they can't be `&'static`; see
|
||||||
/// #272). Returns owned strings to carry both sources.
|
/// #272). Returns owned strings to carry both sources.
|
||||||
pub fn ranked_matches(needle: &str, dynamic: &[String]) -> Vec<String> {
|
///
|
||||||
|
/// `aliases` is the `alias → command` table. Aliases themselves are dropped from
|
||||||
|
/// the results, so a terse `CC` is typeable and dispatches (via the alias table)
|
||||||
|
/// but never clutters the suggestions — only its target `COPYCLIP` is offered.
|
||||||
|
/// And when the whole input is an alias, its target command is forced to the top
|
||||||
|
/// so the highlighted suggestion matches what pressing Enter actually runs
|
||||||
|
/// (typing `AA` highlights `AREA`, `L` highlights `LINE`). (#288)
|
||||||
|
pub fn ranked_matches(
|
||||||
|
needle: &str,
|
||||||
|
dynamic: &[String],
|
||||||
|
aliases: &rustc_hash::FxHashMap<String, String>,
|
||||||
|
) -> Vec<String> {
|
||||||
let needle = needle.trim().to_uppercase();
|
let needle = needle.trim().to_uppercase();
|
||||||
if needle.is_empty() {
|
if needle.is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
|
|
@ -636,7 +657,8 @@ pub fn ranked_matches(needle: &str, dynamic: &[String]) -> Vec<String> {
|
||||||
// Plugin names are uppercased to match the built-ins and the needle,
|
// Plugin names are uppercased to match the built-ins and the needle,
|
||||||
// so ranking and display stay consistent across both sources.
|
// so ranking and display stay consistent across both sources.
|
||||||
.chain(dynamic.iter().map(|cmd| cmd.to_uppercase()))
|
.chain(dynamic.iter().map(|cmd| cmd.to_uppercase()))
|
||||||
.filter(|cmd| cmd.contains(&needle))
|
// Hide aliases (keys of the table); their target command still shows.
|
||||||
|
.filter(|cmd| cmd.contains(&needle) && !aliases.contains_key(cmd))
|
||||||
.collect();
|
.collect();
|
||||||
matches.sort();
|
matches.sort();
|
||||||
matches.dedup();
|
matches.dedup();
|
||||||
|
|
@ -647,6 +669,13 @@ pub fn ranked_matches(needle: &str, dynamic: &[String]) -> Vec<String> {
|
||||||
.cmp(&!b.starts_with(&needle))
|
.cmp(&!b.starts_with(&needle))
|
||||||
.then_with(|| a.cmp(b))
|
.then_with(|| a.cmp(b))
|
||||||
});
|
});
|
||||||
|
// If the entire input is an alias, its target is what Enter runs, so make it
|
||||||
|
// the first (highlighted) suggestion — even when the target has no substring
|
||||||
|
// overlap with the alias (`AA` → `AREA`) and so wouldn't otherwise appear.
|
||||||
|
if let Some(target) = aliases.get(&needle) {
|
||||||
|
matches.retain(|m| m != target);
|
||||||
|
matches.insert(0, target.clone());
|
||||||
|
}
|
||||||
matches.truncate(AUTOCOMPLETE_LIMIT);
|
matches.truncate(AUTOCOMPLETE_LIMIT);
|
||||||
matches
|
matches
|
||||||
}
|
}
|
||||||
|
|
@ -740,24 +769,54 @@ const INFO_COLOR: Color = Color {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::ranked_matches;
|
use super::ranked_matches;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
fn aliases(pairs: &[(&str, &str)]) -> FxHashMap<String, String> {
|
||||||
|
pairs
|
||||||
|
.iter()
|
||||||
|
.map(|(a, c)| (a.to_string(), c.to_string()))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dynamic_plugin_commands_surface_in_autocomplete() {
|
fn dynamic_plugin_commands_surface_in_autocomplete() {
|
||||||
|
let none = FxHashMap::default();
|
||||||
// No built-in command contains the plugin prefix, so an empty pool
|
// No built-in command contains the plugin prefix, so an empty pool
|
||||||
// yields nothing — reproducing the #272 bug's blind autocomplete.
|
// yields nothing — reproducing the #272 bug's blind autocomplete.
|
||||||
assert!(ranked_matches("LS_", &[]).is_empty());
|
assert!(ranked_matches("LS_", &[], &none).is_empty());
|
||||||
// A loaded plugin's commands become typeable, case-insensitively, and
|
// A loaded plugin's commands become typeable, case-insensitively, and
|
||||||
// the prefix substring surfaces every one of them.
|
// the prefix substring surfaces every one of them.
|
||||||
let pool = vec!["LS_LABEL".to_string(), "ls_autolabel".to_string()];
|
let pool = vec!["LS_LABEL".to_string(), "ls_autolabel".to_string()];
|
||||||
let m = ranked_matches("LS_", &pool);
|
let m = ranked_matches("LS_", &pool, &none);
|
||||||
assert!(m.contains(&"LS_LABEL".to_string()), "got {m:?}");
|
assert!(m.contains(&"LS_LABEL".to_string()), "got {m:?}");
|
||||||
assert!(m.contains(&"LS_AUTOLABEL".to_string()), "got {m:?}");
|
assert!(m.contains(&"LS_AUTOLABEL".to_string()), "got {m:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn builtin_commands_still_match_with_an_empty_pool() {
|
fn builtin_commands_still_match_with_an_empty_pool() {
|
||||||
let m = ranked_matches("LINE", &[]);
|
let m = ranked_matches("LINE", &[], &FxHashMap::default());
|
||||||
assert!(m.iter().any(|c| c == "LINE"), "got {m:?}");
|
assert!(m.iter().any(|c| c == "LINE"), "got {m:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn aliases_are_hidden_but_their_command_survives() {
|
||||||
|
// The alias `L` is dropped from suggestions while `LINE` (its target)
|
||||||
|
// still appears. (#288)
|
||||||
|
let a = aliases(&[("L", "LINE")]);
|
||||||
|
let m = ranked_matches("L", &[], &a);
|
||||||
|
assert!(!m.iter().any(|c| c == "L"), "alias L should be hidden: {m:?}");
|
||||||
|
assert!(m.iter().any(|c| c == "LINE"), "LINE should remain: {m:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exact_alias_forces_its_target_to_the_top() {
|
||||||
|
// Typing a full alias highlights the command Enter will run, even when
|
||||||
|
// the target shares no substring with the alias (`AA` → `AREA`). (#288)
|
||||||
|
let a = aliases(&[("AA", "AREA"), ("L", "LINE")]);
|
||||||
|
let m = ranked_matches("AA", &[], &a);
|
||||||
|
assert_eq!(m.first().map(String::as_str), Some("AREA"), "got {m:?}");
|
||||||
|
let m = ranked_matches("L", &[], &a);
|
||||||
|
assert_eq!(m.first().map(String::as_str), Some("LINE"), "got {m:?}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
157
src/ui/window/alias_editor.rs
Normal file
157
src/ui/window/alias_editor.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
//! Command-alias editor — an in-canvas modal (Plan B) for adding, remapping and
|
||||||
|
//! removing command-line aliases (the `ocad.pgp` table). Opened by ALIASEDIT.
|
||||||
|
//! Rows are `(alias, command)`; edits are buffered in `alias_editor_rows` and
|
||||||
|
//! committed to the alias table when the dialog closes. Mirrors the editable-row
|
||||||
|
//! pattern of the attribute editor and plugin manager.
|
||||||
|
|
||||||
|
use crate::app::Message;
|
||||||
|
use iced::widget::{button, column, container, row, scrollable, text, text_input, Space};
|
||||||
|
use iced::{Background, Border, Color, Element, Fill, Length, Theme};
|
||||||
|
|
||||||
|
/// Which column of an alias row a text edit targets.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum AliasField {
|
||||||
|
Alias,
|
||||||
|
Command,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Right-hand lane reserved for the scrollbar so it never overlaps the ✕ column.
|
||||||
|
const GUTTER: f32 = 16.0;
|
||||||
|
|
||||||
|
const BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||||
|
const FIELD_BG: Color = Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 };
|
||||||
|
const BORDER: Color = Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 };
|
||||||
|
const DIM: Color = Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 };
|
||||||
|
const WHITE: Color = Color { r: 0.85, g: 0.85, b: 0.85, a: 1.0 };
|
||||||
|
const ADD_C: Color = Color { r: 0.20, g: 0.40, b: 0.62, a: 1.0 };
|
||||||
|
|
||||||
|
fn field_style(_: &Theme, _s: text_input::Status) -> text_input::Style {
|
||||||
|
text_input::Style {
|
||||||
|
background: Background::Color(FIELD_BG),
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||||
|
icon: WHITE,
|
||||||
|
placeholder: DIM,
|
||||||
|
value: WHITE,
|
||||||
|
selection: Color { r: 0.25, g: 0.40, b: 0.60, a: 1.0 },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the alias editor content. `rows` is the live working buffer.
|
||||||
|
pub fn view_window(rows: &[(String, String)]) -> Element<'_, Message> {
|
||||||
|
let title = text("Command Aliases").size(15).color(WHITE);
|
||||||
|
let hint = text(
|
||||||
|
"Type an alias and the command it runs (e.g. L → LINE). \
|
||||||
|
Apply to save to ocad.pgp; closing discards unapplied edits.",
|
||||||
|
)
|
||||||
|
.size(11)
|
||||||
|
.color(DIM);
|
||||||
|
|
||||||
|
// Right gutter reserved so the scrollbar has its own lane and never sits on
|
||||||
|
// top of the row delete (✕) buttons. Applied to both the header and the
|
||||||
|
// scrollable rows so the columns stay aligned.
|
||||||
|
let gutter = iced::Padding { top: 0.0, right: GUTTER, bottom: 0.0, left: 0.0 };
|
||||||
|
|
||||||
|
let head = container(
|
||||||
|
row![
|
||||||
|
container(text("Alias").size(11).color(DIM)).width(Length::Fixed(120.0)),
|
||||||
|
container(text("Command").size(11).color(DIM)).width(Fill),
|
||||||
|
Space::new().width(Length::Fixed(30.0)),
|
||||||
|
]
|
||||||
|
.spacing(8),
|
||||||
|
)
|
||||||
|
.padding(gutter);
|
||||||
|
|
||||||
|
let mut list = column![].spacing(3);
|
||||||
|
for (idx, (alias, cmd)) in rows.iter().enumerate() {
|
||||||
|
let alias_box = text_input("alias", alias)
|
||||||
|
.on_input(move |v| Message::AliasEditorInput { idx, field: AliasField::Alias, value: v })
|
||||||
|
.style(field_style)
|
||||||
|
.size(13)
|
||||||
|
.padding([3, 6])
|
||||||
|
.width(Length::Fixed(120.0));
|
||||||
|
let cmd_box = text_input("command", cmd)
|
||||||
|
.on_input(move |v| Message::AliasEditorInput { idx, field: AliasField::Command, value: v })
|
||||||
|
.style(field_style)
|
||||||
|
.size(13)
|
||||||
|
.padding([3, 6])
|
||||||
|
.width(Fill);
|
||||||
|
let del = button(crate::ui::icons::tinted(crate::ui::icons::CLOSE, 12.0, WHITE))
|
||||||
|
.on_press(Message::AliasEditorRemove(idx))
|
||||||
|
.padding([2, 6])
|
||||||
|
.style(|_: &Theme, status| {
|
||||||
|
let bg = if matches!(status, button::Status::Hovered) {
|
||||||
|
Color { r: 0.45, g: 0.22, b: 0.22, a: 1.0 }
|
||||||
|
} else {
|
||||||
|
Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 }
|
||||||
|
};
|
||||||
|
button::Style {
|
||||||
|
background: Some(Background::Color(bg)),
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
list = list.push(
|
||||||
|
row![alias_box, cmd_box, del]
|
||||||
|
.spacing(8)
|
||||||
|
.align_y(iced::Center),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let add = button(text("+ Add alias").size(12).color(WHITE))
|
||||||
|
.on_press(Message::AliasEditorAdd)
|
||||||
|
.padding([4, 10])
|
||||||
|
.style(|_: &Theme, status| {
|
||||||
|
let bg = if matches!(status, button::Status::Hovered) {
|
||||||
|
Color { r: 0.30, g: 0.30, b: 0.30, a: 1.0 }
|
||||||
|
} else {
|
||||||
|
Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 }
|
||||||
|
};
|
||||||
|
button::Style {
|
||||||
|
background: Some(Background::Color(bg)),
|
||||||
|
text_color: WHITE,
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 3.0.into() },
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply — primary action; commits the rows to ocad.pgp and stays open.
|
||||||
|
let apply = button(text("Apply").size(12).color(WHITE))
|
||||||
|
.on_press(Message::AliasEditorApply)
|
||||||
|
.padding([4, 16])
|
||||||
|
.style(|_: &Theme, status| {
|
||||||
|
let bg = if matches!(status, button::Status::Hovered | button::Status::Pressed) {
|
||||||
|
Color { r: 0.24, g: 0.46, b: 0.74, a: 1.0 }
|
||||||
|
} else {
|
||||||
|
ADD_C
|
||||||
|
};
|
||||||
|
button::Style {
|
||||||
|
background: Some(Background::Color(bg)),
|
||||||
|
text_color: WHITE,
|
||||||
|
border: Border { radius: 4.0.into(), ..Default::default() },
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
container(
|
||||||
|
column![
|
||||||
|
title,
|
||||||
|
hint,
|
||||||
|
Space::new().height(6),
|
||||||
|
head,
|
||||||
|
scrollable(container(list).padding(gutter)).height(Fill),
|
||||||
|
Space::new().height(6),
|
||||||
|
row![add, Space::new().width(Fill), apply].align_y(iced::Center),
|
||||||
|
]
|
||||||
|
.spacing(6)
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill),
|
||||||
|
)
|
||||||
|
.padding(12)
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.style(|_: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(BG)),
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
@ -7,3 +7,4 @@ pub mod layers;
|
||||||
pub mod update_notice;
|
pub mod update_notice;
|
||||||
pub mod open_progress;
|
pub mod open_progress;
|
||||||
pub mod attribute_editor;
|
pub mod attribute_editor;
|
||||||
|
pub mod alias_editor;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue