fix(overview): inset only the focused maximized cascade column
Only the focused maximized column has cards behind it. A click records the clicked window in the maximize history, and a test covers clicks on the peek strips.
- commit
- 62026dce6ac7e1f10ab1d587bebbfe669c8511ac
- author
- brice · · 2026-10-07 14:05 UTC
- parent
- 079b695
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
3 files · +28 -6
M docs/config.md +1 -1
| @@ -196,7 +196,7 @@ Left/right focus follows column order across band boundaries. Up/down visits sta | |||
| 196 | 196 | ||
| 197 | 197 | Three-finger vertical swipes scroll bands; horizontal swipes switch workspaces. Scroll and fixed workspaces retain their existing gesture axes. A switch keeps the source workspace's axis throughout its transition; the destination's axes apply to the next gesture. The `focus-workspace-prev/next` commands switch to the previous or next workspace, whether the transition is vertical or horizontal. Four-finger and stash gestures keep their existing behaviour. | |
| 198 | 198 | ||
| 199 | - | In the [overview](#overview), the selected band is centred at the usual overview zoom, whatever the band count. The other bands of the workspace sit above and below it, dimmed, and may run off screen. Neighbor workspaces sit left and right, fully dimmed, with their selected row level with the centred band. The stash pile stays left of the band, inside the workspace's slot. A maximized column shows the columns it hides as dimmed cards behind it, peeking out on their side of the band; selecting one (left/right, Return or a click) moves the maximization to it, and Escape gives it back. | |
| 199 | + | In the [overview](#overview), the selected band is centred at the usual overview zoom, whatever the band count. The other bands of the workspace sit above and below it, dimmed, and may run off screen. Neighbor workspaces sit left and right, fully dimmed, with their selected row level with the centred band. The stash pile stays left of the band, inside the workspace's slot. A maximized column shows the columns it hides as dimmed cards behind it, peeking out on their side of the band: the nearest one on each side, then the next ones stacked at the band's edge. Selecting a card (left/right, Return or a click on a visible card) moves the maximization to it, and Escape gives it back. | |
| 200 | 200 | ||
| 201 | 201 | In the overview, up/down move to the previous or next band and stop at the first and last; with a stash card selected they browse the pile. Left/right move along the band; past its first column they enter the stash, or go to the previous workspace when there is none; past its last column they go to the next workspace (with the overview closed, focus crosses into the next band instead). Covering floats fan left and right of the band: left/right traverse them before entering the stash or changing workspace, and up/down do nothing on a float. `h`/`j`/`k`/`l` and the focus binds follow the arrows; `focus-workspace-prev/next` always change workspace, except over a selected stash card, where they browse the pile. Three-finger swipes follow the fingers: vertical moves one band (or one stash card) and horizontal one workspace per gesture, even over a stash card. A vertical wheel or continuous scroll changes workspace, except over a selected stash card, where it browses the pile; a horizontal one moves along the band. | |
| 202 | 202 | ||
| @@ -196,7 +196,7 @@ Left/right focus follows column order across band boundaries. Up/down visits sta | |||||
| 196 | 196 | ||||
| 197 | Three-finger vertical swipes scroll bands; horizontal swipes switch workspaces. Scroll and fixed workspaces retain their existing gesture axes. A switch keeps the source workspace's axis throughout its transition; the destination's axes apply to the next gesture. The `focus-workspace-prev/next` commands switch to the previous or next workspace, whether the transition is vertical or horizontal. Four-finger and stash gestures keep their existing behaviour. | 197 | Three-finger vertical swipes scroll bands; horizontal swipes switch workspaces. Scroll and fixed workspaces retain their existing gesture axes. A switch keeps the source workspace's axis throughout its transition; the destination's axes apply to the next gesture. The `focus-workspace-prev/next` commands switch to the previous or next workspace, whether the transition is vertical or horizontal. Four-finger and stash gestures keep their existing behaviour. | ||
| 198 | 198 | ||||
| 199 | - | In the [overview](#overview), the selected band is centred at the usual overview zoom, whatever the band count. The other bands of the workspace sit above and below it, dimmed, and may run off screen. Neighbor workspaces sit left and right, fully dimmed, with their selected row level with the centred band. The stash pile stays left of the band, inside the workspace's slot. A maximized column shows the columns it hides as dimmed cards behind it, peeking out on their side of the band; selecting one (left/right, Return or a click) moves the maximization to it, and Escape gives it back. | 199 | + | In the [overview](#overview), the selected band is centred at the usual overview zoom, whatever the band count. The other bands of the workspace sit above and below it, dimmed, and may run off screen. Neighbor workspaces sit left and right, fully dimmed, with their selected row level with the centred band. The stash pile stays left of the band, inside the workspace's slot. A maximized column shows the columns it hides as dimmed cards behind it, peeking out on their side of the band: the nearest one on each side, then the next ones stacked at the band's edge. Selecting a card (left/right, Return or a click on a visible card) moves the maximization to it, and Escape gives it back. |
| 200 | 200 | ||||
| 201 | In the overview, up/down move to the previous or next band and stop at the first and last; with a stash card selected they browse the pile. Left/right move along the band; past its first column they enter the stash, or go to the previous workspace when there is none; past its last column they go to the next workspace (with the overview closed, focus crosses into the next band instead). Covering floats fan left and right of the band: left/right traverse them before entering the stash or changing workspace, and up/down do nothing on a float. `h`/`j`/`k`/`l` and the focus binds follow the arrows; `focus-workspace-prev/next` always change workspace, except over a selected stash card, where they browse the pile. Three-finger swipes follow the fingers: vertical moves one band (or one stash card) and horizontal one workspace per gesture, even over a stash card. A vertical wheel or continuous scroll changes workspace, except over a selected stash card, where it browses the pile; a horizontal one moves along the band. | 201 | In the overview, up/down move to the previous or next band and stop at the first and last; with a stash card selected they browse the pile. Left/right move along the band; past its first column they enter the stash, or go to the previous workspace when there is none; past its last column they go to the next workspace (with the overview closed, focus crosses into the next band instead). Covering floats fan left and right of the band: left/right traverse them before entering the stash or changing workspace, and up/down do nothing on a float. `h`/`j`/`k`/`l` and the focus binds follow the arrows; `focus-workspace-prev/next` always change workspace, except over a selected stash card, where they browse the pile. Three-finger swipes follow the fingers: vertical moves one band (or one stash card) and horizontal one workspace per gesture, even over a stash card. A vertical wheel or continuous scroll changes workspace, except over a selected stash card, where it browses the pile; a horizontal one moves along the band. | ||
| 202 | 202 | ||||
M internal/core/overview.go +11 -5
| @@ -596,16 +596,18 @@ func (m *Monitor) OverviewPick(id WindowID) { | |||
| 596 | 596 | } else if item, ok := w.itemOf(id); ok { | |
| 597 | 597 | m.setFront(w, item) | |
| 598 | 598 | if i := w.columnOf(id); i >= 0 { | |
| 599 | - | if item.kind == stackColumns && !w.overviewMaximized() { | |
| 600 | - | w.selectOverviewColumn(i) | |
| 601 | - | } | |
| 602 | - | m.ov.selected = id | |
| 599 | + | // The clicked window first: a maximization moving to its | |
| 600 | + | // column records it in the maximize history. | |
| 603 | 601 | for j, v := range w.Columns[i].Windows { | |
| 604 | 602 | if v == id { | |
| 605 | 603 | w.Columns[i].Focus = j | |
| 606 | 604 | break | |
| 607 | 605 | } | |
| 608 | 606 | } | |
| 607 | + | if item.kind == stackColumns && !w.overviewMaximized() { | |
| 608 | + | w.selectOverviewColumn(i) | |
| 609 | + | } | |
| 610 | + | m.ov.selected = id | |
| 609 | 611 | } | |
| 610 | 612 | } | |
| 611 | 613 | m.closeOverview() | |
| @@ -996,7 +998,11 @@ func (w *Workspace) previewTilesInto(tiles []Placement) ([]Placement, int, Rect) | |||
| 996 | 998 | r.Y = g + w.band(i)*w.Usable.H | |
| 997 | 999 | r.W = w.cellWidth() | |
| 998 | 1000 | if c.FullWidth { | |
| 999 | - | in := w.bandCardsInset(i) | |
| 1001 | + | in := 0 | |
| 1002 | + | if i == w.Focus { | |
| 1003 | + | // Only the focused column has cards behind it. | |
| 1004 | + | in = w.bandCardsInset(i) | |
| 1005 | + | } | |
| 1000 | 1006 | r.X, r.W = g+in, max(w.Usable.W-2*g-2*in, 0) | |
| 1001 | 1007 | } | |
| 1002 | 1008 | } else if w.Overflow == OverflowFixed { | |
| @@ -596,16 +596,18 @@ func (m *Monitor) OverviewPick(id WindowID) { | |||||
| 596 | } else if item, ok := w.itemOf(id); ok { | 596 | } else if item, ok := w.itemOf(id); ok { | ||
| 597 | m.setFront(w, item) | 597 | m.setFront(w, item) | ||
| 598 | if i := w.columnOf(id); i >= 0 { | 598 | if i := w.columnOf(id); i >= 0 { | ||
| 599 | - | if item.kind == stackColumns && !w.overviewMaximized() { | 599 | + | // The clicked window first: a maximization moving to its |
| 600 | - | w.selectOverviewColumn(i) | 600 | + | // column records it in the maximize history. |
| 601 | - | } | |||
| 602 | - | m.ov.selected = id | |||
| 603 | for j, v := range w.Columns[i].Windows { | 601 | for j, v := range w.Columns[i].Windows { | ||
| 604 | if v == id { | 602 | if v == id { | ||
| 605 | w.Columns[i].Focus = j | 603 | w.Columns[i].Focus = j | ||
| 606 | break | 604 | break | ||
| 607 | } | 605 | } | ||
| 608 | } | 606 | } | ||
| 607 | + | if item.kind == stackColumns && !w.overviewMaximized() { | |||
| 608 | + | w.selectOverviewColumn(i) | |||
| 609 | + | } | |||
| 610 | + | m.ov.selected = id | |||
| 609 | } | 611 | } | ||
| 610 | } | 612 | } | ||
| 611 | m.closeOverview() | 613 | m.closeOverview() | ||
| @@ -996,7 +998,11 @@ func (w *Workspace) previewTilesInto(tiles []Placement) ([]Placement, int, Rect) | |||||
| 996 | r.Y = g + w.band(i)*w.Usable.H | 998 | r.Y = g + w.band(i)*w.Usable.H | ||
| 997 | r.W = w.cellWidth() | 999 | r.W = w.cellWidth() | ||
| 998 | if c.FullWidth { | 1000 | if c.FullWidth { | ||
| 999 | - | in := w.bandCardsInset(i) | 1001 | + | in := 0 |
| 1002 | + | if i == w.Focus { | |||
| 1003 | + | // Only the focused column has cards behind it. | |||
| 1004 | + | in = w.bandCardsInset(i) | |||
| 1005 | + | } | |||
| 1000 | r.X, r.W = g+in, max(w.Usable.W-2*g-2*in, 0) | 1006 | r.X, r.W = g+in, max(w.Usable.W-2*g-2*in, 0) | ||
| 1001 | } | 1007 | } | ||
| 1002 | } else if w.Overflow == OverflowFixed { | 1008 | } else if w.Overflow == OverflowFixed { | ||
M internal/core/overview_cascade_test.go +16 -0
| @@ -182,6 +182,22 @@ func TestOverviewCascadeMaximizedCardPick(t *testing.T) { | |||
| 182 | 182 | w.FocusID(7) | |
| 183 | 183 | w.ToggleFullWidth() | |
| 184 | 184 | m.ToggleOverview() | |
| 185 | + | // Each card's strip right of the maximized column hits it: 8 next to | |
| 186 | + | // it, 9 at the band's edge. | |
| 187 | + | ps := m.Layout() | |
| 188 | + | front, near, far := previewOf(t, ps, 7).Rect, previewOf(t, ps, 8).Rect, previewOf(t, ps, 9).Rect | |
| 189 | + | for _, c := range []struct { | |
| 190 | + | x0, x1 int | |
| 191 | + | want WindowID | |
| 192 | + | }{{front.X + front.W, near.X + near.W, 8}, {near.X + near.W, far.X + far.W, 9}} { | |
| 193 | + | x := (c.x0 + c.x1) / 2 | |
| 194 | + | if c.x0 >= c.x1 { | |
| 195 | + | t.Fatalf("no strip for %d: front %v near %v far %v", c.want, front, near, far) | |
| 196 | + | } | |
| 197 | + | if got := overviewIn(ps, float64(x), float64(centreY(far))); got != c.want { | |
| 198 | + | t.Fatalf("strip at x=%d hits %d, want %d", x, got, c.want) | |
| 199 | + | } | |
| 200 | + | } | |
| 185 | 201 | m.OverviewPick(9) | |
| 186 | 202 | if m.ov.open || focusedID(m) != 9 || !w.Columns[w.Focus].FullWidth || w.Columns[w.columnOf(7)].FullWidth { | |
| 187 | 203 | t.Fatalf("pick: open %v, focus %d, maximization not moved", m.ov.open, focusedID(m)) | |
| @@ -182,6 +182,22 @@ func TestOverviewCascadeMaximizedCardPick(t *testing.T) { | |||||
| 182 | w.FocusID(7) | 182 | w.FocusID(7) | ||
| 183 | w.ToggleFullWidth() | 183 | w.ToggleFullWidth() | ||
| 184 | m.ToggleOverview() | 184 | m.ToggleOverview() | ||
| 185 | + | // Each card's strip right of the maximized column hits it: 8 next to | |||
| 186 | + | // it, 9 at the band's edge. | |||
| 187 | + | ps := m.Layout() | |||
| 188 | + | front, near, far := previewOf(t, ps, 7).Rect, previewOf(t, ps, 8).Rect, previewOf(t, ps, 9).Rect | |||
| 189 | + | for _, c := range []struct { | |||
| 190 | + | x0, x1 int | |||
| 191 | + | want WindowID | |||
| 192 | + | }{{front.X + front.W, near.X + near.W, 8}, {near.X + near.W, far.X + far.W, 9}} { | |||
| 193 | + | x := (c.x0 + c.x1) / 2 | |||
| 194 | + | if c.x0 >= c.x1 { | |||
| 195 | + | t.Fatalf("no strip for %d: front %v near %v far %v", c.want, front, near, far) | |||
| 196 | + | } | |||
| 197 | + | if got := overviewIn(ps, float64(x), float64(centreY(far))); got != c.want { | |||
| 198 | + | t.Fatalf("strip at x=%d hits %d, want %d", x, got, c.want) | |||
| 199 | + | } | |||
| 200 | + | } | |||
| 185 | m.OverviewPick(9) | 201 | m.OverviewPick(9) | ||
| 186 | if m.ov.open || focusedID(m) != 9 || !w.Columns[w.Focus].FullWidth || w.Columns[w.columnOf(7)].FullWidth { | 202 | if m.ov.open || focusedID(m) != 9 || !w.Columns[w.Focus].FullWidth || w.Columns[w.columnOf(7)].FullWidth { | ||
| 187 | t.Fatalf("pick: open %v, focus %d, maximization not moved", m.ov.open, focusedID(m)) | 203 | t.Fatalf("pick: open %v, focus %d, maximization not moved", m.ov.open, focusedID(m)) | ||