feat(layers): multi-select layers in the Layer Manager (#236)

The Layer Manager only allowed one layer at a time. Now:

- Ctrl/Cmd-click toggles a layer in the selection; Shift-click selects
  the range from the anchor; a plain click selects just one.
- Colour, linetype and lineweight changes apply to every selected layer.
- Delete removes all selected layers at once (reusing the #237 non-empty
  warning, now pluralised); layer "0" and the current layer are skipped.

Selection is stored as row indices but re-resolved by name across sorts
and layer-table rebuilds, so it never points at the wrong rows. Modifier
state is tracked via a new SetModifiers message (shift + ctrl/cmd).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-07-01 17:44:19 +03:00
commit ada9952f06
6 changed files with 178 additions and 73 deletions

View file

@ -348,10 +348,11 @@ pub(super) struct OpenCADStudio {
aec_drop_count: usize,
/// Set once the user confirms overwriting an existing file on Save-As.
overwrite_acknowledged: bool,
/// Layer awaiting a "delete non-empty layer" confirmation: `(name, object
/// count)`. Set when the user deletes a layer that still has objects; the
/// warning modal reads it, and confirming erases those objects too.
layer_delete_pending: Option<(String, usize)>,
/// Layers awaiting a "delete non-empty layer(s)" confirmation: `(names,
/// total object count)`. Set when the user deletes one or more layers that
/// still have objects; the warning modal reads it, and confirming erases
/// those objects too.
layer_delete_pending: Option<(Vec<String>, usize)>,
/// Pixel offset of the active modal from screen-centre (drag-to-move).
/// Reset to zero whenever a modal closes so each dialog opens centred.
modal_offset: iced::Vector,
@ -433,6 +434,9 @@ pub(super) struct OpenCADStudio {
/// removes the picked entity from the selection). Tracked from keyboard
/// modifier-change events since mouse click messages carry no modifiers.
shift_down: bool,
/// True while Ctrl/Cmd is held — drives Ctrl-click multi-select in the
/// Layer Manager. Tracked from modifier-change events.
ctrl_down: bool,
/// Open in-place MText editor (toolbar + text area + live preview), if any.
mtext_editor: Option<mtext_editor::MTextEditorState>,
/// Open in-place single-line TEXT editor (plain text-entry box), if any.
@ -1546,7 +1550,7 @@ pub enum Message {
InvertSelection,
/// Keyboard modifier state changed — tracks whether Shift is held so the
/// pick path can do subtractive (Shift+click) selection.
SetShiftDown(bool),
SetModifiers { shift: bool, ctrl: bool },
// ── In-place MText editor ───────────────────────────────────────────
/// Text-area edit action from the multi-line editor widget.
MTextEdit(iced::widget::text_editor::Action),
@ -1991,6 +1995,7 @@ impl OpenCADStudio {
clipboard_centroid: glam::DVec3::ZERO,
clipboard_deps: ClipboardDeps::default(),
shift_down: false,
ctrl_down: false,
mtext_editor: None,
text_inline: None,
layout_context_menu: None,