v0.3.0 6800c6a

layer.go RawHistoryFolder

1package ui
2
3import (
4	"fmt"
5	"slices"
6
7	"github.com/bnema/nefergui/internal/platform/wayland"
8)
9
10// LayerLevel is the wlr-layer-shell stacking layer. The zero value is invalid,
11// so a surface never silently lands below every window.
12type LayerLevel uint8
13
14const (
15	LayerBackground LayerLevel = iota + 1
16	LayerBottom
17	LayerTop
18	LayerOverlay
19)
20
21// Anchor is a bit set of output edges a layer surface attaches to.
22type Anchor uint8
23
24const (
25	AnchorTop Anchor = 1 << iota
26	AnchorBottom
27	AnchorLeft
28	AnchorRight
29)
30
31const anchorAll = AnchorTop | AnchorBottom | AnchorLeft | AnchorRight
32
33// KeyboardMode is the layer surface keyboard interactivity. The zero value is
34// KeyboardNone: the surface never takes keyboard focus. KeyboardOnDemand needs
35// layer-shell version 4.
36type KeyboardMode uint8
37
38const (
39	KeyboardNone KeyboardMode = iota
40	KeyboardExclusive
41	KeyboardOnDemand
42)
43
44// Rect is an integer logical-pixel rectangle in surface coordinates.
45type Rect struct{ X, Y, Width, Height int32 }
46
47// LayerConfig requests a wlr-layer-shell surface instead of an xdg toplevel.
48// Size still gives the initial logical size; an axis anchored to both opposite
49// edges is sized by the compositor and reported through OnResize and Frame.Size.
50// ExclusiveZone > 0 asks the compositor to reserve that many logical pixels
51// from an anchored edge (only for one edge, or one edge plus both perpendicular
52// edges); 0 reserves nothing and asks to avoid other surfaces' positive zones;
53// -1 ignores other exclusive zones and extends to the anchored output edges.
54type LayerConfig struct {
55	Output        string // output name; empty lets the compositor choose
56	Namespace     string // defaults to "nefergui"
57	Level         LayerLevel
58	Anchors       Anchor
59	Keyboard      KeyboardMode
60	ExclusiveZone int32
61	Margin        [4]int32 // top, right, bottom, left
62	// InputRects, when non-nil, restricts pointer input to these rectangles.
63	// An empty non-nil slice makes the surface fully click-through. Rects need
64	// positive width and height.
65	InputRects []Rect
66}
67
68// surfaceOptions lowers the config to platform options and validates it.
69func (c LayerConfig) surfaceOptions() (wayland.SurfaceOptions, error) {
70	l := wayland.LayerOptions{Output: c.Output, Namespace: c.Namespace, ExclusiveZone: c.ExclusiveZone, Margin: c.Margin}
71	switch c.Level {
72	case LayerBackground:
73		l.Layer = wayland.LayerBackground
74	case LayerBottom:
75		l.Layer = wayland.LayerBottom
76	case LayerTop:
77		l.Layer = wayland.LayerTop
78	case LayerOverlay:
79		l.Layer = wayland.LayerOverlay
80	default:
81		return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid layer level %d", c.Level)
82	}
83	if c.Anchors&^anchorAll != 0 {
84		return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid layer anchors %#x", uint8(c.Anchors))
85	}
86	if c.Anchors&AnchorTop != 0 {
87		l.Anchor |= wayland.AnchorTop
88	}
89	if c.Anchors&AnchorBottom != 0 {
90		l.Anchor |= wayland.AnchorBottom
91	}
92	if c.Anchors&AnchorLeft != 0 {
93		l.Anchor |= wayland.AnchorLeft
94	}
95	if c.Anchors&AnchorRight != 0 {
96		l.Anchor |= wayland.AnchorRight
97	}
98	switch c.Keyboard {
99	case KeyboardNone:
100		l.Keyboard = wayland.KeyboardNone
101	case KeyboardExclusive:
102		l.Keyboard = wayland.KeyboardExclusive
103	case KeyboardOnDemand:
104		l.Keyboard = wayland.KeyboardOnDemand
105	default:
106		return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: invalid keyboard mode %d", c.Keyboard)
107	}
108	o := wayland.SurfaceOptions{Layer: &l}
109	if c.InputRects != nil {
110		o.InputRects = make([]wayland.Rect, len(c.InputRects))
111		for i, r := range c.InputRects {
112			o.InputRects[i] = wayland.Rect{X: r.X, Y: r.Y, W: r.Width, H: r.Height}
113		}
114	}
115	if err := o.Validate(); err != nil {
116		return wayland.SurfaceOptions{}, fmt.Errorf("nefergui: layer: %w", err)
117	}
118	return o, nil
119}
120
121// Layer selects a wlr-layer-shell surface for the window.
122func Layer(c LayerConfig) WindowOption {
123	c.InputRects = slices.Clone(c.InputRects)
124	return windowOption(func(w *windowConfig) { w.layer = &c })
125}

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 1

Tags 12

↑↓ moveenter openesc close