feat(ui): compatible right-click context menu driven by command options

The viewport context menu is now built from a pure model
(ui::popup::context_menu) rendered by the overlay: while a command runs it
offers Enter / Cancel / every keyword of the command's options() with the
keyword shown as a hint, a Recent Input submenu and M2P; when idle it offers
Repeat, Recent Input, Clipboard, Undo/Redo with history labels, the edit
block (Erase/Move/Copy/Scale/Rotate/Mirror/Draw Order), Isolate, Pan/Zoom,
selection tools, Properties and Options. The default row is anchored under
the pointer and the menu is keyboard-navigable (arrows, Enter, mnemonic
letters; any other key closes it and types into the command line).

Right-click behaviour is a user preference (Options → User Preferences,
SHORTCUTMENU / SHORTCUTMENUDURATION): shortcut menu (commercial solutions default),
time-sensitive, or the previous Enter-first cycle.

LINE gains options() (Close/Undo/eXit).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
PanosandClaude Opus 5 committed 2026-09-17 12:05:14 +03:00
1 parent 65c0fe5494
commit e623c053ab
19 files changed
+2019 -199

No files matched your search

+2
View File
@@ -503,6 +503,8 @@ inventory::submit!(crate::command::CommandRegistration {
"MIRRTEXT",
"ZOOMWHEEL",
"ZOOMFACTOR",
"SHORTCUTMENU",
"SHORTCUTMENUDURATION",
"CURSORSIZE",
"PICKBOX",
"CURSORTYPE",
+50 -1
View File
@@ -956,6 +956,8 @@ impl OpenCADStudio {
"MIRRTEXT"
| "ZOOMWHEEL"
| "ZOOMFACTOR"
| "SHORTCUTMENU"
| "SHORTCUTMENUDURATION"
| "CURSORSIZE"
| "PICKBOX"
| "CURSORTYPE"
@@ -1071,7 +1073,7 @@ impl OpenCADStudio {
let value = it.next().map(|s| s.trim().to_string());
if name.is_empty() || name == "?" {
self.command_line.push_info(
crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(),
crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(),
);
} else {
if name == "CETRANSPARENCY" {
@@ -1490,6 +1492,51 @@ impl OpenCADStudio {
Ok((format!("ZOOMFACTOR = {}", self.zoom_factor), false))
}
},
// AutoCAD bit code: 0 = no shortcut menus (right-click
// is Enter), 16 = time-sensitive; everything else is the
// regular shortcut menu. Reported as AutoCAD's defaults
// (11 = default+edit+command menus, +16 when
// time-sensitive).
"SHORTCUTMENU" => {
use crate::app::settings::RightClickMode;
let code = |mode: RightClickMode| match mode {
RightClickMode::EnterFirst => 0,
RightClickMode::ShortcutMenu => 11,
RightClickMode::TimeSensitive => 27,
};
match &value {
Some(v) => match v.parse::<i32>() {
Ok(bits) if (0..=31).contains(&bits) => {
self.right_click_mode = if bits == 0 {
RightClickMode::EnterFirst
} else if bits & 16 != 0 {
RightClickMode::TimeSensitive
} else {
RightClickMode::ShortcutMenu
};
Ok((format!("SHORTCUTMENU = {}", code(self.right_click_mode)), true))
}
_ => Err("SETVAR: integer from 0 to 31 required.".into()),
},
None => Ok((
format!("SHORTCUTMENU = {}", code(self.right_click_mode)),
false,
)),
}
}
"SHORTCUTMENUDURATION" => match &value {
Some(v) => match v.parse::<i32>() {
Ok(ms) if (100..=1000).contains(&ms) => {
self.right_click_hold_ms = ms;
Ok((format!("SHORTCUTMENUDURATION = {ms}"), true))
}
_ => Err("SETVAR: integer from 100 to 1000 required.".into()),
},
None => Ok((
format!("SHORTCUTMENUDURATION = {}", self.right_click_hold_ms),
false,
)),
},
"CURSORSIZE" => match &value {
Some(v) => match v.parse::<i32>() {
Ok(size) if (1..=100).contains(&size) => {
@@ -2347,6 +2394,8 @@ impl OpenCADStudio {
name.as_str(),
"ZOOMWHEEL"
| "ZOOMFACTOR"
| "SHORTCUTMENU"
| "SHORTCUTMENUDURATION"
| "CURSORSIZE"
| "PICKBOX"
| "CURSORTYPE"