mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-10 20:40:05 -06:00
Merge PR #3226
This commit is contained in:
@@ -137,6 +137,23 @@ pub(super) fn selection_next(s: &mut Session, p: &Json) -> Result<Json> {
|
||||
Ok(json!({"active": d.selection.active.a1()}))
|
||||
}
|
||||
|
||||
pub(super) fn selection_next_corner(s: &mut Session, _: &Json) -> Result<Json> {
|
||||
let d = s.doc_mut()?;
|
||||
let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
|
||||
// A corner in a merged cell lands on its top-left cell. One in a hidden row or column, one
|
||||
// the sheet's protection doesn't let you select, and a merged cell that starts outside the
|
||||
// selected area are skipped.
|
||||
let area = d.selection.current();
|
||||
let stop = |c: CellRef| {
|
||||
Some(sheet.merge_at(c).map_or(c, |m| m.start))
|
||||
.filter(|c| area.contains(*c) && !sheet.is_row_hidden(c.row) && !sheet.is_col_hidden(c.col) && cell_selectable(&d.wb, sheet, *c))
|
||||
};
|
||||
let mut next = d.selection.clone();
|
||||
next.next_corner_by(stop);
|
||||
d.selection = next;
|
||||
Ok(json!({"active": d.selection.active.a1(), "selection": d.selection.a1()}))
|
||||
}
|
||||
|
||||
pub(super) fn select_all(s: &mut Session, _: &Json) -> Result<Json> {
|
||||
let d = s.doc_mut()?;
|
||||
let Some(sheet) = d.wb.active() else { return Err(EngineError::NoDocument) };
|
||||
|
||||
@@ -439,3 +439,75 @@ fn replace_case_insensitive_keeps_text_when_folding_moves_boundaries() {
|
||||
let sh = s.doc().unwrap().wb.active().unwrap();
|
||||
assert_eq!(sh.value(CellRef::new(0, 0)), Value::text(text));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_corner_of_selection_goes_clockwise_and_anchors_there() {
|
||||
// #1442, as Excel does it: B2 → D2 → D4 → B4 → B2, the area stays selected, and Shift+arrows
|
||||
// then move the corner opposite the new active cell.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("selection.set", json!({"range": "B2:D4", "active": "B2"})).unwrap();
|
||||
let mut seen = Vec::new();
|
||||
for _ in 0..4 {
|
||||
let r = s.execute("selection.nextCorner", json!({})).unwrap();
|
||||
assert_eq!(r["selection"], "B2:D4");
|
||||
seen.push(r["active"].as_str().unwrap().to_string());
|
||||
}
|
||||
assert_eq!(seen, ["D2", "D4", "B4", "B2"]);
|
||||
s.execute("selection.nextCorner", json!({})).unwrap();
|
||||
s.execute("selection.nextCorner", json!({})).unwrap();
|
||||
s.execute("selection.move", json!({"dr": 0, "dc": 1, "extend": true})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().selection.current().a1(), "C2:D4");
|
||||
// One row goes end to end; a single cell stays.
|
||||
s.execute("selection.set", json!({"range": "B2:D2", "active": "B2"})).unwrap();
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "D2");
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "B2");
|
||||
s.execute("selection.set", json!({"range": "C5"})).unwrap();
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "C5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_corner_skips_corners_the_sheet_protection_does_not_let_you_select() {
|
||||
// With locked cells unselectable, Ctrl+. goes on to the next corner it may select.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("home.lockCell", json!({"range": "B2:C3", "on": false})).unwrap();
|
||||
s.execute("selection.set", json!({"range": "B2:D3", "active": "B2"})).unwrap();
|
||||
s.execute("review.protectSheet", json!({"selectLockedCells": false})).unwrap();
|
||||
let r = s.execute("selection.nextCorner", json!({})).unwrap();
|
||||
assert_eq!((r["active"].as_str(), r["selection"].as_str()), (Some("B3"), Some("B2:D3")), "D2 and D3 are locked");
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "B2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_corner_stops_once_on_a_merged_corner_at_its_top_left_cell() {
|
||||
// A corner inside a merged cell is that merged cell, whose active cell is its top-left one.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("home.mergeCells", json!({"range": "C2:D2"})).unwrap();
|
||||
s.execute("selection.set", json!({"range": "B2:D3", "active": "B2"})).unwrap();
|
||||
let seen: Vec<String> = (0..4).map(|_| s.execute("selection.nextCorner", json!({})).unwrap()["active"].as_str().unwrap().to_string()).collect();
|
||||
assert_eq!(seen, ["C2", "D3", "B3", "B2"]);
|
||||
// A selection that is one merged cell has no other corner.
|
||||
s.execute("selection.set", json!({"range": "C2:D2", "active": "C2"})).unwrap();
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "C2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn next_corner_skips_hidden_corners_and_stays_inside_the_selected_area() {
|
||||
// A corner in a hidden column is passed over, as keyboard moves pass over hidden cells.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("home.hideColumns", json!({"cols": "D:D"})).unwrap();
|
||||
s.execute("selection.set", json!({"range": "B2:D3", "active": "B2"})).unwrap();
|
||||
assert_eq!(s.execute("selection.nextCorner", json!({})).unwrap()["active"], "B3");
|
||||
// Column C selected with B1:C1 merged: its top corner's merged cell starts in column B,
|
||||
// outside the selection, so the active cell doesn't go there.
|
||||
let mut s = Session::new();
|
||||
s.new_workbook();
|
||||
s.execute("home.mergeCells", json!({"range": "B1:C1"})).unwrap();
|
||||
s.execute("selection.set", json!({"cell": "C5"})).unwrap();
|
||||
s.execute("selection.column", json!({})).unwrap();
|
||||
let r = s.execute("selection.nextCorner", json!({})).unwrap();
|
||||
assert_eq!((r["active"].as_str(), r["selection"].as_str()), (Some("C1048576"), Some("C:C")));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user