fix(core): allow leaving stash-only overview piles
- commit
- 23fbc2c439f31ec12908ca6a5d250a7ad9df7f85
- author
- brice · · 2026-10-05 20:56 UTC
- parent
- ad3ecff
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email - refs
- feat/overview-stash-navigation
4 files · +15 -5
M docs/config.md +2 -2
| @@ -255,9 +255,9 @@ The drop can be on another monitor; focus follows the window. There, the middle | |||
| 255 | 255 | | `return` | Show the selected front card; picking a hidden column moves the maximization to it. Other rows shown keep their focus, maximization and float order | | |
| 256 | 256 | | `escape` | Return to the original workspace; every row shown keeps its focus, maximization and float order | | |
| 257 | 257 | ||
| 258 | - | The focus binds (`focus-column-left/right`, `focus-window-up/down`, `cmd+arrows` by default) move the selection like these keys. In the overview, `focus-workspace-up/down` also traverse cards before crossing workspace rows, including named ones. | |
| 258 | + | The focus binds (`focus-column-left/right`, `focus-window-up/down`, `cmd+arrows` by default) move the selection like these keys. In the overview, `focus-workspace-up/down` also traverse cards before crossing workspace rows, including named ones, except inside a selected stash pile. | |
| 259 | 259 | ||
| 260 | - | Each workspace's [stash](#stash), hidden or not, shows as a pile of cards on the left of its row, which stays centred unless it would overlap the pile: its selected window in front, up to three others behind it, dimmed. In the pile, `k` / `j` or up/down browse stash cards and stop at either end without changing workspace. Right (`l`) leaves the pile immediately when the workspace has a main card; left (`h`) stays in the pile. Browsing brings the selected card to the front without reordering the stash. `return` or a click on a card closes the overview with the stash shown on that window. | |
| 260 | + | Each workspace's [stash](#stash), hidden or not, shows as a pile of cards on the left of its row, which stays centred unless it would overlap the pile: its selected window in front, up to three others behind it, dimmed. In the pile, `k` / `j` or up/down browse stash cards and stop at either end without changing workspace. Right (`l`) leaves the pile immediately; left (`h`) stays in the pile. On a stash-only workspace, right deselects the pile so up/down can change workspace. Browsing brings the selected card to the front without reordering the stash. `return` or a click on a card closes the overview with the stash shown on that window. | |
| 261 | 261 | ||
| 262 | 262 | A four-finger swipe up opens the overview, or hides the [stash](#stash) when it is shown, and a swipe down closes the overview on the selection (with the overview closed, a swipe down shows or hides the stash), whatever `touchpad.natural-scroll` says. Two-finger scrolling and the mouse wheel move the selection: left and right through the columns, up and down through stack cards before crossing workspaces, following `touchpad.natural-scroll` for two-finger scrolling and `mouse.natural-scroll` for the wheel. A two-finger scroll sideways moves it to the next column after a short distance, then one column per longer distance, and stays on the columns until the fingers lift; up or down it moves one step per scroll, so a card in a stack is easy to pick. The wheel moves one step per notch. A three-finger swipe moves it one step when the fingers lift: left/right enters or leaves the stash pile, and up/down browses its cards while it is selected. Two-finger scrolling, the wheel and keyboard navigation follow the same pile boundaries. | |
| 263 | 263 | ||
| @@ -255,9 +255,9 @@ The drop can be on another monitor; focus follows the window. There, the middle | |||||
| 255 | | `return` | Show the selected front card; picking a hidden column moves the maximization to it. Other rows shown keep their focus, maximization and float order | | 255 | | `return` | Show the selected front card; picking a hidden column moves the maximization to it. Other rows shown keep their focus, maximization and float order | | ||
| 256 | | `escape` | Return to the original workspace; every row shown keeps its focus, maximization and float order | | 256 | | `escape` | Return to the original workspace; every row shown keeps its focus, maximization and float order | | ||
| 257 | 257 | ||||
| 258 | - | The focus binds (`focus-column-left/right`, `focus-window-up/down`, `cmd+arrows` by default) move the selection like these keys. In the overview, `focus-workspace-up/down` also traverse cards before crossing workspace rows, including named ones. | 258 | + | The focus binds (`focus-column-left/right`, `focus-window-up/down`, `cmd+arrows` by default) move the selection like these keys. In the overview, `focus-workspace-up/down` also traverse cards before crossing workspace rows, including named ones, except inside a selected stash pile. |
| 259 | 259 | ||||
| 260 | - | Each workspace's [stash](#stash), hidden or not, shows as a pile of cards on the left of its row, which stays centred unless it would overlap the pile: its selected window in front, up to three others behind it, dimmed. In the pile, `k` / `j` or up/down browse stash cards and stop at either end without changing workspace. Right (`l`) leaves the pile immediately when the workspace has a main card; left (`h`) stays in the pile. Browsing brings the selected card to the front without reordering the stash. `return` or a click on a card closes the overview with the stash shown on that window. | 260 | + | Each workspace's [stash](#stash), hidden or not, shows as a pile of cards on the left of its row, which stays centred unless it would overlap the pile: its selected window in front, up to three others behind it, dimmed. In the pile, `k` / `j` or up/down browse stash cards and stop at either end without changing workspace. Right (`l`) leaves the pile immediately; left (`h`) stays in the pile. On a stash-only workspace, right deselects the pile so up/down can change workspace. Browsing brings the selected card to the front without reordering the stash. `return` or a click on a card closes the overview with the stash shown on that window. |
| 261 | 261 | ||||
| 262 | A four-finger swipe up opens the overview, or hides the [stash](#stash) when it is shown, and a swipe down closes the overview on the selection (with the overview closed, a swipe down shows or hides the stash), whatever `touchpad.natural-scroll` says. Two-finger scrolling and the mouse wheel move the selection: left and right through the columns, up and down through stack cards before crossing workspaces, following `touchpad.natural-scroll` for two-finger scrolling and `mouse.natural-scroll` for the wheel. A two-finger scroll sideways moves it to the next column after a short distance, then one column per longer distance, and stays on the columns until the fingers lift; up or down it moves one step per scroll, so a card in a stack is easy to pick. The wheel moves one step per notch. A three-finger swipe moves it one step when the fingers lift: left/right enters or leaves the stash pile, and up/down browses its cards while it is selected. Two-finger scrolling, the wheel and keyboard navigation follow the same pile boundaries. | 262 | A four-finger swipe up opens the overview, or hides the [stash](#stash) when it is shown, and a swipe down closes the overview on the selection (with the overview closed, a swipe down shows or hides the stash), whatever `touchpad.natural-scroll` says. Two-finger scrolling and the mouse wheel move the selection: left and right through the columns, up and down through stack cards before crossing workspaces, following `touchpad.natural-scroll` for two-finger scrolling and `mouse.natural-scroll` for the wheel. A two-finger scroll sideways moves it to the next column after a short distance, then one column per longer distance, and stays on the columns until the fingers lift; up or down it moves one step per scroll, so a card in a stack is easy to pick. The wheel moves one step per notch. A three-finger swipe moves it one step when the fingers lift: left/right enters or leaves the stash pile, and up/down browses its cards while it is selected. Two-finger scrolling, the wheel and keyboard navigation follow the same pile boundaries. | ||
| 263 | 263 | ||||
M internal/core/overview.go +4 -2
| @@ -364,7 +364,8 @@ func (m *Monitor) showOverview(w *Workspace) { | |||
| 364 | 364 | } | |
| 365 | 365 | ||
| 366 | 366 | // OverviewMove shares navigation rules across keys, scrolls and swipes. | |
| 367 | - | // Vertical moves stay in a selected stash pile; horizontal moves leave it. | |
| 367 | + | // Vertical moves stay in a selected stash pile; left stays, and right | |
| 368 | + | // deselects it, even when the row has no main card. | |
| 368 | 369 | func (m *Monitor) OverviewMove(dx, dy int) { | |
| 369 | 370 | if dy != 0 { | |
| 370 | 371 | m.overviewVertical(dy) | |
| @@ -392,8 +393,9 @@ func (m *Monitor) overviewVertical(dy int) { | |||
| 392 | 393 | func (m *Monitor) overviewHorizontal(dx int) { | |
| 393 | 394 | w := m.Current() | |
| 394 | 395 | if m.cardAt(w) >= 0 { | |
| 395 | - | if dx > 0 && len(w.stack()) > 0 { | |
| 396 | + | if dx > 0 { | |
| 396 | 397 | m.ov.card, m.ov.cardOf = 0, nil | |
| 398 | + | m.ov.selected = 0 | |
| 397 | 399 | item := m.stackFront(w) | |
| 398 | 400 | if item.kind == stackColumns { | |
| 399 | 401 | if w.overviewMaximized() { | |
| @@ -364,7 +364,8 @@ func (m *Monitor) showOverview(w *Workspace) { | |||||
| 364 | } | 364 | } | ||
| 365 | 365 | ||||
| 366 | // OverviewMove shares navigation rules across keys, scrolls and swipes. | 366 | // OverviewMove shares navigation rules across keys, scrolls and swipes. | ||
| 367 | - | // Vertical moves stay in a selected stash pile; horizontal moves leave it. | 367 | + | // Vertical moves stay in a selected stash pile; left stays, and right |
| 368 | + | // deselects it, even when the row has no main card. | |||
| 368 | func (m *Monitor) OverviewMove(dx, dy int) { | 369 | func (m *Monitor) OverviewMove(dx, dy int) { | ||
| 369 | if dy != 0 { | 370 | if dy != 0 { | ||
| 370 | m.overviewVertical(dy) | 371 | m.overviewVertical(dy) | ||
| @@ -392,8 +393,9 @@ func (m *Monitor) overviewVertical(dy int) { | |||||
| 392 | func (m *Monitor) overviewHorizontal(dx int) { | 393 | func (m *Monitor) overviewHorizontal(dx int) { | ||
| 393 | w := m.Current() | 394 | w := m.Current() | ||
| 394 | if m.cardAt(w) >= 0 { | 395 | if m.cardAt(w) >= 0 { | ||
| 395 | - | if dx > 0 && len(w.stack()) > 0 { | 396 | + | if dx > 0 { |
| 396 | m.ov.card, m.ov.cardOf = 0, nil | 397 | m.ov.card, m.ov.cardOf = 0, nil | ||
| 398 | + | m.ov.selected = 0 | |||
| 397 | item := m.stackFront(w) | 399 | item := m.stackFront(w) | ||
| 398 | if item.kind == stackColumns { | 400 | if item.kind == stackColumns { | ||
| 399 | if w.overviewMaximized() { | 401 | if w.overviewMaximized() { | ||
M internal/core/overview_stack_test.go +1 -1
| @@ -278,7 +278,7 @@ func TestOverviewStackNoFloatVeil(t *testing.T) { | |||
| 278 | 278 | } | |
| 279 | 279 | } | |
| 280 | 280 | ||
| 281 | - | // l past the last stash card returns to the front card, even with no columns. | |
| 281 | + | // l leaves the pile for the front card, even with no columns. | |
| 282 | 282 | func TestOverviewPileRightToFloatCard(t *testing.T) { | |
| 283 | 283 | m := monitor() | |
| 284 | 284 | m.SetOutput(300, 200) | |
| @@ -278,7 +278,7 @@ func TestOverviewStackNoFloatVeil(t *testing.T) { | |||||
| 278 | } | 278 | } | ||
| 279 | } | 279 | } | ||
| 280 | 280 | ||||
| 281 | - | // l past the last stash card returns to the front card, even with no columns. | 281 | + | // l leaves the pile for the front card, even with no columns. |
| 282 | func TestOverviewPileRightToFloatCard(t *testing.T) { | 282 | func TestOverviewPileRightToFloatCard(t *testing.T) { | ||
| 283 | m := monitor() | 283 | m := monitor() | ||
| 284 | m.SetOutput(300, 200) | 284 | m.SetOutput(300, 200) | ||
M internal/core/overview_test.go +8 -0
| @@ -448,6 +448,14 @@ func TestOverviewPileOnly(t *testing.T) { | |||
| 448 | 448 | if m.Current() != w || m.cardAt(w) != 0 { | |
| 449 | 449 | t.Fatal("vertical navigation escaped a stash-only workspace") | |
| 450 | 450 | } | |
| 451 | + | m.OverviewMove(1, 0) | |
| 452 | + | if m.cardAt(w) >= 0 || m.ov.selected != 0 { | |
| 453 | + | t.Fatal("right did not deselect the stash-only pile") | |
| 454 | + | } | |
| 455 | + | m.OverviewMove(0, 1) | |
| 456 | + | if m.Current() == w { | |
| 457 | + | t.Fatal("vertical navigation after leaving the pile did not change workspace") | |
| 458 | + | } | |
| 451 | 459 | m.CancelOverview() | |
| 452 | 460 | m.Focus(1) | |
| 453 | 461 | m.ToggleOverview() | |
| @@ -448,6 +448,14 @@ func TestOverviewPileOnly(t *testing.T) { | |||||
| 448 | if m.Current() != w || m.cardAt(w) != 0 { | 448 | if m.Current() != w || m.cardAt(w) != 0 { | ||
| 449 | t.Fatal("vertical navigation escaped a stash-only workspace") | 449 | t.Fatal("vertical navigation escaped a stash-only workspace") | ||
| 450 | } | 450 | } | ||
| 451 | + | m.OverviewMove(1, 0) | |||
| 452 | + | if m.cardAt(w) >= 0 || m.ov.selected != 0 { | |||
| 453 | + | t.Fatal("right did not deselect the stash-only pile") | |||
| 454 | + | } | |||
| 455 | + | m.OverviewMove(0, 1) | |||
| 456 | + | if m.Current() == w { | |||
| 457 | + | t.Fatal("vertical navigation after leaving the pile did not change workspace") | |||
| 458 | + | } | |||
| 451 | m.CancelOverview() | 459 | m.CancelOverview() | ||
| 452 | m.Focus(1) | 460 | m.Focus(1) | ||
| 453 | m.ToggleOverview() | 461 | m.ToggleOverview() | ||