Put the rest of the selection settings on the Options card

Options → Selection already carried the pick box, the selection-area effect
and the grip visuals. Four settings behind it were finished, persisted and
reachable only by typing a system variable, and one more was a constant.

**Four that only needed a widget.** `SELECTIONEFFECT`, `SELECTIONPREVIEW`,
`PICKADD` and `PICKDRAG` are all wired end to end already. `SELECTIONEFFECT`
was the odd one: the card's own "Restore Defaults" button resets it, so it
could be turned off by a button and only turned back on from the command
line.

`SELECTIONPREVIEW` is a bitmask and gets one checkbox per bit, which is why
`set_preview_bit` exists rather than two inline expressions — the bits are
the thing this change is most able to get backwards.

The `PICKADD` checkbox is worded as AutoCAD words it, "use Shift to add to
selection", which is the *inverse* of the stored flag. That inversion is
commented at both ends; it reads as a bug otherwise.

**`GRIPOBJLIMIT`.** The limit past which a selection gets no grips arrived
with the selection work as a hardcoded 100, matching AutoCAD's default. It is
now a setting, a slider and a system variable, following `PICKBOX`'s path
exactly. AutoCAD's 0 means "no limit", which the old `>` comparison could not
express, so the read now guards on it — with a test, because zero is the one
value that would otherwise suppress every grip instead of allowing all of
them.

The slider covers 0..=1000, where every useful value lives; the sysvar takes
AutoCAD's full 0..=32767, and a larger value set from the command line parks
the handle at the end while the read-out keeps showing the real number.

**`SelectionPrefs`** replaces the `pick_box` parameter rather than adding
three more. `view_window` already took seventeen positional arguments, and
`pick_add` / `pick_drag_rect` are two adjacent `bool`s that would swap
silently at the call site.

**Two fixes found on the way.** `OPTIONS` and `OP` were registered for
autocomplete but never dispatched, so typing either answered "Unknown
command" and the dialog could only be opened from the start page. And the
`SELECTIONPREVIEW` doc comment had its bits the wrong way round — the code
reads `& 1` for the idle rollover and `& 2` for the in-command one, as
AutoCAD does — which would have put the two new checkboxes' labels backwards.

**Not done, deliberately.** `PICKFIRST` and `PICKAUTO` exist but are always
on, with no variable. `PICKSTYLE` is stored in the header and read by nobody
— group expansion decides from the group's own `selectable` flag — so an
"object grouping" checkbox would control nothing. Associative-hatch selection
does not exist as a selection behaviour. Each needs a behaviour change rather
than an exposure.

Of the ten new strings, en-US and el-GR are translated; the other nineteen
locales carry the English text. Composing them from existing keys, the way
el-GR composes the neighbouring labels, only works for one of the ten —
there is no existing key for "limit", "unlimited", "shift", "rectangle" or
"lasso", and "Grips: Objects" as the label of a numeric limit would be worse
than an untranslated string.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
PanosandClaude Opus 5 committed 2026-09-09 13:19:06 +03:00
1 parent f9cacaec2b
commit cabb20db45
33 files changed
+585 -37

No files matched your search

