v0.23.2 bfd5eb4

gesture.go RawHistoryFolder

1package input
2
3import (
4	"context"
5	"sync"
6
7	"github.com/bnema/dumber/internal/logging"
8	"github.com/jwijenbergh/puregotk/v4/gtk"
9)
10
11const (
12	// Mouse button constants for X11/Wayland
13	mouseButtonBack    = 8 // Side button - back
14	mouseButtonForward = 9 // Side button - forward
15)
16
17// GestureHandler handles mouse button gestures for navigation.
18// It recognizes mouse buttons 8 (back) and 9 (forward) on WebView widgets.
19type GestureHandler struct {
20	clickGesture *gtk.GestureClick
21
22	// Callback retention: must stay reachable by Go GC.
23	pressedCb func(gtk.GestureClick, int, float64, float64)
24
25	// Action handler callback
26	onAction ActionHandler
27
28	ctx context.Context
29	mu  sync.RWMutex
30}
31
32// NewGestureHandler creates a new gesture handler.
33func NewGestureHandler(ctx context.Context) *GestureHandler {
34	log := logging.FromContext(ctx)
35	log.Debug().Msg("creating gesture handler")
36
37	return &GestureHandler{
38		ctx: ctx,
39	}
40}
41
42// SetOnAction sets the callback for when navigation actions are triggered.
43func (h *GestureHandler) SetOnAction(fn ActionHandler) {
44	h.mu.Lock()
45	defer h.mu.Unlock()
46	h.onAction = fn
47}
48
49// AttachTo attaches the gesture handler to a GTK widget.
50// Typically called with a WebView widget to enable mouse button navigation.
51func (h *GestureHandler) AttachTo(widget *gtk.Widget) {
52	log := logging.FromContext(h.ctx)
53
54	if widget == nil {
55		log.Error().Msg("cannot attach gesture handler to nil widget")
56		return
57	}
58
59	// Create click gesture that listens to all buttons
60	h.clickGesture = gtk.NewGestureClick()
61	if h.clickGesture == nil {
62		log.Error().Msg("failed to create gesture click")
63		return
64	}
65
66	// Listen to all mouse buttons (not just primary)
67	h.clickGesture.SetButton(0)
68
69	// Connect pressed handler (retain callback to prevent GC)
70	h.pressedCb = func(_ gtk.GestureClick, nPress int, _ float64, _ float64) {
71		h.handlePressed(nPress)
72	}
73	h.clickGesture.ConnectPressed(&h.pressedCb)
74
75	// Add controller to widget
76	widget.AddController(&h.clickGesture.EventController)
77
78	log.Debug().Msg("gesture handler attached to widget")
79}
80
81// handlePressed processes a mouse button press event.
82func (h *GestureHandler) handlePressed(nPress int) {
83	// Only handle single clicks for navigation
84	if nPress != 1 {
85		return
86	}
87
88	// Get the button that was pressed
89	button := h.clickGesture.GetCurrentButton()
90
91	var action Action
92	switch button {
93	case mouseButtonBack:
94		action = ActionGoBack
95	case mouseButtonForward:
96		action = ActionGoForward
97	default:
98		// Not a navigation button, ignore
99		return
100	}
101
102	// Dispatch action to handler
103	h.mu.RLock()
104	handler := h.onAction
105	h.mu.RUnlock()
106
107	if handler != nil {
108		if err := handler(h.ctx, action); err != nil {
109			log := logging.FromContext(h.ctx)
110			log.Error().
111				Err(err).
112				Str("action", string(action)).
113				Uint("button", button).
114				Msg("gesture action handler error")
115		}
116	}
117}
118
119// Detach removes the gesture handler.
120// Note: GTK handles cleanup when the widget is destroyed,
121// but we clear our reference here.
122func (h *GestureHandler) Detach() {
123	h.clickGesture = nil
124	h.pressedCb = nil
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 8

Tags 42

↑↓ moveenter openesc close