1package core
2
3import (
4 "slices"
5 "testing"
6)
7
8func freeWorkspace() *Workspace {
9 w := workspace()
10 w.SetOutput(1000, 800)
11 w.SetGaps(10)
12 return w
13}
14
15func TestFreeFloatRect(t *testing.T) {
16 for _, size := range [][2]int{{1000, 800}, {2000, 1200}} {
17 w := freeWorkspace()
18 w.SetOutput(size[0], size[1])
19 f := Float{ID: 1, W: 200, H: 100, free: true, cx: 0.25, cy: 0.75}
20 r := w.floatRect(f)
21 u := w.Usable
22 if r.X+r.W/2 != u.X+u.W/4 || r.Y+r.H/2 != u.Y+u.H*3/4 || r.W != 200 || r.H != 100 {
23 t.Fatal(size, r)
24 }
25 // Clamped inside the usable area at the edges.
26 f.cx, f.cy = 0, 1
27 r = w.floatRect(f)
28 if r.X != u.X || r.Y+r.H != u.Y+u.H {
29 t.Fatal(size, r)
30 }
31 }
32}
33
34func TestToggleFloatingRoundTrip(t *testing.T) {
35 w := freeWorkspace()
36 w.Columns = []Column{{Windows: []WindowID{1}}, {Windows: []WindowID{2, 3}, Focus: 1, Cells: 1}}
37 w.Focus = 1
38 r, _ := w.placed(3)
39 w.Apply(ActionToggleFloating)
40 i := w.floatIndex(3)
41 if i < 0 || !w.Floats[i].free || !w.floatFocus || w.Floats[i].W != r.W || w.Floats[i].H != r.H {
42 t.Fatal(w.Floats, r)
43 }
44 if got := w.floatRect(w.Floats[i]); got.X+got.W/2 != w.Usable.X+w.Usable.W/2 {
45 t.Fatal(got)
46 }
47 if !w.imposedFloat(3) || len(w.Columns[1].Windows) != 1 {
48 t.Fatal(w.Columns)
49 }
50 // Client reports do not change a free float's size.
51 w.ResizeFloating(3, 10, 10)
52 if w.Floats[i].W != r.W {
53 t.Fatal(w.Floats[i])
54 }
55 w.Apply(ActionToggleFloating)
56 if w.floatIndex(3) >= 0 || !slices.Equal(w.Columns[1].Windows, []WindowID{2, 3}) || w.Columns[1].Focus != 1 || w.Focus != 1 {
57 t.Fatal(w.Columns)
58 }
59 // A lone column comes back at its width and index.
60 w.Focus = 1
61 w.Columns[1] = Column{Windows: []WindowID{2}, Cells: 1}
62 w.Columns = append(w.Columns, Column{Windows: []WindowID{3}})
63 w.Apply(ActionToggleFloating)
64 w.Apply(ActionToggleFloating)
65 if len(w.Columns) != 3 || w.Columns[1].Windows[0] != 2 || w.Columns[1].Cells != 1 || w.Focus != 1 {
66 t.Fatal(w.Columns, w.Focus)
67 }
68}
69
70func TestToggleFloatingNativeAndNoops(t *testing.T) {
71 w := freeWorkspace()
72 w.AddWindow(1)
73 w.AddFloating(2, 100, 100)
74 w.Apply(ActionToggleFloating)
75 if w.floatIndex(2) >= 0 || len(w.Columns) != 2 {
76 t.Fatal(w.Columns, w.Floats)
77 }
78 // Stashed: no-op.
79 w.Apply(ActionToggleWindowStash)
80 id, _ := w.Focused()
81 w.Apply(ActionToggleFloating)
82 if w.stashIndex(id) < 0 {
83 t.Fatal(w.Stash)
84 }
85 // Fullscreen: no-op.
86 w = freeWorkspace()
87 w.AddWindow(1)
88 w.SetFullscreen(1, true)
89 w.Apply(ActionToggleFloating)
90 if len(w.Columns) != 1 || len(w.Floats) != 0 {
91 t.Fatal(w.Columns)
92 }
93 // Fixed overflow: the fullscreen window covers the others and keeps
94 // the focus, so nothing floats.
95 w = freeWorkspace()
96 w.Overflow = OverflowFixed
97 w.AddWindow(1)
98 w.AddWindow(2)
99 w.SetFullscreen(2, true)
100 w.Apply(ActionToggleFloating)
101 if len(w.Columns) != 2 || len(w.Floats) != 0 {
102 t.Fatal(w.Columns, w.Floats)
103 }
104}
105
106func TestFreeFloatStash(t *testing.T) {
107 w := freeWorkspace()
108 w.AddWindow(1)
109 w.AddWindow(2)
110 w.Apply(ActionToggleFloating)
111 i := w.floatIndex(2)
112 w.Floats[i].cx, w.Floats[i].cy = 0.3, 0.6
113 want := w.Floats[i]
114 w.Apply(ActionToggleWindowStash)
115 if w.stashIndex(2) < 0 || w.floatIndex(2) >= 0 {
116 t.Fatal(w.Stash, w.Floats)
117 }
118 w.Apply(ActionToggleWindowStash)
119 if i := w.floatIndex(2); i < 0 || w.Floats[i].cx != 0.3 || w.Floats[i].cy != 0.6 || w.Floats[i].W != want.W || !w.floatFocus {
120 t.Fatal(w.Floats)
121 }
122 // It still returns to its column.
123 w.Apply(ActionToggleFloating)
124 if len(w.Columns) != 2 || w.Columns[1].Windows[0] != 2 {
125 t.Fatal(w.Columns)
126 }
127}
128
129func TestFreeFloatMovesAcrossWorkspaces(t *testing.T) {
130 m := monitor()
131 m.SetOutput(1000, 800)
132 m.AddWindow(1)
133 m.AddWindow(2)
134 m.Apply(ActionToggleFloating)
135 w := m.Current()
136 i := w.floatIndex(2)
137 w.Floats[i].cx, w.Floats[i].cy = 0.2, 0.8
138 m.MoveToWorkspace(1, false)
139 to := m.Workspaces[1]
140 if i := to.floatIndex(2); i < 0 || !to.Floats[i].free || to.Floats[i].cx != 0.2 || to.Floats[i].cy != 0.8 {
141 t.Fatal(to.Floats)
142 }
143 // Stashed there, moved back, unstashed: still free at its place.
144 m.FocusNumber(2)
145 m.Apply(ActionToggleWindowStash)
146 m.MoveToWorkspace(0, false)
147 m.FocusNumber(1)
148 m.Apply(ActionToggleWindowStash)
149 w = m.Current()
150 if i := w.floatIndex(2); i < 0 || !w.Floats[i].free || w.Floats[i].cx != 0.2 {
151 t.Fatal(w.Floats, w.Stash)
152 }
153}
154
155func TestFreeFloatReturnsAfterWorkspaceMove(t *testing.T) {
156 m := monitor()
157 m.SetOutput(1000, 800)
158 m.AddWindow(1)
159 m.AddWindow(2)
160 m.Apply(ActionMaximizeColumn)
161 m.Apply(ActionToggleFloating)
162 m.MoveToWorkspace(1, false)
163 m.FocusNumber(2)
164 m.Current().FocusID(2)
165 m.Apply(ActionToggleFloating)
166 w := m.Current()
167 if len(w.Columns) != 1 || w.Columns[0].Windows[0] != 2 || w.Columns[0].FullWidth {
168 t.Fatal(w.Columns)
169 }
170}
171
172func TestToggleFloatingReleasesSlot(t *testing.T) {
173 w := freeWorkspace()
174 w.Columns = []Column{{Windows: []WindowID{1}, Slot: 7}}
175 w.Apply(ActionToggleFloating)
176 w.Apply(ActionToggleFloating)
177 if len(w.Columns) != 1 || w.Columns[0].Slot != 0 {
178 t.Fatal(w.Columns)
179 }
180}
181
182func TestFreeFloatNoUsableArea(t *testing.T) {
183 w := freeWorkspace()
184 w.AddWindow(1)
185 w.Apply(ActionToggleFloating)
186 w.SetUsable(Rect{})
187 if fx := w.Apply(ActionCloseWindow); fx.Close != 1 {
188 t.Fatal(fx)
189 }
190 w.Apply(ActionMoveColumnLeft)
191 w.Apply("set-column-width +1")
192 if w.floatIndex(1) < 0 {
193 t.Fatal(w.Floats)
194 }
195}
196
197func TestFreeFloatKeyboard(t *testing.T) {
198 w := freeWorkspace()
199 w.AddWindow(1)
200 w.AddWindow(2)
201 w.Apply(ActionToggleFloating)
202 i := w.floatIndex(2)
203 w.Floats[i].W, w.Floats[i].H = 200, 100
204 r := w.floatRect(w.Floats[i])
205 w.Apply(ActionMoveColumnLeft)
206 w.Apply(ActionMoveWindowDown)
207 if got := w.floatRect(w.Floats[i]); got.X != r.X-freeNudge || got.Y != r.Y+freeNudge {
208 t.Fatal(got, r)
209 }
210 for range 50 {
211 w.Apply(ActionMoveColumnLeft)
212 }
213 if got := w.floatRect(w.Floats[i]); got.X != w.Usable.X {
214 t.Fatal(got)
215 }
216 w.Apply("set-column-width +1")
217 w.Apply("set-window-height -10%")
218 if w.Floats[i].W != 200+w.Usable.W/w.MaxColumns || w.Floats[i].H != freeMinSize {
219 t.Fatal(w.Floats[i])
220 }
221 // The columns did not change.
222 if len(w.Columns) != 1 || w.Columns[0].Cells != 0 {
223 t.Fatal(w.Columns)
224 }
225}