refactor(core): simplify column switcher state and flow
Drop switcherState.open (ws != nil says it), the dead checks of switcherTick and the switch actions from overviewBlocks, share the snapshot-transition-publish-rehit sequence in switcherRun, and name viewShot.preview for both previews.
- commit
- 2ff6b061812e0a553d83e4311822491903400a50
- author
- brice · · 2026-10-08 18:19 UTC
- parent
- 17b575a
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
8 files · +132 -122
M internal/core/input.go +1 -1
| @@ -236,7 +236,7 @@ func (c *Core) keyEvent(ctx context.Context, key ports.KeyEvent) error { | |||
| 236 | 236 | return err | |
| 237 | 237 | } | |
| 238 | 238 | } | |
| 239 | - | if c.switching() && key.Pressed && keyName(key.Keysym) == "Escape" { | |
| 239 | + | if c.switching() && key.Pressed && key.Keysym == "Escape" { | |
| 240 | 240 | // Escape cancels the switcher; its release goes nowhere either. | |
| 241 | 241 | c.pressed[heldKey(key)] = true | |
| 242 | 242 | return c.abortSwitcher(ctx) | |
| @@ -236,7 +236,7 @@ func (c *Core) keyEvent(ctx context.Context, key ports.KeyEvent) error { | |||||
| 236 | return err | 236 | return err | ||
| 237 | } | 237 | } | ||
| 238 | } | 238 | } | ||
| 239 | - | if c.switching() && key.Pressed && keyName(key.Keysym) == "Escape" { | 239 | + | if c.switching() && key.Pressed && key.Keysym == "Escape" { |
| 240 | // Escape cancels the switcher; its release goes nowhere either. | 240 | // Escape cancels the switcher; its release goes nowhere either. | ||
| 241 | c.pressed[heldKey(key)] = true | 241 | c.pressed[heldKey(key)] = true | ||
| 242 | return c.abortSwitcher(ctx) | 242 | return c.abortSwitcher(ctx) | ||
M internal/core/layers.go +1 -1
| @@ -88,7 +88,7 @@ func (c *Core) layerOf(id WindowID) (*screen, ports.SceneLayer, bool) { | |||
| 88 | 88 | ||
| 89 | 89 | // hasFullscreen reports whether a fullscreen window covers the screen. In | |
| 90 | 90 | // scroll overflow, one scrolled off by the user no longer does; nor does | |
| 91 | - | // one shown as an overview preview. | |
| 91 | + | // one shown as a preview (the overview or the column switcher). | |
| 92 | 92 | func hasFullscreen(sc *screen) bool { | |
| 93 | 93 | return !sc.mon.previewing() && sc.mon.Current().cover() != 0 | |
| 94 | 94 | } | |
| @@ -88,7 +88,7 @@ func (c *Core) layerOf(id WindowID) (*screen, ports.SceneLayer, bool) { | |||||
| 88 | 88 | ||||
| 89 | // hasFullscreen reports whether a fullscreen window covers the screen. In | 89 | // hasFullscreen reports whether a fullscreen window covers the screen. In | ||
| 90 | // scroll overflow, one scrolled off by the user no longer does; nor does | 90 | // scroll overflow, one scrolled off by the user no longer does; nor does | ||
| 91 | - | // one shown as an overview preview. | 91 | + | // one shown as a preview (the overview or the column switcher). |
| 92 | func hasFullscreen(sc *screen) bool { | 92 | func hasFullscreen(sc *screen) bool { | ||
| 93 | return !sc.mon.previewing() && sc.mon.Current().cover() != 0 | 93 | return !sc.mon.previewing() && sc.mon.Current().cover() != 0 | ||
| 94 | } | 94 | } | ||
M internal/core/monitor.go +2 -1
| @@ -449,7 +449,8 @@ func (m *Monitor) layoutInto(dst []Placement) []Placement { | |||
| 449 | 449 | func (m *Monitor) Output() Rect { return m.template.Output } | |
| 450 | 450 | ||
| 451 | 451 | // Frame is where the workspace on screen draws and takes input, in monitor | |
| 452 | - | // coordinates: its viewport, or the whole monitor in the overview. Slides | |
| 452 | + | // coordinates: its viewport, or the whole monitor in a preview (the | |
| 453 | + | // overview or the column switcher). Slides | |
| 453 | 454 | // involving a sized workspace remain settled inside the current frame. | |
| 454 | 455 | func (m *Monitor) Frame() Rect { | |
| 455 | 456 | if m.previewing() { | |
| @@ -449,7 +449,8 @@ func (m *Monitor) layoutInto(dst []Placement) []Placement { | |||||
| 449 | func (m *Monitor) Output() Rect { return m.template.Output } | 449 | func (m *Monitor) Output() Rect { return m.template.Output } | ||
| 450 | 450 | ||||
| 451 | // Frame is where the workspace on screen draws and takes input, in monitor | 451 | // Frame is where the workspace on screen draws and takes input, in monitor | ||
| 452 | - | // coordinates: its viewport, or the whole monitor in the overview. Slides | 452 | + | // coordinates: its viewport, or the whole monitor in a preview (the |
| 453 | + | // overview or the column switcher). Slides | |||
| 453 | // involving a sized workspace remain settled inside the current frame. | 454 | // involving a sized workspace remain settled inside the current frame. | ||
| 454 | func (m *Monitor) Frame() Rect { | 455 | func (m *Monitor) Frame() Rect { | ||
| 455 | if m.previewing() { | 456 | if m.previewing() { | ||
M internal/core/overview.go +1 -2
| @@ -202,8 +202,7 @@ func overviewBlocks(a Action) bool { | |||
| 202 | 202 | ActionConsumeOrExpelRight, ActionMoveWorkspaceLeft, ActionMoveWorkspaceRight, | |
| 203 | 203 | ActionMoveWorkspaceToMonitorUp, ActionMoveWorkspaceToMonitorDown, | |
| 204 | 204 | ActionMoveWindowUp, ActionMoveWindowDown, ActionMoveWorkspacePrev, | |
| 205 | - | ActionMoveWorkspaceNext, ActionToggleFloating, ActionSwitchColumnNext, | |
| 206 | - | ActionSwitchColumnPrev: | |
| 205 | + | ActionMoveWorkspaceNext, ActionToggleFloating: | |
| 207 | 206 | return true | |
| 208 | 207 | } | |
| 209 | 208 | return false | |
| @@ -202,8 +202,7 @@ func overviewBlocks(a Action) bool { | |||||
| 202 | ActionConsumeOrExpelRight, ActionMoveWorkspaceLeft, ActionMoveWorkspaceRight, | 202 | ActionConsumeOrExpelRight, ActionMoveWorkspaceLeft, ActionMoveWorkspaceRight, | ||
| 203 | ActionMoveWorkspaceToMonitorUp, ActionMoveWorkspaceToMonitorDown, | 203 | ActionMoveWorkspaceToMonitorUp, ActionMoveWorkspaceToMonitorDown, | ||
| 204 | ActionMoveWindowUp, ActionMoveWindowDown, ActionMoveWorkspacePrev, | 204 | ActionMoveWindowUp, ActionMoveWindowDown, ActionMoveWorkspacePrev, | ||
| 205 | - | ActionMoveWorkspaceNext, ActionToggleFloating, ActionSwitchColumnNext, | 205 | + | ActionMoveWorkspaceNext, ActionToggleFloating: |
| 206 | - | ActionSwitchColumnPrev: | |||
| 207 | return true | 206 | return true | ||
| 208 | } | 207 | } | ||
| 209 | return false | 208 | return false | ||
M internal/core/screens.go +2 -1
| @@ -64,7 +64,8 @@ func (s *screen) settled() bool { return s.mon.settled() && len(s.rects) == 0 } | |||
| 64 | 64 | func (s *screen) stopRects() { clear(s.rects); s.rectsWS = nil } | |
| 65 | 65 | ||
| 66 | 66 | // rectsOwner is what the screen's rect motions belong to: its workspace on | |
| 67 | - | // screen, or nil in the overview, where the rows change under the cards. | |
| 67 | + | // screen, or nil in a preview (the overview or the column switcher), where | |
| 68 | + | // the cards change under the windows. | |
| 68 | 69 | func (s *screen) rectsOwner() *Workspace { | |
| 69 | 70 | if s.mon.previewing() { | |
| 70 | 71 | return nil | |
| @@ -64,7 +64,8 @@ func (s *screen) settled() bool { return s.mon.settled() && len(s.rects) == 0 } | |||||
| 64 | func (s *screen) stopRects() { clear(s.rects); s.rectsWS = nil } | 64 | func (s *screen) stopRects() { clear(s.rects); s.rectsWS = nil } | ||
| 65 | 65 | ||||
| 66 | // rectsOwner is what the screen's rect motions belong to: its workspace on | 66 | // rectsOwner is what the screen's rect motions belong to: its workspace on | ||
| 67 | - | // screen, or nil in the overview, where the rows change under the cards. | 67 | + | // screen, or nil in a preview (the overview or the column switcher), where |
| 68 | + | // the cards change under the windows. | |||
| 68 | func (s *screen) rectsOwner() *Workspace { | 69 | func (s *screen) rectsOwner() *Workspace { | ||
| 69 | if s.mon.previewing() { | 70 | if s.mon.previewing() { | ||
| 70 | return nil | 71 | return nil | ||
M internal/core/switcher.go +38 -49
| @@ -23,10 +23,11 @@ const switcherDelay = 150 * time.Millisecond | |||
| 23 | 23 | // column in order, the focused column first. Nothing moves until the commit; | |
| 24 | 24 | // shown is set once the cards are drawn. | |
| 25 | 25 | type switcherState struct { | |
| 26 | - | open, shown bool | |
| 27 | - | ws *Workspace | |
| 28 | - | order []WindowID | |
| 29 | - | at int | |
| 26 | + | // ws is the workspace the switcher opened on; nil when closed. | |
| 27 | + | ws *Workspace | |
| 28 | + | shown bool | |
| 29 | + | order []WindowID | |
| 30 | + | at int | |
| 30 | 31 | // sizes are the client sizes the cards are scaled from. | |
| 31 | 32 | sizes map[WindowID]Rect | |
| 32 | 33 | } | |
| @@ -94,7 +95,7 @@ func (w *Workspace) switchOrder() []WindowID { | |||
| 94 | 95 | // heir is the window of id's column that is not id, or 0: call it before id | |
| 95 | 96 | // is removed, then hand it to remove. | |
| 96 | 97 | func (s *switcherState) heir(id WindowID) WindowID { | |
| 97 | - | if !s.open || s.ws == nil || !slices.Contains(s.order, id) { | |
| 98 | + | if s.ws == nil || !slices.Contains(s.order, id) { | |
| 98 | 99 | return 0 | |
| 99 | 100 | } | |
| 100 | 101 | if i := s.ws.columnOf(id); i >= 0 { | |
| @@ -113,7 +114,7 @@ func (s *switcherState) heir(id WindowID) WindowID { | |||
| 113 | 114 | // goes, and the selection stays on the same card (or the one that took its | |
| 114 | 115 | // place). With fewer than two columns left there is nothing to switch. | |
| 115 | 116 | func (s *switcherState) remove(id, heir WindowID) { | |
| 116 | - | if !s.open { | |
| 117 | + | if s.ws == nil { | |
| 117 | 118 | return | |
| 118 | 119 | } | |
| 119 | 120 | i := slices.Index(s.order, id) | |
| @@ -150,7 +151,7 @@ func (s *switcherState) remove(id, heir WindowID) { | |||
| 150 | 151 | func (m *Monitor) commitSwitcher() bool { | |
| 151 | 152 | s := m.sw | |
| 152 | 153 | m.sw = switcherState{} | |
| 153 | - | if !s.open || s.ws == nil || s.at < 0 || s.at >= len(s.order) { | |
| 154 | + | if s.ws == nil || s.at < 0 || s.at >= len(s.order) { | |
| 154 | 155 | return false | |
| 155 | 156 | } | |
| 156 | 157 | id, w := s.order[s.at], s.ws | |
| @@ -185,14 +186,14 @@ func switchDir(a Action) int { | |||
| 185 | 186 | ||
| 186 | 187 | // switching reports whether a switcher is open. | |
| 187 | 188 | func (c *Core) switching() bool { | |
| 188 | - | return c.switcher.sc != nil && c.switcher.sc.mon.sw.open | |
| 189 | + | return c.switcher.sc != nil && c.switcher.sc.mon.sw.ws != nil | |
| 189 | 190 | } | |
| 190 | 191 | ||
| 191 | 192 | // switcherStale reports whether the open switcher belongs to a workspace | |
| 192 | 193 | // that is not on its screen any more. | |
| 193 | 194 | func (c *Core) switcherStale() bool { | |
| 194 | 195 | m := c.switcher.sc.mon | |
| 195 | - | return m.sw.open && m.sw.ws != m.Current() | |
| 196 | + | return m.sw.ws != nil && m.sw.ws != m.Current() | |
| 196 | 197 | } | |
| 197 | 198 | ||
| 198 | 199 | func (c *Core) stopSwitcherTimer() { | |
| @@ -260,7 +261,7 @@ func (c *Core) switchStep(dir int) { | |||
| 260 | 261 | if dir < 0 { | |
| 261 | 262 | at = len(order) - 1 | |
| 262 | 263 | } | |
| 263 | - | m.sw = switcherState{open: true, ws: w, order: order, at: at, sizes: sizes} | |
| 264 | + | m.sw = switcherState{ws: w, order: order, at: at, sizes: sizes} | |
| 264 | 265 | c.switcher.sc = sc | |
| 265 | 266 | opened = true | |
| 266 | 267 | } else { | |
| @@ -276,12 +277,13 @@ func (c *Core) switchStep(dir int) { | |||
| 276 | 277 | } | |
| 277 | 278 | } | |
| 278 | 279 | ||
| 279 | - | // switchBind runs a switch-column bind. | |
| 280 | - | func (c *Core) switchBind(ctx context.Context, dir int) error { | |
| 280 | + | // switcherRun runs f, which changes the switcher, between a snapshot and | |
| 281 | + | // the transition of what the screens showed, then publishes and rehits so | |
| 282 | + | // the pointer never points at a stale window. | |
| 283 | + | func (c *Core) switcherRun(ctx context.Context, f func()) error { | |
| 281 | 284 | now := c.now() | |
| 282 | 285 | shots := c.snapshot(now) | |
| 283 | - | c.keyboard.takeBack() // a bind acts on the windows | |
| 284 | - | c.switchStep(dir) | |
| 286 | + | f() | |
| 285 | 287 | c.transition(shots, now) | |
| 286 | 288 | if err := c.publish(ctx); err != nil { | |
| 287 | 289 | return err | |
| @@ -289,28 +291,22 @@ func (c *Core) switchBind(ctx context.Context, dir int) error { | |||
| 289 | 291 | return c.rehit(ctx) | |
| 290 | 292 | } | |
| 291 | 293 | ||
| 294 | + | // switchBind runs a switch-column bind. | |
| 295 | + | func (c *Core) switchBind(ctx context.Context, dir int) error { | |
| 296 | + | return c.switcherRun(ctx, func() { | |
| 297 | + | c.keyboard.takeBack() // a bind acts on the windows | |
| 298 | + | c.switchStep(dir) | |
| 299 | + | }) | |
| 300 | + | } | |
| 301 | + | ||
| 292 | 302 | // releaseSwitcher commits the selection once the command key is up. | |
| 293 | 303 | func (c *Core) releaseSwitcher(ctx context.Context) error { | |
| 294 | - | now := c.now() | |
| 295 | - | shots := c.snapshot(now) | |
| 296 | - | c.commitSwitcher() | |
| 297 | - | c.transition(shots, now) | |
| 298 | - | if err := c.publish(ctx); err != nil { | |
| 299 | - | return err | |
| 300 | - | } | |
| 301 | - | return c.rehit(ctx) | |
| 304 | + | return c.switcherRun(ctx, c.commitSwitcher) | |
| 302 | 305 | } | |
| 303 | 306 | ||
| 304 | 307 | // abortSwitcher cancels the switcher and publishes the workspace again. | |
| 305 | 308 | func (c *Core) abortSwitcher(ctx context.Context) error { | |
| 306 | - | now := c.now() | |
| 307 | - | shots := c.snapshot(now) | |
| 308 | - | c.cancelSwitcher() | |
| 309 | - | c.transition(shots, now) | |
| 310 | - | if err := c.publish(ctx); err != nil { | |
| 311 | - | return err | |
| 312 | - | } | |
| 313 | - | return c.rehit(ctx) | |
| 309 | + | return c.switcherRun(ctx, c.cancelSwitcher) | |
| 314 | 310 | } | |
| 315 | 311 | ||
| 316 | 312 | // switcherTick runs when the delay passed with the command key held: the | |
| @@ -323,13 +319,10 @@ func (c *Core) switcherTick() bool { | |||
| 323 | 319 | return false | |
| 324 | 320 | } | |
| 325 | 321 | m := sc.mon | |
| 326 | - | if !c.hasScreen(sc) || c.security.Protected || m.sw.ws != m.Current() { | |
| 322 | + | if m.sw.ws != m.Current() { | |
| 327 | 323 | c.cancelSwitcher() | |
| 328 | 324 | return true | |
| 329 | 325 | } | |
| 330 | - | if m.sw.shown { | |
| 331 | - | return false | |
| 332 | - | } | |
| 333 | 326 | now := c.now() | |
| 334 | 327 | shots := c.snapshot(now) | |
| 335 | 328 | m.sw.shown = true | |
| @@ -350,21 +343,17 @@ func (c *Core) switcherClick(ctx context.Context, button uint32) (handled bool, | |||
| 350 | 343 | c.cancelSwitcher() | |
| 351 | 344 | return false, nil | |
| 352 | 345 | } | |
| 353 | - | now := c.now() | |
| 354 | - | shots := c.snapshot(now) | |
| 355 | - | id := overviewIn(sc.shownLayout(), c.cursorX-float64(sc.x), c.cursorY-float64(sc.y)) | |
| 356 | - | if i := slices.Index(sc.mon.sw.order, id); id != 0 && i >= 0 { | |
| 357 | - | sc.mon.sw.at = i | |
| 358 | - | c.commitSwitcher() | |
| 359 | - | } else { | |
| 360 | - | c.cancelSwitcher() | |
| 361 | - | } | |
| 362 | - | c.buttons[button], c.swallow[button] = true, true | |
| 363 | - | c.transition(shots, now) | |
| 364 | - | if err := c.publish(ctx); err != nil { | |
| 365 | - | return true, err | |
| 366 | - | } | |
| 367 | - | return true, c.rehit(ctx) | |
| 346 | + | err = c.switcherRun(ctx, func() { | |
| 347 | + | id := overviewIn(sc.shownLayout(), c.cursorX-float64(sc.x), c.cursorY-float64(sc.y)) | |
| 348 | + | if i := slices.Index(sc.mon.sw.order, id); id != 0 && i >= 0 { | |
| 349 | + | sc.mon.sw.at = i | |
| 350 | + | c.commitSwitcher() | |
| 351 | + | } else { | |
| 352 | + | c.cancelSwitcher() | |
| 353 | + | } | |
| 354 | + | c.buttons[button], c.swallow[button] = true, true | |
| 355 | + | }) | |
| 356 | + | return true, err | |
| 368 | 357 | } | |
| 369 | 358 | ||
| 370 | 359 | // switcherLayout lays out the shown switcher: one card per column in | |
| @@ -23,10 +23,11 @@ const switcherDelay = 150 * time.Millisecond | |||||
| 23 | // column in order, the focused column first. Nothing moves until the commit; | 23 | // column in order, the focused column first. Nothing moves until the commit; | ||
| 24 | // shown is set once the cards are drawn. | 24 | // shown is set once the cards are drawn. | ||
| 25 | type switcherState struct { | 25 | type switcherState struct { | ||
| 26 | - | open, shown bool | 26 | + | // ws is the workspace the switcher opened on; nil when closed. |
| 27 | - | ws *Workspace | 27 | + | ws *Workspace |
| 28 | - | order []WindowID | 28 | + | shown bool |
| 29 | - | at int | 29 | + | order []WindowID |
| 30 | + | at int | |||
| 30 | // sizes are the client sizes the cards are scaled from. | 31 | // sizes are the client sizes the cards are scaled from. | ||
| 31 | sizes map[WindowID]Rect | 32 | sizes map[WindowID]Rect | ||
| 32 | } | 33 | } | ||
| @@ -94,7 +95,7 @@ func (w *Workspace) switchOrder() []WindowID { | |||||
| 94 | // heir is the window of id's column that is not id, or 0: call it before id | 95 | // heir is the window of id's column that is not id, or 0: call it before id | ||
| 95 | // is removed, then hand it to remove. | 96 | // is removed, then hand it to remove. | ||
| 96 | func (s *switcherState) heir(id WindowID) WindowID { | 97 | func (s *switcherState) heir(id WindowID) WindowID { | ||
| 97 | - | if !s.open || s.ws == nil || !slices.Contains(s.order, id) { | 98 | + | if s.ws == nil || !slices.Contains(s.order, id) { |
| 98 | return 0 | 99 | return 0 | ||
| 99 | } | 100 | } | ||
| 100 | if i := s.ws.columnOf(id); i >= 0 { | 101 | if i := s.ws.columnOf(id); i >= 0 { | ||
| @@ -113,7 +114,7 @@ func (s *switcherState) heir(id WindowID) WindowID { | |||||
| 113 | // goes, and the selection stays on the same card (or the one that took its | 114 | // goes, and the selection stays on the same card (or the one that took its | ||
| 114 | // place). With fewer than two columns left there is nothing to switch. | 115 | // place). With fewer than two columns left there is nothing to switch. | ||
| 115 | func (s *switcherState) remove(id, heir WindowID) { | 116 | func (s *switcherState) remove(id, heir WindowID) { | ||
| 116 | - | if !s.open { | 117 | + | if s.ws == nil { |
| 117 | return | 118 | return | ||
| 118 | } | 119 | } | ||
| 119 | i := slices.Index(s.order, id) | 120 | i := slices.Index(s.order, id) | ||
| @@ -150,7 +151,7 @@ func (s *switcherState) remove(id, heir WindowID) { | |||||
| 150 | func (m *Monitor) commitSwitcher() bool { | 151 | func (m *Monitor) commitSwitcher() bool { | ||
| 151 | s := m.sw | 152 | s := m.sw | ||
| 152 | m.sw = switcherState{} | 153 | m.sw = switcherState{} | ||
| 153 | - | if !s.open || s.ws == nil || s.at < 0 || s.at >= len(s.order) { | 154 | + | if s.ws == nil || s.at < 0 || s.at >= len(s.order) { |
| 154 | return false | 155 | return false | ||
| 155 | } | 156 | } | ||
| 156 | id, w := s.order[s.at], s.ws | 157 | id, w := s.order[s.at], s.ws | ||
| @@ -185,14 +186,14 @@ func switchDir(a Action) int { | |||||
| 185 | 186 | ||||
| 186 | // switching reports whether a switcher is open. | 187 | // switching reports whether a switcher is open. | ||
| 187 | func (c *Core) switching() bool { | 188 | func (c *Core) switching() bool { | ||
| 188 | - | return c.switcher.sc != nil && c.switcher.sc.mon.sw.open | 189 | + | return c.switcher.sc != nil && c.switcher.sc.mon.sw.ws != nil |
| 189 | } | 190 | } | ||
| 190 | 191 | ||||
| 191 | // switcherStale reports whether the open switcher belongs to a workspace | 192 | // switcherStale reports whether the open switcher belongs to a workspace | ||
| 192 | // that is not on its screen any more. | 193 | // that is not on its screen any more. | ||
| 193 | func (c *Core) switcherStale() bool { | 194 | func (c *Core) switcherStale() bool { | ||
| 194 | m := c.switcher.sc.mon | 195 | m := c.switcher.sc.mon | ||
| 195 | - | return m.sw.open && m.sw.ws != m.Current() | 196 | + | return m.sw.ws != nil && m.sw.ws != m.Current() |
| 196 | } | 197 | } | ||
| 197 | 198 | ||||
| 198 | func (c *Core) stopSwitcherTimer() { | 199 | func (c *Core) stopSwitcherTimer() { | ||
| @@ -260,7 +261,7 @@ func (c *Core) switchStep(dir int) { | |||||
| 260 | if dir < 0 { | 261 | if dir < 0 { | ||
| 261 | at = len(order) - 1 | 262 | at = len(order) - 1 | ||
| 262 | } | 263 | } | ||
| 263 | - | m.sw = switcherState{open: true, ws: w, order: order, at: at, sizes: sizes} | 264 | + | m.sw = switcherState{ws: w, order: order, at: at, sizes: sizes} |
| 264 | c.switcher.sc = sc | 265 | c.switcher.sc = sc | ||
| 265 | opened = true | 266 | opened = true | ||
| 266 | } else { | 267 | } else { | ||
| @@ -276,12 +277,13 @@ func (c *Core) switchStep(dir int) { | |||||
| 276 | } | 277 | } | ||
| 277 | } | 278 | } | ||
| 278 | 279 | ||||
| 279 | - | // switchBind runs a switch-column bind. | 280 | + | // switcherRun runs f, which changes the switcher, between a snapshot and |
| 280 | - | func (c *Core) switchBind(ctx context.Context, dir int) error { | 281 | + | // the transition of what the screens showed, then publishes and rehits so |
| 282 | + | // the pointer never points at a stale window. | |||
| 283 | + | func (c *Core) switcherRun(ctx context.Context, f func()) error { | |||
| 281 | now := c.now() | 284 | now := c.now() | ||
| 282 | shots := c.snapshot(now) | 285 | shots := c.snapshot(now) | ||
| 283 | - | c.keyboard.takeBack() // a bind acts on the windows | 286 | + | f() |
| 284 | - | c.switchStep(dir) | |||
| 285 | c.transition(shots, now) | 287 | c.transition(shots, now) | ||
| 286 | if err := c.publish(ctx); err != nil { | 288 | if err := c.publish(ctx); err != nil { | ||
| 287 | return err | 289 | return err | ||
| @@ -289,28 +291,22 @@ func (c *Core) switchBind(ctx context.Context, dir int) error { | |||||
| 289 | return c.rehit(ctx) | 291 | return c.rehit(ctx) | ||
| 290 | } | 292 | } | ||
| 291 | 293 | ||||
| 294 | + | // switchBind runs a switch-column bind. | |||
| 295 | + | func (c *Core) switchBind(ctx context.Context, dir int) error { | |||
| 296 | + | return c.switcherRun(ctx, func() { | |||
| 297 | + | c.keyboard.takeBack() // a bind acts on the windows | |||
| 298 | + | c.switchStep(dir) | |||
| 299 | + | }) | |||
| 300 | + | } | |||
| 301 | + | ||||
| 292 | // releaseSwitcher commits the selection once the command key is up. | 302 | // releaseSwitcher commits the selection once the command key is up. | ||
| 293 | func (c *Core) releaseSwitcher(ctx context.Context) error { | 303 | func (c *Core) releaseSwitcher(ctx context.Context) error { | ||
| 294 | - | now := c.now() | 304 | + | return c.switcherRun(ctx, c.commitSwitcher) |
| 295 | - | shots := c.snapshot(now) | |||
| 296 | - | c.commitSwitcher() | |||
| 297 | - | c.transition(shots, now) | |||
| 298 | - | if err := c.publish(ctx); err != nil { | |||
| 299 | - | return err | |||
| 300 | - | } | |||
| 301 | - | return c.rehit(ctx) | |||
| 302 | } | 305 | } | ||
| 303 | 306 | ||||
| 304 | // abortSwitcher cancels the switcher and publishes the workspace again. | 307 | // abortSwitcher cancels the switcher and publishes the workspace again. | ||
| 305 | func (c *Core) abortSwitcher(ctx context.Context) error { | 308 | func (c *Core) abortSwitcher(ctx context.Context) error { | ||
| 306 | - | now := c.now() | 309 | + | return c.switcherRun(ctx, c.cancelSwitcher) |
| 307 | - | shots := c.snapshot(now) | |||
| 308 | - | c.cancelSwitcher() | |||
| 309 | - | c.transition(shots, now) | |||
| 310 | - | if err := c.publish(ctx); err != nil { | |||
| 311 | - | return err | |||
| 312 | - | } | |||
| 313 | - | return c.rehit(ctx) | |||
| 314 | } | 310 | } | ||
| 315 | 311 | ||||
| 316 | // switcherTick runs when the delay passed with the command key held: the | 312 | // switcherTick runs when the delay passed with the command key held: the | ||
| @@ -323,13 +319,10 @@ func (c *Core) switcherTick() bool { | |||||
| 323 | return false | 319 | return false | ||
| 324 | } | 320 | } | ||
| 325 | m := sc.mon | 321 | m := sc.mon | ||
| 326 | - | if !c.hasScreen(sc) || c.security.Protected || m.sw.ws != m.Current() { | 322 | + | if m.sw.ws != m.Current() { |
| 327 | c.cancelSwitcher() | 323 | c.cancelSwitcher() | ||
| 328 | return true | 324 | return true | ||
| 329 | } | 325 | } | ||
| 330 | - | if m.sw.shown { | |||
| 331 | - | return false | |||
| 332 | - | } | |||
| 333 | now := c.now() | 326 | now := c.now() | ||
| 334 | shots := c.snapshot(now) | 327 | shots := c.snapshot(now) | ||
| 335 | m.sw.shown = true | 328 | m.sw.shown = true | ||
| @@ -350,21 +343,17 @@ func (c *Core) switcherClick(ctx context.Context, button uint32) (handled bool, | |||||
| 350 | c.cancelSwitcher() | 343 | c.cancelSwitcher() | ||
| 351 | return false, nil | 344 | return false, nil | ||
| 352 | } | 345 | } | ||
| 353 | - | now := c.now() | 346 | + | err = c.switcherRun(ctx, func() { |
| 354 | - | shots := c.snapshot(now) | 347 | + | id := overviewIn(sc.shownLayout(), c.cursorX-float64(sc.x), c.cursorY-float64(sc.y)) |
| 355 | - | id := overviewIn(sc.shownLayout(), c.cursorX-float64(sc.x), c.cursorY-float64(sc.y)) | 348 | + | if i := slices.Index(sc.mon.sw.order, id); id != 0 && i >= 0 { |
| 356 | - | if i := slices.Index(sc.mon.sw.order, id); id != 0 && i >= 0 { | 349 | + | sc.mon.sw.at = i |
| 357 | - | sc.mon.sw.at = i | 350 | + | c.commitSwitcher() |
| 358 | - | c.commitSwitcher() | 351 | + | } else { |
| 359 | - | } else { | 352 | + | c.cancelSwitcher() |
| 360 | - | c.cancelSwitcher() | 353 | + | } |
| 361 | - | } | 354 | + | c.buttons[button], c.swallow[button] = true, true |
| 362 | - | c.buttons[button], c.swallow[button] = true, true | 355 | + | }) |
| 363 | - | c.transition(shots, now) | 356 | + | return true, err |
| 364 | - | if err := c.publish(ctx); err != nil { | |||
| 365 | - | return true, err | |||
| 366 | - | } | |||
| 367 | - | return true, c.rehit(ctx) | |||
| 368 | } | 357 | } | ||
| 369 | 358 | ||||
| 370 | // switcherLayout lays out the shown switcher: one card per column in | 359 | // switcherLayout lays out the shown switcher: one card per column in | ||
M internal/core/switcher_test.go +67 -49
| @@ -248,7 +248,7 @@ func switcherMonitor(overflow Overflow, n int) (*Monitor, *Workspace) { | |||
| 248 | 248 | ||
| 249 | 249 | func openSwitcher(m *Monitor, at int) { | |
| 250 | 250 | w := m.Current() | |
| 251 | - | m.sw = switcherState{open: true, ws: w, order: w.switchOrder(), at: at} | |
| 251 | + | m.sw = switcherState{ws: w, order: w.switchOrder(), at: at} | |
| 252 | 252 | } | |
| 253 | 253 | ||
| 254 | 254 | func TestSwitchCommitFocusesSelectedColumn(t *testing.T) { | |
| @@ -256,8 +256,8 @@ func TestSwitchCommitFocusesSelectedColumn(t *testing.T) { | |||
| 256 | 256 | openSwitcher(m, 1) | |
| 257 | 257 | want := m.sw.order[1] | |
| 258 | 258 | m.commitSwitcher() | |
| 259 | - | if id, _ := w.Focused(); id != want || m.sw.open { | |
| 260 | - | t.Fatalf("focus %d (want %d), switcher open %v", id, want, m.sw.open) | |
| 259 | + | if id, _ := w.Focused(); id != want || m.sw.ws != nil { | |
| 260 | + | t.Fatalf("focus %d (want %d), switcher open %v", id, want, m.sw.ws != nil) | |
| 261 | 261 | } | |
| 262 | 262 | } | |
| 263 | 263 | ||
| @@ -308,13 +308,13 @@ func TestSwitchCommitIgnoresGoneWindowAndOtherWorkspace(t *testing.T) { | |||
| 308 | 308 | openSwitcher(m, 1) | |
| 309 | 309 | m.sw.order[1] = 99 // not in the workspace any more | |
| 310 | 310 | m.commitSwitcher() | |
| 311 | - | if id, _ := w.Focused(); id != before || m.sw.open { | |
| 312 | - | t.Fatalf("focus %d, want %d; open %v", id, before, m.sw.open) | |
| 311 | + | if id, _ := w.Focused(); id != before || m.sw.ws != nil { | |
| 312 | + | t.Fatalf("focus %d, want %d; open %v", id, before, m.sw.ws != nil) | |
| 313 | 313 | } | |
| 314 | 314 | openSwitcher(m, 1) | |
| 315 | 315 | m.sw.ws = &Workspace{} | |
| 316 | 316 | m.commitSwitcher() | |
| 317 | - | if id, _ := w.Focused(); id != before || m.sw.open { | |
| 317 | + | if id, _ := w.Focused(); id != before || m.sw.ws != nil { | |
| 318 | 318 | t.Fatalf("focus %d for a foreign workspace, want %d", id, before) | |
| 319 | 319 | } | |
| 320 | 320 | } | |
| @@ -329,8 +329,8 @@ func TestSwitchStepOpensOnPreviousColumnAndCycles(t *testing.T) { | |||
| 329 | 329 | t.Fatal(err) | |
| 330 | 330 | } | |
| 331 | 331 | sw := &c.cur().mon.sw | |
| 332 | - | if !sw.open || sw.at != 1 || switcherFocus(c) != before { | |
| 333 | - | t.Fatalf("open %v at %d focus %d (was %d)", sw.open, sw.at, switcherFocus(c), before) | |
| 332 | + | if sw.ws == nil || sw.at != 1 || switcherFocus(c) != before { | |
| 333 | + | t.Fatalf("open %v at %d focus %d (was %d)", sw.ws != nil, sw.at, switcherFocus(c), before) | |
| 334 | 334 | } | |
| 335 | 335 | if len(ic.timers) != 1 || ic.timers[0] != switcherDelay { | |
| 336 | 336 | t.Fatalf("timers %v, want one of %v", ic.timers, switcherDelay) | |
| @@ -360,7 +360,7 @@ func TestSwitchPrevOpensOnOldestColumn(t *testing.T) { | |||
| 360 | 360 | c, _, _ := switcherCore(t, 3) | |
| 361 | 361 | c.mods = ports.ModSuper | |
| 362 | 362 | bind(t, c, ActionSwitchColumnPrev) | |
| 363 | - | if sw := c.cur().mon.sw; !sw.open || sw.at != len(sw.order)-1 { | |
| 363 | + | if sw := c.cur().mon.sw; sw.ws == nil || sw.at != len(sw.order)-1 { | |
| 364 | 364 | t.Fatalf("at %d of %d", sw.at, len(sw.order)) | |
| 365 | 365 | } | |
| 366 | 366 | } | |
| @@ -372,8 +372,8 @@ func TestSwitchWithoutCmdCommitsAtOnce(t *testing.T) { | |||
| 372 | 372 | if err := c.runBind(context.Background(), ActionSwitchColumnNext); err != nil { | |
| 373 | 373 | t.Fatal(err) | |
| 374 | 374 | } | |
| 375 | - | if c.cur().mon.sw.open || c.switcher.timerC != nil || len(ic.timers) != 0 { | |
| 376 | - | t.Fatalf("open %v, timer %v %v", c.cur().mon.sw.open, c.switcher.timerC != nil, ic.timers) | |
| 375 | + | if c.cur().mon.sw.ws != nil || c.switcher.timerC != nil || len(ic.timers) != 0 { | |
| 376 | + | t.Fatalf("open %v, timer %v %v", c.cur().mon.sw.ws != nil, c.switcher.timerC != nil, ic.timers) | |
| 377 | 377 | } | |
| 378 | 378 | if switcherFocus(c) == start { | |
| 379 | 379 | t.Fatal("focus did not move") | |
| @@ -388,14 +388,14 @@ func TestSwitchIgnoredWithOneColumnOrOverview(t *testing.T) { | |||
| 388 | 388 | c, _, _ := switcherCore(t, 1) | |
| 389 | 389 | c.mods = ports.ModSuper | |
| 390 | 390 | bind(t, c, ActionSwitchColumnNext) | |
| 391 | - | if c.cur().mon.sw.open || c.switcher.sc != nil { | |
| 391 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil { | |
| 392 | 392 | t.Fatal("switcher opened with one column") | |
| 393 | 393 | } | |
| 394 | 394 | c, _, _ = switcherCore(t, 3) | |
| 395 | 395 | c.cur().mon.ToggleOverview() | |
| 396 | 396 | before := switcherFocus(c) | |
| 397 | 397 | bind(t, c, ActionSwitchColumnNext) | |
| 398 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != before { | |
| 398 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != before { | |
| 399 | 399 | t.Fatal("switcher acted in the overview") | |
| 400 | 400 | } | |
| 401 | 401 | } | |
| @@ -421,7 +421,7 @@ func TestSwitchQuickTapSwapsWithoutCards(t *testing.T) { | |||
| 421 | 421 | t.Fatalf("cards published during a quick tap: %+v", p) | |
| 422 | 422 | } | |
| 423 | 423 | } | |
| 424 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 424 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 425 | 425 | t.Fatal("switcher left open") | |
| 426 | 426 | } | |
| 427 | 427 | } | |
| @@ -440,13 +440,13 @@ func TestSwitchHoldCommitsOnReleaseAndForwardsIt(t *testing.T) { | |||
| 440 | 440 | start := switcherFocus(c) | |
| 441 | 441 | order := c.cur().mon.Current().switchOrder() | |
| 442 | 442 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true), tab(ports.ModSuper, false)) | |
| 443 | - | if switcherFocus(c) != start || !c.cur().mon.sw.open { | |
| 443 | + | if switcherFocus(c) != start || c.cur().mon.sw.ws == nil { | |
| 444 | 444 | t.Fatal("focus moved or switcher closed before the release") | |
| 445 | 445 | } | |
| 446 | 446 | forwarded(cmds) | |
| 447 | 447 | sendKeys(t, c, superKey(0, false)) | |
| 448 | - | if switcherFocus(c) != order[1] || c.cur().mon.sw.open || c.switcher.timerC != nil { | |
| 449 | - | t.Fatalf("focus %d (want %d), open %v", switcherFocus(c), order[1], c.cur().mon.sw.open) | |
| 448 | + | if switcherFocus(c) != order[1] || c.cur().mon.sw.ws != nil || c.switcher.timerC != nil { | |
| 449 | + | t.Fatalf("focus %d (want %d), open %v", switcherFocus(c), order[1], c.cur().mon.sw.ws != nil) | |
| 450 | 450 | } | |
| 451 | 451 | fk := forwarded(cmds) | |
| 452 | 452 | if len(fk) != 1 || fk[0].Key.Keysym != "Super_L" || fk[0].Key.Pressed || fk[0].ID != order[1] { | |
| @@ -489,8 +489,8 @@ func TestSwitchEscapeCancelsAndIsNotForwarded(t *testing.T) { | |||
| 489 | 489 | sendKeys(t, c, esc) | |
| 490 | 490 | esc.Pressed = false | |
| 491 | 491 | sendKeys(t, c, esc, superKey(0, false)) | |
| 492 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != start { | |
| 493 | - | t.Fatalf("open %v focus %d (want %d)", c.cur().mon.sw.open, switcherFocus(c), start) | |
| 492 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != start { | |
| 493 | + | t.Fatalf("open %v focus %d (want %d)", c.cur().mon.sw.ws != nil, switcherFocus(c), start) | |
| 494 | 494 | } | |
| 495 | 495 | for _, k := range forwarded(cmds) { | |
| 496 | 496 | if k.Key.Keysym == "Escape" { | |
| @@ -504,7 +504,7 @@ func TestSwitchAnotherBindCancelsThenRuns(t *testing.T) { | |||
| 504 | 504 | start := switcherFocus(c) | |
| 505 | 505 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |
| 506 | 506 | sendKeys(t, c, ports.KeyEvent{Keysym: "f", Keycode: 33, Mods: ports.ModSuper, Pressed: true}) | |
| 507 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 507 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 508 | 508 | t.Fatal("switcher still open") | |
| 509 | 509 | } | |
| 510 | 510 | if switcherFocus(c) != start { | |
| @@ -519,8 +519,8 @@ func TestSwitchShiftTabGoesBackward(t *testing.T) { | |||
| 519 | 519 | c, _, _ := switcherCore(t, 3) | |
| 520 | 520 | sendKeys(t, c, superKey(ports.ModSuper, true), shiftTab(true)) | |
| 521 | 521 | sw := c.cur().mon.sw | |
| 522 | - | if !sw.open || sw.at != len(sw.order)-1 { | |
| 523 | - | t.Fatalf("open %v at %d of %d", sw.open, sw.at, len(sw.order)) | |
| 522 | + | if sw.ws == nil || sw.at != len(sw.order)-1 { | |
| 523 | + | t.Fatalf("open %v at %d of %d", sw.ws != nil, sw.at, len(sw.order)) | |
| 524 | 524 | } | |
| 525 | 525 | sendKeys(t, c, shiftTab(false), tab(ports.ModSuper, true)) | |
| 526 | 526 | if c.cur().mon.sw.at != 0 { | |
| @@ -533,7 +533,7 @@ func TestSwitchSwipeBeginCancels(t *testing.T) { | |||
| 533 | 533 | start := switcherFocus(c) | |
| 534 | 534 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |
| 535 | 535 | c.swipeBegin(ports.SwipeBegin{Fingers: 3}) | |
| 536 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 536 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 537 | 537 | t.Fatal("swipe did not cancel the switcher") | |
| 538 | 538 | } | |
| 539 | 539 | } | |
| @@ -550,7 +550,7 @@ func TestSwitchSecurityChangeCancels(t *testing.T) { | |||
| 550 | 550 | if !c.syncSecurity() { | |
| 551 | 551 | t.Fatal("security change not seen") | |
| 552 | 552 | } | |
| 553 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 553 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 554 | 554 | t.Fatal("security change did not cancel the switcher") | |
| 555 | 555 | } | |
| 556 | 556 | } | |
| @@ -564,12 +564,12 @@ func TestSwitchCmdKeyFollowsConfig(t *testing.T) { | |||
| 564 | 564 | } | |
| 565 | 565 | order := c.cur().mon.Current().switchOrder() | |
| 566 | 566 | sendKeys(t, c, tab(ports.ModAlt, true)) | |
| 567 | - | if !c.cur().mon.sw.open { | |
| 567 | + | if c.cur().mon.sw.ws == nil { | |
| 568 | 568 | t.Fatal("Alt+Tab did not open the switcher") | |
| 569 | 569 | } | |
| 570 | 570 | sendKeys(t, c, ports.KeyEvent{Keysym: "Alt_L", Keycode: 64, Pressed: false}) | |
| 571 | - | if c.cur().mon.sw.open || switcherFocus(c) != order[1] { | |
| 572 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), order[1]) | |
| 571 | + | if c.cur().mon.sw.ws != nil || switcherFocus(c) != order[1] { | |
| 572 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), order[1]) | |
| 573 | 573 | } | |
| 574 | 574 | } | |
| 575 | 575 | ||
| @@ -712,8 +712,8 @@ func TestSwitcherClickOnCardCommits(t *testing.T) { | |||
| 712 | 712 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | |
| 713 | 713 | t.Fatal(err) | |
| 714 | 714 | } | |
| 715 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != target { | |
| 716 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), target) | |
| 715 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != target { | |
| 716 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), target) | |
| 717 | 717 | } | |
| 718 | 718 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110}); err != nil { | |
| 719 | 719 | t.Fatal(err) | |
| @@ -735,8 +735,8 @@ func TestSwitcherClickElsewhereCancels(t *testing.T) { | |||
| 735 | 735 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | |
| 736 | 736 | t.Fatal(err) | |
| 737 | 737 | } | |
| 738 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != start { | |
| 739 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), start) | |
| 738 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != start { | |
| 739 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), start) | |
| 740 | 740 | } | |
| 741 | 741 | } | |
| 742 | 742 | ||
| @@ -748,7 +748,7 @@ func TestSwitcherPressBeforeCardsCancelsAndContinues(t *testing.T) { | |||
| 748 | 748 | if err != nil || handled { | |
| 749 | 749 | t.Fatalf("handled %v err %v", handled, err) | |
| 750 | 750 | } | |
| 751 | - | if c.cur().mon.sw.open || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 751 | + | if c.cur().mon.sw.ws != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | |
| 752 | 752 | t.Fatal("press did not cancel") | |
| 753 | 753 | } | |
| 754 | 754 | } | |
| @@ -762,8 +762,8 @@ func TestSwitcherClosingSelectedWindowKeepsSelectionValid(t *testing.T) { | |||
| 762 | 762 | sw := &c.cur().mon.sw | |
| 763 | 763 | gone := sw.order[sw.at] | |
| 764 | 764 | c.cur().mon.RemoveWindow(gone) | |
| 765 | - | if !sw.open || len(sw.order) != 2 || sw.at < 0 || sw.at >= len(sw.order) || slices.Contains(sw.order, gone) { | |
| 766 | - | t.Fatalf("open %v order %v at %d", sw.open, sw.order, sw.at) | |
| 765 | + | if sw.ws == nil || len(sw.order) != 2 || sw.at < 0 || sw.at >= len(sw.order) || slices.Contains(sw.order, gone) { | |
| 766 | + | t.Fatalf("open %v order %v at %d", sw.ws != nil, sw.order, sw.at) | |
| 767 | 767 | } | |
| 768 | 768 | s := indicatorScene(t, c) | |
| 769 | 769 | if n := len(scenePreviews(s)); n != 2 { | |
| @@ -783,7 +783,7 @@ func TestSwitcherClosingBeforeSelectionKeepsCard(t *testing.T) { | |||
| 783 | 783 | sw := &c.cur().mon.sw | |
| 784 | 784 | selected := sw.order[sw.at] | |
| 785 | 785 | c.cur().mon.RemoveWindow(sw.order[0]) | |
| 786 | - | if !sw.open || sw.order[sw.at] != selected { | |
| 786 | + | if sw.ws == nil || sw.order[sw.at] != selected { | |
| 787 | 787 | t.Fatalf("selection moved from %d: order %v at %d", selected, sw.order, sw.at) | |
| 788 | 788 | } | |
| 789 | 789 | } | |
| @@ -793,7 +793,7 @@ func TestSwitcherClosingDownToOneColumnClosesIt(t *testing.T) { | |||
| 793 | 793 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |
| 794 | 794 | c.switcherTick() | |
| 795 | 795 | c.cur().mon.RemoveWindow(c.cur().mon.sw.order[1]) | |
| 796 | - | if c.cur().mon.sw.open || c.cur().mon.sw.shown { | |
| 796 | + | if c.cur().mon.sw.ws != nil || c.cur().mon.sw.shown { | |
| 797 | 797 | t.Fatal("switcher still open with one column") | |
| 798 | 798 | } | |
| 799 | 799 | if s := indicatorScene(t, c); len(scenePreviews(s)) != 0 { | |
| @@ -816,7 +816,7 @@ func TestSwitcherOutputRemovalCancels(t *testing.T) { | |||
| 816 | 816 | t.Fatal("no switcher screen") | |
| 817 | 817 | } | |
| 818 | 818 | c.removeScreen(sc.name()) | |
| 819 | - | if c.switcher.sc != nil || c.switcher.timerC != nil || sc.mon.sw.open { | |
| 819 | + | if c.switcher.sc != nil || c.switcher.timerC != nil || sc.mon.sw.ws != nil { | |
| 820 | 820 | t.Fatal("switcher survived its output") | |
| 821 | 821 | } | |
| 822 | 822 | if err := c.publish(context.Background()); err != nil { | |
| @@ -842,11 +842,11 @@ func TestSwitcherFocusMovingToAnotherOutputReopensThere(t *testing.T) { | |||
| 842 | 842 | } | |
| 843 | 843 | c.focusScreen = c.screenIndex("B") | |
| 844 | 844 | bind(t, c, ActionSwitchColumnNext) | |
| 845 | - | if first.mon.sw.open { | |
| 845 | + | if first.mon.sw.ws != nil { | |
| 846 | 846 | t.Fatal("old switcher left open") | |
| 847 | 847 | } | |
| 848 | - | if c.switcher.sc != c.cur() || !c.cur().mon.sw.open || c.cur().mon.sw.at != 1 { | |
| 849 | - | t.Fatalf("new switcher on %v open %v", c.switcher.sc == c.cur(), c.cur().mon.sw.open) | |
| 848 | + | if c.switcher.sc != c.cur() || c.cur().mon.sw.ws == nil || c.cur().mon.sw.at != 1 { | |
| 849 | + | t.Fatalf("new switcher on %v open %v", c.switcher.sc == c.cur(), c.cur().mon.sw.ws != nil) | |
| 850 | 850 | } | |
| 851 | 851 | } | |
| 852 | 852 | ||
| @@ -918,7 +918,7 @@ func TestSwitcherShownDropsWhenAnotherWorkspaceComesOnScreen(t *testing.T) { | |||
| 918 | 918 | if len(scenePreviews(s)) != 0 || s.Dim != 0 || s.DimBehind { | |
| 919 | 919 | t.Fatalf("scene still dimmed or previewing: dim %v previews %+v", s.Dim, scenePreviews(s)) | |
| 920 | 920 | } | |
| 921 | - | if m.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 921 | + | if m.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 922 | 922 | t.Fatal("stale switcher left open") | |
| 923 | 923 | } | |
| 924 | 924 | var any bool | |
| @@ -937,8 +937,8 @@ func TestSwitcherShownDropsWhenAnotherWorkspaceComesOnScreen(t *testing.T) { | |||
| 937 | 937 | // The next switch opens fresh on the new workspace. | |
| 938 | 938 | c.mods = ports.ModSuper | |
| 939 | 939 | bind(t, c, ActionSwitchColumnNext) | |
| 940 | - | if sw := m.sw; !sw.open || sw.ws != other || sw.at != 1 { | |
| 941 | - | t.Fatalf("open %v on other %v at %d", sw.open, sw.ws == other, sw.at) | |
| 940 | + | if sw := m.sw; sw.ws == nil || sw.ws != other || sw.at != 1 { | |
| 941 | + | t.Fatalf("open %v on other %v at %d", sw.ws != nil, sw.ws == other, sw.at) | |
| 942 | 942 | } | |
| 943 | 943 | } | |
| 944 | 944 | ||
| @@ -953,18 +953,36 @@ func TestSwitcherStepOnStaleWorkspaceOpensFresh(t *testing.T) { | |||
| 953 | 953 | } | |
| 954 | 954 | sendKeys(t, c, tab(ports.ModSuper, true)) | |
| 955 | 955 | sw := c.cur().mon.sw | |
| 956 | - | if !sw.open || sw.ws != other || sw.shown || sw.at != 1 { | |
| 957 | - | t.Fatalf("open %v on other %v shown %v at %d", sw.open, sw.ws == other, sw.shown, sw.at) | |
| 956 | + | if sw.ws == nil || sw.ws != other || sw.shown || sw.at != 1 { | |
| 957 | + | t.Fatalf("open %v on other %v shown %v at %d", sw.ws != nil, sw.ws == other, sw.shown, sw.at) | |
| 958 | 958 | } | |
| 959 | 959 | // A release on a stale switcher commits nothing and closes it. | |
| 960 | 960 | c.cur().mon.show(c.cur().mon.Workspaces[0]) | |
| 961 | 961 | focus := switcherFocus(c) | |
| 962 | 962 | sendKeys(t, c, superKey(0, false)) | |
| 963 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != focus { | |
| 963 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != focus { | |
| 964 | 964 | t.Fatal("stale switcher survived the release") | |
| 965 | 965 | } | |
| 966 | 966 | } | |
| 967 | 967 | ||
| 968 | + | // The delay passes after another workspace came on screen with no publish | |
| 969 | + | // in between: the tick drops the switcher instead of showing stale cards. | |
| 970 | + | func TestSwitcherTickOnStaleWorkspaceCancels(t *testing.T) { | |
| 971 | + | c, other := twoWorkspaces(t) | |
| 972 | + | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |
| 973 | + | m := c.cur().mon | |
| 974 | + | if m.sw.ws == nil { | |
| 975 | + | t.Fatal("switcher not open") | |
| 976 | + | } | |
| 977 | + | m.show(other) | |
| 978 | + | if !c.switcherTick() { | |
| 979 | + | t.Fatal("tick on a stale workspace asks for no scene") | |
| 980 | + | } | |
| 981 | + | if m.sw.ws != nil || m.sw.shown || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 982 | + | t.Fatal("stale switcher survived the tick") | |
| 983 | + | } | |
| 984 | + | } | |
| 985 | + | ||
| 968 | 986 | func TestSwitchIgnoredDuringDrag(t *testing.T) { | |
| 969 | 987 | c, _, _ := switcherCore(t, 3) | |
| 970 | 988 | start := switcherFocus(c) | |
| @@ -972,7 +990,7 @@ func TestSwitchIgnoredDuringDrag(t *testing.T) { | |||
| 972 | 990 | c.drag = d | |
| 973 | 991 | c.buttons[btnLeft] = true | |
| 974 | 992 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |
| 975 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 993 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { | |
| 976 | 994 | t.Fatal("switcher opened during a drag") | |
| 977 | 995 | } | |
| 978 | 996 | if c.drag != d || switcherFocus(c) != start { | |
| @@ -1096,7 +1114,7 @@ func TestSwitchUnmodifiedBindThroughKeyEvent(t *testing.T) { | |||
| 1096 | 1114 | c, ic, _ := switcherCore(t, 3) | |
| 1097 | 1115 | start := switcherFocus(c) | |
| 1098 | 1116 | sendKeys(t, c, ports.KeyEvent{Keysym: "F9", Keycode: 75, Pressed: true}) | |
| 1099 | - | if c.cur().mon.sw.open || len(ic.timers) != 0 || switcherFocus(c) == start { | |
| 1100 | - | t.Fatalf("open %v timers %v focus %d (was %d)", c.cur().mon.sw.open, ic.timers, switcherFocus(c), start) | |
| 1117 | + | if c.cur().mon.sw.ws != nil || len(ic.timers) != 0 || switcherFocus(c) == start { | |
| 1118 | + | t.Fatalf("open %v timers %v focus %d (was %d)", c.cur().mon.sw.ws != nil, ic.timers, switcherFocus(c), start) | |
| 1101 | 1119 | } | |
| 1102 | 1120 | } | |
| @@ -248,7 +248,7 @@ func switcherMonitor(overflow Overflow, n int) (*Monitor, *Workspace) { | |||||
| 248 | 248 | ||||
| 249 | func openSwitcher(m *Monitor, at int) { | 249 | func openSwitcher(m *Monitor, at int) { | ||
| 250 | w := m.Current() | 250 | w := m.Current() | ||
| 251 | - | m.sw = switcherState{open: true, ws: w, order: w.switchOrder(), at: at} | 251 | + | m.sw = switcherState{ws: w, order: w.switchOrder(), at: at} |
| 252 | } | 252 | } | ||
| 253 | 253 | ||||
| 254 | func TestSwitchCommitFocusesSelectedColumn(t *testing.T) { | 254 | func TestSwitchCommitFocusesSelectedColumn(t *testing.T) { | ||
| @@ -256,8 +256,8 @@ func TestSwitchCommitFocusesSelectedColumn(t *testing.T) { | |||||
| 256 | openSwitcher(m, 1) | 256 | openSwitcher(m, 1) | ||
| 257 | want := m.sw.order[1] | 257 | want := m.sw.order[1] | ||
| 258 | m.commitSwitcher() | 258 | m.commitSwitcher() | ||
| 259 | - | if id, _ := w.Focused(); id != want || m.sw.open { | 259 | + | if id, _ := w.Focused(); id != want || m.sw.ws != nil { |
| 260 | - | t.Fatalf("focus %d (want %d), switcher open %v", id, want, m.sw.open) | 260 | + | t.Fatalf("focus %d (want %d), switcher open %v", id, want, m.sw.ws != nil) |
| 261 | } | 261 | } | ||
| 262 | } | 262 | } | ||
| 263 | 263 | ||||
| @@ -308,13 +308,13 @@ func TestSwitchCommitIgnoresGoneWindowAndOtherWorkspace(t *testing.T) { | |||||
| 308 | openSwitcher(m, 1) | 308 | openSwitcher(m, 1) | ||
| 309 | m.sw.order[1] = 99 // not in the workspace any more | 309 | m.sw.order[1] = 99 // not in the workspace any more | ||
| 310 | m.commitSwitcher() | 310 | m.commitSwitcher() | ||
| 311 | - | if id, _ := w.Focused(); id != before || m.sw.open { | 311 | + | if id, _ := w.Focused(); id != before || m.sw.ws != nil { |
| 312 | - | t.Fatalf("focus %d, want %d; open %v", id, before, m.sw.open) | 312 | + | t.Fatalf("focus %d, want %d; open %v", id, before, m.sw.ws != nil) |
| 313 | } | 313 | } | ||
| 314 | openSwitcher(m, 1) | 314 | openSwitcher(m, 1) | ||
| 315 | m.sw.ws = &Workspace{} | 315 | m.sw.ws = &Workspace{} | ||
| 316 | m.commitSwitcher() | 316 | m.commitSwitcher() | ||
| 317 | - | if id, _ := w.Focused(); id != before || m.sw.open { | 317 | + | if id, _ := w.Focused(); id != before || m.sw.ws != nil { |
| 318 | t.Fatalf("focus %d for a foreign workspace, want %d", id, before) | 318 | t.Fatalf("focus %d for a foreign workspace, want %d", id, before) | ||
| 319 | } | 319 | } | ||
| 320 | } | 320 | } | ||
| @@ -329,8 +329,8 @@ func TestSwitchStepOpensOnPreviousColumnAndCycles(t *testing.T) { | |||||
| 329 | t.Fatal(err) | 329 | t.Fatal(err) | ||
| 330 | } | 330 | } | ||
| 331 | sw := &c.cur().mon.sw | 331 | sw := &c.cur().mon.sw | ||
| 332 | - | if !sw.open || sw.at != 1 || switcherFocus(c) != before { | 332 | + | if sw.ws == nil || sw.at != 1 || switcherFocus(c) != before { |
| 333 | - | t.Fatalf("open %v at %d focus %d (was %d)", sw.open, sw.at, switcherFocus(c), before) | 333 | + | t.Fatalf("open %v at %d focus %d (was %d)", sw.ws != nil, sw.at, switcherFocus(c), before) |
| 334 | } | 334 | } | ||
| 335 | if len(ic.timers) != 1 || ic.timers[0] != switcherDelay { | 335 | if len(ic.timers) != 1 || ic.timers[0] != switcherDelay { | ||
| 336 | t.Fatalf("timers %v, want one of %v", ic.timers, switcherDelay) | 336 | t.Fatalf("timers %v, want one of %v", ic.timers, switcherDelay) | ||
| @@ -360,7 +360,7 @@ func TestSwitchPrevOpensOnOldestColumn(t *testing.T) { | |||||
| 360 | c, _, _ := switcherCore(t, 3) | 360 | c, _, _ := switcherCore(t, 3) | ||
| 361 | c.mods = ports.ModSuper | 361 | c.mods = ports.ModSuper | ||
| 362 | bind(t, c, ActionSwitchColumnPrev) | 362 | bind(t, c, ActionSwitchColumnPrev) | ||
| 363 | - | if sw := c.cur().mon.sw; !sw.open || sw.at != len(sw.order)-1 { | 363 | + | if sw := c.cur().mon.sw; sw.ws == nil || sw.at != len(sw.order)-1 { |
| 364 | t.Fatalf("at %d of %d", sw.at, len(sw.order)) | 364 | t.Fatalf("at %d of %d", sw.at, len(sw.order)) | ||
| 365 | } | 365 | } | ||
| 366 | } | 366 | } | ||
| @@ -372,8 +372,8 @@ func TestSwitchWithoutCmdCommitsAtOnce(t *testing.T) { | |||||
| 372 | if err := c.runBind(context.Background(), ActionSwitchColumnNext); err != nil { | 372 | if err := c.runBind(context.Background(), ActionSwitchColumnNext); err != nil { | ||
| 373 | t.Fatal(err) | 373 | t.Fatal(err) | ||
| 374 | } | 374 | } | ||
| 375 | - | if c.cur().mon.sw.open || c.switcher.timerC != nil || len(ic.timers) != 0 { | 375 | + | if c.cur().mon.sw.ws != nil || c.switcher.timerC != nil || len(ic.timers) != 0 { |
| 376 | - | t.Fatalf("open %v, timer %v %v", c.cur().mon.sw.open, c.switcher.timerC != nil, ic.timers) | 376 | + | t.Fatalf("open %v, timer %v %v", c.cur().mon.sw.ws != nil, c.switcher.timerC != nil, ic.timers) |
| 377 | } | 377 | } | ||
| 378 | if switcherFocus(c) == start { | 378 | if switcherFocus(c) == start { | ||
| 379 | t.Fatal("focus did not move") | 379 | t.Fatal("focus did not move") | ||
| @@ -388,14 +388,14 @@ func TestSwitchIgnoredWithOneColumnOrOverview(t *testing.T) { | |||||
| 388 | c, _, _ := switcherCore(t, 1) | 388 | c, _, _ := switcherCore(t, 1) | ||
| 389 | c.mods = ports.ModSuper | 389 | c.mods = ports.ModSuper | ||
| 390 | bind(t, c, ActionSwitchColumnNext) | 390 | bind(t, c, ActionSwitchColumnNext) | ||
| 391 | - | if c.cur().mon.sw.open || c.switcher.sc != nil { | 391 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil { |
| 392 | t.Fatal("switcher opened with one column") | 392 | t.Fatal("switcher opened with one column") | ||
| 393 | } | 393 | } | ||
| 394 | c, _, _ = switcherCore(t, 3) | 394 | c, _, _ = switcherCore(t, 3) | ||
| 395 | c.cur().mon.ToggleOverview() | 395 | c.cur().mon.ToggleOverview() | ||
| 396 | before := switcherFocus(c) | 396 | before := switcherFocus(c) | ||
| 397 | bind(t, c, ActionSwitchColumnNext) | 397 | bind(t, c, ActionSwitchColumnNext) | ||
| 398 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != before { | 398 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != before { |
| 399 | t.Fatal("switcher acted in the overview") | 399 | t.Fatal("switcher acted in the overview") | ||
| 400 | } | 400 | } | ||
| 401 | } | 401 | } | ||
| @@ -421,7 +421,7 @@ func TestSwitchQuickTapSwapsWithoutCards(t *testing.T) { | |||||
| 421 | t.Fatalf("cards published during a quick tap: %+v", p) | 421 | t.Fatalf("cards published during a quick tap: %+v", p) | ||
| 422 | } | 422 | } | ||
| 423 | } | 423 | } | ||
| 424 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | 424 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { |
| 425 | t.Fatal("switcher left open") | 425 | t.Fatal("switcher left open") | ||
| 426 | } | 426 | } | ||
| 427 | } | 427 | } | ||
| @@ -440,13 +440,13 @@ func TestSwitchHoldCommitsOnReleaseAndForwardsIt(t *testing.T) { | |||||
| 440 | start := switcherFocus(c) | 440 | start := switcherFocus(c) | ||
| 441 | order := c.cur().mon.Current().switchOrder() | 441 | order := c.cur().mon.Current().switchOrder() | ||
| 442 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true), tab(ports.ModSuper, false)) | 442 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true), tab(ports.ModSuper, false)) | ||
| 443 | - | if switcherFocus(c) != start || !c.cur().mon.sw.open { | 443 | + | if switcherFocus(c) != start || c.cur().mon.sw.ws == nil { |
| 444 | t.Fatal("focus moved or switcher closed before the release") | 444 | t.Fatal("focus moved or switcher closed before the release") | ||
| 445 | } | 445 | } | ||
| 446 | forwarded(cmds) | 446 | forwarded(cmds) | ||
| 447 | sendKeys(t, c, superKey(0, false)) | 447 | sendKeys(t, c, superKey(0, false)) | ||
| 448 | - | if switcherFocus(c) != order[1] || c.cur().mon.sw.open || c.switcher.timerC != nil { | 448 | + | if switcherFocus(c) != order[1] || c.cur().mon.sw.ws != nil || c.switcher.timerC != nil { |
| 449 | - | t.Fatalf("focus %d (want %d), open %v", switcherFocus(c), order[1], c.cur().mon.sw.open) | 449 | + | t.Fatalf("focus %d (want %d), open %v", switcherFocus(c), order[1], c.cur().mon.sw.ws != nil) |
| 450 | } | 450 | } | ||
| 451 | fk := forwarded(cmds) | 451 | fk := forwarded(cmds) | ||
| 452 | if len(fk) != 1 || fk[0].Key.Keysym != "Super_L" || fk[0].Key.Pressed || fk[0].ID != order[1] { | 452 | if len(fk) != 1 || fk[0].Key.Keysym != "Super_L" || fk[0].Key.Pressed || fk[0].ID != order[1] { | ||
| @@ -489,8 +489,8 @@ func TestSwitchEscapeCancelsAndIsNotForwarded(t *testing.T) { | |||||
| 489 | sendKeys(t, c, esc) | 489 | sendKeys(t, c, esc) | ||
| 490 | esc.Pressed = false | 490 | esc.Pressed = false | ||
| 491 | sendKeys(t, c, esc, superKey(0, false)) | 491 | sendKeys(t, c, esc, superKey(0, false)) | ||
| 492 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != start { | 492 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != start { |
| 493 | - | t.Fatalf("open %v focus %d (want %d)", c.cur().mon.sw.open, switcherFocus(c), start) | 493 | + | t.Fatalf("open %v focus %d (want %d)", c.cur().mon.sw.ws != nil, switcherFocus(c), start) |
| 494 | } | 494 | } | ||
| 495 | for _, k := range forwarded(cmds) { | 495 | for _, k := range forwarded(cmds) { | ||
| 496 | if k.Key.Keysym == "Escape" { | 496 | if k.Key.Keysym == "Escape" { | ||
| @@ -504,7 +504,7 @@ func TestSwitchAnotherBindCancelsThenRuns(t *testing.T) { | |||||
| 504 | start := switcherFocus(c) | 504 | start := switcherFocus(c) | ||
| 505 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | 505 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | ||
| 506 | sendKeys(t, c, ports.KeyEvent{Keysym: "f", Keycode: 33, Mods: ports.ModSuper, Pressed: true}) | 506 | sendKeys(t, c, ports.KeyEvent{Keysym: "f", Keycode: 33, Mods: ports.ModSuper, Pressed: true}) | ||
| 507 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | 507 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { |
| 508 | t.Fatal("switcher still open") | 508 | t.Fatal("switcher still open") | ||
| 509 | } | 509 | } | ||
| 510 | if switcherFocus(c) != start { | 510 | if switcherFocus(c) != start { | ||
| @@ -519,8 +519,8 @@ func TestSwitchShiftTabGoesBackward(t *testing.T) { | |||||
| 519 | c, _, _ := switcherCore(t, 3) | 519 | c, _, _ := switcherCore(t, 3) | ||
| 520 | sendKeys(t, c, superKey(ports.ModSuper, true), shiftTab(true)) | 520 | sendKeys(t, c, superKey(ports.ModSuper, true), shiftTab(true)) | ||
| 521 | sw := c.cur().mon.sw | 521 | sw := c.cur().mon.sw | ||
| 522 | - | if !sw.open || sw.at != len(sw.order)-1 { | 522 | + | if sw.ws == nil || sw.at != len(sw.order)-1 { |
| 523 | - | t.Fatalf("open %v at %d of %d", sw.open, sw.at, len(sw.order)) | 523 | + | t.Fatalf("open %v at %d of %d", sw.ws != nil, sw.at, len(sw.order)) |
| 524 | } | 524 | } | ||
| 525 | sendKeys(t, c, shiftTab(false), tab(ports.ModSuper, true)) | 525 | sendKeys(t, c, shiftTab(false), tab(ports.ModSuper, true)) | ||
| 526 | if c.cur().mon.sw.at != 0 { | 526 | if c.cur().mon.sw.at != 0 { | ||
| @@ -533,7 +533,7 @@ func TestSwitchSwipeBeginCancels(t *testing.T) { | |||||
| 533 | start := switcherFocus(c) | 533 | start := switcherFocus(c) | ||
| 534 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | 534 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | ||
| 535 | c.swipeBegin(ports.SwipeBegin{Fingers: 3}) | 535 | c.swipeBegin(ports.SwipeBegin{Fingers: 3}) | ||
| 536 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | 536 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { |
| 537 | t.Fatal("swipe did not cancel the switcher") | 537 | t.Fatal("swipe did not cancel the switcher") | ||
| 538 | } | 538 | } | ||
| 539 | } | 539 | } | ||
| @@ -550,7 +550,7 @@ func TestSwitchSecurityChangeCancels(t *testing.T) { | |||||
| 550 | if !c.syncSecurity() { | 550 | if !c.syncSecurity() { | ||
| 551 | t.Fatal("security change not seen") | 551 | t.Fatal("security change not seen") | ||
| 552 | } | 552 | } | ||
| 553 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { | 553 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil || switcherFocus(c) != start { |
| 554 | t.Fatal("security change did not cancel the switcher") | 554 | t.Fatal("security change did not cancel the switcher") | ||
| 555 | } | 555 | } | ||
| 556 | } | 556 | } | ||
| @@ -564,12 +564,12 @@ func TestSwitchCmdKeyFollowsConfig(t *testing.T) { | |||||
| 564 | } | 564 | } | ||
| 565 | order := c.cur().mon.Current().switchOrder() | 565 | order := c.cur().mon.Current().switchOrder() | ||
| 566 | sendKeys(t, c, tab(ports.ModAlt, true)) | 566 | sendKeys(t, c, tab(ports.ModAlt, true)) | ||
| 567 | - | if !c.cur().mon.sw.open { | 567 | + | if c.cur().mon.sw.ws == nil { |
| 568 | t.Fatal("Alt+Tab did not open the switcher") | 568 | t.Fatal("Alt+Tab did not open the switcher") | ||
| 569 | } | 569 | } | ||
| 570 | sendKeys(t, c, ports.KeyEvent{Keysym: "Alt_L", Keycode: 64, Pressed: false}) | 570 | sendKeys(t, c, ports.KeyEvent{Keysym: "Alt_L", Keycode: 64, Pressed: false}) | ||
| 571 | - | if c.cur().mon.sw.open || switcherFocus(c) != order[1] { | 571 | + | if c.cur().mon.sw.ws != nil || switcherFocus(c) != order[1] { |
| 572 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), order[1]) | 572 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), order[1]) |
| 573 | } | 573 | } | ||
| 574 | } | 574 | } | ||
| 575 | 575 | ||||
| @@ -712,8 +712,8 @@ func TestSwitcherClickOnCardCommits(t *testing.T) { | |||||
| 712 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | 712 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | ||
| 713 | t.Fatal(err) | 713 | t.Fatal(err) | ||
| 714 | } | 714 | } | ||
| 715 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != target { | 715 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != target { |
| 716 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), target) | 716 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), target) |
| 717 | } | 717 | } | ||
| 718 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110}); err != nil { | 718 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110}); err != nil { | ||
| 719 | t.Fatal(err) | 719 | t.Fatal(err) | ||
| @@ -735,8 +735,8 @@ func TestSwitcherClickElsewhereCancels(t *testing.T) { | |||||
| 735 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | 735 | if err := c.pointerButton(context.Background(), ports.PointerButton{Button: 0x110, Pressed: true}); err != nil { | ||
| 736 | t.Fatal(err) | 736 | t.Fatal(err) | ||
| 737 | } | 737 | } | ||
| 738 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != start { | 738 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != start { |
| 739 | - | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.open, switcherFocus(c), start) | 739 | + | t.Fatalf("open %v focus %d, want %d", c.cur().mon.sw.ws != nil, switcherFocus(c), start) |
| 740 | } | 740 | } | ||
| 741 | } | 741 | } | ||
| 742 | 742 | ||||
| @@ -748,7 +748,7 @@ func TestSwitcherPressBeforeCardsCancelsAndContinues(t *testing.T) { | |||||
| 748 | if err != nil || handled { | 748 | if err != nil || handled { | ||
| 749 | t.Fatalf("handled %v err %v", handled, err) | 749 | t.Fatalf("handled %v err %v", handled, err) | ||
| 750 | } | 750 | } | ||
| 751 | - | if c.cur().mon.sw.open || c.switcher.timerC != nil || switcherFocus(c) != start { | 751 | + | if c.cur().mon.sw.ws != nil || c.switcher.timerC != nil || switcherFocus(c) != start { |
| 752 | t.Fatal("press did not cancel") | 752 | t.Fatal("press did not cancel") | ||
| 753 | } | 753 | } | ||
| 754 | } | 754 | } | ||
| @@ -762,8 +762,8 @@ func TestSwitcherClosingSelectedWindowKeepsSelectionValid(t *testing.T) { | |||||
| 762 | sw := &c.cur().mon.sw | 762 | sw := &c.cur().mon.sw | ||
| 763 | gone := sw.order[sw.at] | 763 | gone := sw.order[sw.at] | ||
| 764 | c.cur().mon.RemoveWindow(gone) | 764 | c.cur().mon.RemoveWindow(gone) | ||
| 765 | - | if !sw.open || len(sw.order) != 2 || sw.at < 0 || sw.at >= len(sw.order) || slices.Contains(sw.order, gone) { | 765 | + | if sw.ws == nil || len(sw.order) != 2 || sw.at < 0 || sw.at >= len(sw.order) || slices.Contains(sw.order, gone) { |
| 766 | - | t.Fatalf("open %v order %v at %d", sw.open, sw.order, sw.at) | 766 | + | t.Fatalf("open %v order %v at %d", sw.ws != nil, sw.order, sw.at) |
| 767 | } | 767 | } | ||
| 768 | s := indicatorScene(t, c) | 768 | s := indicatorScene(t, c) | ||
| 769 | if n := len(scenePreviews(s)); n != 2 { | 769 | if n := len(scenePreviews(s)); n != 2 { | ||
| @@ -783,7 +783,7 @@ func TestSwitcherClosingBeforeSelectionKeepsCard(t *testing.T) { | |||||
| 783 | sw := &c.cur().mon.sw | 783 | sw := &c.cur().mon.sw | ||
| 784 | selected := sw.order[sw.at] | 784 | selected := sw.order[sw.at] | ||
| 785 | c.cur().mon.RemoveWindow(sw.order[0]) | 785 | c.cur().mon.RemoveWindow(sw.order[0]) | ||
| 786 | - | if !sw.open || sw.order[sw.at] != selected { | 786 | + | if sw.ws == nil || sw.order[sw.at] != selected { |
| 787 | t.Fatalf("selection moved from %d: order %v at %d", selected, sw.order, sw.at) | 787 | t.Fatalf("selection moved from %d: order %v at %d", selected, sw.order, sw.at) | ||
| 788 | } | 788 | } | ||
| 789 | } | 789 | } | ||
| @@ -793,7 +793,7 @@ func TestSwitcherClosingDownToOneColumnClosesIt(t *testing.T) { | |||||
| 793 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | 793 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | ||
| 794 | c.switcherTick() | 794 | c.switcherTick() | ||
| 795 | c.cur().mon.RemoveWindow(c.cur().mon.sw.order[1]) | 795 | c.cur().mon.RemoveWindow(c.cur().mon.sw.order[1]) | ||
| 796 | - | if c.cur().mon.sw.open || c.cur().mon.sw.shown { | 796 | + | if c.cur().mon.sw.ws != nil || c.cur().mon.sw.shown { |
| 797 | t.Fatal("switcher still open with one column") | 797 | t.Fatal("switcher still open with one column") | ||
| 798 | } | 798 | } | ||
| 799 | if s := indicatorScene(t, c); len(scenePreviews(s)) != 0 { | 799 | if s := indicatorScene(t, c); len(scenePreviews(s)) != 0 { | ||
| @@ -816,7 +816,7 @@ func TestSwitcherOutputRemovalCancels(t *testing.T) { | |||||
| 816 | t.Fatal("no switcher screen") | 816 | t.Fatal("no switcher screen") | ||
| 817 | } | 817 | } | ||
| 818 | c.removeScreen(sc.name()) | 818 | c.removeScreen(sc.name()) | ||
| 819 | - | if c.switcher.sc != nil || c.switcher.timerC != nil || sc.mon.sw.open { | 819 | + | if c.switcher.sc != nil || c.switcher.timerC != nil || sc.mon.sw.ws != nil { |
| 820 | t.Fatal("switcher survived its output") | 820 | t.Fatal("switcher survived its output") | ||
| 821 | } | 821 | } | ||
| 822 | if err := c.publish(context.Background()); err != nil { | 822 | if err := c.publish(context.Background()); err != nil { | ||
| @@ -842,11 +842,11 @@ func TestSwitcherFocusMovingToAnotherOutputReopensThere(t *testing.T) { | |||||
| 842 | } | 842 | } | ||
| 843 | c.focusScreen = c.screenIndex("B") | 843 | c.focusScreen = c.screenIndex("B") | ||
| 844 | bind(t, c, ActionSwitchColumnNext) | 844 | bind(t, c, ActionSwitchColumnNext) | ||
| 845 | - | if first.mon.sw.open { | 845 | + | if first.mon.sw.ws != nil { |
| 846 | t.Fatal("old switcher left open") | 846 | t.Fatal("old switcher left open") | ||
| 847 | } | 847 | } | ||
| 848 | - | if c.switcher.sc != c.cur() || !c.cur().mon.sw.open || c.cur().mon.sw.at != 1 { | 848 | + | if c.switcher.sc != c.cur() || c.cur().mon.sw.ws == nil || c.cur().mon.sw.at != 1 { |
| 849 | - | t.Fatalf("new switcher on %v open %v", c.switcher.sc == c.cur(), c.cur().mon.sw.open) | 849 | + | t.Fatalf("new switcher on %v open %v", c.switcher.sc == c.cur(), c.cur().mon.sw.ws != nil) |
| 850 | } | 850 | } | ||
| 851 | } | 851 | } | ||
| 852 | 852 | ||||
| @@ -918,7 +918,7 @@ func TestSwitcherShownDropsWhenAnotherWorkspaceComesOnScreen(t *testing.T) { | |||||
| 918 | if len(scenePreviews(s)) != 0 || s.Dim != 0 || s.DimBehind { | 918 | if len(scenePreviews(s)) != 0 || s.Dim != 0 || s.DimBehind { | ||
| 919 | t.Fatalf("scene still dimmed or previewing: dim %v previews %+v", s.Dim, scenePreviews(s)) | 919 | t.Fatalf("scene still dimmed or previewing: dim %v previews %+v", s.Dim, scenePreviews(s)) | ||
| 920 | } | 920 | } | ||
| 921 | - | if m.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | 921 | + | if m.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { |
| 922 | t.Fatal("stale switcher left open") | 922 | t.Fatal("stale switcher left open") | ||
| 923 | } | 923 | } | ||
| 924 | var any bool | 924 | var any bool | ||
| @@ -937,8 +937,8 @@ func TestSwitcherShownDropsWhenAnotherWorkspaceComesOnScreen(t *testing.T) { | |||||
| 937 | // The next switch opens fresh on the new workspace. | 937 | // The next switch opens fresh on the new workspace. | ||
| 938 | c.mods = ports.ModSuper | 938 | c.mods = ports.ModSuper | ||
| 939 | bind(t, c, ActionSwitchColumnNext) | 939 | bind(t, c, ActionSwitchColumnNext) | ||
| 940 | - | if sw := m.sw; !sw.open || sw.ws != other || sw.at != 1 { | 940 | + | if sw := m.sw; sw.ws == nil || sw.ws != other || sw.at != 1 { |
| 941 | - | t.Fatalf("open %v on other %v at %d", sw.open, sw.ws == other, sw.at) | 941 | + | t.Fatalf("open %v on other %v at %d", sw.ws != nil, sw.ws == other, sw.at) |
| 942 | } | 942 | } | ||
| 943 | } | 943 | } | ||
| 944 | 944 | ||||
| @@ -953,18 +953,36 @@ func TestSwitcherStepOnStaleWorkspaceOpensFresh(t *testing.T) { | |||||
| 953 | } | 953 | } | ||
| 954 | sendKeys(t, c, tab(ports.ModSuper, true)) | 954 | sendKeys(t, c, tab(ports.ModSuper, true)) | ||
| 955 | sw := c.cur().mon.sw | 955 | sw := c.cur().mon.sw | ||
| 956 | - | if !sw.open || sw.ws != other || sw.shown || sw.at != 1 { | 956 | + | if sw.ws == nil || sw.ws != other || sw.shown || sw.at != 1 { |
| 957 | - | t.Fatalf("open %v on other %v shown %v at %d", sw.open, sw.ws == other, sw.shown, sw.at) | 957 | + | t.Fatalf("open %v on other %v shown %v at %d", sw.ws != nil, sw.ws == other, sw.shown, sw.at) |
| 958 | } | 958 | } | ||
| 959 | // A release on a stale switcher commits nothing and closes it. | 959 | // A release on a stale switcher commits nothing and closes it. | ||
| 960 | c.cur().mon.show(c.cur().mon.Workspaces[0]) | 960 | c.cur().mon.show(c.cur().mon.Workspaces[0]) | ||
| 961 | focus := switcherFocus(c) | 961 | focus := switcherFocus(c) | ||
| 962 | sendKeys(t, c, superKey(0, false)) | 962 | sendKeys(t, c, superKey(0, false)) | ||
| 963 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || switcherFocus(c) != focus { | 963 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || switcherFocus(c) != focus { |
| 964 | t.Fatal("stale switcher survived the release") | 964 | t.Fatal("stale switcher survived the release") | ||
| 965 | } | 965 | } | ||
| 966 | } | 966 | } | ||
| 967 | 967 | ||||
| 968 | + | // The delay passes after another workspace came on screen with no publish | |||
| 969 | + | // in between: the tick drops the switcher instead of showing stale cards. | |||
| 970 | + | func TestSwitcherTickOnStaleWorkspaceCancels(t *testing.T) { | |||
| 971 | + | c, other := twoWorkspaces(t) | |||
| 972 | + | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | |||
| 973 | + | m := c.cur().mon | |||
| 974 | + | if m.sw.ws == nil { | |||
| 975 | + | t.Fatal("switcher not open") | |||
| 976 | + | } | |||
| 977 | + | m.show(other) | |||
| 978 | + | if !c.switcherTick() { | |||
| 979 | + | t.Fatal("tick on a stale workspace asks for no scene") | |||
| 980 | + | } | |||
| 981 | + | if m.sw.ws != nil || m.sw.shown || c.switcher.sc != nil || c.switcher.timerC != nil { | |||
| 982 | + | t.Fatal("stale switcher survived the tick") | |||
| 983 | + | } | |||
| 984 | + | } | |||
| 985 | + | ||||
| 968 | func TestSwitchIgnoredDuringDrag(t *testing.T) { | 986 | func TestSwitchIgnoredDuringDrag(t *testing.T) { | ||
| 969 | c, _, _ := switcherCore(t, 3) | 987 | c, _, _ := switcherCore(t, 3) | ||
| 970 | start := switcherFocus(c) | 988 | start := switcherFocus(c) | ||
| @@ -972,7 +990,7 @@ func TestSwitchIgnoredDuringDrag(t *testing.T) { | |||||
| 972 | c.drag = d | 990 | c.drag = d | ||
| 973 | c.buttons[btnLeft] = true | 991 | c.buttons[btnLeft] = true | ||
| 974 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | 992 | sendKeys(t, c, superKey(ports.ModSuper, true), tab(ports.ModSuper, true)) | ||
| 975 | - | if c.cur().mon.sw.open || c.switcher.sc != nil || c.switcher.timerC != nil { | 993 | + | if c.cur().mon.sw.ws != nil || c.switcher.sc != nil || c.switcher.timerC != nil { |
| 976 | t.Fatal("switcher opened during a drag") | 994 | t.Fatal("switcher opened during a drag") | ||
| 977 | } | 995 | } | ||
| 978 | if c.drag != d || switcherFocus(c) != start { | 996 | if c.drag != d || switcherFocus(c) != start { | ||
| @@ -1096,7 +1114,7 @@ func TestSwitchUnmodifiedBindThroughKeyEvent(t *testing.T) { | |||||
| 1096 | c, ic, _ := switcherCore(t, 3) | 1114 | c, ic, _ := switcherCore(t, 3) | ||
| 1097 | start := switcherFocus(c) | 1115 | start := switcherFocus(c) | ||
| 1098 | sendKeys(t, c, ports.KeyEvent{Keysym: "F9", Keycode: 75, Pressed: true}) | 1116 | sendKeys(t, c, ports.KeyEvent{Keysym: "F9", Keycode: 75, Pressed: true}) | ||
| 1099 | - | if c.cur().mon.sw.open || len(ic.timers) != 0 || switcherFocus(c) == start { | 1117 | + | if c.cur().mon.sw.ws != nil || len(ic.timers) != 0 || switcherFocus(c) == start { |
| 1100 | - | t.Fatalf("open %v timers %v focus %d (was %d)", c.cur().mon.sw.open, ic.timers, switcherFocus(c), start) | 1118 | + | t.Fatalf("open %v timers %v focus %d (was %d)", c.cur().mon.sw.ws != nil, ic.timers, switcherFocus(c), start) |
| 1101 | } | 1119 | } | ||
| 1102 | } | 1120 | } | ||
M internal/core/transition.go +20 -18
| @@ -25,9 +25,10 @@ import ( | |||
| 25 | 25 | type viewShot struct { | |
| 26 | 26 | sc *screen | |
| 27 | 27 | ws *Workspace | |
| 28 | - | // overview is set when the screen showed its overview: ws is then the | |
| 29 | - | // row on screen, the camera fields stay zero, and rects are the cards. | |
| 30 | - | overview bool | |
| 28 | + | // preview is set when the screen showed a preview (the overview or the | |
| 29 | + | // column switcher): ws is then the workspace on screen, the camera | |
| 30 | + | // fields stay zero, and rects are the cards. | |
| 31 | + | preview bool | |
| 31 | 32 | // settledView is ws's settled view and view where its columns were on screen | |
| 32 | 33 | // (settledView plus shift). | |
| 33 | 34 | settledView int | |
| @@ -403,9 +404,9 @@ func (c *Core) mapWindow(v ports.WindowMapped) { | |||
| 403 | 404 | } | |
| 404 | 405 | shots := c.snapshot(now) | |
| 405 | 406 | for i := range shots { | |
| 406 | - | // A screen with its overview open re-lays its cards at once: a map | |
| 407 | - | // is not an overview action (transition skips a shot without ws). | |
| 408 | - | if shots[i].overview { | |
| 407 | + | // A screen showing a preview re-lays its cards at once: a map is | |
| 408 | + | // not a preview action (transition skips a shot without ws). | |
| 409 | + | if shots[i].preview { | |
| 409 | 410 | shots[i].ws = nil | |
| 410 | 411 | } | |
| 411 | 412 | } | |
| @@ -416,7 +417,7 @@ func (c *Core) mapWindow(v ports.WindowMapped) { | |||
| 416 | 417 | ||
| 417 | 418 | // appearMapped starts the entrance of a window that just mapped when it | |
| 418 | 419 | // shows on its screen's current workspace as a tile or an ordinary float: | |
| 419 | - | // not with the overview open or a session lock, not fullscreen (it keeps | |
| 420 | + | // not with a preview open or a session lock, not fullscreen (it keeps | |
| 420 | 421 | // the direct scanout path), not a float below the columns, not a stash | |
| 421 | 422 | // peek. | |
| 422 | 423 | func (c *Core) appearMapped(id WindowID, now time.Time) { | |
| @@ -442,7 +443,7 @@ func (c *Core) appearMapped(id WindowID, now time.Time) { | |||
| 442 | 443 | // leaves from its drawn rect, fade and speed, not from its settled rect). | |
| 443 | 444 | // Like a map, an unmap is a client event that animates; the settled layout | |
| 444 | 445 | // is the same with animations off. A fullscreen window, one under a | |
| 445 | - | // session lock or with the overview open just goes. | |
| 446 | + | // session lock or with a preview open just goes. | |
| 446 | 447 | func (c *Core) unmapWindow(v ports.WindowUnmapped) { | |
| 447 | 448 | // The window is unmapped: a toplevel that maps again keeps its ID and | |
| 448 | 449 | // must be configured from nothing (a leaving entry keeps no configure). | |
| @@ -471,7 +472,7 @@ func (c *Core) unmapWindow(v ports.WindowUnmapped) { | |||
| 471 | 472 | shots := c.snapshot(now) | |
| 472 | 473 | var shot *rectShot | |
| 473 | 474 | for i := range shots { | |
| 474 | - | if shots[i].overview { | |
| 475 | + | if shots[i].preview { | |
| 475 | 476 | shots[i].ws = nil | |
| 476 | 477 | } | |
| 477 | 478 | if shots[i].sc == sc { | |
| @@ -620,7 +621,7 @@ func (c *Core) snapshot(now time.Time) []viewShot { | |||
| 620 | 621 | } | |
| 621 | 622 | s.sc = sc | |
| 622 | 623 | w := m.Current() | |
| 623 | - | s.ws, s.overview = w, m.previewing() | |
| 624 | + | s.ws, s.preview = w, m.previewing() | |
| 624 | 625 | if !m.previewing() { | |
| 625 | 626 | // The overview does not scroll or slide: no camera. | |
| 626 | 627 | s.settledView, s.view = w.View, float64(w.View)+w.view.off | |
| @@ -705,7 +706,7 @@ func (c *Core) transition(before []viewShot, now time.Time) { | |||
| 705 | 706 | if b.ws == nil || !c.hasScreen(b.sc) || c.following(b.sc) { | |
| 706 | 707 | continue | |
| 707 | 708 | } | |
| 708 | - | if b.overview || b.sc.mon.previewing() { | |
| 709 | + | if b.preview || b.sc.mon.previewing() { | |
| 709 | 710 | c.transitionOverview(b, now) | |
| 710 | 711 | continue | |
| 711 | 712 | } | |
| @@ -886,18 +887,19 @@ func (c *Core) peekDim(p Placement) float64 { | |||
| 886 | 887 | } | |
| 887 | 888 | ||
| 888 | 889 | // transitionOverview starts the motions of an action that opened or closed | |
| 889 | - | // the overview, or moved inside it (the selection, the rows). The camera | |
| 890 | - | // springs stay stopped (ToggleOverview): every window drawn before and | |
| 891 | - | // after moves from its shown rect to its settled one, its content scaled | |
| 892 | - | // with it (a card's Zoom, rectMotion.show). A window drawn after and not | |
| 893 | - | // before (another row, a column the overview hid) fades in with no motion | |
| 890 | + | // a preview (the overview or the column switcher), or moved inside it (the | |
| 891 | + | // selection, the rows). The camera springs stay stopped (ToggleOverview): | |
| 892 | + | // every window drawn before and after moves from its shown rect to its | |
| 893 | + | // settled one, its content scaled with it (a card's Zoom, | |
| 894 | + | // rectMotion.show). A window drawn after and not before (another row, a | |
| 895 | + | // column a preview hid) fades in with no motion | |
| 894 | 896 | // of its own; one drawn before and not after just goes. Motions of the | |
| 895 | 897 | // other state are dropped first: their rects are not the ones snapshot | |
| 896 | 898 | // measured from. A fade that ran on a window drawn in both states carries | |
| 897 | 899 | // over (picking a card that still fades in does not pop it). | |
| 898 | 900 | func (c *Core) transitionOverview(b *viewShot, now time.Time) { | |
| 899 | 901 | sc := b.sc | |
| 900 | - | changed := b.overview != sc.mon.previewing() | |
| 902 | + | changed := b.preview != sc.mon.previewing() | |
| 901 | 903 | if changed { | |
| 902 | 904 | sc.stopRects() | |
| 903 | 905 | } | |
| @@ -937,7 +939,7 @@ func (c *Core) transitionOverview(b *viewShot, now time.Time) { | |||
| 937 | 939 | func movedFrom(b *viewShot, before []viewShot) *viewShot { | |
| 938 | 940 | w := b.sc.mon.Current() | |
| 939 | 941 | for i := range before { | |
| 940 | - | if o := &before[i]; o.ws == w && o.sc != b.sc && !o.overview { | |
| 942 | + | if o := &before[i]; o.ws == w && o.sc != b.sc && !o.preview { | |
| 941 | 943 | return o | |
| 942 | 944 | } | |
| 943 | 945 | } | |
| @@ -25,9 +25,10 @@ import ( | |||||
| 25 | type viewShot struct { | 25 | type viewShot struct { | ||
| 26 | sc *screen | 26 | sc *screen | ||
| 27 | ws *Workspace | 27 | ws *Workspace | ||
| 28 | - | // overview is set when the screen showed its overview: ws is then the | 28 | + | // preview is set when the screen showed a preview (the overview or the |
| 29 | - | // row on screen, the camera fields stay zero, and rects are the cards. | 29 | + | // column switcher): ws is then the workspace on screen, the camera |
| 30 | - | overview bool | 30 | + | // fields stay zero, and rects are the cards. |
| 31 | + | preview bool | |||
| 31 | // settledView is ws's settled view and view where its columns were on screen | 32 | // settledView is ws's settled view and view where its columns were on screen | ||
| 32 | // (settledView plus shift). | 33 | // (settledView plus shift). | ||
| 33 | settledView int | 34 | settledView int | ||
| @@ -403,9 +404,9 @@ func (c *Core) mapWindow(v ports.WindowMapped) { | |||||
| 403 | } | 404 | } | ||
| 404 | shots := c.snapshot(now) | 405 | shots := c.snapshot(now) | ||
| 405 | for i := range shots { | 406 | for i := range shots { | ||
| 406 | - | // A screen with its overview open re-lays its cards at once: a map | 407 | + | // A screen showing a preview re-lays its cards at once: a map is |
| 407 | - | // is not an overview action (transition skips a shot without ws). | 408 | + | // not a preview action (transition skips a shot without ws). |
| 408 | - | if shots[i].overview { | 409 | + | if shots[i].preview { |
| 409 | shots[i].ws = nil | 410 | shots[i].ws = nil | ||
| 410 | } | 411 | } | ||
| 411 | } | 412 | } | ||
| @@ -416,7 +417,7 @@ func (c *Core) mapWindow(v ports.WindowMapped) { | |||||
| 416 | 417 | ||||
| 417 | // appearMapped starts the entrance of a window that just mapped when it | 418 | // appearMapped starts the entrance of a window that just mapped when it | ||
| 418 | // shows on its screen's current workspace as a tile or an ordinary float: | 419 | // shows on its screen's current workspace as a tile or an ordinary float: | ||
| 419 | - | // not with the overview open or a session lock, not fullscreen (it keeps | 420 | + | // not with a preview open or a session lock, not fullscreen (it keeps |
| 420 | // the direct scanout path), not a float below the columns, not a stash | 421 | // the direct scanout path), not a float below the columns, not a stash | ||
| 421 | // peek. | 422 | // peek. | ||
| 422 | func (c *Core) appearMapped(id WindowID, now time.Time) { | 423 | func (c *Core) appearMapped(id WindowID, now time.Time) { | ||
| @@ -442,7 +443,7 @@ func (c *Core) appearMapped(id WindowID, now time.Time) { | |||||
| 442 | // leaves from its drawn rect, fade and speed, not from its settled rect). | 443 | // leaves from its drawn rect, fade and speed, not from its settled rect). | ||
| 443 | // Like a map, an unmap is a client event that animates; the settled layout | 444 | // Like a map, an unmap is a client event that animates; the settled layout | ||
| 444 | // is the same with animations off. A fullscreen window, one under a | 445 | // is the same with animations off. A fullscreen window, one under a | ||
| 445 | - | // session lock or with the overview open just goes. | 446 | + | // session lock or with a preview open just goes. |
| 446 | func (c *Core) unmapWindow(v ports.WindowUnmapped) { | 447 | func (c *Core) unmapWindow(v ports.WindowUnmapped) { | ||
| 447 | // The window is unmapped: a toplevel that maps again keeps its ID and | 448 | // The window is unmapped: a toplevel that maps again keeps its ID and | ||
| 448 | // must be configured from nothing (a leaving entry keeps no configure). | 449 | // must be configured from nothing (a leaving entry keeps no configure). | ||
| @@ -471,7 +472,7 @@ func (c *Core) unmapWindow(v ports.WindowUnmapped) { | |||||
| 471 | shots := c.snapshot(now) | 472 | shots := c.snapshot(now) | ||
| 472 | var shot *rectShot | 473 | var shot *rectShot | ||
| 473 | for i := range shots { | 474 | for i := range shots { | ||
| 474 | - | if shots[i].overview { | 475 | + | if shots[i].preview { |
| 475 | shots[i].ws = nil | 476 | shots[i].ws = nil | ||
| 476 | } | 477 | } | ||
| 477 | if shots[i].sc == sc { | 478 | if shots[i].sc == sc { | ||
| @@ -620,7 +621,7 @@ func (c *Core) snapshot(now time.Time) []viewShot { | |||||
| 620 | } | 621 | } | ||
| 621 | s.sc = sc | 622 | s.sc = sc | ||
| 622 | w := m.Current() | 623 | w := m.Current() | ||
| 623 | - | s.ws, s.overview = w, m.previewing() | 624 | + | s.ws, s.preview = w, m.previewing() |
| 624 | if !m.previewing() { | 625 | if !m.previewing() { | ||
| 625 | // The overview does not scroll or slide: no camera. | 626 | // The overview does not scroll or slide: no camera. | ||
| 626 | s.settledView, s.view = w.View, float64(w.View)+w.view.off | 627 | s.settledView, s.view = w.View, float64(w.View)+w.view.off | ||
| @@ -705,7 +706,7 @@ func (c *Core) transition(before []viewShot, now time.Time) { | |||||
| 705 | if b.ws == nil || !c.hasScreen(b.sc) || c.following(b.sc) { | 706 | if b.ws == nil || !c.hasScreen(b.sc) || c.following(b.sc) { | ||
| 706 | continue | 707 | continue | ||
| 707 | } | 708 | } | ||
| 708 | - | if b.overview || b.sc.mon.previewing() { | 709 | + | if b.preview || b.sc.mon.previewing() { |
| 709 | c.transitionOverview(b, now) | 710 | c.transitionOverview(b, now) | ||
| 710 | continue | 711 | continue | ||
| 711 | } | 712 | } | ||
| @@ -886,18 +887,19 @@ func (c *Core) peekDim(p Placement) float64 { | |||||
| 886 | } | 887 | } | ||
| 887 | 888 | ||||
| 888 | // transitionOverview starts the motions of an action that opened or closed | 889 | // transitionOverview starts the motions of an action that opened or closed | ||
| 889 | - | // the overview, or moved inside it (the selection, the rows). The camera | 890 | + | // a preview (the overview or the column switcher), or moved inside it (the |
| 890 | - | // springs stay stopped (ToggleOverview): every window drawn before and | 891 | + | // selection, the rows). The camera springs stay stopped (ToggleOverview): |
| 891 | - | // after moves from its shown rect to its settled one, its content scaled | 892 | + | // every window drawn before and after moves from its shown rect to its |
| 892 | - | // with it (a card's Zoom, rectMotion.show). A window drawn after and not | 893 | + | // settled one, its content scaled with it (a card's Zoom, |
| 893 | - | // before (another row, a column the overview hid) fades in with no motion | 894 | + | // rectMotion.show). A window drawn after and not before (another row, a |
| 895 | + | // column a preview hid) fades in with no motion | |||
| 894 | // of its own; one drawn before and not after just goes. Motions of the | 896 | // of its own; one drawn before and not after just goes. Motions of the | ||
| 895 | // other state are dropped first: their rects are not the ones snapshot | 897 | // other state are dropped first: their rects are not the ones snapshot | ||
| 896 | // measured from. A fade that ran on a window drawn in both states carries | 898 | // measured from. A fade that ran on a window drawn in both states carries | ||
| 897 | // over (picking a card that still fades in does not pop it). | 899 | // over (picking a card that still fades in does not pop it). | ||
| 898 | func (c *Core) transitionOverview(b *viewShot, now time.Time) { | 900 | func (c *Core) transitionOverview(b *viewShot, now time.Time) { | ||
| 899 | sc := b.sc | 901 | sc := b.sc | ||
| 900 | - | changed := b.overview != sc.mon.previewing() | 902 | + | changed := b.preview != sc.mon.previewing() |
| 901 | if changed { | 903 | if changed { | ||
| 902 | sc.stopRects() | 904 | sc.stopRects() | ||
| 903 | } | 905 | } | ||
| @@ -937,7 +939,7 @@ func (c *Core) transitionOverview(b *viewShot, now time.Time) { | |||||
| 937 | func movedFrom(b *viewShot, before []viewShot) *viewShot { | 939 | func movedFrom(b *viewShot, before []viewShot) *viewShot { | ||
| 938 | w := b.sc.mon.Current() | 940 | w := b.sc.mon.Current() | ||
| 939 | for i := range before { | 941 | for i := range before { | ||
| 940 | - | if o := &before[i]; o.ws == w && o.sc != b.sc && !o.overview { | 942 | + | if o := &before[i]; o.ws == w && o.sc != b.sc && !o.preview { |
| 941 | return o | 943 | return o | ||
| 942 | } | 944 | } | ||
| 943 | } | 945 | } | ||