|
| @@ -50,26 +53,22 @@ func TestCascadeFocusColumnStepsWorkspaceAtBandEdge(t *testing.T) { |
| 50 | | } | 53 | | } |
| 51 | | } | 54 | | } |
| 52 | | | 55 | | |
| 53 | - | // A monitor on that side wins over an occupied workspace, so the opposite | 56 | + | // Occupied workspaces win over monitors; monitors still precede trailing empties. |
| 54 | - | // key brings focus back; the workspace is reached only with no monitor. | 57 | + | func TestCascadeFocusColumnGoesToWorkspaceBeforeMonitor(t *testing.T) { |
| 55 | - | func TestCascadeFocusColumnGoesToMonitorBeforeWorkspace(t *testing.T) { | | | |
| 56 | | c, m := axisCore(t, true, OverflowCascade, 3) | 58 | | c, m := axisCore(t, true, OverflowCascade, 3) |
| 57 | | m.Current().FocusID(23) | 59 | | m.Current().FocusID(23) |
| 58 | | c.applyAction(ActionFocusColumnRight) | 60 | | c.applyAction(ActionFocusColumnRight) |
| 59 | - | wantFocus(t, c, "R", 0) | 61 | + | wantActive(t, m, 2) |
| 60 | - | wantActive(t, m, 1) | 62 | + | if c.cur().mon != m { |
| 61 | - | // Back with the opposite key. | 63 | + | t.Fatal("left monitor before workspace") |
| 62 | - | c.applyAction(ActionFocusColumnLeft) | 64 | + | } |
| 63 | - | wantFocus(t, c, "M", 23) | 65 | + | m.Focus(1) |
| 64 | - | wantActive(t, m, 1) | | | |
| 65 | - | // The first column of a band: the monitor on the left. | | | |
| 66 | | m.Current().FocusID(24) | 66 | | m.Current().FocusID(24) |
| 67 | | c.applyAction(ActionFocusColumnLeft) | 67 | | c.applyAction(ActionFocusColumnLeft) |
| 68 | - | wantFocus(t, c, "L", 0) | 68 | + | wantActive(t, m, 0) |
| 69 | - | wantActive(t, m, 1) | 69 | + | if c.cur().mon != m { |
| 70 | - | // From the last occupied workspace: the monitor on the right, not the | 70 | + | t.Fatal("left monitor before workspace") |
| 71 | - | // trailing empty workspace. | 71 | + | } |
| 72 | - | c.focusScreen = 1 | | | |
| 73 | | m.Focus(2) | 72 | | m.Focus(2) |
| 74 | | m.Current().FocusID(35) | 73 | | m.Current().FocusID(35) |
| 75 | | c.applyAction(ActionFocusColumnRight) | 74 | | c.applyAction(ActionFocusColumnRight) |
|
| @@ -93,8 +92,8 @@ func TestCascadeFocusColumnReachesTrailingWorkspaceWithoutMonitor(t *testing.T) |
| 93 | | } | 92 | | } |
| 94 | | | 93 | | |
| 95 | | // One monitor on the left only. Right steps to the next workspace; left | 94 | | // One monitor on the left only. Right steps to the next workspace; left |
| 96 | - | // from its empty band leaves for that monitor, which comes first. | 95 | + | // from its empty band returns to the occupied workspace before that monitor. |
| 97 | - | func TestCascadeStepRightThenLeftLeavesForMonitor(t *testing.T) { | 96 | + | func TestCascadeStepRightThenLeftReturnsToWorkspace(t *testing.T) { |
| 98 | | c := layoutCore(1, tile{"L", 0, 0, 900, 600}, tile{"M", 900, 0, 900, 600}) | 97 | | c := layoutCore(1, tile{"L", 0, 0, 900, 600}, tile{"M", 900, 0, 900, 600}) |
| 99 | | m := c.cur().mon | 98 | | m := c.cur().mon |
| 100 | | m.SetOverflow(OverflowCascade) | 99 | | m.SetOverflow(OverflowCascade) |
|
| @@ -189,15 +188,17 @@ func TestCascadeFullscreenBandEdgeStepsWorkspace(t *testing.T) { |
| 189 | | } | 188 | | } |
| 190 | | | 189 | | |
| 191 | | // A covering fullscreen tile follows the same order: the monitor on that | 190 | | // A covering fullscreen tile follows the same order: the monitor on that |
| 192 | - | // side before the occupied workspace. | 191 | + | // side after the occupied workspace. |
| 193 | - | func TestCascadeFullscreenGoesToMonitorBeforeWorkspace(t *testing.T) { | 192 | + | func TestCascadeFullscreenGoesToWorkspaceBeforeMonitor(t *testing.T) { |
| 194 | | c, m := axisCore(t, true, OverflowCascade, 3) | 193 | | c, m := axisCore(t, true, OverflowCascade, 3) |
| 195 | | w := m.Current() | 194 | | w := m.Current() |
| 196 | | w.FocusID(23) | 195 | | w.FocusID(23) |
| 197 | | w.ToggleFullscreen() | 196 | | w.ToggleFullscreen() |
| 198 | | c.applyAction(ActionFocusColumnRight) | 197 | | c.applyAction(ActionFocusColumnRight) |
| 199 | - | wantFocus(t, c, "R", 0) | 198 | + | wantActive(t, m, 2) |
| 200 | - | wantActive(t, m, 1) | 199 | + | if c.cur().mon != m { |
| | | 200 | + | t.Fatal("left monitor before workspace") |
| | | 201 | + | } |
| 201 | | if w.fullscreen != 23 { | 202 | | if w.fullscreen != 23 { |
| 202 | | t.Fatalf("fullscreen %d, want 23", w.fullscreen) | 203 | | t.Fatalf("fullscreen %d, want 23", w.fullscreen) |
| 203 | | } | 204 | | } |
|
| @@ -244,3 +245,100 @@ func TestCascadeLoneFloatAndEmptyWorkspaceStepWorkspace(t *testing.T) { |
| 244 | | wantActive(t, m, tc.want) | 245 | | wantActive(t, m, tc.want) |
| 245 | | } | 246 | | } |
| 246 | | } | 247 | | } |
| | | 248 | + | |
| | | 249 | + | // Column edges visit occupied workspaces before crossing outputs, in both directions. |
| | | 250 | + | func TestColumnEdgeFocusWorkspaceBeforeScreen(t *testing.T) { |
| | | 251 | + | for _, tc := range []struct { |
| | | 252 | + | name string |
| | | 253 | + | screen, start, next, neighbor int |
| | | 254 | + | action Action |
| | | 255 | + | }{ |
| | | 256 | + | {"left", 1, 1, 0, 0, ActionFocusColumnLeft}, |
| | | 257 | + | {"right", 0, 0, 1, 1, ActionFocusColumnRight}, |
| | | 258 | + | } { |
| | | 259 | + | t.Run(tc.name, func(t *testing.T) { |
| | | 260 | + | c := layoutCore(tc.screen, tile{"laptop", 0, 0, 1920, 1080}, tile{"4K", 1920, 0, 3840, 2160}) |
| | | 261 | + | m := c.cur().mon |
| | | 262 | + | m.SetOverflow(OverflowCascade) |
| | | 263 | + | for ws := range 2 { |
| | | 264 | + | m.Focus(ws) |
| | | 265 | + | m.AddWindow(WindowID(ws + 1)) |
| | | 266 | + | } |
| | | 267 | + | m.Focus(tc.start) |
| | | 268 | + | c.applyAction(tc.action) |
| | | 269 | + | if c.focusScreen != tc.screen { |
| | | 270 | + | t.Fatalf("screen %d, want %d before crossing workspace", c.focusScreen, tc.screen) |
| | | 271 | + | } |
| | | 272 | + | wantActive(t, m, tc.next) |
| | | 273 | + | c.applyAction(tc.action) |
| | | 274 | + | if c.focusScreen != tc.neighbor { |
| | | 275 | + | t.Fatalf("screen %d, want neighbor %d", c.focusScreen, tc.neighbor) |
| | | 276 | + | } |
| | | 277 | + | wantActive(t, m, tc.next) |
| | | 278 | + | }) |
| | | 279 | + | } |
| | | 280 | + | } |
| | | 281 | + | |
| | | 282 | + | func TestVerticalEdgeFocusWorkspaceBeforeScreen(t *testing.T) { |
| | | 283 | + | for _, overflow := range []Overflow{OverflowScroll, OverflowFixed} { |
| | | 284 | + | for _, tc := range []struct { |
| | | 285 | + | name string |
| | | 286 | + | screen, start, next, neighbor int |
| | | 287 | + | action Action |
| | | 288 | + | }{ |
| | | 289 | + | {"up", 1, 1, 0, 0, ActionFocusWindowUp}, |
| | | 290 | + | {"down", 0, 0, 1, 1, ActionFocusWindowDown}, |
| | | 291 | + | } { |
| | | 292 | + | t.Run(string(overflow)+"/"+tc.name, func(t *testing.T) { |
| | | 293 | + | c := layoutCore(tc.screen, tile{"top", 0, 0, 900, 600}, tile{"bottom", 0, 600, 900, 600}) |
| | | 294 | + | m := c.cur().mon |
| | | 295 | + | m.SetOverflow(overflow) |
| | | 296 | + | for ws := range 2 { |
| | | 297 | + | m.Focus(ws) |
| | | 298 | + | m.AddWindow(WindowID(ws + 1)) |
| | | 299 | + | } |
| | | 300 | + | m.Focus(tc.start) |
| | | 301 | + | c.applyAction(tc.action) |
| | | 302 | + | if c.focusScreen != tc.screen { |
| | | 303 | + | t.Fatal("crossed screen before occupied workspace") |
| | | 304 | + | } |
| | | 305 | + | wantActive(t, m, tc.next) |
| | | 306 | + | c.applyAction(tc.action) |
| | | 307 | + | if c.focusScreen != tc.neighbor { |
| | | 308 | + | t.Fatalf("screen %d, want %d", c.focusScreen, tc.neighbor) |
| | | 309 | + | } |
| | | 310 | + | wantActive(t, m, tc.next) |
| | | 311 | + | }) |
| | | 312 | + | } |
| | | 313 | + | } |
| | | 314 | + | } |
| | | 315 | + | |
| | | 316 | + | func TestCascadeVerticalEdgeDoesNotCrossScreen(t *testing.T) { |
| | | 317 | + | c := layoutCore(1, tile{"top", 0, 0, 900, 600}, tile{"middle", 0, 600, 900, 600}, tile{"bottom", 0, 1200, 900, 600}) |
| | | 318 | + | m := c.cur().mon |
| | | 319 | + | m.SetOverflow(OverflowCascade) |
| | | 320 | + | m.AddWindow(1) |
| | | 321 | + | for _, a := range []Action{ActionFocusWindowUp, ActionFocusWindowDown} { |
| | | 322 | + | c.applyAction(a) |
| | | 323 | + | wantFocus(t, c, "middle", 1) |
| | | 324 | + | wantActive(t, m, 0) |
| | | 325 | + | } |
| | | 326 | + | } |
| | | 327 | + | |
| | | 328 | + | func TestVerticalEdgeFocusFlashesDestination(t *testing.T) { |
| | | 329 | + | for _, tc := range []struct { |
| | | 330 | + | a Action |
| | | 331 | + | y int |
| | | 332 | + | }{{ActionFocusWindowUp, -300}, {ActionFocusWindowDown, 300}} { |
| | | 333 | + | t.Run(string(tc.a), func(t *testing.T) { |
| | | 334 | + | c, _ := flashCore(t) |
| | | 335 | + | c.cur().mon.SetOverflow(OverflowScroll) |
| | | 336 | + | c.addScreen(ports.OutputInfo{Name: "B", Width: 400, Height: 300}) |
| | | 337 | + | c.screens[1].x, c.screens[1].y = 0, tc.y |
| | | 338 | + | c.applyAction(tc.a) |
| | | 339 | + | if c.focusScreen != 1 || c.flash.pending == nil || *c.flash.pending != (flashTarget{level: flashMonitor, output: "B"}) { |
| | | 340 | + | t.Fatalf("screen %d, flash %+v", c.focusScreen, c.flash.pending) |
| | | 341 | + | } |
| | | 342 | + | }) |
| | | 343 | + | } |
| | | 344 | + | } |