mouse_panes.go RawHistoryFolder

1package daemon
2
3import (
4	"math"
5	"strconv"
6	"strings"
7
8	"github.com/bnema/vev/internal/domain"
9	"github.com/bnema/vev/internal/usecase/layout"
10	"github.com/bnema/vev/internal/usecase/mouse"
11)
12
13func hitTestPlacementLocked(tb *tab, col, row int) (layout.Placement, bool) {
14	if tb == nil || tb.tree == nil || tb.tree.Root == nil || !tb.size.Valid() {
15		return layout.Placement{}, false
16	}
17	placements, ok := solvedPlacementsLocked(tb)
18	if !ok {
19		return layout.Placement{}, false
20	}
21	for _, pl := range placements {
22		if pointInRect(col, row, pl.TitleBar) || pointInRect(col, row, pl.Content) {
23			return pl, true
24		}
25	}
26	return layout.Placement{}, false
27}
28
29type tabLayoutSnapshot struct {
30	fingerprint string
31	area        domain.Rect
32	focus       layout.PaneID
33	placements  []layout.Placement
34	dividers    []layout.Divider
35	ok          bool
36}
37
38func solvedPlacementsLocked(tb *tab) ([]layout.Placement, bool) {
39	snap := solveTabLayoutLocked(tb)
40	return snap.placements, snap.ok
41}
42
43func solveTabLayoutLocked(tb *tab) tabLayoutSnapshot {
44	if tb == nil || tb.tree == nil || tb.tree.Root == nil || !tb.size.Valid() {
45		return tabLayoutSnapshot{}
46	}
47	area := domain.Rect{Width: tb.size.Cols, Height: tb.size.Rows}
48	placements, dividers, ok := layout.SolveWithDividers(tb.tree.Root, area)
49	return tabLayoutSnapshot{fingerprint: layoutFingerprint(tb.tree.Root), area: area, focus: tb.tree.Focus, placements: placements, dividers: dividers, ok: ok}
50}
51
52func layoutFingerprint(root *layout.Node) string {
53	var b strings.Builder
54	b.Grow(layoutFingerprintLength(root))
55	writeLayoutFingerprint(&b, root)
56	return b.String()
57}
58
59const weightFingerprintMaxLength = 16
60
61func layoutFingerprintLength(n *layout.Node) int {
62	if n == nil {
63		return 1
64	}
65	length := 2 + weightFingerprintMaxLength + 1 + paneIDFingerprintLength(n.Leaf) + 1 + paneIDFingerprintLength(n.Expanded) + 2
66	for _, child := range n.Children {
67		length += layoutFingerprintLength(child) + 1
68	}
69	return length
70}
71
72func paneIDFingerprintLength(id layout.PaneID) int {
73	length := len(id)
74	return decimalDigits(length) + 1 + length
75}
76
77func decimalDigits(value int) int {
78	if value == 0 {
79		return 1
80	}
81	digits := 0
82	for value > 0 {
83		value /= 10
84		digits++
85	}
86	return digits
87}
88
89func writeLayoutFingerprint(b *strings.Builder, n *layout.Node) {
90	if n == nil {
91		b.WriteByte('0')
92		return
93	}
94	b.WriteByte(byte('0' + n.Kind))
95	b.WriteByte(byte('0' + n.Dir))
96	writeWeightFingerprint(b, n.Weight)
97	b.WriteByte('|')
98	writePaneIDFingerprint(b, n.Leaf)
99	b.WriteByte('|')
100	writePaneIDFingerprint(b, n.Expanded)
101	b.WriteByte('[')
102	for _, child := range n.Children {
103		writeLayoutFingerprint(b, child)
104		b.WriteByte(',')
105	}
106	b.WriteByte(']')
107}
108
109func writeWeightFingerprint(b *strings.Builder, weight float64) {
110	const hex = "0123456789abcdef"
111	bits := math.Float64bits(weight)
112	started := false
113	for shift := uint(60); ; shift -= 4 {
114		digit := byte(bits >> shift & 0xf)
115		if digit != 0 || started || shift == 0 {
116			b.WriteByte(hex[digit])
117			started = true
118		}
119		if shift == 0 {
120			return
121		}
122	}
123}
124
125func writePaneIDFingerprint(b *strings.Builder, id layout.PaneID) {
126	s := string(id)
127	b.WriteString(strconv.Itoa(len(s)))
128	b.WriteByte(':')
129	b.WriteString(s)
130}
131
132func pointInRect(col, row int, r domain.Rect) bool {
133	return r.Width > 0 && r.Height > 0 && col >= r.X && col < r.X+r.Width && row >= r.Y && row < r.Y+r.Height
134}
135
136func focusPlacementLocked(tb *tab, id layout.PaneID) bool {
137	if tb == nil || tb.tree == nil {
138		return false
139	}
140	oldFocus := tb.tree.Focus
141	oldLayout := layoutFingerprint(tb.tree.Root)
142	tb.tree.Focus = id
143	setExpandedLocked(tb.tree.Root, id)
144	changed := oldFocus != tb.tree.Focus || oldLayout != layoutFingerprint(tb.tree.Root)
145	if changed {
146		tb.bumpLayoutGenerationLocked()
147	}
148	return changed
149}
150
151func setExpandedLocked(n *layout.Node, id layout.PaneID) bool {
152	if n == nil {
153		return false
154	}
155	if n.Kind == layout.Stack {
156		for _, child := range n.Children {
157			if child.Kind == layout.Leaf && child.Leaf == id {
158				n.Expanded = id
159				return true
160			}
161		}
162	}
163	for _, child := range n.Children {
164		if setExpandedLocked(child, id) {
165			return true
166		}
167	}
168	return false
169}
170
171func translateMouseEvent(ev mouse.Event, colOffset, contentYOffset int) mouse.Event {
172	ev.Col -= colOffset
173	ev.Row -= contentYOffset
174	ev.Raw = sgrOffset(ev.Raw, -colOffset, -contentYOffset-clientTopBarRows)
175	return ev
176}
177
178func (d *Daemon) exitCopyMode(ac *attachedClient) {
179	if ac == nil {
180		return
181	}
182	rt := ac.overlays
183	if rt == nil {
184		return
185	}
186	rt.copyMu.Lock()
187	rt.clearCopyModeLocked()
188	rt.copyMu.Unlock()
189}

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 12

Tags 11

↑↓ moveenter openesc close