feat/data-dnd e05cb15

input_region.go RawHistoryFolder

1package wayland
2
3import (
4	"slices"
5
6	"github.com/bnema/go-wayland-bindings/server/wayland"
7	"github.com/bnema/neferwl/internal/ports"
8)
9
10// intersectRect returns the overlap of two half-open rectangles.
11func intersectRect(a, b ports.Rect) ports.Rect {
12	x, y := max(a.X, b.X), max(a.Y, b.Y)
13	x2, y2 := min(a.X+a.W, b.X+b.W), min(a.Y+a.H, b.Y+b.H)
14	if x2 <= x || y2 <= y {
15		return ports.Rect{}
16	}
17	return ports.Rect{X: x, Y: y, W: x2 - x, H: y2 - y}
18}
19
20func (g *region) boxRect() ports.Rect {
21	if len(g.rects) == 0 {
22		return ports.Rect{}
23	}
24	x, y := g.rects[0].X, g.rects[0].Y
25	x2, y2 := x+g.rects[0].W, y+g.rects[0].H
26	for _, r := range g.rects[1:] {
27		x = min(x, r.X)
28		y = min(y, r.Y)
29		x2 = max(x2, r.X+r.W)
30		y2 = max(y2, r.Y+r.H)
31	}
32	return ports.Rect{X: x, Y: y, W: x2 - x, H: y2 - y}
33}
34
35// effectiveInput includes all committed, bounded subsurface regions.
36func (s *surface) effectiveInput() (bool, []ports.Rect) {
37	if s.inputAll && len(s.sub.children) == 0 {
38		return true, nil
39	}
40	rects := s.appendEffectiveInput(nil, 0, 0)
41	if s.xdg != nil {
42		for i := range rects {
43			rects[i].X -= s.xdg.geometry.X
44			rects[i].Y -= s.xdg.geometry.Y
45		}
46	}
47	return false, rects
48}
49
50// appendEffectiveInput walks the committed tree without a per-call recursive
51// closure. The returned slice belongs to the caller (emitInput publishes it).
52func (s *surface) appendEffectiveInput(rects []ports.Rect, x, y int) []ports.Rect {
53	if s.has {
54		bounds := ports.Rect{W: s.content.LogicalW, H: s.content.LogicalH}
55		if s.inputAll {
56			if bounds.W > 0 && bounds.H > 0 {
57				rects = append(rects, ports.Rect{X: x, Y: y, W: bounds.W, H: bounds.H})
58			}
59		} else {
60			for _, r := range s.inputRects {
61				if r = intersectRect(r, bounds); r.W > 0 {
62					r.X += x
63					r.Y += y
64					rects = append(rects, r)
65				}
66			}
67		}
68	}
69	for _, item := range s.sub.layout {
70		rects = item.child.appendEffectiveInput(rects, x+item.x, y+item.y)
71	}
72	return rects
73}
74
75// resetInputEmission makes the next mapping publish its current region again.
76func (s *surface) resetInputEmission() {
77	s.sentInput = false
78	s.lastInputAll = false
79	s.lastInputRects = nil
80}
81
82func (s *surface) emitInput() {
83	root := s.root()
84	id := root.windowID()
85	if id == 0 || root.kind == roleDragIcon {
86		// A drag icon takes no input.
87		return
88	}
89	all, rects := root.effectiveInput()
90	if !root.sentInput && all {
91		return
92	}
93	if root.sentInput && root.lastInputAll == all && slices.Equal(root.lastInputRects, rects) {
94		return
95	}
96	root.sentInput = true
97	root.lastInputAll = all
98	root.lastInputRects = slices.Clone(rects)
99	s.server.emit(ports.InputRegionChanged{ID: id, All: all, Rects: rects})
100	if c := s.server.constraints[root]; c != nil {
101		if c.active {
102			w := s.server.windows[id]
103			if w == nil || !c.contains(w) {
104				s.server.deactivateConstraint()
105			} else {
106				s.server.emitConstraint(c)
107			}
108		} else {
109			s.server.updateConstraint()
110		}
111	}
112}
113
114func (s *surface) SetInputRegion(_ *wayland.Surface, reg *wayland.Region) {
115	s.next.inputSet = true
116	s.next.inputAll = reg == nil || reg.Resource == nil
117	s.next.inputRects = nil
118	if !s.next.inputAll {
119		if g := s.server.regions[reg.Resource]; g != nil {
120			s.next.inputRects = slices.Clone(g.rects)
121		}
122	}
123}

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 23

Tags 10

↑↓ moveenter openesc close