mirror of
https://github.com/storytold/cadcraft.git
synced 2026-11-09 20:10:12 -06:00
Options: Right-Click Customization (click duration; Default, Edit and Command modes) (F20)
Options > User Preferences > Right-Click Customization... sets the time-sensitive right-click and its duration (rightClickDuration, 100-2000 ms), what a right-click does with nothing or something selected and no command running (rightClickDefault, rightClickEdit: the shortcut menu or repeat the last command), and, with the time-sensitive right-click off, while a command runs (rightClickCommand: the menu, Enter, or the menu only when the prompt has options). The defaults keep today's behaviour; the settings are kept in the profile. context_menu::show repeats Session::last_command in Repeat mode, and in_command uses the configured hold time and Command mode. Italian and Ukrainian strings; gaps F20 and ui-parity updated. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
26da98e320
commit
811bd3cb4e
@@ -6,8 +6,12 @@
|
||||
//! Pan/Zoom, Quick Select, Find, Properties and Options.
|
||||
//! - **While a command runs** ([`command_entries`]): a quick right-click is Enter; holding the
|
||||
//! button for [`HOLD_SECS`] opens Enter, Cancel, Recent Input, the prompt's options, Snap
|
||||
//! Overrides and Pan/Zoom instead (AutoCAD's time-sensitive right-click). With that turned off
|
||||
//! in OPTIONS (`timeSensitiveRightClick`), every right-click opens the menu.
|
||||
//! Overrides and Pan/Zoom instead (AutoCAD's time-sensitive right-click; Options ▸ User
|
||||
//! Preferences ▸ Right-Click Customization sets how long, `rightClickDuration`). With that turned
|
||||
//! off (`timeSensitiveRightClick`), Command mode says what a right-click does
|
||||
//! (`rightClickCommand`: the menu, Enter, or the menu only when the prompt has options).
|
||||
//! `rightClickDefault` and `rightClickEdit` can make a right-click with no command running
|
||||
//! repeat the last command instead of opening the menu.
|
||||
//! - **Shift/Ctrl+right-click** ([`snap_entries`]): the object snap menu. Its items type the point
|
||||
//! modifiers and snap overrides (`_from`, `_endp`, `_non`…) at the current point prompt.
|
||||
//!
|
||||
@@ -41,7 +45,8 @@ const SETTINGS: &[&str] = &["options"];
|
||||
/// How many recent commands and inputs the menus list.
|
||||
pub const RECENT: usize = 8;
|
||||
/// How long the right button must be held while a command runs to open the command menu instead
|
||||
/// of acting as Enter (AutoCAD's time-sensitive right-click; 250 ms there by default).
|
||||
/// of acting as Enter, by default (AutoCAD's time-sensitive right-click; 250 ms there by
|
||||
/// default). OPTIONS `rightClickDuration` changes it.
|
||||
pub const HOLD_SECS: f64 = 0.25;
|
||||
|
||||
/// Item ids that are not commands: keys, typed text and snap menu items.
|
||||
@@ -290,6 +295,19 @@ pub fn groups_ui(ui: &mut egui::Ui, groups: &[Vec<Entry>], clicked: &mut Option<
|
||||
pub fn show(app: &mut CadApp, resp: &egui::Response) {
|
||||
let mut clicked = None;
|
||||
let snap = app.canvas.snap_menu;
|
||||
// Right-Click Customization: with nothing selected (Default mode) or a selection (Edit mode),
|
||||
// a right-click may repeat the last command instead of opening the menu.
|
||||
let o = &app.options;
|
||||
let mode =
|
||||
if o.right_click_default == o.right_click_edit || app.session.selection().is_empty() { o.right_click_default } else { o.right_click_edit };
|
||||
if !snap && mode == crate::options::IdleClick::Repeat {
|
||||
if resp.secondary_clicked()
|
||||
&& let Some(last) = app.session.last_command.clone()
|
||||
{
|
||||
app.start(&last);
|
||||
}
|
||||
return;
|
||||
}
|
||||
resp.context_menu(|ui| {
|
||||
let groups = if snap { snap_entries(app) } else { entries(app) };
|
||||
groups_ui(ui, &groups, &mut clicked);
|
||||
@@ -307,8 +325,9 @@ struct Press(f64, egui::Pos2);
|
||||
|
||||
/// The canvas's right button while a command runs (Shift/Ctrl+right-click is the snap menu's): a
|
||||
/// quick click is Enter — returns `true` when the caller should send it — and a click held for
|
||||
/// [`HOLD_SECS`] opens the command menu ([`command_entries`]) at the pointer (any click does with
|
||||
/// the time-sensitive right-click turned off). Call it every frame
|
||||
/// OPTIONS `rightClickDuration` opens the command menu ([`command_entries`]) at the pointer. With
|
||||
/// the time-sensitive right-click turned off, `rightClickCommand` says whether a click opens the
|
||||
/// menu or is Enter. Call it every frame
|
||||
/// with whether the pointer is on the canvas and whether a right-click ended there; it also shows
|
||||
/// the menu while it is open.
|
||||
pub fn in_command(app: &mut CadApp, ui: &egui::Ui, inside: bool, right_clicked: bool) -> bool {
|
||||
@@ -325,14 +344,23 @@ pub fn in_command(app: &mut CadApp, ui: &egui::Ui, inside: bool, right_clicked:
|
||||
ctx.data_mut(|d| d.insert_temp(press_id, Press(now, p)));
|
||||
}
|
||||
let running = app.session.prompting() && !app.canvas.snap_menu;
|
||||
let o = &app.options;
|
||||
let timed = o.time_sensitive_right_click;
|
||||
// Without the time-sensitive right-click, Command mode says whether a click opens the menu.
|
||||
let menu = match o.right_click_command {
|
||||
crate::options::CommandClick::Menu => true,
|
||||
crate::options::CommandClick::Enter => false,
|
||||
crate::options::CommandClick::MenuWithOptions => app.session.current_prompt().is_some_and(|p| !p.keywords.is_empty()),
|
||||
};
|
||||
let hold = f64::from(o.right_click_hold_ms) / 1000.0;
|
||||
let mut open = false;
|
||||
if released && let Some(Press(t0, p0)) = ctx.data_mut(|d| d.remove_temp::<Press>(press_id)) {
|
||||
// A drag isn't a click; a click held long enough asks for the menu.
|
||||
let still = pos.is_some_and(|p| p.distance(p0) <= 6.0);
|
||||
let held = now - t0 >= HOLD_SECS || !app.options.time_sensitive_right_click;
|
||||
let held = if timed { now - t0 >= hold } else { menu };
|
||||
open = running && still && held;
|
||||
}
|
||||
let enter = running && inside && right_clicked && !open && app.options.time_sensitive_right_click;
|
||||
let enter = running && inside && right_clicked && !open && (timed || !menu);
|
||||
if !running {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1367,3 +1367,20 @@ menu Modify Edita
|
||||
Check for Updates… Verifica aggiornamenti…
|
||||
CADCraft is up to date. CADCraft è aggiornato.
|
||||
Could not check for updates. Impossibile verificare gli aggiornamenti.
|
||||
Right-Click Customization... Personalizzazione pulsante destro...
|
||||
Right-Click Customization Personalizzazione pulsante destro
|
||||
Turn on time-sensitive right-click Attiva clic destro sensibile al tempo
|
||||
Longer click duration: Durata clic lungo:
|
||||
milliseconds millisecondi
|
||||
While a command runs, a quick click is Enter and a longer click opens the shortcut menu. Durante un comando un clic rapido equivale a Invio, un clic più lungo apre il menu di scelta rapida.
|
||||
Default Mode Modalità predefinita
|
||||
No objects selected and no command running: Nessun oggetto selezionato e nessun comando in corso:
|
||||
Edit Mode Modalità modifica
|
||||
Objects selected and no command running: Oggetti selezionati e nessun comando in corso:
|
||||
Repeat last command Ripeti ultimo comando
|
||||
Shortcut Menu Menu di scelta rapida
|
||||
Command Mode Modalità comando
|
||||
While a command runs: Durante un comando:
|
||||
ENTER INVIO
|
||||
Shortcut Menu: always enabled Menu di scelta rapida: sempre attivo
|
||||
Shortcut Menu: enabled when command options are present Menu di scelta rapida: attivo se il comando ha opzioni
|
||||
|
||||
|
@@ -1373,3 +1373,20 @@
|
||||
Check for Updates… Перевірити оновлення…
|
||||
CADCraft is up to date. Встановлено найновішу версію CADCraft.
|
||||
Could not check for updates. Не вдалося перевірити оновлення.
|
||||
Right-Click Customization... Налаштування правої кнопки...
|
||||
Right-Click Customization Налаштування правої кнопки
|
||||
Turn on time-sensitive right-click Увімкнути залежне від часу клацання правою кнопкою
|
||||
Longer click duration: Тривалість довгого клацання:
|
||||
milliseconds мілісекунд
|
||||
While a command runs, a quick click is Enter and a longer click opens the shortcut menu. Під час команди коротке клацання — Enter, довше відкриває контекстне меню.
|
||||
Default Mode Режим за замовчуванням
|
||||
No objects selected and no command running: Нічого не вибрано й команда не виконується:
|
||||
Edit Mode Режим редагування
|
||||
Objects selected and no command running: Об'єкти вибрано, команда не виконується:
|
||||
Repeat last command Повторити останню команду
|
||||
Shortcut Menu Контекстне меню
|
||||
Command Mode Режим команди
|
||||
While a command runs: Під час виконання команди:
|
||||
ENTER Клавіша Enter
|
||||
Shortcut Menu: always enabled Контекстне меню: завжди
|
||||
Shortcut Menu: enabled when command options are present Контекстне меню: коли команда має параметри
|
||||
|
||||
|
@@ -15,7 +15,10 @@
|
||||
//! | `saveVersion` | `"2000"` … `"2018"`: the version drawings with no file yet are saved in |
|
||||
//! | `SAVETIME` | minutes between automatic saves, 0–600 (0: off) |
|
||||
//! | `SAVEFILEPATH` | the folder automatic saves go to |
|
||||
//! | `timeSensitiveRightClick` | while a command runs, a quick right-click is Enter and holding the button opens the shortcut menu (default); `false`: a right-click always opens the menu |
|
||||
//! | `timeSensitiveRightClick` | while a command runs, a quick right-click is Enter and holding the button opens the shortcut menu (default); `false`: `rightClickCommand` decides |
|
||||
//! | `rightClickDuration` | how long the right button is held to open the menu while a command runs, 100–2000 ms (250) |
|
||||
//! | `rightClickDefault`, `rightClickEdit` | a right-click with no command running and nothing (`Default`) or something (`Edit`) selected: `"menu"` (the shortcut menu, default) or `"repeat"` (the last command) |
|
||||
//! | `rightClickCommand` | a right-click while a command runs, with the time-sensitive right-click off: `"menu"` (default), `"enter"`, or `"menuWithOptions"` (the menu when the prompt has options, else Enter) |
|
||||
//! | `systemCursor` | keep the system cursor visible over the drawing area |
|
||||
//! | `historyLines` | command-line history lines, 0–12 |
|
||||
//! | `modelBackground`, `paperBackground` | `"#rrggbb"`, or `null` for the default |
|
||||
@@ -56,6 +59,66 @@ pub const SNAP_MARKER_SIZES: std::ops::RangeInclusive<f32> = 5.0..=31.0;
|
||||
/// Interface scales the JSON form accepts (1: the system's own size).
|
||||
pub const INTERFACE_SCALES: std::ops::RangeInclusive<f32> = 0.5..=3.0;
|
||||
|
||||
/// How long a right-click may be held before it opens the menu, in milliseconds.
|
||||
pub const RIGHT_CLICK_DURATIONS: std::ops::RangeInclusive<u32> = 100..=2000;
|
||||
|
||||
/// What a right-click does with no command running (Right-Click Customization's Default and Edit
|
||||
/// modes).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum IdleClick {
|
||||
/// Repeat the last command.
|
||||
Repeat,
|
||||
/// The shortcut menu.
|
||||
#[default]
|
||||
Menu,
|
||||
}
|
||||
|
||||
impl IdleClick {
|
||||
pub const ALL: [IdleClick; 2] = [IdleClick::Repeat, IdleClick::Menu];
|
||||
|
||||
/// The JSON name.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
IdleClick::Repeat => "repeat",
|
||||
IdleClick::Menu => "menu",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(s: &str) -> Option<IdleClick> {
|
||||
IdleClick::ALL.into_iter().find(|c| c.as_str().eq_ignore_ascii_case(s.trim()))
|
||||
}
|
||||
}
|
||||
|
||||
/// What a right-click does while a command runs with the time-sensitive right-click off
|
||||
/// (Right-Click Customization's Command mode).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum CommandClick {
|
||||
/// Enter.
|
||||
Enter,
|
||||
/// The shortcut menu, always.
|
||||
#[default]
|
||||
Menu,
|
||||
/// The shortcut menu when the prompt has options, else Enter.
|
||||
MenuWithOptions,
|
||||
}
|
||||
|
||||
impl CommandClick {
|
||||
pub const ALL: [CommandClick; 3] = [CommandClick::Enter, CommandClick::Menu, CommandClick::MenuWithOptions];
|
||||
|
||||
/// The JSON name.
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
CommandClick::Enter => "enter",
|
||||
CommandClick::Menu => "menu",
|
||||
CommandClick::MenuWithOptions => "menuWithOptions",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(s: &str) -> Option<CommandClick> {
|
||||
CommandClick::ALL.into_iter().find(|c| c.as_str().eq_ignore_ascii_case(s.trim()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The interface's own preferences (the drafting ones are engine settings).
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct UiOptions {
|
||||
@@ -68,8 +131,16 @@ pub struct UiOptions {
|
||||
/// The selection highlight colour; `None` keeps the theme's.
|
||||
pub selection_color: Option<Color32>,
|
||||
/// While a command runs: a quick right-click is Enter and a held one opens the shortcut menu
|
||||
/// (AutoCAD's time-sensitive right-click). Off: a right-click always opens the menu.
|
||||
/// (AutoCAD's time-sensitive right-click). Off: [`Self::right_click_command`] decides.
|
||||
pub time_sensitive_right_click: bool,
|
||||
/// How long the right button is held to open the menu while a command runs, in milliseconds.
|
||||
pub right_click_hold_ms: u32,
|
||||
/// A right-click with no command running and nothing selected (Default mode) or a selection
|
||||
/// (Edit mode).
|
||||
pub right_click_default: IdleClick,
|
||||
pub right_click_edit: IdleClick,
|
||||
/// A right-click while a command runs, with the time-sensitive right-click off.
|
||||
pub right_click_command: CommandClick,
|
||||
/// The size of every menu, palette, dialog and the command line (1: the system's own).
|
||||
pub interface_scale: f32,
|
||||
}
|
||||
@@ -82,6 +153,10 @@ impl Default for UiOptions {
|
||||
snap_marker_size: theme::SNAP_MARKER,
|
||||
selection_color: None,
|
||||
time_sensitive_right_click: true,
|
||||
right_click_hold_ms: (crate::context_menu::HOLD_SECS * 1000.0) as u32,
|
||||
right_click_default: IdleClick::Menu,
|
||||
right_click_edit: IdleClick::Menu,
|
||||
right_click_command: CommandClick::Menu,
|
||||
interface_scale: 1.0,
|
||||
}
|
||||
}
|
||||
@@ -148,6 +223,10 @@ struct UiChanges {
|
||||
savetime: Option<u32>,
|
||||
savefilepath: Option<String>,
|
||||
time_sensitive_right_click: Option<bool>,
|
||||
right_click_hold_ms: Option<u32>,
|
||||
right_click_default: Option<IdleClick>,
|
||||
right_click_edit: Option<IdleClick>,
|
||||
right_click_command: Option<CommandClick>,
|
||||
interface_scale: Option<f32>,
|
||||
}
|
||||
|
||||
@@ -191,6 +270,17 @@ fn parse_ui(key: &str, v: &Value, c: &mut UiChanges) -> Result<bool, String> {
|
||||
c.save_version = Some(crate::SaveVersion::parse(name.trim()).ok_or("saveVersion must be 2000, 2004, 2007, 2010, 2013 or 2018")?);
|
||||
}
|
||||
"timeSensitiveRightClick" => c.time_sensitive_right_click = Some(v.as_bool().ok_or("timeSensitiveRightClick must be true or false")?),
|
||||
"rightClickDuration" => {
|
||||
let (lo, hi) = (f64::from(*RIGHT_CLICK_DURATIONS.start()), f64::from(*RIGHT_CLICK_DURATIONS.end()));
|
||||
let n =
|
||||
v.as_f64().filter(|n| n.is_finite() && (lo..=hi).contains(n)).ok_or("rightClickDuration must be from 100 to 2000 milliseconds")?;
|
||||
c.right_click_hold_ms = Some(n.round() as u32);
|
||||
}
|
||||
"rightClickDefault" => c.right_click_default = Some(IdleClick::parse(text()?).ok_or("rightClickDefault must be menu or repeat")?),
|
||||
"rightClickEdit" => c.right_click_edit = Some(IdleClick::parse(text()?).ok_or("rightClickEdit must be menu or repeat")?),
|
||||
"rightClickCommand" => {
|
||||
c.right_click_command = Some(CommandClick::parse(text()?).ok_or("rightClickCommand must be menu, enter or menuWithOptions")?)
|
||||
}
|
||||
k if k.eq_ignore_ascii_case("SAVETIME") => {
|
||||
let max = cadcraft_engine::autosave::MAX_SAVETIME;
|
||||
let n = v
|
||||
@@ -227,6 +317,10 @@ fn ui_json(app: &CadApp) -> Map<String, Value> {
|
||||
m.insert("SAVETIME".into(), json!(cadcraft_engine::autosave::savetime()));
|
||||
m.insert("SAVEFILEPATH".into(), json!(cadcraft_engine::autosave::savefilepath()));
|
||||
m.insert("timeSensitiveRightClick".into(), json!(o.time_sensitive_right_click));
|
||||
m.insert("rightClickDuration".into(), json!(o.right_click_hold_ms));
|
||||
m.insert("rightClickDefault".into(), json!(o.right_click_default.as_str()));
|
||||
m.insert("rightClickEdit".into(), json!(o.right_click_edit.as_str()));
|
||||
m.insert("rightClickCommand".into(), json!(o.right_click_command.as_str()));
|
||||
m
|
||||
}
|
||||
|
||||
@@ -300,9 +394,26 @@ fn run_json(app: &mut CadApp, params: &Value) -> Result<Value, String> {
|
||||
if let Some(b) = ui.time_sensitive_right_click {
|
||||
app.options.time_sensitive_right_click = b;
|
||||
}
|
||||
apply_right_click(&mut app.options, &ui);
|
||||
Ok(report(app))
|
||||
}
|
||||
|
||||
/// Right-Click Customization's settings (the time-sensitive switch aside) from `c`.
|
||||
fn apply_right_click(o: &mut UiOptions, c: &UiChanges) {
|
||||
if let Some(ms) = c.right_click_hold_ms {
|
||||
o.right_click_hold_ms = ms;
|
||||
}
|
||||
if let Some(m) = c.right_click_default {
|
||||
o.right_click_default = m;
|
||||
}
|
||||
if let Some(m) = c.right_click_edit {
|
||||
o.right_click_edit = m;
|
||||
}
|
||||
if let Some(m) = c.right_click_command {
|
||||
o.right_click_command = m;
|
||||
}
|
||||
}
|
||||
|
||||
/// The interface preferences back to their defaults (the interface language stays).
|
||||
fn reset_ui(app: &mut CadApp) {
|
||||
let d = crate::UiState::default();
|
||||
@@ -330,6 +441,10 @@ impl CadApp {
|
||||
"selectionColor": color_json(self.options.selection_color),
|
||||
"snapMarkerSize": self.options.snap_marker_size,
|
||||
"timeSensitiveRightClick": self.options.time_sensitive_right_click,
|
||||
"rightClickDuration": self.options.right_click_hold_ms,
|
||||
"rightClickDefault": self.options.right_click_default.as_str(),
|
||||
"rightClickEdit": self.options.right_click_edit.as_str(),
|
||||
"rightClickCommand": self.options.right_click_command.as_str(),
|
||||
"interfaceScale": self.options.interface_scale,
|
||||
})
|
||||
.to_string()
|
||||
@@ -346,9 +461,19 @@ impl CadApp {
|
||||
cadcraft_engine::cmd::options::restore(&mut self.session, vars);
|
||||
}
|
||||
let mut c = UiChanges::default();
|
||||
for key in
|
||||
["systemCursor", "modelBackground", "paperBackground", "selectionColor", "snapMarkerSize", "timeSensitiveRightClick", "interfaceScale"]
|
||||
{
|
||||
for key in [
|
||||
"systemCursor",
|
||||
"modelBackground",
|
||||
"paperBackground",
|
||||
"selectionColor",
|
||||
"snapMarkerSize",
|
||||
"timeSensitiveRightClick",
|
||||
"rightClickDuration",
|
||||
"rightClickDefault",
|
||||
"rightClickEdit",
|
||||
"rightClickCommand",
|
||||
"interfaceScale",
|
||||
] {
|
||||
if let Some(value) = v.get(key) {
|
||||
let _ = parse_ui(key, value, &mut c);
|
||||
}
|
||||
@@ -372,6 +497,7 @@ impl CadApp {
|
||||
if let Some(b) = c.time_sensitive_right_click {
|
||||
self.options.time_sensitive_right_click = b;
|
||||
}
|
||||
apply_right_click(&mut self.options, &c);
|
||||
if let Some(n) = c.interface_scale {
|
||||
self.options.interface_scale = n;
|
||||
}
|
||||
@@ -391,4 +517,16 @@ mod tests {
|
||||
assert_eq!(parse_hex(bad), None, "{bad}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_and_command_clicks_parse_their_names() {
|
||||
for c in IdleClick::ALL {
|
||||
assert_eq!(IdleClick::parse(c.as_str()), Some(c));
|
||||
}
|
||||
for c in CommandClick::ALL {
|
||||
assert_eq!(CommandClick::parse(c.as_str()), Some(c));
|
||||
}
|
||||
assert_eq!(IdleClick::parse("enter"), None);
|
||||
assert_eq!(CommandClick::parse("repeat"), None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,8 +526,9 @@ fn aci_row(ui: &mut egui::Ui, current: u8, key: &str, changes: &mut Map<String,
|
||||
});
|
||||
}
|
||||
|
||||
/// Preferences tab: interface language, largest-array limit and time-sensitive right-click; edits accumulate in `changes`.
|
||||
/// Preferences tab: interface language, largest-array limit, time-sensitive right-click and the Right-Click Customization window; edits accumulate in `changes`.
|
||||
fn preferences(app: &CadApp, ui: &mut egui::Ui, changes: &mut Map<String, Value>) {
|
||||
let mut right_click = ui.ctx().data(|d| d.get_temp::<bool>(egui::Id::unique(RIGHT_CLICK)).unwrap_or(false));
|
||||
grid(ui, "options_preferences", |ui| {
|
||||
let language_caption = ui.label(crate::tl!("Interface language"));
|
||||
let mut lang = app.ui.interface_language;
|
||||
@@ -560,6 +561,94 @@ fn preferences(app: &CadApp, ui: &mut egui::Ui, changes: &mut Map<String, Value>
|
||||
changes.insert("timeSensitiveRightClick".into(), json!(timed));
|
||||
}
|
||||
hint(ui, crate::tl!("Turned off, a right-click while a command runs always opens the shortcut menu."));
|
||||
if ui.button(crate::tl!("Right-Click Customization...")).clicked() {
|
||||
right_click = true;
|
||||
}
|
||||
if right_click {
|
||||
right_click_window(&app.options, ui.ctx(), &mut right_click, changes);
|
||||
}
|
||||
ui.ctx().data_mut(|d| d.insert_temp(egui::Id::unique(RIGHT_CLICK), right_click));
|
||||
}
|
||||
|
||||
/// Whether the Right-Click Customization window is open (egui temp data).
|
||||
const RIGHT_CLICK: &str = "cc_options_right_click";
|
||||
|
||||
/// Right-Click Customization (Options ▸ User Preferences): the time-sensitive right-click and its
|
||||
/// duration, and what a right-click does in Default, Edit and Command modes. Changes apply at
|
||||
/// once, as on the Options tabs.
|
||||
fn right_click_window(o: &super::UiOptions, ctx: &egui::Context, open: &mut bool, changes: &mut Map<String, Value>) {
|
||||
use super::{CommandClick, IdleClick};
|
||||
let mut close = false;
|
||||
egui::Window::new(crate::tl!("Right-Click Customization"))
|
||||
.id(egui::Id::unique("cc_right_click"))
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.collapsible(false)
|
||||
.pivot(egui::Align2::CENTER_CENTER)
|
||||
.default_pos(ctx.content_rect().center())
|
||||
.show(ctx, |ui| {
|
||||
ui.set_width(460.0);
|
||||
let mut timed = o.time_sensitive_right_click;
|
||||
if ui.checkbox(&mut timed, crate::tl!("Turn on time-sensitive right-click")).changed() {
|
||||
changes.insert("timeSensitiveRightClick".into(), json!(timed));
|
||||
}
|
||||
ui.add_enabled_ui(o.time_sensitive_right_click, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(20.0);
|
||||
let caption = ui.label(crate::tl!("Longer click duration:"));
|
||||
let mut ms = o.right_click_hold_ms;
|
||||
let range = super::RIGHT_CLICK_DURATIONS;
|
||||
if ui.add(egui::DragValue::new(&mut ms).range(range).speed(10.0)).labelled_by(caption.id).changed() {
|
||||
changes.insert("rightClickDuration".into(), json!(ms));
|
||||
}
|
||||
ui.label(crate::tl!("milliseconds"));
|
||||
});
|
||||
hint(ui, crate::tl!("While a command runs, a quick click is Enter and a longer click opens the shortcut menu."));
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
for (title, note, key, current) in [
|
||||
(crate::tl!("Default Mode"), crate::tl!("No objects selected and no command running:"), "rightClickDefault", o.right_click_default),
|
||||
(crate::tl!("Edit Mode"), crate::tl!("Objects selected and no command running:"), "rightClickEdit", o.right_click_edit),
|
||||
] {
|
||||
heading(ui, title);
|
||||
ui.group(|ui| {
|
||||
ui.set_width(ui.available_width());
|
||||
hint(ui, note);
|
||||
for (choice, label) in [(IdleClick::Repeat, crate::tl!("Repeat last command")), (IdleClick::Menu, crate::tl!("Shortcut Menu"))] {
|
||||
if ui.radio(current == choice, label).clicked() && current != choice {
|
||||
changes.insert(key.into(), json!(choice.as_str()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ui.add_enabled_ui(!o.time_sensitive_right_click, |ui| {
|
||||
heading(ui, crate::tl!("Command Mode"));
|
||||
ui.group(|ui| {
|
||||
ui.set_width(ui.available_width());
|
||||
hint(ui, crate::tl!("While a command runs:"));
|
||||
for (choice, label) in [
|
||||
(CommandClick::Enter, crate::tl!("ENTER")),
|
||||
(CommandClick::Menu, crate::tl!("Shortcut Menu: always enabled")),
|
||||
(CommandClick::MenuWithOptions, crate::tl!("Shortcut Menu: enabled when command options are present")),
|
||||
] {
|
||||
if ui.radio(o.right_click_command == choice, label).clicked() && o.right_click_command != choice {
|
||||
changes.insert("rightClickCommand".into(), json!(choice.as_str()));
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
ui.separator();
|
||||
ui.horizontal(|ui| {
|
||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||
if ui.button(crate::tl!("Close")).clicked() {
|
||||
close = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
if close {
|
||||
*open = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn profiles(app: &CadApp, ui: &mut egui::Ui, changes: &mut Map<String, Value>) {
|
||||
|
||||
@@ -22,6 +22,7 @@ mod named_viewports_dialog;
|
||||
mod native_menu_text_keys;
|
||||
mod native_windows;
|
||||
mod options_dialog;
|
||||
mod options_right_click;
|
||||
mod parameters_rename;
|
||||
mod pickdrag;
|
||||
mod properties_units;
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
//! Options ▸ User Preferences ▸ Right-Click Customization: the time-sensitive right-click's
|
||||
//! duration, and what a right-click does with nothing selected (Default mode), with a selection
|
||||
//! (Edit mode) and while a command runs (Command mode), through the OPTIONS JSON form, the
|
||||
//! drawing area and the dialog.
|
||||
|
||||
use cadcraft_engine::Session;
|
||||
use cadcraft_ui_egui::options::{CommandClick, IdleClick};
|
||||
use cadcraft_ui_egui::{CadApp, Services, menus};
|
||||
use egui_kittest::Harness;
|
||||
use egui_kittest::kittest::Queryable;
|
||||
use serde_json::json;
|
||||
|
||||
fn app() -> CadApp {
|
||||
CadApp::new(Session::new(), Services::default())
|
||||
}
|
||||
|
||||
/// One frame at `time` (seconds) with `events`; returns the painted text.
|
||||
fn frame(app: &mut CadApp, ctx: &egui::Context, events: Vec<egui::Event>, time: f64) -> String {
|
||||
fn collect(shape: &egui::Shape, text: &mut String) {
|
||||
match shape {
|
||||
egui::Shape::Text(s) => {
|
||||
text.push_str(s.galley.text());
|
||||
text.push('\n');
|
||||
}
|
||||
egui::Shape::Vec(shapes) => shapes.iter().for_each(|s| collect(s, text)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let input = egui::RawInput {
|
||||
screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1600.0, 1000.0))),
|
||||
time: Some(time),
|
||||
events,
|
||||
..Default::default()
|
||||
};
|
||||
app.logic(ctx);
|
||||
let mut out = ctx.run_ui(input, |ui| app.ui(ui));
|
||||
out.textures_delta.clear();
|
||||
let mut text = String::new();
|
||||
out.shapes.iter().for_each(|s| collect(&s.shape, &mut text));
|
||||
text
|
||||
}
|
||||
|
||||
const AT: egui::Pos2 = egui::pos2(800.0, 500.0);
|
||||
|
||||
fn button(pressed: bool) -> egui::Event {
|
||||
egui::Event::PointerButton { pos: AT, button: egui::PointerButton::Secondary, pressed, modifiers: egui::Modifiers::NONE }
|
||||
}
|
||||
|
||||
/// A right-click on the drawing area from `t` held for `held` seconds; returns the text painted
|
||||
/// just after.
|
||||
fn right_click(app: &mut CadApp, ctx: &egui::Context, t: f64, held: f64) -> String {
|
||||
frame(app, ctx, vec![egui::Event::PointerMoved(AT)], t);
|
||||
frame(app, ctx, vec![button(true)], t + 0.01);
|
||||
frame(app, ctx, vec![button(false)], t + 0.01 + held);
|
||||
frame(app, ctx, Vec::new(), t + 0.05 + held)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_click_settings_are_checked_and_survive_a_restart() {
|
||||
let mut a = app();
|
||||
let all = a.run("options", json!({})).unwrap();
|
||||
assert_eq!(
|
||||
(all["rightClickDuration"].clone(), all["rightClickDefault"].clone(), all["rightClickEdit"].clone(), all["rightClickCommand"].clone()),
|
||||
(json!(250), json!("menu"), json!("menu"), json!("menu"))
|
||||
);
|
||||
let r = a
|
||||
.run(
|
||||
"options",
|
||||
json!({"rightClickDuration": 400, "rightClickDefault": "repeat", "rightClickEdit": "Repeat", "rightClickCommand": "menuWithOptions"}),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!((r["rightClickDuration"].clone(), r["rightClickEdit"].clone()), (json!(400), json!("repeat")));
|
||||
assert_eq!(a.options.right_click_command, CommandClick::MenuWithOptions);
|
||||
|
||||
let before = a.run("options", json!({})).unwrap();
|
||||
for bad in [
|
||||
json!({"rightClickDuration": 50}),
|
||||
json!({"rightClickDuration": 2001}),
|
||||
json!({"rightClickDuration": f64::NAN}),
|
||||
json!({"rightClickDuration": "long"}),
|
||||
json!({"rightClickDefault": "enter"}),
|
||||
json!({"rightClickEdit": 1}),
|
||||
json!({"rightClickCommand": "repeat", "PICKBOX": 9}),
|
||||
] {
|
||||
assert!(a.run("options", bad.clone()).is_err(), "{bad}");
|
||||
assert_eq!(a.run("options", json!({})).unwrap(), before, "{bad} changed nothing");
|
||||
}
|
||||
|
||||
let mut b = app();
|
||||
b.load_profile(&a.profile_json());
|
||||
assert_eq!(b.options, a.options);
|
||||
let mut c = app();
|
||||
c.load_profile(r#"{"rightClickDuration": 99999, "rightClickDefault": "fly", "rightClickCommand": null}"#);
|
||||
assert_eq!(c.options, app().options, "hostile saved values are skipped");
|
||||
let r = a.run("options", json!({"reset": true})).unwrap();
|
||||
assert_eq!((r["rightClickDuration"].clone(), r["rightClickDefault"].clone()), (json!(250), json!("menu")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_idle_right_click_can_repeat_the_last_command() {
|
||||
let mut a = app();
|
||||
a.run("options", json!({"rightClickDefault": "repeat"})).unwrap();
|
||||
a.cmdline("circle 0,0 5");
|
||||
assert!(!a.session.prompting());
|
||||
let ctx = egui::Context::default();
|
||||
frame(&mut a, &ctx, Vec::new(), 0.0);
|
||||
right_click(&mut a, &ctx, 0.1, 0.05);
|
||||
assert!(a.session.prompting(), "CIRCLE runs again: {}", a.session.prompt_text());
|
||||
a.cmdline("10,10 2");
|
||||
|
||||
// With a selection, Edit mode decides: the menu (its default) opens instead.
|
||||
a.run("selectall", json!({})).unwrap();
|
||||
assert!(!a.session.selection().is_empty());
|
||||
let text = right_click(&mut a, &ctx, 1.0, 0.05);
|
||||
assert!(!a.session.prompting(), "no command started: {}", a.session.prompt_text());
|
||||
assert!(text.contains("Erase"), "the shortcut menu opens:\n{text}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn command_mode_and_click_duration_decide_enter_or_the_menu() {
|
||||
let ctx = egui::Context::default();
|
||||
|
||||
// A longer duration: a 0.5 s click is still Enter.
|
||||
let mut a = app();
|
||||
a.run("options", json!({"rightClickDuration": 1000})).unwrap();
|
||||
frame(&mut a, &ctx, Vec::new(), 0.0);
|
||||
a.cmdline("LINE 0,0 10,0");
|
||||
right_click(&mut a, &ctx, 0.1, 0.5);
|
||||
assert!(a.session.running.is_none(), "a click shorter than the duration is Enter: {}", a.session.prompt_text());
|
||||
|
||||
// Time-sensitive off, Command mode Enter: every right-click is Enter.
|
||||
let ctx = egui::Context::default();
|
||||
let mut a = app();
|
||||
a.run("options", json!({"timeSensitiveRightClick": false, "rightClickCommand": "enter"})).unwrap();
|
||||
frame(&mut a, &ctx, Vec::new(), 0.0);
|
||||
a.cmdline("LINE 0,0 10,0");
|
||||
let text = right_click(&mut a, &ctx, 0.1, 0.6);
|
||||
assert!(a.session.running.is_none(), "Enter ends LINE: {}", a.session.prompt_text());
|
||||
assert!(!text.contains("Snap Overrides"), "no menu:\n{text}");
|
||||
|
||||
// Command mode "enabled when command options are present": CIRCLE's first prompt has
|
||||
// options, so the menu opens and the command keeps running.
|
||||
a.run("options", json!({"rightClickCommand": "menuWithOptions"})).unwrap();
|
||||
a.cmdline("CIRCLE");
|
||||
let text = right_click(&mut a, &ctx, 2.0, 0.05);
|
||||
assert!(text.contains("Cancel"), "the command menu opens:\n{text}");
|
||||
assert!(a.session.prompting(), "CIRCLE still runs");
|
||||
}
|
||||
|
||||
/// With `CADCRAFT_OPTIONS_SHOTS` set to a folder, the dialog is rendered there for a look.
|
||||
const SHOTS: &str = "CADCRAFT_OPTIONS_SHOTS";
|
||||
|
||||
fn harness(app: CadApp) -> Harness<'static, CadApp> {
|
||||
let builder = Harness::builder().with_size(egui::vec2(1400.0, 900.0));
|
||||
let builder = if std::env::var_os(SHOTS).is_some() { builder.wgpu() } else { builder };
|
||||
let mut h = builder.build_ui_state(
|
||||
|ui, app: &mut CadApp| {
|
||||
app.logic(ui.ctx());
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
h.run_steps(3);
|
||||
h
|
||||
}
|
||||
|
||||
fn shot(h: &mut Harness<'static, CadApp>, name: &str) {
|
||||
if let Some(dir) = std::env::var_os(SHOTS).map(std::path::PathBuf::from) {
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
h.render().unwrap().save(dir.join(name)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_customization_window_sets_every_mode() {
|
||||
let mut a = app();
|
||||
menus::activate(&mut a, "options");
|
||||
a.ui.dialog = Some("options:preferences".into());
|
||||
let mut h = harness(a);
|
||||
h.get_by_label("Right-Click Customization...").click();
|
||||
h.run_steps(3);
|
||||
h.get_by_label("Longer click duration:");
|
||||
shot(&mut h, "options-right-click.png");
|
||||
|
||||
// Default mode's "Repeat last command" comes first, Edit mode's second.
|
||||
if let Some(n) = h.get_all_by_label("Repeat last command").next() {
|
||||
n.click();
|
||||
}
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().options.right_click_default, IdleClick::Repeat);
|
||||
assert_eq!(h.state().options.right_click_edit, IdleClick::Menu);
|
||||
|
||||
// Command mode applies once the time-sensitive right-click is off.
|
||||
h.get_by_label("Turn on time-sensitive right-click").click();
|
||||
h.run_steps(3);
|
||||
assert!(!h.state().options.time_sensitive_right_click);
|
||||
h.get_by_label("Shortcut Menu: enabled when command options are present").click();
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().options.right_click_command, CommandClick::MenuWithOptions);
|
||||
|
||||
let unnamed = h.unnamed_widgets();
|
||||
assert!(unnamed.is_empty(), "unnamed controls: {unnamed:?}");
|
||||
// The window's Close comes after the Options dialog's in the tree.
|
||||
if let Some(n) = h.get_all_by_label("Close").last() {
|
||||
n.click();
|
||||
}
|
||||
h.run_steps(3);
|
||||
assert!(h.query_by_label("Longer click duration:").is_none());
|
||||
assert_eq!(h.state().ui.dialog.as_deref(), Some("options:preferences"));
|
||||
}
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
# Where CADCraft falls short of AutoCAD
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (Named Viewports dialog: U11 narrowed; MVIEW Restore and PURGE of multiline styles had landed: F15 and F21 corrected) · **Target:** Autodesk AutoCAD 2027
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-27 · **Change:** minor (Right-Click Customization: F20 narrowed) · **Target:** Autodesk AutoCAD 2027
|
||||
|
||||
Every known shortfall, one row each, ranked within each section by user impact. This is the work
|
||||
list: agents pick from here (top of a section first), and remove or update a row in the same PR
|
||||
@@ -41,7 +41,7 @@ do them first.
|
||||
| F16 | Groups: no group bounding box or single grip when a group is selected (GROUPDISPLAYMODE), copying a whole group does not make a new group, no Object Grouping dialog (GROUP, GROUPEDIT, UNGROUP, -GROUP, PICKSTYLE group selection and the DXF/DWG round trip landed, #417); PICKSTYLE's associative-hatch bit is stored but unused | `cmd/groups.rs` | Low | 2–4 |
|
||||
| F17 | Attributes (ATTSYNC, global -ATTEDIT, BATTMAN editing and DATAEXTRACTION/EATTEXT landed, #433): no verify, preset or lock-position modes, and multiline attributes (read, drawn and saved since 2026-10-12) can't be created or switched in BATTMAN; data extraction covers block references and attribute values (CSV or a table) only — no wizard, nested blocks, other objects' properties, `.dxe` templates or spreadsheet output | `cmd/blocks/battman.rs`, `cmd/blocks/extract.rs` | Low | 4–6 |
|
||||
| F19 | MATCHPROP Settings is a keyword prompt (no dialog) and has no material, shadow or centre-object settings; TEXTTOFRONT and HATCHTOBACK act on the current space only; OVERKILL doesn't compare polyline segments with other objects (picker and Settings, DRAWORDER Above/Under, TEXTTOFRONT, HATCHTOBACK, OVERKILL tolerance/ignore/overlap/end-to-end options landed, #439) | `props/matchprop.rs`, `draworder.rs`, `overkill.rs` | Low | 2–4 |
|
||||
| F20 | OPTIONS dialog done (#23): Display, Open and Save, Drafting, Selection, User Preferences and Profiles tabs, and the user profile (object snap modes, drafting toggles, Dynamic Input, PICKBOX, APERTURE, GRIPSIZE, CURSORSIZE, PICKFIRST, PICKADD, QPMODE, model and paper background, snap marker size, system cursor) survives restarts. Left: right-click customization, the autosave interval and default save version on Open and Save (the features themselves landed, #406, #421), AutoSnap magnet and tooltip toggles, colours of the other drawing-area elements, named profiles with import and export | `ui-egui/src/options/` | Low | 2–4 |
|
||||
| F20 | OPTIONS dialog done (#23): Display, Open and Save, Drafting, Selection, User Preferences and Profiles tabs, and the user profile (object snap modes, drafting toggles, Dynamic Input, PICKBOX, APERTURE, GRIPSIZE, CURSORSIZE, PICKFIRST, PICKADD, QPMODE, model and paper background, snap marker size, system cursor) survives restarts; User Preferences ▸ Right-Click Customization... (the time-sensitive right-click's duration; Default, Edit and Command modes). Left: the autosave interval and default save version on Open and Save (the features themselves landed, #406, #421), AutoSnap magnet and tooltip toggles, colours of the other drawing-area elements, named profiles with import and export | `ui-egui/src/options/` | Low | 2–4 |
|
||||
| F21 | MLINE, REGION and HELIX are objects since #510, with gaps: MLEDIT tees trim square; UNION/SUBTRACT/INTERSECT only combine regions whose boundaries don't meet; regions are saved as closed polylines (no ACIS data) | `draw2/mline.rs`, `inquiry/regions.rs`, `dxf_region.rs` | Low | 6–10 |
|
||||
| F22 | Inquiry leftovers (COUNT across spaces and nested blocks, the QuickCalc palette and COMPARE landed, #563; MASSPROP on regions, #510): COMPARE compares the tab shown, not every layout at once (COMPAREEXPORT, the comparison as a new drawing, landed); COMPAREEXPORT puts the two sets of objects in model-space groups, where AutoCAD puts them in blocks; QuickCalc keeps its history and variables for the session only | `file/compare.rs`, `palettes/quickcalc.rs` | Low | 3–5 |
|
||||
| F23 | No Sheet Set Manager, eTransmit, Share, Traces, Markup Import, Activity Insights | menu items uncovered | Low-medium (team workflows) | 40–70 |
|
||||
@@ -150,6 +150,7 @@ Detail: [localization-parity.md](localization-parity.md).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-27 | minor | Options ▸ User Preferences ▸ Right-Click Customization...: the time-sensitive right-click's duration (`rightClickDuration`), a right-click with nothing or something selected that repeats the last command (`rightClickDefault`, `rightClickEdit`), and Command mode with the time-sensitive right-click off (`rightClickCommand`: menu, Enter, or the menu only when the prompt has options), kept in the profile: F20 narrowed |
|
||||
| 2026-10-25 | minor | Named Viewports (View ▸ Viewports ▸ Named Viewports..., `vports.named`) lists the saved viewport configurations with a preview and the current one's name, and restores or deletes one; in a layout Restore places it as MVIEW Restore does: U11 narrowed. F15 and U11 no longer list MVIEW Restore (#633), and F21 no longer says PURGE skips multiline styles |
|
||||
| 2026-10-25 | minor | DIMTFILL limits: with DIMTFILL 1 (the drawing's background color) the written `*D` block and EXPLODE keep no background mask (a WIPEOUT over the text box would), and a true-color DIMTFILLCLR is written to DXF as the nearest ACI: F7 updated |
|
||||
| 2026-10-24 | minor | COMPAREEXPORT (typed, or Export on the comparison bar) opens the comparison as a new untitled drawing, `compare_<this> vs <other>.dwg`: this drawing's objects in one group and the other drawing's differing objects in a second group, in the comparison colors (green, red, gray for objects in both), block contents and style colors included: F22 narrowed |
|
||||
|
||||
+8
-3
@@ -1,6 +1,6 @@
|
||||
# UI parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-22 · **Change:** minor (distinct polyline midpoint grips) · **Target:** Autodesk AutoCAD 2027 for Mac (26.0)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-27 · **Change:** minor (Right-Click Customization) · **Target:** Autodesk AutoCAD 2027 for Mac (26.0)
|
||||
|
||||
How CADCraft's interaction compares with AutoCAD's: point entry, object snaps, tracking, dynamic
|
||||
input, grips, selection, the command line, chrome and navigation. A drafter's speed in AutoCAD
|
||||
@@ -166,7 +166,7 @@ down-arrow options menu (DYNPROMPT), and DYNDIM.
|
||||
| ViewCube | interactive 3D | clickable for 2D (Top/Home → extents); dragging does nothing (#7) |
|
||||
| Navigation bar, SteeringWheels | yes | no |
|
||||
| Workspaces | Drafting & Annotation, 3D Modeling | no |
|
||||
| Options / Preferences dialog | many tabs | **no** (issue #23) |
|
||||
| Options / Preferences dialog | many tabs | yes (#23): Display, Open and Save, Drafting, Selection (with Visual Effect Settings), User Preferences (with Right-Click Customization) and Profiles tabs; changes apply at once and are kept in the profile |
|
||||
| Customisation (CUI, aliases editor, tool palettes) | yes | Command Aliases dialog (ALIASEDIT, #1381); no CUI or tool palettes |
|
||||
| Themes | Dark, Light | Dark, Light, System (#179) |
|
||||
| Accessibility | VoiceOver basics | AccessKit through eframe; screen-reader coverage untested; magnifier issue #39; animation effects follow the OS setting (View ▸ Accessibility ▸ Animation Effects: System, On, Off) |
|
||||
@@ -190,7 +190,7 @@ down-arrow options menu (DYNPROMPT), and DYNDIM.
|
||||
shortcuts. AutoCAD for Mac's defaults are similar in number; F4 (3D osnap) and F6 (dynamic UCS)
|
||||
are missing.
|
||||
- Delete erases the selection on macOS (#64); Escape cancels.
|
||||
- Right-click on the canvas is Enter while a command runs; holding it (250 ms, AutoCAD's
|
||||
- Right-click on the canvas is Enter while a command runs; holding it (250 ms by default, AutoCAD's
|
||||
time-sensitive right-click) opens the command menu: Enter, Cancel, Recent Input, the prompt's
|
||||
options, Snap Overrides, Pan/Zoom. Otherwise it opens a shortcut menu (Repeat, Recent Commands,
|
||||
Clipboard ▸, Isolate ▸, the selection's editing commands, Draw Order ▸, Group, the selected
|
||||
@@ -198,11 +198,16 @@ down-arrow options menu (DYNPROMPT), and DYNDIM.
|
||||
The command line, drawing tabs, layout tabs, Layers palette rows and the Properties palette
|
||||
have menus too (#512). Still missing: in-place layout tab rename, Move or Copy, Select All
|
||||
Layouts.
|
||||
- Options ▸ User Preferences ▸ Right-Click Customization sets the hold time, what a right-click
|
||||
does with nothing or something selected (the menu, or repeat the last command) and, with the
|
||||
time-sensitive right-click off, while a command runs (Enter, the menu, or the menu only when
|
||||
the prompt has options).
|
||||
|
||||
## Revision history
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-27 | minor | Options ▸ User Preferences ▸ Right-Click Customization...: the time-sensitive right-click's duration, Default and Edit modes (shortcut menu or repeat the last command) and Command mode (Enter, the menu, or the menu only when the prompt has options), set through OPTIONS and kept in the profile |
|
||||
| 2026-10-22 | minor | Polyline segment midpoint grips use rectangles, distinct from square vertex grips, with matching hover and pick bounds (#1950) |
|
||||
| 2026-10-13 | minor | Filled-region highlight boundaries are traversed in deterministic coordinate order, fixing dash-phase jumps on CPU hover/selection redraws and GPU highlight rebuilds; CPU dash-position, pixel and GPU mesh regressions |
|
||||
| 2026-10-14 | minor | Selection cycling (#1143): the list and badge of SELECTIONCYCLING, Shift+Space through overlapping objects (#1145) |
|
||||
|
||||
Reference in New Issue
Block a user