Merge PR #1110: expose selection settings

# Conflicts:
#	src/app/mod.rs
#	src/app/properties.rs
#	src/app/settings.rs
#	src/app/update/file.rs
#	src/app/view/modal.rs
#	src/ui/window/options.rs
This commit is contained in:
Hakan Seven committed 2026-09-09 14:40:37 +03:00
commit 9fe6033304
34 files changed
+616 -34

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;
+26 -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 {
@@ -2982,6 +3000,13 @@ mod tests {
// SELECTIONPREVIEW
let _ = app.run_command_line("SETVAR SELECTIONPREVIEW 2");
assert_eq!(app.model_space.selection_preview, 2);
let _ = app.run_command_line("SETVAR GRIPOBJLIMIT 0");
assert_eq!(app.grip_object_limit, 0);
let _ = app.run_command_line("GRIPOBJLIMIT 32767");
assert_eq!(app.grip_object_limit, 32767);
let _ = app.run_command_line("GRIPOBJLIMIT 32768");
assert_eq!(app.grip_object_limit, 32767);
}
#[test]
+18
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" => {
@@ -1547,6 +1555,16 @@ mod tests {
app
}
#[test]
fn options_and_its_alias_dispatch_from_the_start_page() {
let mut app = fresh_app();
let i = app.active_tab;
assert!(app.dispatch_view("OPTIONS", i).is_some());
assert!(app.dispatch_view("OP", i).is_some());
assert!(crate::app::commands::start_allowed("OPTIONS"));
assert!(crate::app::commands::start_allowed("OP"));
}
#[test]
fn hatchtoback_no_hatches_warns_and_noop() {
let mut app = fresh_app();
+1 -1
View File
@@ -270,7 +270,7 @@ 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 bitmask: 1 = idle, 2 = during a command, 3 = both.
pub selection_preview: u8,
/// GRIPSIZE: grip marker half-size in pixels (1–25, default 5).
pub grip_size: u8,
+17
View File
@@ -504,6 +504,9 @@ pub(super) struct OpenCADStudio {
double_click_block_refedit: bool,
/// Open ATTEDIT when double-clicking a block with attributes.
double_click_block_attedit: bool,
/// 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.
@@ -1985,6 +1988,19 @@ 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"; this is 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.
@@ -3381,6 +3397,7 @@ impl OpenCADStudio {
pick_box: 3,
double_click_block_refedit: false,
double_click_block_attedit: true,
grip_object_limit: settings::DEFAULT_GRIP_OBJECT_LIMIT,
cursor_type: settings::CursorType::Crosshair,
crosshair_color: None,
crosshair_color_input: String::new(),
+72 -6
View File
@@ -2485,8 +2485,11 @@ handles={handles_ms:.1} panel={:.1} ribbon={ribbon_ms:.1} tail={:.1} selected={}
}
}
const GRIP_OBJECT_LIMIT: usize = 100;
/// Rebuild the cached selected_grips from the current entity selection.
///
/// 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| {
@@ -2514,7 +2517,10 @@ handles={handles_ms:.1} panel={:.1} ribbon={ribbon_ms:.1} tail={:.1} selected={}
.then(|| selected[0].0);
let mut grips = Vec::new();
let mut handles = Vec::new();
let selected = if selected.len() > Self::GRIP_OBJECT_LIMIT {
// Avoid rebuilding every entity's grip markers above the configured
// limit. Zero disables the limit.
let limit = self.grip_object_limit;
let selected = if limit > 0 && selected.len() > limit as usize {
Vec::new()
} else {
selected
@@ -4107,9 +4113,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| {
@@ -4130,15 +4139,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)]
+7
View File
@@ -145,6 +145,9 @@ 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.
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.
@@ -164,6 +167,9 @@ pub struct UserSettings {
pub double_click_block_refedit: bool,
/// When true, double-clicking a block with attributes opens ATTEDIT.
pub double_click_block_attedit: bool,
/// GRIPOBJLIMIT: past this many selected objects, no grips are drawn at
/// all. 0 means no limit. The drawing 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.
@@ -313,6 +319,7 @@ impl Default for UserSettings {
pick_box: 3,
double_click_block_refedit: false,
double_click_block_attedit: true,
grip_object_limit: DEFAULT_GRIP_OBJECT_LIMIT,
cursor_type: CursorType::Crosshair,
crosshair_color: None,
lineweight_display_scale: 100,
+2
View File
@@ -367,6 +367,7 @@ impl OpenCADStudio {
pick_box: self.pick_box,
double_click_block_refedit: self.double_click_block_refedit,
double_click_block_attedit: self.double_click_block_attedit,
grip_object_limit: self.grip_object_limit,
cursor_type: self.cursor_type,
crosshair_color: self.crosshair_color,
lineweight_display_scale: self.lineweight_display_scale,
@@ -422,6 +423,7 @@ impl OpenCADStudio {
self.pick_box = s.pick_box.clamp(0, 50);
self.double_click_block_refedit = s.double_click_block_refedit;
self.double_click_block_attedit = s.double_click_block_attedit;
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
+55
View File
@@ -6137,6 +6137,48 @@ 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()
}
// "Use Shift to add" is the inverse of PICKADD.
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;
@@ -6149,6 +6191,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()
@@ -8661,3 +8707,12 @@ mod free_text_entry_tests {
assert_eq!(app.text_entry_mode(), TextEntryMode::Command);
}
}
/// Set or clear one bit of `SELECTIONPREVIEW`.
fn set_preview_bit(current: u8, bit: u8, enabled: bool) -> u8 {
if enabled {
current | bit
} else {
current & !bit
}
}
+54
View File
@@ -5778,3 +5778,57 @@ properties={:.1}ms picked={}",
Task::none()
}
}
#[cfg(test)]
mod selection_preview_tests {
use super::*;
use crate::app::{HoverDwell, OpenCADStudio, HOVER_DWELL_MS};
/// Drive one settled rollover pick over a line and report what the scene
/// ended up highlighting.
fn rollover_hits(preview: u8) -> bool {
let mut app = OpenCADStudio::new_for_test();
app.automation_op(r#"{"op":"new"}"#);
let i = app.active_tab;
app.model_space.selection_preview = preview;
let _ = app.run_command_line("LINE 0,0 10,10");
app.tabs[i].scene.selection.borrow_mut().vp_size = (800.0, 600.0);
let _ = app.run_command_line("ZOOM EXTENTS");
// The pick only runs once the cursor has been still for the dwell
// window, so the arming timestamp is backdated past it.
app.hover_dwell = Some(HoverDwell {
last_move_at: Instant::now()
- std::time::Duration::from_millis(HOVER_DWELL_MS as u64 * 2),
point: iced::Point::new(400.0, 300.0),
tile_size: (800.0, 600.0),
tab: i,
});
let _ = app.on_hover_dwell_tick();
app.tabs[i].scene.hover_highlight.is_some()
}
/// Bit 1 of `SELECTIONPREVIEW` is the rollover that runs with no command
/// active, and the Options card gives it a checkbox. The config comment
/// used to describe the bits the wrong way round, so which bit does what
/// is worth asserting rather than reading.
#[test]
fn the_idle_rollover_follows_bit_one() {
assert!(
rollover_hits(1),
"bit 1 set: the line under the cursor must be highlighted",
);
assert!(
rollover_hits(3),
"both bits set: still highlighted",
);
assert!(
!rollover_hits(0),
"preview off: nothing may be highlighted",
);
assert!(
!rollover_hits(2),
"only the in-command bit: the idle rollover stays off",
);
}
}
+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.double_click_block_refedit,
self.double_click_block_attedit,
self.cursor_type,
+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")),
+111 -3
View File
@@ -16,6 +16,24 @@ pub enum OptionsTab {
Drawing,
}
/// 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,
@@ -37,7 +55,7 @@ pub fn view_window<'a>(
language: crate::i18n::Language,
active_tab: OptionsTab,
cursor_size: i32,
pick_box: i32,
selection: SelectionPrefs,
double_click_block_refedit: bool,
double_click_block_attedit: bool,
cursor_type: CursorType,
@@ -547,10 +565,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),
@@ -561,6 +581,30 @@ 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![
// Checked is the inverse of PICKADD: plain clicks replace 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),
@@ -629,6 +673,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(
@@ -642,6 +695,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![
@@ -660,6 +741,33 @@ 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),
// Values above the slider range remain available through SETVAR.
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(