1package core
2
3import (
4 "testing"
5
6 "github.com/bnema/neferwl/internal/ports"
7)
8
9// stashCoverCore has two screens; the first holds tile 1 and the hidden
10// stash [2 3], 3 selected, under a covering fullscreen window: tile 1, or
11// float 4 with float set.
12func stashCoverCore(t *testing.T, overflow Overflow, float bool) (*Core, *Workspace, WindowID) {
13 t.Helper()
14 cfg := ports.Config{}
15 cfg.Keyboard.CmdKey = "super"
16 cfg.Layout.Overflow = string(overflow)
17 cfg.Layout.MaxColumns = 2
18 c, err := New(cfg, Channels{Scenes: make(chan []ports.Scene, 1)})
19 if err != nil {
20 t.Fatal(err)
21 }
22 c.addScreen(ports.OutputInfo{Name: "A", Width: 100, Height: 80})
23 c.addScreen(ports.OutputInfo{Name: "B", Width: 100, Height: 80})
24 m := c.screens[0].mon
25 for id := WindowID(1); id <= 3; id++ {
26 m.AddWindow(id)
27 }
28 w := m.Current()
29 for _, id := range []WindowID{2, 3} {
30 w.FocusID(id)
31 w.ToggleWindowStash()
32 }
33 c.applyAction(ActionToggleStashVisible) // hidden
34 cover := WindowID(1)
35 if float {
36 cover = 4
37 m.AddFloating(4, 100, 80)
38 m.SetFullscreen(4, true)
39 } else {
40 c.applyAction(ActionToggleFullscreen)
41 }
42 if w.cover() != cover || c.focusScreen != 0 {
43 t.Fatalf("setup: cover %d", w.cover())
44 }
45 return c, w, cover
46}
47
48// toggle-stash-visible is a user override: under any covering fullscreen
49// window it shows the stash on top, focused, the window fullscreen
50// behind. Moves stay in the stash, at its edges too. Toggling again
51// hides it, back on the fullscreen window.
52func TestStashShowsOverCover(t *testing.T) {
53 for _, tc := range []struct {
54 name string
55 overflow Overflow
56 float bool
57 }{
58 {"fixed tile", OverflowFixed, false},
59 {"scroll tile", OverflowScroll, false},
60 {"fixed float", OverflowFixed, true},
61 {"scroll float", OverflowScroll, true},
62 } {
63 t.Run(tc.name, func(t *testing.T) {
64 c, w, cover := stashCoverCore(t, tc.overflow, tc.float)
65 c.applyAction(ActionToggleStashVisible)
66 ps := w.Layout()
67 if id, _ := w.Focused(); id != 3 || w.cover() != cover {
68 t.Fatalf("focused %d cover %d", id, w.cover())
69 }
70 if p := previewOf(t, ps, cover); p.Hidden || !p.Fullscreen {
71 t.Fatalf("cover %+v", p)
72 }
73 if p := previewOf(t, ps, 3); p.Hidden || ps[len(ps)-1].ID != 3 && ps[len(ps)-2].ID != 3 {
74 t.Fatalf("stash not on top: %+v", ps)
75 }
76 c.applyAction(ActionFocusColumnLeft)
77 if id, _ := w.Focused(); id != 2 || w.cover() != cover {
78 t.Fatalf("left: focused %d cover %d", id, w.cover())
79 }
80 c.applyAction(ActionFocusColumnRight)
81 c.applyAction(ActionFocusColumnRight) // stash edge: stays, like a shown stash
82 if id, _ := w.Focused(); id != 3 || c.focusScreen != 0 {
83 t.Fatalf("edge: focused %d screen %d", id, c.focusScreen)
84 }
85 c.applyAction(ActionToggleStashVisible)
86 if id, _ := w.Focused(); id != cover || w.cover() != cover || !previewOf(t, w.Layout(), 3).Hidden {
87 t.Fatalf("hide: focused %d cover %d", id, w.cover())
88 }
89 })
90 }
91}
92
93// Only the user shows the stash over the cover: a window mapping
94// meanwhile is not captured, a dialog of the cover shows above the
95// stash, and the override ends with its cover.
96func TestStashOverCoverEdges(t *testing.T) {
97 c, w, cover := stashCoverCore(t, OverflowFixed, false)
98 m := c.screens[0].mon
99 m.SetStashCapture(true)
100 c.applyAction(ActionToggleStashVisible)
101 m.AddWindow(5)
102 if w.stashIndex(5) >= 0 || !previewOf(t, w.Layout(), 5).Hidden {
103 t.Fatalf("new window shown: %+v", w.Layout())
104 }
105 w.AddDialog(6, cover, 20, 20)
106 if ps := w.Layout(); ps[len(ps)-1].ID != 6 {
107 t.Fatalf("dialog under the stash: %+v", ps)
108 }
109 if id, _ := w.Focused(); id != 6 {
110 t.Fatalf("dialog focus %d", id)
111 }
112 c.applyAction(ActionFocusColumnLeft) // back to the stash
113 if id, _ := w.Focused(); id != 3 || !w.stashOverCover() {
114 t.Fatalf("from dialog: focused %d", id)
115 }
116 w.RemoveWindow(6)
117 // The cover closes: the stash it showed over is a plain shown stash;
118 // a later fullscreen window is never covered by it.
119 w.RemoveWindow(cover)
120 if w.stashOver != 0 {
121 t.Fatal("override outlived its cover")
122 }
123 w.FocusID(5)
124 w.ToggleFullscreen()
125 if p := previewOf(t, w.Layout(), 3); !p.Hidden {
126 t.Fatalf("stash over a new cover: %+v", p)
127 }
128}
129
130// Another window's fullscreen request ends the override: the old cover
131// asking again never brings the stash back over it.
132func TestStashOverCoverOtherRequest(t *testing.T) {
133 c, w, cover := stashCoverCore(t, OverflowScroll, false)
134 m := c.screens[0].mon
135 m.AddWindow(5)
136 c.applyAction(ActionToggleStashVisible)
137 m.SetFullscreen(5, true)
138 m.SetFullscreen(cover, true)
139 if w.cover() != cover || w.stashOverCover() {
140 t.Fatalf("cover %d, override revived", w.cover())
141 }
142}
143
144// Acting on a stashed window over the cover leaves the cover for it:
145// toggle-window-stash returns it to a visible column, toggle-fullscreen
146// makes it the fullscreen window.
147func TestStashOverCoverLeaves(t *testing.T) {
148 c, w, _ := stashCoverCore(t, OverflowFixed, false)
149 c.applyAction(ActionToggleStashVisible)
150 c.applyAction(ActionToggleWindowStash)
151 if id, _ := w.Focused(); id != 3 || w.fullscreen != 0 || previewOf(t, w.Layout(), 3).Hidden {
152 t.Fatalf("unstash: focused %d fullscreen %d", id, w.fullscreen)
153 }
154 c, w, _ = stashCoverCore(t, OverflowFixed, false)
155 c.applyAction(ActionToggleStashVisible)
156 c.applyAction(ActionToggleFullscreen)
157 if w.cover() != 3 || w.left != 1 {
158 t.Fatalf("fullscreen: cover %d left %d", w.cover(), w.left)
159 }
160}