forked from 1b-agent/opencadstudio-upstream
fix(layers): VP glyph and filter-aware shift ranges
VP-freeze columns used the global-freeze glyph; shift-ranges swept filtered-out rows into bulk ops. Glyph swapped to layer_vp_freeze (ribbon precedent); new visible_indices helper shared by the view loop and the shift arm. Test: shift_select_skips_filtered_out_rows (RED: hidden row in range). Full suite: 2028 passed / 0 failed / 25 ignored.
This commit is contained in:
1 parent
ea7e79bfab
commit
6c3cfb55b5
3 files changed
+53
-8
No files matched your search
@@ -4787,6 +4787,35 @@ mod layer_name_target_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn shift_select_skips_filtered_out_rows() {
|
||||||
|
// #50: rows hidden by the live filter must not join a shift-range.
|
||||||
|
// Sorted panel: A1(0), B9(1), C1(2); filter "1" hides B9.
|
||||||
|
let mut app = OpenCADStudio::new_for_test();
|
||||||
|
app.automation_op(r#"{"op":"new"}"#);
|
||||||
|
for new in ["C1", "B9", "A1"] {
|
||||||
|
let _ = app.on_layer_new();
|
||||||
|
let fresh = app.tabs[app.active_tab].layers.edit_buf.clone();
|
||||||
|
rename_layer(&mut app, &fresh, new);
|
||||||
|
}
|
||||||
|
let i = app.active_tab;
|
||||||
|
app.tabs[i].layers.filter = "1".to_string();
|
||||||
|
let _ = app.update(Message::LayerSelect("A1".to_string()));
|
||||||
|
app.shift_down = true;
|
||||||
|
let _ = app.update(Message::LayerSelect("C1".to_string()));
|
||||||
|
app.shift_down = false;
|
||||||
|
let multi = app.tabs[i].layers.selected_multi.clone();
|
||||||
|
let names: Vec<String> = multi
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&x| app.tabs[i].layers.layers.get(x).map(|l| l.name.clone()))
|
||||||
|
.collect();
|
||||||
|
assert!(
|
||||||
|
!names.contains(&"B9".to_string()),
|
||||||
|
"hidden row must not join the range, got {names:?}"
|
||||||
|
);
|
||||||
|
assert!(names.contains(&"A1".to_string()) && names.contains(&"C1".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn layer_toggle_visible_unknown_name_is_noop() {
|
fn layer_toggle_visible_unknown_name_is_noop() {
|
||||||
let mut app = app_with_zulu_alpha();
|
let mut app = app_with_zulu_alpha();
|
||||||
|
|||||||
@@ -3590,10 +3590,14 @@ impl OpenCADStudio {
|
|||||||
panel.selected_multi.push(idx);
|
panel.selected_multi.push(idx);
|
||||||
}
|
}
|
||||||
} else if shift {
|
} else if shift {
|
||||||
// Shift-click selects the range from the anchor to here.
|
// Shift-click selects the range from the anchor to here —
|
||||||
|
// visible rows only, so filtered-out rows between them
|
||||||
|
// cannot join bulk edits/deletes (#50).
|
||||||
let anchor = panel.selected.unwrap_or(idx);
|
let anchor = panel.selected.unwrap_or(idx);
|
||||||
let (lo, hi) = (anchor.min(idx), anchor.max(idx));
|
let (lo, hi) = (anchor.min(idx), anchor.max(idx));
|
||||||
panel.selected_multi = (lo..=hi).collect();
|
let visible: std::collections::HashSet<usize> =
|
||||||
|
panel.visible_indices().into_iter().collect();
|
||||||
|
panel.selected_multi = (lo..=hi).filter(|r| visible.contains(r)).collect();
|
||||||
} else if !panel.selected_multi.contains(&idx) {
|
} else if !panel.selected_multi.contains(&idx) {
|
||||||
// Plain click collapses to this row — but NOT when it is
|
// Plain click collapses to this row — but NOT when it is
|
||||||
// already part of a multi-selection, so clicking a property
|
// already part of a multi-selection, so clicking a property
|
||||||
|
|||||||
+18
-6
@@ -327,6 +327,21 @@ impl LayerPanel {
|
|||||||
self.linetype_items = items;
|
self.linetype_items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Row indices passing the live name filter — the rows the view actually
|
||||||
|
/// draws. Index math over "what the user sees" (shift-ranges) must use
|
||||||
|
/// these; raw `selected_multi` ranges would sweep in hidden rows (#50).
|
||||||
|
pub fn visible_indices(&self) -> Vec<usize> {
|
||||||
|
let filter = self.filter.to_lowercase();
|
||||||
|
self.layers
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter(|(_, layer)| {
|
||||||
|
filter.is_empty() || layer.name.to_lowercase().contains(&filter)
|
||||||
|
})
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
/// Render the layer panel as the full content of its own OS window.
|
/// Render the layer panel as the full content of its own OS window.
|
||||||
pub fn view_window(
|
pub fn view_window(
|
||||||
&self,
|
&self,
|
||||||
@@ -453,11 +468,8 @@ impl LayerPanel {
|
|||||||
|
|
||||||
// ── Layer rows ────────────────────────────────────────────────────
|
// ── Layer rows ────────────────────────────────────────────────────
|
||||||
let mut rows_col = column![].spacing(0);
|
let mut rows_col = column![].spacing(0);
|
||||||
let filter = self.filter.to_lowercase();
|
for i in self.visible_indices() {
|
||||||
for (i, layer) in self.layers.iter().enumerate() {
|
let layer = &self.layers[i];
|
||||||
if !filter.is_empty() && !layer.name.to_lowercase().contains(&filter) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Highlight every selected row; show the editable combos only on the
|
// Highlight every selected row; show the editable combos only on the
|
||||||
// anchor (a single shared combo state can't drive several rows).
|
// anchor (a single shared combo state can't drive several rows).
|
||||||
let is_anchor = self.selected == Some(i);
|
let is_anchor = self.selected == Some(i);
|
||||||
@@ -896,7 +908,7 @@ fn layer_row<'a>(
|
|||||||
// Per-viewport freeze columns
|
// Per-viewport freeze columns
|
||||||
for (vp_idx, _vp_col) in vp_cols.iter().enumerate() {
|
for (vp_idx, _vp_col) in vp_cols.iter().enumerate() {
|
||||||
let is_vp_frozen = layer.vp_frozen.get(vp_idx).copied().unwrap_or(false);
|
let is_vp_frozen = layer.vp_frozen.get(vp_idx).copied().unwrap_or(false);
|
||||||
let vp_frz_svg = crate::ui::icons::layer_freeze(is_vp_frozen);
|
let vp_frz_svg = crate::ui::icons::layer_vp_freeze(is_vp_frozen);
|
||||||
row_content = row_content.push(
|
row_content = row_content.push(
|
||||||
container(svg_btn(
|
container(svg_btn(
|
||||||
vp_frz_svg,
|
vp_frz_svg,
|
||||||
|
|||||||
Reference in new issue
Block a user