forked from 1b-agent/opencadstudio-upstream
Merge pull request #1330 from mechpanos/feat/right-click-menus
feat(ui): right-click context menus with command keywords, PLINE/ARC keyword parity, grip menu
This commit is contained in:
58 files changed
+6437
-538
No files matched your search
+68
-16
@@ -90,6 +90,7 @@ fn reorder_insertion_index(from: usize, to: usize, after: bool, len: usize) -> O
|
||||
}
|
||||
|
||||
mod command;
|
||||
mod context_menu;
|
||||
mod dialog;
|
||||
mod dynamic;
|
||||
mod file;
|
||||
@@ -301,6 +302,14 @@ impl OpenCADStudio {
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
// The open right-click context menu owns the keyboard the same way:
|
||||
// arrows / Enter / mnemonic letters drive it, any other key closes it
|
||||
// and falls through to the command line (the behaviour of commercial solutions).
|
||||
if self.context_menu_open() {
|
||||
if let Some(task) = self.intercept_context_menu_key(&msg) {
|
||||
return task;
|
||||
}
|
||||
}
|
||||
let task = self.update_inner(msg);
|
||||
self.refresh_gpu_status();
|
||||
self.show_next_startup_modal();
|
||||
@@ -745,6 +754,14 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::SnapOverrideNone => {
|
||||
self.snap_override_popup = None;
|
||||
self.snapper.set_override_none();
|
||||
self.command_line
|
||||
.push_info(crate::t!("Snap override: None (next pick only).").as_ref());
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::SnapOverrideMtp => {
|
||||
self.snap_override_popup = None;
|
||||
self.tabs[self.active_tab]
|
||||
@@ -3536,6 +3553,10 @@ impl OpenCADStudio {
|
||||
return Task::none();
|
||||
}
|
||||
let was_click = !sel.right_dragging;
|
||||
// How long the button was held, for the time-sensitive mode.
|
||||
let held_ms = sel
|
||||
.right_press_time
|
||||
.map_or(0, |t| t.elapsed().as_millis() as i32);
|
||||
sel.right_down = false;
|
||||
sel.right_press_pos = None;
|
||||
sel.right_press_time = None;
|
||||
@@ -3560,21 +3581,43 @@ impl OpenCADStudio {
|
||||
drop(sel);
|
||||
return self.update(Message::CommandFinalize);
|
||||
}
|
||||
// A right-click (no orbit). While a command is active the first
|
||||
// right-click acts as Enter (commit / close); a second
|
||||
// consecutive right-click opens the context menu instead. When
|
||||
// idle it always opens the menu. (Right-drag, handled above,
|
||||
// always orbits.) Any other interaction — a left-click pick or a
|
||||
// new command — resets the cycle so the next right-click is Enter.
|
||||
if self.tabs[i].active_cmd.is_some() && !sel.right_click_entered {
|
||||
// A right-click. What it does is the user's choice (Options →
|
||||
// User Preferences, SHORTCUTMENU in commercial solutions):
|
||||
// • Shortcut menu — always open the context menu, whose
|
||||
// default row (Enter / Repeat) sits under the pointer.
|
||||
// • Time-sensitive — a quick click is Enter while a command
|
||||
// runs (repeat the last command when idle); a held click
|
||||
// opens the menu.
|
||||
// • Enter first — while a command is active the first
|
||||
// right-click acts as Enter and a second consecutive one
|
||||
// opens the menu; idle always opens the menu. Any other
|
||||
// interaction — a left-click pick or a new command — resets
|
||||
// that cycle so the next right-click is Enter again.
|
||||
let has_cmd = self.tabs[i].active_cmd.is_some();
|
||||
let open_menu = match self.right_click_mode {
|
||||
super::settings::RightClickMode::ShortcutMenu => true,
|
||||
super::settings::RightClickMode::TimeSensitive => {
|
||||
held_ms >= self.right_click_hold_ms
|
||||
}
|
||||
super::settings::RightClickMode::EnterFirst => {
|
||||
!(has_cmd && !sel.right_click_entered)
|
||||
}
|
||||
};
|
||||
if !open_menu {
|
||||
sel.right_click_entered = true;
|
||||
drop(sel);
|
||||
// CommandFinalize is Enter during a command and "repeat
|
||||
// the last command" when idle — exactly the quick
|
||||
// right-click.
|
||||
return self.update(Message::CommandFinalize);
|
||||
}
|
||||
sel.right_click_entered = false;
|
||||
sel.context_menu = Some(click_pos);
|
||||
sel.draworder_submenu = false;
|
||||
Task::none()
|
||||
sel.open_context_menu(click_pos);
|
||||
drop(sel);
|
||||
// Take the keyboard away from the command-line field so keys
|
||||
// reach the menu through the global subscription; the field
|
||||
// is re-focused when the menu closes.
|
||||
self.unfocus_widgets()
|
||||
}
|
||||
|
||||
Message::ViewportMiddlePress => self.on_viewport_middle_press(),
|
||||
@@ -5336,12 +5379,9 @@ impl OpenCADStudio {
|
||||
self.post_editor_closed(committed)
|
||||
}
|
||||
|
||||
Message::DrawOrderSubmenuToggle => {
|
||||
let i = self.active_tab;
|
||||
let mut sel = self.tabs[i].scene.selection.borrow_mut();
|
||||
sel.draworder_submenu = !sel.draworder_submenu;
|
||||
Task::none()
|
||||
}
|
||||
Message::ContextMenuPick(action) => self.on_context_menu_pick(action),
|
||||
Message::ContextMenuSubmenuToggle(id) => self.on_context_menu_submenu_toggle(id),
|
||||
Message::ContextMenuNavigate(nav) => self.on_context_menu_navigate(nav),
|
||||
|
||||
Message::DrawOrderPickRef(above) => {
|
||||
let i = self.active_tab;
|
||||
@@ -7236,6 +7276,18 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::RightClickModeChanged(mode) => {
|
||||
self.right_click_mode = mode;
|
||||
self.persist_settings_if_changed();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::RightClickHoldMsChanged(ms) => {
|
||||
self.right_click_hold_ms = super::settings::clamp_right_click_hold_ms(ms);
|
||||
self.persist_settings_if_changed();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::TextEditModeChanged(single) => {
|
||||
self.texteditmode = single;
|
||||
self.persist_settings_if_changed();
|
||||
|
||||
Reference in new issue
Block a user