fix(core): drop the fullscreen latch once its window has the focus
The latch only cleared when its window asked for fullscreen while focused, so a refocus without a request left it stale and later requests were refused. Core now settles it at each publish outside the overview, and a column moved away drops it.
- commit
- c2347ef1042d568bac4eb9366800150d594be411
- author
- brice · · 2026-10-02 15:41 UTC
- parent
- adeb040
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +151 -3
M internal/core/core.go +6 -0
| @@ -483,6 +483,12 @@ func (c *Core) publish(ctx context.Context) error { | |||
| 483 | 483 | if c.security.Protected { | |
| 484 | 484 | return c.publishProtected(ctx) | |
| 485 | 485 | } | |
| 486 | + | for _, sc := range c.screens { | |
| 487 | + | // The overview's selection is provisional: it focuses nothing yet. | |
| 488 | + | if !sc.mon.ov.open { | |
| 489 | + | sc.mon.Current().settleLeft() | |
| 490 | + | } | |
| 491 | + | } | |
| 486 | 492 | c.captureExpire() | |
| 487 | 493 | capture, err := c.captureEvaluate(ctx) | |
| 488 | 494 | if err != nil { | |
| @@ -483,6 +483,12 @@ func (c *Core) publish(ctx context.Context) error { | |||||
| 483 | if c.security.Protected { | 483 | if c.security.Protected { | ||
| 484 | return c.publishProtected(ctx) | 484 | return c.publishProtected(ctx) | ||
| 485 | } | 485 | } | ||
| 486 | + | for _, sc := range c.screens { | |||
| 487 | + | // The overview's selection is provisional: it focuses nothing yet. | |||
| 488 | + | if !sc.mon.ov.open { | |||
| 489 | + | sc.mon.Current().settleLeft() | |||
| 490 | + | } | |||
| 491 | + | } | |||
| 486 | c.captureExpire() | 492 | c.captureExpire() | ||
| 487 | capture, err := c.captureEvaluate(ctx) | 493 | capture, err := c.captureEvaluate(ctx) | ||
| 488 | if err != nil { | 494 | if err != nil { | ||
A internal/core/fullscreen_left_core_test.go +80 -0
| @@ -0,0 +1,80 @@ | |||
| 1 | + | package core | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "context" | |
| 5 | + | "testing" | |
| 6 | + | "time" | |
| 7 | + | ||
| 8 | + | "github.com/bnema/neferwl/internal/ports" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | // Through core, scroll overflow: an app whose fullscreen the user left by | |
| 12 | + | // activating another window cannot cover it again by asking at once; once | |
| 13 | + | // activated again, it may ask and is honoured. | |
| 14 | + | func TestCoreLeftFullscreenLatch(t *testing.T) { | |
| 15 | + | client := make(chan ports.ClientEvent) | |
| 16 | + | output := make(chan ports.OutputEvent) | |
| 17 | + | commands := make(chan ports.ClientCommand, 64) | |
| 18 | + | scenes := make(chan []ports.Scene, 1) | |
| 19 | + | cfg := ports.Config{} | |
| 20 | + | cfg.Keyboard.CmdKey = "super" | |
| 21 | + | cfg.Layout.MaxColumns = 2 | |
| 22 | + | c, err := New(cfg, Channels{Client: client, Output: output, Commands: commands, Scenes: scenes, Clock: laterClock(t)}) | |
| 23 | + | if err != nil { | |
| 24 | + | t.Fatal(err) | |
| 25 | + | } | |
| 26 | + | ctx, cancel := context.WithCancel(context.Background()) | |
| 27 | + | defer cancel() | |
| 28 | + | done := make(chan struct{}) | |
| 29 | + | go func() { defer close(done); _ = c.Run(ctx) }() | |
| 30 | + | go func() { | |
| 31 | + | for range commands { | |
| 32 | + | } | |
| 33 | + | }() | |
| 34 | + | full := func() ports.WindowID { | |
| 35 | + | t.Helper() | |
| 36 | + | select { | |
| 37 | + | case s := <-scenes: | |
| 38 | + | for _, w := range s[0].Windows { | |
| 39 | + | if w.Fullscreen && !w.Hidden { | |
| 40 | + | return w.ID | |
| 41 | + | } | |
| 42 | + | } | |
| 43 | + | return 0 | |
| 44 | + | case <-time.After(time.Second): | |
| 45 | + | t.Fatal("scene timeout") | |
| 46 | + | return 0 | |
| 47 | + | } | |
| 48 | + | } | |
| 49 | + | output <- ports.OutputAdded{Info: ports.OutputInfo{Name: "OUT-1", Width: 100, Height: 80}} | |
| 50 | + | full() | |
| 51 | + | client <- ports.WindowMapped{ID: 1} | |
| 52 | + | full() | |
| 53 | + | client <- ports.WindowMapped{ID: 2} | |
| 54 | + | full() | |
| 55 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |
| 56 | + | if id := full(); id != 2 { | |
| 57 | + | t.Fatalf("fullscreen %d, want 2", id) | |
| 58 | + | } | |
| 59 | + | client <- ports.WindowActivate{ID: 1} | |
| 60 | + | if id := full(); id != 0 { | |
| 61 | + | t.Fatalf("after activation: fullscreen %d", id) | |
| 62 | + | } | |
| 63 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |
| 64 | + | if id := full(); id != 0 { | |
| 65 | + | t.Fatalf("re-request covered: fullscreen %d", id) | |
| 66 | + | } | |
| 67 | + | client <- ports.WindowActivate{ID: 2} | |
| 68 | + | full() | |
| 69 | + | client <- ports.WindowActivate{ID: 1} | |
| 70 | + | full() | |
| 71 | + | // The latch settled when 2 had the focus: scroll overflow honours an | |
| 72 | + | // unfocused window's request again. | |
| 73 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |
| 74 | + | if id := full(); id != 2 { | |
| 75 | + | t.Fatalf("after refocus: fullscreen %d, want 2", id) | |
| 76 | + | } | |
| 77 | + | cancel() | |
| 78 | + | <-done | |
| 79 | + | close(commands) | |
| 80 | + | } | |
| @@ -0,0 +1,80 @@ | |||||
| 1 | + | package core | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "context" | |||
| 5 | + | "testing" | |||
| 6 | + | "time" | |||
| 7 | + | ||||
| 8 | + | "github.com/bnema/neferwl/internal/ports" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | // Through core, scroll overflow: an app whose fullscreen the user left by | |||
| 12 | + | // activating another window cannot cover it again by asking at once; once | |||
| 13 | + | // activated again, it may ask and is honoured. | |||
| 14 | + | func TestCoreLeftFullscreenLatch(t *testing.T) { | |||
| 15 | + | client := make(chan ports.ClientEvent) | |||
| 16 | + | output := make(chan ports.OutputEvent) | |||
| 17 | + | commands := make(chan ports.ClientCommand, 64) | |||
| 18 | + | scenes := make(chan []ports.Scene, 1) | |||
| 19 | + | cfg := ports.Config{} | |||
| 20 | + | cfg.Keyboard.CmdKey = "super" | |||
| 21 | + | cfg.Layout.MaxColumns = 2 | |||
| 22 | + | c, err := New(cfg, Channels{Client: client, Output: output, Commands: commands, Scenes: scenes, Clock: laterClock(t)}) | |||
| 23 | + | if err != nil { | |||
| 24 | + | t.Fatal(err) | |||
| 25 | + | } | |||
| 26 | + | ctx, cancel := context.WithCancel(context.Background()) | |||
| 27 | + | defer cancel() | |||
| 28 | + | done := make(chan struct{}) | |||
| 29 | + | go func() { defer close(done); _ = c.Run(ctx) }() | |||
| 30 | + | go func() { | |||
| 31 | + | for range commands { | |||
| 32 | + | } | |||
| 33 | + | }() | |||
| 34 | + | full := func() ports.WindowID { | |||
| 35 | + | t.Helper() | |||
| 36 | + | select { | |||
| 37 | + | case s := <-scenes: | |||
| 38 | + | for _, w := range s[0].Windows { | |||
| 39 | + | if w.Fullscreen && !w.Hidden { | |||
| 40 | + | return w.ID | |||
| 41 | + | } | |||
| 42 | + | } | |||
| 43 | + | return 0 | |||
| 44 | + | case <-time.After(time.Second): | |||
| 45 | + | t.Fatal("scene timeout") | |||
| 46 | + | return 0 | |||
| 47 | + | } | |||
| 48 | + | } | |||
| 49 | + | output <- ports.OutputAdded{Info: ports.OutputInfo{Name: "OUT-1", Width: 100, Height: 80}} | |||
| 50 | + | full() | |||
| 51 | + | client <- ports.WindowMapped{ID: 1} | |||
| 52 | + | full() | |||
| 53 | + | client <- ports.WindowMapped{ID: 2} | |||
| 54 | + | full() | |||
| 55 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |||
| 56 | + | if id := full(); id != 2 { | |||
| 57 | + | t.Fatalf("fullscreen %d, want 2", id) | |||
| 58 | + | } | |||
| 59 | + | client <- ports.WindowActivate{ID: 1} | |||
| 60 | + | if id := full(); id != 0 { | |||
| 61 | + | t.Fatalf("after activation: fullscreen %d", id) | |||
| 62 | + | } | |||
| 63 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |||
| 64 | + | if id := full(); id != 0 { | |||
| 65 | + | t.Fatalf("re-request covered: fullscreen %d", id) | |||
| 66 | + | } | |||
| 67 | + | client <- ports.WindowActivate{ID: 2} | |||
| 68 | + | full() | |||
| 69 | + | client <- ports.WindowActivate{ID: 1} | |||
| 70 | + | full() | |||
| 71 | + | // The latch settled when 2 had the focus: scroll overflow honours an | |||
| 72 | + | // unfocused window's request again. | |||
| 73 | + | client <- ports.WindowFullscreenRequest{ID: 2, Fullscreen: true} | |||
| 74 | + | if id := full(); id != 2 { | |||
| 75 | + | t.Fatalf("after refocus: fullscreen %d, want 2", id) | |||
| 76 | + | } | |||
| 77 | + | cancel() | |||
| 78 | + | <-done | |||
| 79 | + | close(commands) | |||
| 80 | + | } | |||
M internal/core/fullscreen_left_test.go +49 -0
| @@ -15,6 +15,8 @@ func TestLeftFullscreenRefusesRequest(t *testing.T) { | |||
| 15 | 15 | m.OverviewPick(1) | |
| 16 | 16 | }, | |
| 17 | 17 | } | |
| 18 | + | // Fixed overflow refuses an unfocused window's request anyway; scroll | |
| 19 | + | // with a float is where the latch alone refuses it. | |
| 18 | 20 | for _, tc := range pinnedCases { | |
| 19 | 21 | for name, leave := range leaves { | |
| 20 | 22 | t.Run(tc.name+"/"+name, func(t *testing.T) { | |
| @@ -53,6 +55,53 @@ func TestToggleFullscreenOffNoLatch(t *testing.T) { | |||
| 53 | 55 | } | |
| 54 | 56 | } | |
| 55 | 57 | ||
| 58 | + | // Focusing the window again drops the latch even without a request, so a | |
| 59 | + | // later request while unfocused follows the usual rules again. | |
| 60 | + | func TestLeftFullscreenSettlesOnFocus(t *testing.T) { | |
| 61 | + | m := coverMonitor(OverflowScroll, true) | |
| 62 | + | w := m.Current() | |
| 63 | + | w.Activate(1) | |
| 64 | + | w.FocusID(2) | |
| 65 | + | w.settleLeft() | |
| 66 | + | if w.left != 0 { | |
| 67 | + | t.Fatalf("left %d", w.left) | |
| 68 | + | } | |
| 69 | + | w.FocusID(1) | |
| 70 | + | m.SetFullscreen(2, true) | |
| 71 | + | if w.fullscreen != 2 { | |
| 72 | + | t.Fatalf("request refused: %d", w.fullscreen) | |
| 73 | + | } | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | // A taskbar fullscreen request activates the window first (core), so it | |
| 77 | + | // passes the latch. | |
| 78 | + | func TestLeftFullscreenTaskbar(t *testing.T) { | |
| 79 | + | m := coverMonitor(OverflowScroll, true) | |
| 80 | + | w := m.Current() | |
| 81 | + | w.Activate(1) | |
| 82 | + | w.Activate(2) | |
| 83 | + | m.SetFullscreen(2, true) | |
| 84 | + | if w.fullscreen != 2 || w.left != 0 { | |
| 85 | + | t.Fatalf("fullscreen %d, left %d", w.fullscreen, w.left) | |
| 86 | + | } | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // A latched window moved away with its column drops the latch. | |
| 90 | + | func TestLeftFullscreenColumnMoved(t *testing.T) { | |
| 91 | + | m := monitor() | |
| 92 | + | m.AddWindow(1) | |
| 93 | + | m.AddWindow(2) | |
| 94 | + | w := m.Current() | |
| 95 | + | m.ToggleFullscreen() | |
| 96 | + | w.Activate(1) | |
| 97 | + | w.FocusID(2) | |
| 98 | + | w.left = 2 // as if moved before the latch settled | |
| 99 | + | m.MoveToWorkspace(1, true) | |
| 100 | + | if w.left != 0 { | |
| 101 | + | t.Fatalf("left %d", w.left) | |
| 102 | + | } | |
| 103 | + | } | |
| 104 | + | ||
| 56 | 105 | // A latched window that goes away drops the latch. | |
| 57 | 106 | func TestLeftFullscreenRemoved(t *testing.T) { | |
| 58 | 107 | m := coverMonitor(OverflowScroll, true) | |
| @@ -15,6 +15,8 @@ func TestLeftFullscreenRefusesRequest(t *testing.T) { | |||||
| 15 | m.OverviewPick(1) | 15 | m.OverviewPick(1) | ||
| 16 | }, | 16 | }, | ||
| 17 | } | 17 | } | ||
| 18 | + | // Fixed overflow refuses an unfocused window's request anyway; scroll | |||
| 19 | + | // with a float is where the latch alone refuses it. | |||
| 18 | for _, tc := range pinnedCases { | 20 | for _, tc := range pinnedCases { | ||
| 19 | for name, leave := range leaves { | 21 | for name, leave := range leaves { | ||
| 20 | t.Run(tc.name+"/"+name, func(t *testing.T) { | 22 | t.Run(tc.name+"/"+name, func(t *testing.T) { | ||
| @@ -53,6 +55,53 @@ func TestToggleFullscreenOffNoLatch(t *testing.T) { | |||||
| 53 | } | 55 | } | ||
| 54 | } | 56 | } | ||
| 55 | 57 | ||||
| 58 | + | // Focusing the window again drops the latch even without a request, so a | |||
| 59 | + | // later request while unfocused follows the usual rules again. | |||
| 60 | + | func TestLeftFullscreenSettlesOnFocus(t *testing.T) { | |||
| 61 | + | m := coverMonitor(OverflowScroll, true) | |||
| 62 | + | w := m.Current() | |||
| 63 | + | w.Activate(1) | |||
| 64 | + | w.FocusID(2) | |||
| 65 | + | w.settleLeft() | |||
| 66 | + | if w.left != 0 { | |||
| 67 | + | t.Fatalf("left %d", w.left) | |||
| 68 | + | } | |||
| 69 | + | w.FocusID(1) | |||
| 70 | + | m.SetFullscreen(2, true) | |||
| 71 | + | if w.fullscreen != 2 { | |||
| 72 | + | t.Fatalf("request refused: %d", w.fullscreen) | |||
| 73 | + | } | |||
| 74 | + | } | |||
| 75 | + | ||||
| 76 | + | // A taskbar fullscreen request activates the window first (core), so it | |||
| 77 | + | // passes the latch. | |||
| 78 | + | func TestLeftFullscreenTaskbar(t *testing.T) { | |||
| 79 | + | m := coverMonitor(OverflowScroll, true) | |||
| 80 | + | w := m.Current() | |||
| 81 | + | w.Activate(1) | |||
| 82 | + | w.Activate(2) | |||
| 83 | + | m.SetFullscreen(2, true) | |||
| 84 | + | if w.fullscreen != 2 || w.left != 0 { | |||
| 85 | + | t.Fatalf("fullscreen %d, left %d", w.fullscreen, w.left) | |||
| 86 | + | } | |||
| 87 | + | } | |||
| 88 | + | ||||
| 89 | + | // A latched window moved away with its column drops the latch. | |||
| 90 | + | func TestLeftFullscreenColumnMoved(t *testing.T) { | |||
| 91 | + | m := monitor() | |||
| 92 | + | m.AddWindow(1) | |||
| 93 | + | m.AddWindow(2) | |||
| 94 | + | w := m.Current() | |||
| 95 | + | m.ToggleFullscreen() | |||
| 96 | + | w.Activate(1) | |||
| 97 | + | w.FocusID(2) | |||
| 98 | + | w.left = 2 // as if moved before the latch settled | |||
| 99 | + | m.MoveToWorkspace(1, true) | |||
| 100 | + | if w.left != 0 { | |||
| 101 | + | t.Fatalf("left %d", w.left) | |||
| 102 | + | } | |||
| 103 | + | } | |||
| 104 | + | ||||
| 56 | // A latched window that goes away drops the latch. | 105 | // A latched window that goes away drops the latch. | ||
| 57 | func TestLeftFullscreenRemoved(t *testing.T) { | 106 | func TestLeftFullscreenRemoved(t *testing.T) { | ||
| 58 | m := coverMonitor(OverflowScroll, true) | 107 | m := coverMonitor(OverflowScroll, true) | ||
M internal/core/layout.go +13 -1
| @@ -818,6 +818,9 @@ func (w *Workspace) takeColumn() (Column, bool) { | |||
| 818 | 818 | if slices.Contains(col.Windows, w.fullscreen) { | |
| 819 | 819 | w.fullscreen = 0 | |
| 820 | 820 | } | |
| 821 | + | if slices.Contains(col.Windows, w.left) { | |
| 822 | + | w.left = 0 | |
| 823 | + | } | |
| 821 | 824 | w.Columns = slices.Delete(w.Columns, w.Focus, w.Focus+1) | |
| 822 | 825 | w.Focus = min(w.Focus, max(len(w.Columns)-1, 0)) | |
| 823 | 826 | if len(w.Columns) == 0 { | |
| @@ -953,7 +956,8 @@ func (w *Workspace) endFullscreen() { | |||
| 953 | 956 | } | |
| 954 | 957 | ||
| 955 | 958 | // leaveFullscreen ends the fullscreen for another window the user chose, | |
| 956 | - | // and refuses its client's requests until it is focused again (mayCover). | |
| 959 | + | // and refuses its client's requests until it is focused again (mayCover, | |
| 960 | + | // settleLeft). | |
| 957 | 961 | func (w *Workspace) leaveFullscreen() { | |
| 958 | 962 | if w.fullscreen != 0 { | |
| 959 | 963 | w.left = w.fullscreen | |
| @@ -1080,6 +1084,14 @@ func (w *Workspace) overviewArea() Rect { return w.reserved } | |||
| 1080 | 1084 | ||
| 1081 | 1085 | func (w *Workspace) gap() int { return min(w.Gaps, w.Usable.W/2, w.Usable.H/2) } | |
| 1082 | 1086 | ||
| 1087 | + | // settleLeft drops the latch once its window has the focus again, however | |
| 1088 | + | // it got it; core calls it after each event. | |
| 1089 | + | func (w *Workspace) settleLeft() { | |
| 1090 | + | if id, ok := w.Focused(); ok && id == w.left { | |
| 1091 | + | w.left = 0 | |
| 1092 | + | } | |
| 1093 | + | } | |
| 1094 | + | ||
| 1083 | 1095 | // mayCover reports whether a client may make id fullscreen. In fixed | |
| 1084 | 1096 | // overflow it would cover the output at once and take the keyboard from | |
| 1085 | 1097 | // the focused window, so only the focused window may; the request of | |
| @@ -818,6 +818,9 @@ func (w *Workspace) takeColumn() (Column, bool) { | |||||
| 818 | if slices.Contains(col.Windows, w.fullscreen) { | 818 | if slices.Contains(col.Windows, w.fullscreen) { | ||
| 819 | w.fullscreen = 0 | 819 | w.fullscreen = 0 | ||
| 820 | } | 820 | } | ||
| 821 | + | if slices.Contains(col.Windows, w.left) { | |||
| 822 | + | w.left = 0 | |||
| 823 | + | } | |||
| 821 | w.Columns = slices.Delete(w.Columns, w.Focus, w.Focus+1) | 824 | w.Columns = slices.Delete(w.Columns, w.Focus, w.Focus+1) | ||
| 822 | w.Focus = min(w.Focus, max(len(w.Columns)-1, 0)) | 825 | w.Focus = min(w.Focus, max(len(w.Columns)-1, 0)) | ||
| 823 | if len(w.Columns) == 0 { | 826 | if len(w.Columns) == 0 { | ||
| @@ -953,7 +956,8 @@ func (w *Workspace) endFullscreen() { | |||||
| 953 | } | 956 | } | ||
| 954 | 957 | ||||
| 955 | // leaveFullscreen ends the fullscreen for another window the user chose, | 958 | // leaveFullscreen ends the fullscreen for another window the user chose, | ||
| 956 | - | // and refuses its client's requests until it is focused again (mayCover). | 959 | + | // and refuses its client's requests until it is focused again (mayCover, |
| 960 | + | // settleLeft). | |||
| 957 | func (w *Workspace) leaveFullscreen() { | 961 | func (w *Workspace) leaveFullscreen() { | ||
| 958 | if w.fullscreen != 0 { | 962 | if w.fullscreen != 0 { | ||
| 959 | w.left = w.fullscreen | 963 | w.left = w.fullscreen | ||
| @@ -1080,6 +1084,14 @@ func (w *Workspace) overviewArea() Rect { return w.reserved } | |||||
| 1080 | 1084 | ||||
| 1081 | func (w *Workspace) gap() int { return min(w.Gaps, w.Usable.W/2, w.Usable.H/2) } | 1085 | func (w *Workspace) gap() int { return min(w.Gaps, w.Usable.W/2, w.Usable.H/2) } | ||
| 1082 | 1086 | ||||
| 1087 | + | // settleLeft drops the latch once its window has the focus again, however | |||
| 1088 | + | // it got it; core calls it after each event. | |||
| 1089 | + | func (w *Workspace) settleLeft() { | |||
| 1090 | + | if id, ok := w.Focused(); ok && id == w.left { | |||
| 1091 | + | w.left = 0 | |||
| 1092 | + | } | |||
| 1093 | + | } | |||
| 1094 | + | ||||
| 1083 | // mayCover reports whether a client may make id fullscreen. In fixed | 1095 | // mayCover reports whether a client may make id fullscreen. In fixed | ||
| 1084 | // overflow it would cover the output at once and take the keyboard from | 1096 | // overflow it would cover the output at once and take the keyboard from | ||
| 1085 | // the focused window, so only the focused window may; the request of | 1097 | // the focused window, so only the focused window may; the request of | ||
M internal/core/overview.go +2 -1
| @@ -704,7 +704,8 @@ func (w *Workspace) rowHeight() int { | |||
| 704 | 704 | // corner. Fixed overflow uses its on-screen geometry; scroll overflow uses | |
| 705 | 705 | // an unscrolled row. span is the row's width; sel is the focused column. | |
| 706 | 706 | func (w *Workspace) previewTiles() (tiles []Placement, span int, sel Rect) { | |
| 707 | - | if c := w.cover(); c != 0 && w.pinned() { | |
| 707 | + | if w.pinned() { | |
| 708 | + | c := w.cover() | |
| 708 | 709 | // The row shows the columns a pinned fullscreen window hides, | |
| 709 | 710 | // laid out as without it; its own tile stays marked fullscreen. | |
| 710 | 711 | v := *w | |
| @@ -704,7 +704,8 @@ func (w *Workspace) rowHeight() int { | |||||
| 704 | // corner. Fixed overflow uses its on-screen geometry; scroll overflow uses | 704 | // corner. Fixed overflow uses its on-screen geometry; scroll overflow uses | ||
| 705 | // an unscrolled row. span is the row's width; sel is the focused column. | 705 | // an unscrolled row. span is the row's width; sel is the focused column. | ||
| 706 | func (w *Workspace) previewTiles() (tiles []Placement, span int, sel Rect) { | 706 | func (w *Workspace) previewTiles() (tiles []Placement, span int, sel Rect) { | ||
| 707 | - | if c := w.cover(); c != 0 && w.pinned() { | 707 | + | if w.pinned() { |
| 708 | + | c := w.cover() | |||
| 708 | // The row shows the columns a pinned fullscreen window hides, | 709 | // The row shows the columns a pinned fullscreen window hides, | ||
| 709 | // laid out as without it; its own tile stays marked fullscreen. | 710 | // laid out as without it; its own tile stays marked fullscreen. | ||
| 710 | v := *w | 711 | v := *w | ||
M internal/core/overview_stack.go +1 -1
| @@ -57,7 +57,7 @@ func (w *Workspace) stack() []stackItem { | |||
| 57 | 57 | case w.overviewMaximized(): | |
| 58 | 58 | items = append(items, stackItem{stackColumn, c}) | |
| 59 | 59 | default: | |
| 60 | - | // Its column is the only one. | |
| 60 | + | // One card of all columns, its own marked fullscreen. | |
| 61 | 61 | items = append(items, stackItem{kind: stackColumns}) | |
| 62 | 62 | columns = false | |
| 63 | 63 | } | |
| @@ -57,7 +57,7 @@ func (w *Workspace) stack() []stackItem { | |||||
| 57 | case w.overviewMaximized(): | 57 | case w.overviewMaximized(): | ||
| 58 | items = append(items, stackItem{stackColumn, c}) | 58 | items = append(items, stackItem{stackColumn, c}) | ||
| 59 | default: | 59 | default: | ||
| 60 | - | // Its column is the only one. | 60 | + | // One card of all columns, its own marked fullscreen. |
| 61 | items = append(items, stackItem{kind: stackColumns}) | 61 | items = append(items, stackItem{kind: stackColumns}) | ||
| 62 | columns = false | 62 | columns = false | ||
| 63 | } | 63 | } | ||