webview_scroll_cancel.go RawHistoryFolder

1package cef
2
3import (
4	"github.com/bnema/dumber/internal/shared/syncdispatch"
5)
6
7// This file owns inertial-scroll cancellation ordering for CEF WebViews.
8//
9// Threading contract: scrollMotionRetired is safe from any thread (it only
10// bumps the library epoch and never touches GTK). GTK cleanup runs either
11// inline inside runNavigationWithScrollCancel's ordered GTK operation or via
12// runOnGTK-queued epoch-scoped cleanup. Library imports stay inside this
13// infrastructure boundary; only the Cef2gtkAdapter seam is extended.
14
15// scrollCancelBridge is the narrow adapter seam for scroll cancellation.
16// *Cef2gtkAdapter satisfies it in production; tests inject a recording
17// fake. It covers only invalidation and epoch-scoped cleanup.
18type scrollCancelBridge interface {
19	InvalidateScroll() uint64
20	CancelScrollEpoch(epoch uint64) bool
21}
22
23// scrollCancelTarget resolves the live adapter for scroll cancellation.
24func (wv *WebView) scrollCancelTarget() scrollCancelBridge {
25	if wv == nil {
26		return nil
27	}
28	if wv.scrollCancelSeam != nil {
29		return wv.scrollCancelSeam
30	}
31	if wv.viewBridge == nil {
32		return nil
33	}
34	return wv.viewBridge
35}
36
37// scrollMotionRetired immediately retires animated scroll motion and release
38// eligibility, returning the retired epoch for epoch-scoped GTK cleanup.
39// Safe to call from any thread, including during destruction: a destroyed
40// or absent bridge reports zero, so no destroyed check is needed here.
41func (wv *WebView) scrollMotionRetired() uint64 {
42	target := wv.scrollCancelTarget()
43	if wv == nil || target == nil {
44		return 0
45	}
46	return target.InvalidateScroll()
47}
48
49// cancelScrollEpochOnGTK performs GTK-only cleanup for a retired epoch. It
50// never clears a newer session. Call only on the GTK thread.
51func (wv *WebView) cancelScrollEpochOnGTK(epoch uint64) {
52	if wv == nil || epoch == 0 {
53		return
54	}
55	target := wv.scrollCancelTarget()
56	if target == nil {
57		return
58	}
59	target.CancelScrollEpoch(epoch)
60}
61
62// invalidateScrollMotion retires motion immediately and queues epoch-scoped
63// GTK cleanup through the owning dispatcher. Safe to call from any thread;
64// used by lifecycle callbacks (load handlers, visibility) where navigation
65// dispatch is not part of the call.
66func (wv *WebView) invalidateScrollMotion() uint64 {
67	epoch := wv.scrollMotionRetired()
68	if epoch == 0 {
69		return 0
70	}
71	target := wv.scrollCancelTarget()
72	wv.runOnGTK(func() {
73		if target == nil {
74			return
75		}
76		target.CancelScrollEpoch(epoch)
77	})
78	return epoch
79}
80
81// runNavigationWithScrollCancel invalidates scroll motion immediately, then
82// performs GTK cleanup followed by the existing navigation dispatch within
83// one ordered GTK operation. Calls already on the GTK thread execute inline;
84// off-thread calls use the existing bounded synchronous dispatch, which
85// skips abandoned queued work so a timed-out dispatch can never navigate
86// late. Dispatch errors preserve the command's existing error contract;
87// dispatch failure reports the existing GTK-sync error form.
88func (wv *WebView) runNavigationWithScrollCancel(label string, dispatch func() error) error {
89	if wv.destroyed.Load() {
90		return errDestroyed
91	}
92	epoch := wv.scrollMotionRetired()
93	var dispatchErr error
94	result := wv.runOnGTKSyncLabel(label, func() {
95		wv.cancelScrollEpochOnGTK(epoch)
96		dispatchErr = dispatch()
97	})
98	switch result.Status {
99	case syncdispatch.SyncDispatchInline,
100		syncdispatch.SyncDispatchCompleted,
101		syncdispatch.SyncDispatchCompletedAfterTimeout:
102		return dispatchErr
103	default:
104		return errGTKSyncDispatchIncomplete(label, result)
105	}
106}

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