+5
View File
@@ -294,6 +294,10 @@ pub fn start_allowed(cmd: &str) -> bool {
| "ALIASEDIT"
| "CUILOAD"
| "CUIIMPORT"
// The start page already offers Options as a button, so the
// command that opens the same dialog belongs here too.
| "OPTIONS"
| "OP"
)
}
@@ -548,6 +552,7 @@ inventory::submit!(crate::command::CommandRegistration {
"GRIPCOLOR",
"GRIPHOT",
"GRIPHOVER",
"GRIPOBJLIMIT",
// Reset selected entities' overrides to follow their layer.
"SETBYLAYER",
// Remove duplicate objects; set drawing base point; audit integrity;
+19 -1
View File
@@ -918,6 +918,7 @@ impl OpenCADStudio {
| "GRIPCOLOR"
| "GRIPHOT"
| "GRIPHOVER"
| "GRIPOBJLIMIT"
) =>
{
return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i);
@@ -940,7 +941,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: 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 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 | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(),
crate::t!("SETVAR: 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 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 matches!(name.as_str(), "SHOWHIST" | "SOLIDHIST") {
@@ -1444,6 +1445,22 @@ impl OpenCADStudio {
},
None => Ok((format!("GRIPSIZE = {}", self.model_space.grip_size), false)),
},
"GRIPOBJLIMIT" => match &value {
Some(v) => match v.parse::<i32>() {
Ok(limit @ 0..=32767) => {
self.grip_object_limit = limit;
Ok((format!("GRIPOBJLIMIT = {limit}"), true))
}
_ => Err(
"SETVAR: integer from 0 to 32767 required (0 = no limit)."
.into(),
),
},
None => Ok((
format!("GRIPOBJLIMIT = {}", self.grip_object_limit),
false,
)),
},
"GRIPCOLOR" => match &value {
Some(v) => match v.parse::<u8>() {
Ok(color) => {
@@ -2126,6 +2143,7 @@ impl OpenCADStudio {
| "GRIPCOLOR"
| "GRIPHOT"
| "GRIPHOVER"
| "GRIPOBJLIMIT"
) {
self.persist_settings_if_changed();
} else {
+8
View File
@@ -245,6 +245,14 @@ impl OpenCADStudio {
return Some(Task::done(Message::ShortcutsPanelOpen));
}
// ── OPTIONS / OP — the preferences dialog ──────────────────────
// Both names were registered for autocomplete but never dispatched,
// so typing either one answered "Unknown command" and the dialog
// could only be reached from the button on the start page.
"OPTIONS" | "OP" => {
return Some(Task::done(Message::OptionsOpen));
}
// CUIEXPORT <path> — write the keyboard-shortcut customizations
// (the drawing-independent CUI data) to a plain "KEY COMMAND" file.
"CUIEXPORT" => {
+5 -1
View File
@@ -270,7 +270,11 @@ pub struct ModelSpaceThemeConfig {
pub selection_highlight_color: u8,
/// SELECTIONEFFECT: whether selected objects glow with solid highlight (true) or dash (false).
pub selection_effect: bool,
/// SELECTIONPREVIEW: rollover/hover highlight mode (0 = off, 1 = in cmd, 2 = idle, 3 = both).
/// SELECTIONPREVIEW: rollover/hover highlight mode, as a bitmask —
/// 1 = preview while no command is running, 2 = preview during a command,
/// 3 = both, 0 = off. The bits are read that way round in
/// `update::viewport` (`& 1` at the idle pick, `& 2` inside a command), and
/// that is also AutoCAD's order; this comment used to state the reverse.
pub selection_preview: u8,
/// GRIPSIZE: grip marker half-size in pixels (1–25, default 5).
pub grip_size: u8,
+18
View File
@@ -500,6 +500,9 @@ pub(super) struct OpenCADStudio {
cursor_size: i32,
/// Selection-box size setting (PICKBOX, 0..=50).
pick_box: i32,
/// Selected-object count past which grips stop being generated
/// (GRIPOBJLIMIT, 0..=32767; 0 = no limit).
grip_object_limit: i32,
/// Drawing viewport cursor style (CURSORTYPE).
cursor_type: settings::CursorType,
/// Explicit crosshair colour; `None` retains automatic contrast.
@@ -1977,6 +1980,20 @@ pub enum Message {
GripHotChanged(u8),
/// Change Hover/Warm Grip color ACI index (0 = Theme Primary Strong, 1..=255 = ACI, GRIPHOVER).
GripHoverChanged(u8),
/// Change the selected-object count past which grips stop being drawn
/// (0..=32767, 0 = no limit, GRIPOBJLIMIT).
GripObjectLimitChanged(i32),
/// Toggle the solid selection highlight (SELECTIONEFFECT).
SelectionEffectToggled(bool),
/// Toggle rollover preview while no command is running (SELECTIONPREVIEW bit 1).
SelectionPreviewIdleToggled(bool),
/// Toggle rollover preview during a command (SELECTIONPREVIEW bit 2).
SelectionPreviewCommandToggled(bool),
/// Toggle "use Shift to add to selection" — the checkbox reads the way
/// AutoCAD words it, so it carries the *inverse* of PICKADD.
ShiftToAddToggled(bool),
/// Toggle press-and-drag drawing a rectangle instead of a lasso (PICKDRAG).
PickDragRectToggled(bool),
/// Restore Model Space display/canvas appearance to defaults.
RestoreModelSpaceDisplayDefaults,
/// Restore Selection visual effect settings to defaults.
@@ -3369,6 +3386,7 @@ impl OpenCADStudio {
zoom_factor: 60,
cursor_size: 5,
pick_box: 3,
grip_object_limit: settings::DEFAULT_GRIP_OBJECT_LIMIT,
cursor_type: settings::CursorType::Crosshair,
crosshair_color: None,
crosshair_color_input: String::new(),
+72 -9
View File
@@ -2508,11 +2508,10 @@ handles={handles_ms:.1} panel={:.1} ribbon={ribbon_ms:.1} tail={:.1} selected={}
}
/// Rebuild the cached selected_grips from the current entity selection.
/// Selections larger than this get no grips, matching AutoCAD's
/// `GRIPOBJLIMIT` default. The DWG header does not carry the variable, so
/// the default stands in for it.
const GRIP_OBJECT_LIMIT: usize = 100;
///
/// Selections past `GRIPOBJLIMIT` get no grips at all. That used to be a
/// constant here; it is now [`crate::app::settings::UserSettings::grip_object_limit`],
/// reachable from Options and from the sysvar of the same name.
pub(super) fn refresh_selected_grips(&mut self) {
let i = self.active_tab;
let locked_active_grip = self.tabs[i].active_grip.as_ref().is_some_and(|grip| {
@@ -2551,7 +2550,11 @@ handles={handles_ms:.1} panel={:.1} ribbon={ribbon_ms:.1} tail={:.1} selected={}
// consumers all iterate the list, so they simply find nothing, and
// grip editing is unavailable for a selection far past the size
// where dragging one is a sensible thing to do.
let selected = if selected.len() > Self::GRIP_OBJECT_LIMIT {
//
// The limit is `GRIPOBJLIMIT`, and 0 means no limit — which is why
// this cannot be a bare `>` against the stored value.
let limit = self.grip_object_limit;
let selected = if limit > 0 && selected.len() > limit as usize {
Vec::new()
} else {
selected
@@ -4155,9 +4158,12 @@ mod grip_limit_tests {
use acadrust::entities::{EntityType, Line};
use acadrust::types::Vector3;
let select_n = |n: usize| -> usize {
let select_n = |n: usize, limit: Option<i32>| -> usize {
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
if let Some(limit) = limit {
app.grip_object_limit = limit;
}
let i = app.active_tab;
let handles: Vec<_> = (0..n)
.map(|k| {
@@ -4178,15 +4184,72 @@ mod grip_limit_tests {
};
assert!(
select_n(OpenCADStudio::GRIP_OBJECT_LIMIT) > 0,
select_n(crate::app::settings::DEFAULT_GRIP_OBJECT_LIMIT as usize, None) > 0,
"a selection at the limit must still show its grips",
);
assert_eq!(
select_n(OpenCADStudio::GRIP_OBJECT_LIMIT + 1),
select_n(crate::app::settings::DEFAULT_GRIP_OBJECT_LIMIT as usize + 1, None),
0,
"one past the limit must show none at all",
);
}
/// The Options card writes these four straight onto the app and relies on
/// the snapshot/restore pair to carry them across a restart. Three of them
/// had no UI until now and so no reason for anyone to notice if the pair
/// missed one.
#[test]
fn the_selection_settings_survive_a_save_and_load() {
let mut app = OpenCADStudio::new_for_test();
app.pick_add = false;
app.pick_drag_rect = true;
app.pick_box = 11;
app.grip_object_limit = 0;
let saved = app.current_settings();
let mut restored = OpenCADStudio::new_for_test();
restored.apply_settings(&saved);
assert!(!restored.pick_add);
assert!(restored.pick_drag_rect);
assert_eq!(restored.pick_box, 11);
assert_eq!(
restored.grip_object_limit, 0,
"zero is a real value here, not an unset field",
);
}
/// `GRIPOBJLIMIT 0` means no limit, and the old comparison — a bare `>`
/// against the stored number — could not express that: zero would have
/// suppressed every grip instead of allowing all of them.
#[test]
fn a_limit_of_zero_means_no_limit() {
use acadrust::entities::{EntityType, Line};
use acadrust::types::Vector3;
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
app.grip_object_limit = 0;
let i = app.active_tab;
let over = crate::app::settings::DEFAULT_GRIP_OBJECT_LIMIT as usize * 3;
let handles: Vec<_> = (0..over)
.map(|k| {
let x = k as f64;
app.tabs[i].scene.add_entity(EntityType::Line(Line::from_points(
Vector3::new(x, 0.0, 0.0),
Vector3::new(x + 1.0, 1.0, 0.0),
)))
})
.collect();
for handle in handles {
app.tabs[i].scene.select_entity(handle, false);
}
app.refresh_selected_grips();
assert!(
app.tabs[i].selected_grips.len() > 0,
"zero must read as unlimited, not as a limit of none",
);
}
}
#[cfg(test)]
+11
View File
@@ -145,6 +145,12 @@ pub(crate) fn snaps_from_osmode(osmode: i32) -> (Vec<SnapType>, bool) {
(modes, osmode & OSMODE_SUPPRESS == 0)
}
/// GRIPOBJLIMIT default: past this many selected objects, no grips are drawn.
///
/// AutoCAD's own default, and the value the limit was hardcoded to when it
/// arrived with the selection work.
pub const DEFAULT_GRIP_OBJECT_LIMIT: i32 = 100;
/// The "settings" section of the consolidated config ([`crate::app::config`]).
/// Field defaults mirror the app's in-code defaults so a missing key restores
/// the value the app boots with.
@@ -160,6 +166,10 @@ pub struct UserSettings {
pub cursor_size: i32,
/// PICKBOX: normalized visible-box and click-aperture size.
pub pick_box: i32,
/// GRIPOBJLIMIT: past this many selected objects, no grips are drawn at
/// all. 0 means no limit. Registry-style app preference, as in AutoCAD —
/// the DWG header carries no slot for it.
pub grip_object_limit: i32,
/// CURSORTYPE: crosshair or the platform pointer over the drawing.
pub cursor_type: CursorType,
/// Explicit crosshair RGB. `None` keeps automatic background contrast.
@@ -307,6 +317,7 @@ impl Default for UserSettings {
zoom_factor: 60,
cursor_size: 5,
pick_box: 3,
grip_object_limit: DEFAULT_GRIP_OBJECT_LIMIT,
cursor_type: CursorType::Crosshair,
crosshair_color: None,
lineweight_display_scale: 100,
+2
View File
@@ -365,6 +365,7 @@ impl OpenCADStudio {
zoom_factor: self.zoom_factor,
cursor_size: self.cursor_size,
pick_box: self.pick_box,
grip_object_limit: self.grip_object_limit,
cursor_type: self.cursor_type,
crosshair_color: self.crosshair_color,
lineweight_display_scale: self.lineweight_display_scale,
@@ -418,6 +419,7 @@ impl OpenCADStudio {
self.zoom_factor = s.zoom_factor.clamp(3, 100);
self.cursor_size = s.cursor_size.clamp(1, 100);
self.pick_box = s.pick_box.clamp(0, 50);
self.grip_object_limit = s.grip_object_limit.clamp(0, 32767);
self.cursor_type = s.cursor_type;
self.crosshair_color = s.crosshair_color;
self.crosshair_color_input = s
+82
View File
@@ -6103,6 +6103,50 @@ impl OpenCADStudio {
Task::none()
}
Message::GripObjectLimitChanged(limit) => {
self.grip_object_limit = limit.clamp(0, 32767);
self.persist_settings_if_changed();
Task::none()
}
Message::SelectionEffectToggled(enabled) => {
self.model_space.selection_effect = enabled;
self.sync_model_space_theme(false);
self.persist_settings_if_changed();
Task::none()
}
// SELECTIONPREVIEW is a bitmask and the two checkboxes own one bit
// each: 1 = rollover while idle, 2 = rollover during a command.
Message::SelectionPreviewIdleToggled(enabled) => {
self.model_space.selection_preview =
set_preview_bit(self.model_space.selection_preview, 1, enabled);
self.persist_settings_if_changed();
Task::none()
}
Message::SelectionPreviewCommandToggled(enabled) => {
self.model_space.selection_preview =
set_preview_bit(self.model_space.selection_preview, 2, enabled);
self.persist_settings_if_changed();
Task::none()
}
// The checkbox is worded as AutoCAD words it — "use Shift to add"
// — which is the opposite of PICKADD, so it is inverted here rather
// than leaving a reader to work that out from the field name.
Message::ShiftToAddToggled(shift_to_add) => {
self.pick_add = !shift_to_add;
self.persist_settings_if_changed();
Task::none()
}
Message::PickDragRectToggled(rectangle) => {
self.pick_drag_rect = rectangle;
self.persist_settings_if_changed();
Task::none()
}
Message::RestoreSelectionVisualDefaults => {
self.model_space.selection_area = true;
self.model_space.selection_opacity = 12;
@@ -6115,6 +6159,10 @@ impl OpenCADStudio {
self.model_space.grip_color = 0;
self.model_space.grip_hot = 0;
self.model_space.grip_hover = 0;
// The button restores the *visual* defaults, which is why the
// grip limit is here and PICKADD / PICKDRAG are not — those are
// how selection behaves, not how it looks.
self.grip_object_limit = crate::app::settings::DEFAULT_GRIP_OBJECT_LIMIT;
self.sync_model_space_theme(false);
self.persist_settings_if_changed();
Task::none()
@@ -8627,3 +8675,37 @@ mod free_text_entry_tests {
assert_eq!(app.text_entry_mode(), TextEntryMode::Command);
}
}
/// Set or clear one bit of `SELECTIONPREVIEW`.
///
/// The variable is a bitmask — 1 = rollover while no command is running,
/// 2 = rollover during a command — and the Options card gives each bit its own
/// checkbox. Worth a named function rather than two inline `|` / `& !`
/// expressions: the config comment used to describe these bits the wrong way
/// round, so the one place that names them is the place to be careful.
fn set_preview_bit(current: u8, bit: u8, enabled: bool) -> u8 {
if enabled {
current | bit
} else {
current & !bit
}
}
#[cfg(test)]
mod selection_preview_bit_tests {
use super::set_preview_bit;
#[test]
fn each_checkbox_owns_its_own_bit() {
// Both on, then clear the idle bit: the in-command bit must survive.
assert_eq!(set_preview_bit(3, 1, false), 2);
assert_eq!(set_preview_bit(3, 2, false), 1);
// Setting a bit that is already set changes nothing.
assert_eq!(set_preview_bit(3, 1, true), 3);
// From off, each bit turns on alone.
assert_eq!(set_preview_bit(0, 1, true), 1);
assert_eq!(set_preview_bit(0, 2, true), 2);
// Clearing both reaches 0, which is SELECTIONPREVIEW off.
assert_eq!(set_preview_bit(set_preview_bit(3, 1, false), 2, false), 0);
}
}
+6 -1
View File
@@ -186,7 +186,12 @@ impl OpenCADStudio {
self.language,
self.options_tab,
self.cursor_size,
self.pick_box,
crate::ui::window::options::SelectionPrefs {
pick_box: self.pick_box,
pick_add: self.pick_add,
pick_drag_rect: self.pick_drag_rect,
grip_object_limit: self.grip_object_limit,
},
self.cursor_type,
self.crosshair_color,
&self.crosshair_color_input,
+11 -1
View File
@@ -4120,6 +4120,16 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Pick box size" => Some(("common", "pick-box-size")),
"Controls both the visible selection box and the click aperture." => Some(("common", "controls-both-the-visible-selection-box-and-the-click-aperture")),
"Visual Effect Settings" => Some(("common", "visual-effect-settings")),
"Selection Modes" => Some(("common", "selection-modes")),
"Use Shift to add to selection (PICKADD)" => Some(("common", "use-shift-to-add-to-selection-pickadd")),
"Press and drag draws a rectangle instead of a lasso (PICKDRAG)" => Some(("common", "press-and-drag-draws-a-rectangle-instead-of-a-lasso-pickdrag")),
"Show selection effect (SELECTIONEFFECT)" => Some(("common", "show-selection-effect-selectioneffect")),
"Preview selection when no command is active" => Some(("common", "preview-selection-when-no-command-is-active")),
"Preview selection during a command" => Some(("common", "preview-selection-during-a-command")),
"Highlights the object under the cursor before it is picked (SELECTIONPREVIEW)." => Some(("common", "highlights-the-object-under-the-cursor-before-it-is-picked-selectionpreview")),
"Object limit for grips" => Some(("common", "object-limit-for-grips")),
"Unlimited" => Some(("common", "unlimited")),
"Past this many selected objects no grips are drawn; 0 removes the limit (GRIPOBJLIMIT)." => Some(("common", "past-this-many-selected-objects-no-grips-are-drawn-0-removes-the-limit-gripobjlimit")),
"Indicate selection area with transparent fill (SELECTIONAREA)" => Some(("common", "indicate-selection-area-with-transparent-fill-selectionarea")),
"Selection area opacity" => Some(("common", "selection-area-opacity")),
"Transparency of the window and crossing selection areas (SELECTIONAREAOPACITY)." => Some(("common", "transparency-of-the-window-and-crossing-selection-areas-selectionareaopacity")),
@@ -4406,7 +4416,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"LAYERSTATE: no saved states. Use LAYERSTATE SAVE <name>." => Some(("common", "layerstate-no-saved-states-use-layerstate-save-name")),
"Usage: COLOR <ByLayer|ByBlock|1-255|red|yellow|green|cyan|blue|magenta|white>" => Some(("common", "usage-color-bylayer-byblock-1-255-red-yellow-green-cyan-blue-magenta-white")),
"Usage: CHPROP <prop> <val> (props: LAYER COLOR LINETYPE LTSCALE)" => Some(("common", "usage-chprop-prop-val-props-layer-color-linetype-ltscale")),
"SETVAR: 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 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 | CLAYER CELTYPE TEXTSTYLE (read-only)" => Some(("common", "setvar-ltscale-celtscale-pdmode-pdsize-textsize-orthomode-fillmode-mirrtext-fram")),
"SETVAR: 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 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)" => Some(("common", "setvar-ltscale-celtscale-pdmode-pdsize-textsize-orthomode-fillmode-mirrtext-fram")),
"SETVAR: DIMCONTINUEMODE requires 0 or 1." => Some(("common", "setvar-dimcontinuemode-requires-0-or-1")),
"SETVAR: DONUTID requires a finite value greater than or equal to zero." => Some(("common", "setvar-donutid-requires-a-finite-value-greater-than-or-equal-to-zero")),
"SETVAR: DONUTOD requires a finite value greater than zero." => Some(("common", "setvar-donutod-requires-a-finite-value-greater-than-zero")),
+115 -3
View File
@@ -15,6 +15,24 @@ pub enum OptionsTab {
Selection,
}
/// The Selection-card settings that live on `UserSettings` rather than on the
/// model-space theme.
///
/// Gathered into one value instead of four more positional parameters: the
/// function already takes seventeen, and `pick_add` / `pick_drag_rect` are two
/// adjacent `bool`s that would swap silently at the call site.
#[derive(Clone, Copy)]
pub struct SelectionPrefs {
/// PICKBOX, 0..=50.
pub pick_box: i32,
/// PICKADD: true = a click adds to the selection.
pub pick_add: bool,
/// PICKDRAG: true = press-drag draws a rectangle instead of a lasso.
pub pick_drag_rect: bool,
/// GRIPOBJLIMIT, 0..=32767; 0 = no limit.
pub grip_object_limit: i32,
}
#[derive(Clone, Debug, PartialEq, Eq)]
struct Labelled<T> {
value: T,
@@ -36,7 +54,7 @@ pub fn view_window<'a>(
language: crate::i18n::Language,
active_tab: OptionsTab,
cursor_size: i32,
pick_box: i32,
selection: SelectionPrefs,
cursor_type: CursorType,
crosshair_color: Option<[u8; 3]>,
crosshair_color_input: &'a str,
@@ -544,10 +562,12 @@ pub fn view_window<'a>(
Space::new().height(10),
row![
text(crate::t!("Pick box size")).size(12).width(140),
slider(0..=50, pick_box.clamp(0, 50), Message::PickBoxChanged)
slider(0..=50, selection.pick_box.clamp(0, 50), Message::PickBoxChanged)
.step(1)
.width(Fill),
text(pick_box.clamp(0, 50).to_string()).size(11).width(44),
text(selection.pick_box.clamp(0, 50).to_string())
.size(11)
.width(44),
]
.spacing(10)
.align_y(iced::Center),
@@ -558,6 +578,31 @@ pub fn view_window<'a>(
.size(11)
.width(sizing.width),
Space::new().height(24),
text(crate::t!("Selection Modes")).size(15),
Space::new().height(10),
row![
// Worded the way AutoCAD words it, which is the inverse of PICKADD:
// ticked means a plain click replaces the selection and Shift adds.
iced::widget::checkbox(!selection.pick_add)
.on_toggle(Message::ShiftToAddToggled)
.size(15),
text(crate::t!("Use Shift to add to selection (PICKADD)")).size(12),
]
.spacing(8)
.align_y(iced::Center),
Space::new().height(10),
row![
iced::widget::checkbox(selection.pick_drag_rect)
.on_toggle(Message::PickDragRectToggled)
.size(15),
text(crate::t!(
"Press and drag draws a rectangle instead of a lasso (PICKDRAG)"
))
.size(12),
]
.spacing(8)
.align_y(iced::Center),
Space::new().height(24),
row![
text(crate::t!("Visual Effect Settings")).size(15),
Space::new().width(Fill),
@@ -626,6 +671,15 @@ pub fn view_window<'a>(
.spacing(10)
.align_y(iced::Center),
Space::new().height(10),
row![
iced::widget::checkbox(model_space.selection_effect)
.on_toggle(Message::SelectionEffectToggled)
.size(15),
text(crate::t!("Show selection effect (SELECTIONEFFECT)")).size(12),
]
.spacing(8)
.align_y(iced::Center),
Space::new().height(10),
row![
text(crate::t!("Selection highlight color")).size(12).width(140),
iced::widget::pick_list(
@@ -639,6 +693,34 @@ pub fn view_window<'a>(
.spacing(10)
.align_y(iced::Center),
Space::new().height(24),
text(crate::t!("Preview")).size(15),
Space::new().height(10),
row![
// SELECTIONPREVIEW is a bitmask; bit 1 is the idle rollover and
// bit 2 the one that runs while a command is gathering objects.
iced::widget::checkbox(model_space.selection_preview & 1 != 0)
.on_toggle(Message::SelectionPreviewIdleToggled)
.size(15),
text(crate::t!("Preview selection when no command is active")).size(12),
]
.spacing(8)
.align_y(iced::Center),
Space::new().height(10),
row![
iced::widget::checkbox(model_space.selection_preview & 2 != 0)
.on_toggle(Message::SelectionPreviewCommandToggled)
.size(15),
text(crate::t!("Preview selection during a command")).size(12),
]
.spacing(8)
.align_y(iced::Center),
Space::new().height(6),
text(crate::t!(
"Highlights the object under the cursor before it is picked (SELECTIONPREVIEW)."
))
.size(11)
.width(sizing.width),
Space::new().height(24),
text(crate::t!("Grip Settings")).size(15),
Space::new().height(10),
row![
@@ -657,6 +739,36 @@ pub fn view_window<'a>(
.spacing(10)
.align_y(iced::Center),
Space::new().height(10),
row![
text(crate::t!("Object limit for grips")).size(12).width(140),
// The slider covers the range where the setting is worth changing;
// the sysvar accepts AutoCAD's full 0..=32767, so a larger value
// set from the command line parks the handle at the end while the
// read-out keeps showing the real number.
slider(
0..=1000,
selection.grip_object_limit.clamp(0, 1000),
Message::GripObjectLimitChanged,
)
.step(1)
.width(Fill),
text(if selection.grip_object_limit == 0 {
crate::t!("Unlimited").into_owned()
} else {
selection.grip_object_limit.to_string()
})
.size(11)
.width(44),
]
.spacing(10)
.align_y(iced::Center),
Space::new().height(6),
text(crate::t!(
"Past this many selected objects no grips are drawn; 0 removes the limit (GRIPOBJLIMIT)."
))
.size(11)
.width(sizing.width),
Space::new().height(10),
row![
text(crate::t!("Unselected grip color")).size(12).width(140),
iced::widget::pick_list(