copy_mouse_map.go RawHistoryFolder

1package daemon
2
3import (
4	"github.com/bnema/vev/internal/domain"
5	scopy "github.com/bnema/vev/internal/usecase/copy"
6	"github.com/bnema/vev/internal/usecase/mouse"
7)
8
9// clientTopBarRows is deliberately kept at the client/frame boundary. Layout
10// coordinates are content-relative and must cross this boundary exactly once.
11const clientTopBarRows = 1
12
13type copyMouseGeometry struct {
14	pane    *pane
15	content domain.Rect // absolute client-frame coordinates
16}
17
18type mappedCopyMouse struct {
19	pane *pane
20	pos  scopy.Pos
21}
22
23func copyContentToClientRect(content domain.Rect) domain.Rect {
24	content.Y += clientTopBarRows
25	return content
26}
27
28func mapCopyMouse(ev mouse.Event, geometry copyMouseGeometry, viewportTop int, document *scopy.Document, clamp bool) (mappedCopyMouse, bool) {
29	if document == nil || geometry.pane == nil || geometry.content.Width <= 0 || geometry.content.Height <= 0 {
30		return mappedCopyMouse{}, false
31	}
32	col, row := ev.Col, ev.Row
33	if clamp {
34		col = clampInt(col, geometry.content.X, geometry.content.X+geometry.content.Width-1)
35		row = clampInt(row, geometry.content.Y, geometry.content.Y+geometry.content.Height-1)
36	} else if !pointInRect(col, row, geometry.content) {
37		return mappedCopyMouse{}, false
38	}
39	pos, ok := document.Normalize(scopy.Pos{Row: viewportTop + row - geometry.content.Y, Col: col - geometry.content.X})
40	if !ok {
41		return mappedCopyMouse{}, false
42	}
43	return mappedCopyMouse{pane: geometry.pane, pos: pos}, true
44}
45
46// hitTestCopyMouseGeometryLocked resolves a new press. Floating geometry is
47// exclusive while visible; normal title bars and dividers are intentionally
48// not copy content. Caller holds tb.mu.
49func hitTestCopyMouseGeometryLocked(tb *tab, cfg domain.FloatingConfig, col, row int) (copyMouseGeometry, bool) {
50	if tb == nil || row < clientTopBarRows {
51		return copyMouseGeometry{}, false
52	}
53	if p, geometry, visible := tb.visibleFloatingSnapshotLocked(cfg); visible {
54		content := copyContentToClientRect(geometry.Inner)
55		if !pointInRect(col, row, content) {
56			return copyMouseGeometry{}, false
57		}
58		return copyMouseGeometry{pane: p, content: content}, true
59	}
60	pl, ok := hitTestPlacementLocked(tb, col, row-clientTopBarRows)
61	if !ok || pl.Collapsed || !pointInRect(col, row-clientTopBarRows, pl.Content) {
62		return copyMouseGeometry{}, false
63	}
64	p := tb.panes[pl.ID]
65	if p == nil {
66		return copyMouseGeometry{}, false
67	}
68	return copyMouseGeometry{pane: p, content: copyContentToClientRect(pl.Content)}, true
69}
70
71// copyMouseGeometryForPaneLocked resolves geometry for an existing drag. It
72// never hit-tests the new pointer, preventing cross-pane document switches.
73func copyMouseGeometryForPaneLocked(tb *tab, cfg domain.FloatingConfig, target *pane) (copyMouseGeometry, bool) {
74	if tb == nil || target == nil {
75		return copyMouseGeometry{}, false
76	}
77	if p, geometry, visible := tb.visibleFloatingSnapshotLocked(cfg); visible {
78		if p != target {
79			return copyMouseGeometry{}, false
80		}
81		return copyMouseGeometry{pane: p, content: copyContentToClientRect(geometry.Inner)}, true
82	}
83	if tb.panes[target.id] != target {
84		return copyMouseGeometry{}, false
85	}
86	placements, ok := solvedPlacementsLocked(tb)
87	if !ok {
88		return copyMouseGeometry{}, false
89	}
90	for _, pl := range placements {
91		if pl.ID == target.id && !pl.Collapsed && pl.Content.Width > 0 && pl.Content.Height > 0 {
92			return copyMouseGeometry{pane: target, content: copyContentToClientRect(pl.Content)}, true
93		}
94	}
95	return copyMouseGeometry{}, false
96}

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