fix(layout): center the middle zone on the middle of the bar, not of the space between the side zones
- commit
- b752a4a8f858459b31c7ac884aa32430b133d123
- author
- brice · · 2026-10-05 16:04 UTC
- parent
- a3516e3
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +44 -2
M internal/layout/layout.go +7 -2
| @@ -234,10 +234,15 @@ func (l *Layout) Compose() []gpu.Cell { | |||
| 234 | 234 | x += copy(l.row[x:limit], s.cells) | |
| 235 | 235 | } | |
| 236 | 236 | } | |
| 237 | - | // Center zone in the free gap. | |
| 237 | + | // Center zone: on the middle of the bar, and only as far from it as the | |
| 238 | + | // left and right zones force. Centering it in the space they leave would | |
| 239 | + | // shift it whenever the two zones differ in width. | |
| 238 | 240 | gap := l.cols - right - left | |
| 239 | 241 | if center = min(center, gap); center > 0 { | |
| 240 | - | x, limit = left+(gap-center)/2, left+(gap-center)/2+center | |
| 242 | + | start := (l.cols - center) / 2 // exactly on the middle | |
| 243 | + | start = max(start, left) // not over the left zone | |
| 244 | + | start = min(start, l.cols-right-center) // not over the right zone | |
| 245 | + | x, limit = start, start+center | |
| 241 | 246 | for _, s := range l.sources { | |
| 242 | 247 | if s.M.Zone == module.Center && x < limit { | |
| 243 | 248 | x += copy(l.row[x:limit], s.cells) | |
| @@ -234,10 +234,15 @@ func (l *Layout) Compose() []gpu.Cell { | |||||
| 234 | x += copy(l.row[x:limit], s.cells) | 234 | x += copy(l.row[x:limit], s.cells) | ||
| 235 | } | 235 | } | ||
| 236 | } | 236 | } | ||
| 237 | - | // Center zone in the free gap. | 237 | + | // Center zone: on the middle of the bar, and only as far from it as the |
| 238 | + | // left and right zones force. Centering it in the space they leave would | |||
| 239 | + | // shift it whenever the two zones differ in width. | |||
| 238 | gap := l.cols - right - left | 240 | gap := l.cols - right - left | ||
| 239 | if center = min(center, gap); center > 0 { | 241 | if center = min(center, gap); center > 0 { | ||
| 240 | - | x, limit = left+(gap-center)/2, left+(gap-center)/2+center | 242 | + | start := (l.cols - center) / 2 // exactly on the middle |
| 243 | + | start = max(start, left) // not over the left zone | |||
| 244 | + | start = min(start, l.cols-right-center) // not over the right zone | |||
| 245 | + | x, limit = start, start+center | |||
| 241 | for _, s := range l.sources { | 246 | for _, s := range l.sources { | ||
| 242 | if s.M.Zone == module.Center && x < limit { | 247 | if s.M.Zone == module.Center && x < limit { | ||
| 243 | x += copy(l.row[x:limit], s.cells) | 248 | x += copy(l.row[x:limit], s.cells) | ||
M internal/layout/layout_test.go +37 -0
| @@ -184,3 +184,40 @@ func TestBrokenEscapeDoesNotSwallowNextFrames(t *testing.T) { | |||
| 184 | 184 | } | |
| 185 | 185 | } | |
| 186 | 186 | } | |
| 187 | + | ||
| 188 | + | // The center zone sits on the middle of the bar, not on the middle of the | |
| 189 | + | // space left between a wide left zone and a narrow right one. | |
| 190 | + | func TestCenterIsOnTheMiddleOfTheBar(t *testing.T) { | |
| 191 | + | l, m := newTestLayout(40) | |
| 192 | + | publish(m[0], "LLLLLLLLLL") // 10 cells on the left | |
| 193 | + | publish(m[1], "cccc") // 4 in the middle | |
| 194 | + | publish(m[2], "RRRR") // 4 on the right | |
| 195 | + | l.Update() | |
| 196 | + | got := rowText(l) | |
| 197 | + | if i := indexOf(got, "cccc"); i != 18 { | |
| 198 | + | t.Fatalf("center starts at cell %d, want 18 (the bar is 40 cells wide): %q", i, got) | |
| 199 | + | } | |
| 200 | + | } | |
| 201 | + | ||
| 202 | + | // When the middle of the bar is taken, the center moves just enough to fit. | |
| 203 | + | func TestCenterMovesOnlyAsFarAsNeeded(t *testing.T) { | |
| 204 | + | l, m := newTestLayout(40) | |
| 205 | + | publish(m[0], "LLLLLLLLLLLLLLLLLLLL") // 20 cells: reaches past the ideal start of 18 | |
| 206 | + | publish(m[1], "cccc") | |
| 207 | + | publish(m[2], "RRRR") | |
| 208 | + | l.Update() | |
| 209 | + | got := rowText(l) | |
| 210 | + | if i := indexOf(got, "cccc"); i != 20 { | |
| 211 | + | t.Fatalf("center starts at cell %d, want 20 (right after the left zone): %q", i, got) | |
| 212 | + | } | |
| 213 | + | // And the other way round: a wide right zone pushes it left. | |
| 214 | + | l, m = newTestLayout(40) | |
| 215 | + | publish(m[0], "LL") | |
| 216 | + | publish(m[1], "cccc") | |
| 217 | + | publish(m[2], "RRRRRRRRRRRRRRRRRRRR") // 20 cells: the center must end by cell 20 | |
| 218 | + | l.Update() | |
| 219 | + | got = rowText(l) | |
| 220 | + | if i := indexOf(got, "cccc"); i != 16 { | |
| 221 | + | t.Fatalf("center starts at cell %d, want 16 (ending where the right zone starts): %q", i, got) | |
| 222 | + | } | |
| 223 | + | } | |
| @@ -184,3 +184,40 @@ func TestBrokenEscapeDoesNotSwallowNextFrames(t *testing.T) { | |||||
| 184 | } | 184 | } | ||
| 185 | } | 185 | } | ||
| 186 | } | 186 | } | ||
| 187 | + | ||||
| 188 | + | // The center zone sits on the middle of the bar, not on the middle of the | |||
| 189 | + | // space left between a wide left zone and a narrow right one. | |||
| 190 | + | func TestCenterIsOnTheMiddleOfTheBar(t *testing.T) { | |||
| 191 | + | l, m := newTestLayout(40) | |||
| 192 | + | publish(m[0], "LLLLLLLLLL") // 10 cells on the left | |||
| 193 | + | publish(m[1], "cccc") // 4 in the middle | |||
| 194 | + | publish(m[2], "RRRR") // 4 on the right | |||
| 195 | + | l.Update() | |||
| 196 | + | got := rowText(l) | |||
| 197 | + | if i := indexOf(got, "cccc"); i != 18 { | |||
| 198 | + | t.Fatalf("center starts at cell %d, want 18 (the bar is 40 cells wide): %q", i, got) | |||
| 199 | + | } | |||
| 200 | + | } | |||
| 201 | + | ||||
| 202 | + | // When the middle of the bar is taken, the center moves just enough to fit. | |||
| 203 | + | func TestCenterMovesOnlyAsFarAsNeeded(t *testing.T) { | |||
| 204 | + | l, m := newTestLayout(40) | |||
| 205 | + | publish(m[0], "LLLLLLLLLLLLLLLLLLLL") // 20 cells: reaches past the ideal start of 18 | |||
| 206 | + | publish(m[1], "cccc") | |||
| 207 | + | publish(m[2], "RRRR") | |||
| 208 | + | l.Update() | |||
| 209 | + | got := rowText(l) | |||
| 210 | + | if i := indexOf(got, "cccc"); i != 20 { | |||
| 211 | + | t.Fatalf("center starts at cell %d, want 20 (right after the left zone): %q", i, got) | |||
| 212 | + | } | |||
| 213 | + | // And the other way round: a wide right zone pushes it left. | |||
| 214 | + | l, m = newTestLayout(40) | |||
| 215 | + | publish(m[0], "LL") | |||
| 216 | + | publish(m[1], "cccc") | |||
| 217 | + | publish(m[2], "RRRRRRRRRRRRRRRRRRRR") // 20 cells: the center must end by cell 20 | |||
| 218 | + | l.Update() | |||
| 219 | + | got = rowText(l) | |||
| 220 | + | if i := indexOf(got, "cccc"); i != 16 { | |||
| 221 | + | t.Fatalf("center starts at cell %d, want 16 (ending where the right zone starts): %q", i, got) | |||
| 222 | + | } | |||
| 223 | + | } | |||