fix(layout): share lines only within the usable area
A cascade band above a bottom panel took the next band, which starts under the panel, for a neighbor and drew a line along the panel.
- commit
- b1ff1a9e163218ea065a7657832dfd4e072b7f14
- author
- brice · · 2026-10-07 14:05 UTC
- parent
- 62026dc
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +27 -1
M internal/core/layout.go +3 -1
| @@ -1658,7 +1658,9 @@ func (w *Workspace) appendLayout(dst []Placement) []Placement { | |||
| 1658 | 1658 | tiles = append(tiles, Placement{ID: id, Rect: r, Fullscreen: full, Focused: id == focusedID, Hidden: hidden}) | |
| 1659 | 1659 | } | |
| 1660 | 1660 | } | |
| 1661 | - | setVisibleNeighbors(tiles, gap, w.Output) | |
| 1661 | + | // Lines are shared within the usable area: a tile under a panel (the | |
| 1662 | + | // next cascade band starts right below a bottom one) is no neighbor. | |
| 1663 | + | setVisibleNeighbors(tiles, gap, w.Usable) | |
| 1662 | 1664 | // tiles lives in the workspace's scratch until they are copied below. | |
| 1663 | 1665 | w.tileBuf = tiles | |
| 1664 | 1666 | // The column/stash group sits between demoted covering floats and | |
| @@ -1658,7 +1658,9 @@ func (w *Workspace) appendLayout(dst []Placement) []Placement { | |||||
| 1658 | tiles = append(tiles, Placement{ID: id, Rect: r, Fullscreen: full, Focused: id == focusedID, Hidden: hidden}) | 1658 | tiles = append(tiles, Placement{ID: id, Rect: r, Fullscreen: full, Focused: id == focusedID, Hidden: hidden}) | ||
| 1659 | } | 1659 | } | ||
| 1660 | } | 1660 | } | ||
| 1661 | - | setVisibleNeighbors(tiles, gap, w.Output) | 1661 | + | // Lines are shared within the usable area: a tile under a panel (the |
| 1662 | + | // next cascade band starts right below a bottom one) is no neighbor. | |||
| 1663 | + | setVisibleNeighbors(tiles, gap, w.Usable) | |||
| 1662 | // tiles lives in the workspace's scratch until they are copied below. | 1664 | // tiles lives in the workspace's scratch until they are copied below. | ||
| 1663 | w.tileBuf = tiles | 1665 | w.tileBuf = tiles | ||
| 1664 | // The column/stash group sits between demoted covering floats and | 1666 | // The column/stash group sits between demoted covering floats and | ||
M internal/core/separators_test.go +24 -0
| @@ -147,6 +147,30 @@ func TestSeparatorsScroll(t *testing.T) { | |||
| 147 | 147 | } | |
| 148 | 148 | } | |
| 149 | 149 | ||
| 150 | + | // A cascade band above a bottom panel: the next band starts under the | |
| 151 | + | // panel, so the focused tile gets no line along the panel, only the one it | |
| 152 | + | // shares with its band neighbor. | |
| 153 | + | func TestSeparatorsCascadeBottomPanel(t *testing.T) { | |
| 154 | + | w := &Workspace{Overflow: OverflowCascade, MaxColumns: 2} | |
| 155 | + | w.SetOutput(900, 600) | |
| 156 | + | w.SetUsable(Rect{W: 900, H: 580}) | |
| 157 | + | for id := WindowID(1); id <= 3; id++ { | |
| 158 | + | w.AddWindow(id) | |
| 159 | + | } | |
| 160 | + | w.FocusID(1) | |
| 161 | + | ps := w.Layout() | |
| 162 | + | if p := previewOf(t, ps, 1); p.Neighbors != ports.SideRight { | |
| 163 | + | t.Fatalf("neighbors %04b, want right only", p.Neighbors) | |
| 164 | + | } | |
| 165 | + | seps := separators(ps, 2, 0, w.Output, true) | |
| 166 | + | if got := colorAt(seps, 200, 578); got != "" { | |
| 167 | + | t.Fatalf("line along the panel: %v", seps) | |
| 168 | + | } | |
| 169 | + | if got := colorAt(seps, 449, 100); got != "lit" { | |
| 170 | + | t.Fatalf("shared line (449,100) = %q; %v", got, seps) | |
| 171 | + | } | |
| 172 | + | } | |
| 173 | + | ||
| 150 | 174 | // With gaps each tile has its own line: the focused one lights it whole. | |
| 151 | 175 | func TestSeparatorsGaps(t *testing.T) { | |
| 152 | 176 | ps := []Placement{{ID: 1, Rect: Rect{X: 0, W: 16, H: 16}, Focused: true}, {ID: 2, Rect: Rect{X: 20, W: 16, H: 16}}} | |
| @@ -147,6 +147,30 @@ func TestSeparatorsScroll(t *testing.T) { | |||||
| 147 | } | 147 | } | ||
| 148 | } | 148 | } | ||
| 149 | 149 | ||||
| 150 | + | // A cascade band above a bottom panel: the next band starts under the | |||
| 151 | + | // panel, so the focused tile gets no line along the panel, only the one it | |||
| 152 | + | // shares with its band neighbor. | |||
| 153 | + | func TestSeparatorsCascadeBottomPanel(t *testing.T) { | |||
| 154 | + | w := &Workspace{Overflow: OverflowCascade, MaxColumns: 2} | |||
| 155 | + | w.SetOutput(900, 600) | |||
| 156 | + | w.SetUsable(Rect{W: 900, H: 580}) | |||
| 157 | + | for id := WindowID(1); id <= 3; id++ { | |||
| 158 | + | w.AddWindow(id) | |||
| 159 | + | } | |||
| 160 | + | w.FocusID(1) | |||
| 161 | + | ps := w.Layout() | |||
| 162 | + | if p := previewOf(t, ps, 1); p.Neighbors != ports.SideRight { | |||
| 163 | + | t.Fatalf("neighbors %04b, want right only", p.Neighbors) | |||
| 164 | + | } | |||
| 165 | + | seps := separators(ps, 2, 0, w.Output, true) | |||
| 166 | + | if got := colorAt(seps, 200, 578); got != "" { | |||
| 167 | + | t.Fatalf("line along the panel: %v", seps) | |||
| 168 | + | } | |||
| 169 | + | if got := colorAt(seps, 449, 100); got != "lit" { | |||
| 170 | + | t.Fatalf("shared line (449,100) = %q; %v", got, seps) | |||
| 171 | + | } | |||
| 172 | + | } | |||
| 173 | + | ||||
| 150 | // With gaps each tile has its own line: the focused one lights it whole. | 174 | // With gaps each tile has its own line: the focused one lights it whole. | ||
| 151 | func TestSeparatorsGaps(t *testing.T) { | 175 | func TestSeparatorsGaps(t *testing.T) { | ||
| 152 | ps := []Placement{{ID: 1, Rect: Rect{X: 0, W: 16, H: 16}, Focused: true}, {ID: 2, Rect: Rect{X: 20, W: 16, H: 16}}} | 176 | ps := []Placement{{ID: 1, Rect: Rect{X: 0, W: 16, H: 16}, Focused: true}, {ID: 2, Rect: Rect{X: 20, W: 16, H: 16}}} | ||