feat: add 't' key to transpose row/column axes
Pressing 't' swaps all Row-axis categories to Column and all Column-axis categories to Row, leaving Page categories unchanged. Selection and scroll offsets are reset to (0,0). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -283,6 +283,11 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Grid transpose ─────────────────────────────────────────────
|
||||
(KeyCode::Char('t'), KeyModifiers::NONE) => {
|
||||
self.model.active_view_mut().transpose_axes();
|
||||
}
|
||||
|
||||
// ── Tile movement ──────────────────────────────────────────────
|
||||
// T = enter tile select mode (single key, no Ctrl needed)
|
||||
(KeyCode::Char('T'), _) => {
|
||||
@ -1135,7 +1140,7 @@ impl App {
|
||||
/// Hint text for the status bar (context-sensitive)
|
||||
pub fn hint_text(&self) -> &'static str {
|
||||
match &self.mode {
|
||||
AppMode::Normal => "hjkl:nav Enter:advance i:edit x:clear /:search F/C/V:panels T:tiles [:]:page ::cmd",
|
||||
AppMode::Normal => "hjkl:nav Enter:advance i:edit x:clear t:transpose /:search F/C/V:panels T:tiles [:]:page ::cmd",
|
||||
AppMode::Editing { .. } => "Enter:commit Esc:cancel",
|
||||
AppMode::FormulaPanel => "n:new d:delete jk:nav Esc:back",
|
||||
AppMode::FormulaEdit { .. } => "Enter:save Esc:cancel — type: Name = expression",
|
||||
|
||||
@ -112,6 +112,18 @@ impl View {
|
||||
self.hidden_items.get(cat_name).map(|s| s.contains(item_name)).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Swap all Row categories to Column and all Column categories to Row.
|
||||
/// Page categories are unaffected.
|
||||
pub fn transpose_axes(&mut self) {
|
||||
let rows: Vec<String> = self.categories_on(Axis::Row).iter().map(|s| s.to_string()).collect();
|
||||
let cols: Vec<String> = self.categories_on(Axis::Column).iter().map(|s| s.to_string()).collect();
|
||||
for cat in &rows { self.set_axis(cat, Axis::Column); }
|
||||
for cat in &cols { self.set_axis(cat, Axis::Row); }
|
||||
self.selected = (0, 0);
|
||||
self.row_offset = 0;
|
||||
self.col_offset = 0;
|
||||
}
|
||||
|
||||
/// Cycle axis for a category: Row → Column → Page → Row
|
||||
pub fn cycle_axis(&mut self, cat_name: &str) {
|
||||
let next = match self.axis_of(cat_name) {
|
||||
@ -171,6 +183,34 @@ mod tests {
|
||||
assert_eq!(v.categories_on(Axis::Page), vec!["Time"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_axes_swaps_row_and_column() {
|
||||
let mut v = view_with_cats(&["Region", "Product"]);
|
||||
// Default: Region=Row, Product=Column
|
||||
assert_eq!(v.axis_of("Region"), Axis::Row);
|
||||
assert_eq!(v.axis_of("Product"), Axis::Column);
|
||||
v.transpose_axes();
|
||||
assert_eq!(v.axis_of("Region"), Axis::Column);
|
||||
assert_eq!(v.axis_of("Product"), Axis::Row);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_axes_leaves_page_categories_unchanged() {
|
||||
let mut v = view_with_cats(&["Region", "Product", "Time"]);
|
||||
// Default: Region=Row, Product=Column, Time=Page
|
||||
v.transpose_axes();
|
||||
assert_eq!(v.axis_of("Time"), Axis::Page);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transpose_axes_is_its_own_inverse() {
|
||||
let mut v = view_with_cats(&["Region", "Product"]);
|
||||
v.transpose_axes();
|
||||
v.transpose_axes();
|
||||
assert_eq!(v.axis_of("Region"), Axis::Row);
|
||||
assert_eq!(v.axis_of("Product"), Axis::Column);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "axis_of called for category not registered")]
|
||||
fn axis_of_unknown_category_panics() {
|
||||
|
||||
Reference in New Issue
Block a user