v0.3.0 cf370a0

free_test.go RawHistoryFolder

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, Width: Width{Num: 1, Den: 2}}}
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}, Width: Width{Num: 1, Den: 2}}
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].Width != (Width{Num: 1, Den: 2}) || 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 +10%")
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 +10%")
217	w.Apply("set-window-height -10%")
218	if w.Floats[i].W != 200+w.Usable.W/10 || 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].Width != (Width{}) {
223		t.Fatal(w.Columns)
224	}
225}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 25

Tags 10

↑↓ moveenter openesc close