v0.2.0 13c6a90

keyboard.go RawHistoryFolder

1package core
2
3import "maps"
4
5// keyboardCandidates is what the keyboard focus picks from, strongest first.
6// The layer checks are lazy: they only run while a clicked layer holds the
7// keyboard.
8type keyboardCandidates struct {
9	// exclusive is the mapped top/overlay layer with exclusive keyboard
10	// interactivity and the highest ID; grab the topmost grabbing popup.
11	exclusive, grab WindowID
12	// window is the focused window of the focused output.
13	window WindowID
14	// layerShown reports whether a layer is still mapped on-demand and
15	// visible; windows is the focused window of each output.
16	layerShown func(WindowID) bool
17	windows    func() map[*screen]WindowID
18}
19
20// keyboard owns who holds the keyboard: the rules picking the focus, the
21// on-demand layer the user clicked, the focus last told to wayland and the
22// shortcuts inhibitor active now.
23type keyboard struct {
24	// layer is the on-demand layer surface the user clicked; it keeps the
25	// keyboard while it stays mapped on-demand and no window focus changes
26	// on any output (over, taken at the click): any click elsewhere, bind,
27	// activation or new window takes the keyboard back. The pointer moving
28	// to another output does not.
29	layer WindowID
30	over  map[*screen]WindowID
31	// sent is the focus last sent to wayland; inhibiting the shortcuts
32	// inhibitor active now (0: none).
33	sent       WindowID
34	inhibiting WindowID
35}
36
37// focus is the exclusive layer, else a grabbing popup, else the clicked
38// on-demand layer, else the focused window. A clicked layer that is hidden
39// (by a fullscreen window) or outlived its window focus loses the keyboard
40// for good.
41func (k *keyboard) focus(c keyboardCandidates) WindowID {
42	if c.exclusive != 0 {
43		return c.exclusive
44	}
45	// A menu with a grab takes the keyboard until it closes.
46	if c.grab != 0 {
47		return c.grab
48	}
49	if k.layer != 0 && (!c.layerShown(k.layer) || !maps.Equal(k.over, c.windows())) {
50		k.layer, k.over = 0, nil
51	}
52	if k.layer != 0 {
53		return k.layer
54	}
55	return c.window
56}
57
58// clickLayer gives the keyboard to the clicked on-demand layer while the
59// window focus stays windows. It reports whether the holder changed.
60func (k *keyboard) clickLayer(id WindowID, windows map[*screen]WindowID) bool {
61	if k.layer == id {
62		return false
63	}
64	k.layer, k.over = id, windows
65	return true
66}
67
68// takeBack returns the keyboard from a clicked layer to the windows.
69func (k *keyboard) takeBack() { k.layer, k.over = 0, nil }
70
71// inhibit makes the focus the active shortcuts inhibitor when it asks to
72// be (wants). It returns the inhibitor to release and the one to activate
73// (0: none) when the active one changes.
74func (k *keyboard) inhibit(focus WindowID, wants bool) (release, activate WindowID, changed bool) {
75	want := WindowID(0)
76	if focus != 0 && wants {
77		want = focus
78	}
79	if want == k.inhibiting {
80		return 0, 0, false
81	}
82	release, k.inhibiting = k.inhibiting, want
83	return release, want, true
84}
85
86// inhibited reports whether the focus gets every key: it is the active
87// shortcuts inhibitor.
88func (k *keyboard) inhibited(focus WindowID) bool {
89	return k.inhibiting != 0 && k.inhibiting == focus
90}

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