Ctrl+click on an already selected header or cell deselects it (#359)

This commit is contained in:
MarekSpodBiedry 2026-10-11 19:10:49 +02:00
parent fa33d0b90f
commit 568b754211
1 changed files with 35 additions and 6 deletions

View File

@ -1479,9 +1479,7 @@ fn interact(app: &mut SheetApp, ui: &mut egui::Ui, resp: &egui::Response, geo: &
d.selection.anchor = sel.anchor;
}
} else if mods.command {
let mut ranges: Vec<String> = sel.ranges.iter().map(|r| r.a1()).collect();
ranges.push(c.a1());
let _ = app.session.run("selection.set", json!({"range": ranges.join(","), "active": c.a1()}));
select_ranges(app, RangeRef::cell(c), true, c);
} else {
let _ = app.session.run("selection.set", json!({"cell": c.a1()}));
if primary_clicked && wb.styles.get(sh.style_id(c)).num_fmt.as_str() == "checkbox" {
@ -1903,11 +1901,32 @@ pub fn follow_link(app: &mut SheetApp, target: &str) {
}
}
/// The areas after a click: with `add` (Ctrl) the clicked area is added, or removed when it is already
/// selected (unless it is the only one, so the selection never ends up empty).
fn toggled_ranges(mut ranges: Vec<String>, clicked: String, add: bool) -> Vec<String> {
if !add {
return vec![clicked];
}
if ranges.len() > 1 && ranges.contains(&clicked) {
ranges.retain(|x| *x != clicked);
} else if !ranges.contains(&clicked) {
ranges.push(clicked);
}
ranges
}
fn select_ranges(app: &mut SheetApp, r: RangeRef, add: bool, active: CellRef) {
let mut ranges: Vec<String> =
let current: Vec<String> =
if add { app.session.active().map(|d| d.selection.ranges.iter().map(|x| x.a1()).collect()).unwrap_or_default() } else { vec![] };
ranges.push(r.a1());
let _ = app.session.run("selection.set", json!({"range": ranges.join(","), "active": active.a1()}));
let clicked = r.a1();
let ranges = toggled_ranges(current, clicked.clone(), add);
// A removed area takes the active cell with it; `selection.set` then puts it at the start of the last area.
let params = if ranges.contains(&clicked) {
json!({"range": ranges.join(","), "active": active.a1()})
} else {
json!({"range": ranges.join(",")})
};
let _ = app.session.run("selection.set", params);
}
/// Grid keyboard handling when no text field has focus.
@ -2590,6 +2609,16 @@ mod pane_tests {
assert!(!range_in_pane(sel, corner.0, corner.1));
}
#[test]
fn ctrl_click_on_a_selected_header_deselects_it() {
let s = |v: &[&str]| v.iter().map(|x| x.to_string()).collect::<Vec<_>>();
assert_eq!(toggled_ranges(s(&["A:A", "C:C"]), "C:C".into(), true), s(&["A:A"]));
assert_eq!(toggled_ranges(s(&["A:A"]), "C:C".into(), true), s(&["A:A", "C:C"]));
assert_eq!(toggled_ranges(s(&["A:A"]), "A:A".into(), true), s(&["A:A"]));
assert_eq!(toggled_ranges(s(&["A1", "B2", "3:3"]), "B2".into(), true), s(&["A1", "3:3"]));
assert_eq!(toggled_ranges(s(&["A:A", "C:C"]), "C:C".into(), false), s(&["C:C"]));
}
#[test]
fn selection_across_the_freeze_line_is_drawn_in_both_panes() {
let sel = rng(5, 0, 7, 6);