v0.30.0 eb3068f

render_handler_adapter.go RawHistoryFolder

1package cef
2
3import (
4	"sync"
5
6	purecef "github.com/bnema/purego-cef/cef"
7	cef2gtk "github.com/bnema/purego-cef2gtk"
8
9	"github.com/bnema/dumber/internal/logging"
10)
11
12type popupSurface interface {
13	SetPopupVisible(bool)
14	SetPopupRect(purecef.Rect)
15}
16
17type dumberRenderHandler struct {
18	wv           *WebView
19	main         purecef.RenderHandler
20	popup        purecef.RenderHandler
21	popupSurface popupSurface
22}
23
24var _ purecef.RenderHandler = (*dumberRenderHandler)(nil)
25
26func newDumberRenderHandler(wv *WebView) purecef.RenderHandler {
27	if wv == nil || wv.viewBridge == nil {
28		return nil
29	}
30	h := &dumberRenderHandler{wv: wv}
31
32	var unsupportedPaintOnce sync.Once
33	h.main = wv.viewBridge.RenderHandler(cef2gtk.Hooks{
34		OnUnsupportedPaint: func() {
35			unsupportedPaintOnce.Do(func() {
36				if wv.ctx != nil {
37					logging.FromContext(wv.ctx).Warn().Msg("cef: unsupported CPU paint from accelerated bridge")
38				}
39			})
40		},
41		OnError: func(err error) {
42			if wv.ctx != nil {
43				logging.FromContext(wv.ctx).Warn().Err(err).Msg("cef: accelerated render bridge error")
44			}
45		},
46		OnTextSelectionChanged: func(selectedText string, _ *purecef.Range) {
47			handleRenderTextSelectionChanged(wv, selectedText)
48		},
49	})
50
51	if wv.popupSurface != nil {
52		var popupUnsupportedPaintOnce sync.Once
53		h.popupSurface = wv.popupSurface
54		h.popup = wv.popupSurface.RenderHandler(cef2gtk.Hooks{
55			OnUnsupportedPaint: func() {
56				popupUnsupportedPaintOnce.Do(func() {
57					if wv.ctx != nil {
58						logging.FromContext(wv.ctx).Warn().Msg("cef: unsupported CPU paint from popup surface")
59					}
60				})
61			},
62			OnError: func(err error) {
63				if wv.ctx != nil {
64					logging.FromContext(wv.ctx).Warn().Err(err).Msg("cef: popup surface render error")
65				}
66			},
67		})
68	}
69
70	return h
71}
72
73func (h *dumberRenderHandler) renderTargetForPaint(elementType purecef.PaintElementType) purecef.RenderHandler {
74	if h == nil {
75		return nil
76	}
77	if elementType == purecef.PaintElementTypePetPopup && h.popup != nil {
78		return h.popup
79	}
80	return h.main
81}
82
83func (h *dumberRenderHandler) GetAccessibilityHandler() purecef.AccessibilityHandler {
84	if h == nil || h.main == nil {
85		return nil
86	}
87	return h.main.GetAccessibilityHandler()
88}
89
90func (h *dumberRenderHandler) GetRootScreenRect(browser purecef.Browser, rect *purecef.Rect) int32 {
91	if h == nil || h.main == nil {
92		return 0
93	}
94	return h.main.GetRootScreenRect(browser, rect)
95}
96
97func (h *dumberRenderHandler) GetViewRect(browser purecef.Browser, rect *purecef.Rect) {
98	if h == nil || h.main == nil {
99		if rect != nil {
100			rect.X, rect.Y, rect.Width, rect.Height = 0, 0, 1, 1
101		}
102		return
103	}
104	h.main.GetViewRect(browser, rect)
105}
106
107func (h *dumberRenderHandler) GetScreenPoint(browser purecef.Browser, viewX, viewY int32, screenX, screenY *int32) int32 {
108	if h == nil || h.main == nil {
109		return 0
110	}
111	return h.main.GetScreenPoint(browser, viewX, viewY, screenX, screenY)
112}
113
114func (h *dumberRenderHandler) GetScreenInfo(browser purecef.Browser, info *purecef.ScreenInfo) int32 {
115	if h == nil || h.main == nil {
116		return 0
117	}
118	return h.main.GetScreenInfo(browser, info)
119}
120
121func (h *dumberRenderHandler) OnPopupShow(browser purecef.Browser, show int32) {
122	if h != nil && h.popupSurface != nil {
123		visible := show != 0
124		if h.wv != nil {
125			h.wv.runOnGTKSync(func() {
126				h.popupSurface.SetPopupVisible(visible)
127			})
128		} else {
129			h.popupSurface.SetPopupVisible(visible)
130		}
131	}
132	if h != nil && h.popup != nil {
133		h.popup.OnPopupShow(browser, show)
134	}
135}
136
137func (h *dumberRenderHandler) OnPopupSize(browser purecef.Browser, rect *purecef.Rect) {
138	if h != nil && h.popupSurface != nil && rect != nil {
139		popupRect := *rect
140		if h.wv != nil {
141			popupRect = popupRectForWidget(h.wv, popupRect)
142			h.wv.runOnGTKSync(func() {
143				h.popupSurface.SetPopupRect(popupRect)
144			})
145		} else {
146			h.popupSurface.SetPopupRect(popupRect)
147		}
148	}
149	if h != nil && h.popup != nil {
150		h.popup.OnPopupSize(browser, rect)
151	}
152}
153
154func popupRectForWidget(wv *WebView, rect purecef.Rect) purecef.Rect {
155	if wv == nil {
156		return rect
157	}
158	// Only convert when the OSR backing-scale compatibility path is active.
159	// A monitor scale above 1 is not enough by itself because CEF already reports
160	// logical popup coordinates when backing-scale mode is off.
161	if wv.osrBackingScaleFactor() <= 1 {
162		return rect
163	}
164	scale := wv.viewBridgeScale()
165	return purecef.Rect{
166		X:      deviceToLogicalCoord(rect.X, scale),
167		Y:      deviceToLogicalCoord(rect.Y, scale),
168		Width:  deviceToLogicalSize(rect.Width, scale),
169		Height: deviceToLogicalSize(rect.Height, scale),
170	}
171}
172
173func (h *dumberRenderHandler) OnPaint(
174	browser purecef.Browser,
175	elementType purecef.PaintElementType,
176	dirtyRects []purecef.Rect,
177	buffer []byte,
178	width, height int32,
179) {
180	if delegate := h.renderTargetForPaint(elementType); delegate != nil {
181		delegate.OnPaint(browser, elementType, dirtyRects, buffer, width, height)
182	}
183}
184
185func (h *dumberRenderHandler) OnAcceleratedPaint(
186	browser purecef.Browser,
187	elementType purecef.PaintElementType,
188	dirtyRects []purecef.Rect,
189	info *purecef.AcceleratedPaintInfo,
190) {
191	if delegate := h.renderTargetForPaint(elementType); delegate != nil {
192		delegate.OnAcceleratedPaint(browser, elementType, dirtyRects, info)
193	}
194}
195
196func (h *dumberRenderHandler) GetTouchHandleSize(browser purecef.Browser, orientation purecef.HorizontalAlignment, size *purecef.Size) {
197	if h == nil || h.main == nil {
198		return
199	}
200	h.main.GetTouchHandleSize(browser, orientation, size)
201}
202
203func (h *dumberRenderHandler) OnTouchHandleStateChanged(browser purecef.Browser, state *purecef.TouchHandleState) {
204	if h == nil || h.main == nil {
205		return
206	}
207	h.main.OnTouchHandleStateChanged(browser, state)
208}
209
210func (h *dumberRenderHandler) StartDragging(
211	browser purecef.Browser,
212	dragData purecef.DragData,
213	allowedOps purecef.DragOperationsMask,
214	x, y int32,
215) int32 {
216	if h == nil || h.main == nil {
217		return 0
218	}
219	return h.main.StartDragging(browser, dragData, allowedOps, x, y)
220}
221
222func (h *dumberRenderHandler) UpdateDragCursor(browser purecef.Browser, operation purecef.DragOperationsMask) {
223	if h == nil || h.main == nil {
224		return
225	}
226	h.main.UpdateDragCursor(browser, operation)
227}
228
229func (h *dumberRenderHandler) OnScrollOffsetChanged(browser purecef.Browser, x, y float64) {
230	if h == nil || h.main == nil {
231		return
232	}
233	h.main.OnScrollOffsetChanged(browser, x, y)
234}
235
236func (h *dumberRenderHandler) OnImeCompositionRangeChanged(
237	browser purecef.Browser,
238	selectedRange *purecef.Range,
239	characterBounds []purecef.Rect,
240) {
241	if h == nil || h.main == nil {
242		return
243	}
244	h.main.OnImeCompositionRangeChanged(browser, selectedRange, characterBounds)
245}
246
247func (h *dumberRenderHandler) OnTextSelectionChanged(browser purecef.Browser, selectedText string, selectedRange *purecef.Range) {
248	if h == nil || h.main == nil {
249		return
250	}
251	h.main.OnTextSelectionChanged(browser, selectedText, selectedRange)
252}
253
254func (h *dumberRenderHandler) OnVirtualKeyboardRequested(browser purecef.Browser, mode purecef.TextInputMode) {
255	if h == nil || h.main == nil {
256		return
257	}
258	h.main.OnVirtualKeyboardRequested(browser, mode)
259}
260
261func handleRenderTextSelectionChanged(wv *WebView, selectedText string) {
262	if wv == nil {
263		return
264	}
265	previous, changed := wv.setSelectedText(selectedText)
266	if !changed {
267		return
268	}
269	if wv.ctx != nil && selectedText == "" {
270		if previous != "" {
271			logging.FromContext(wv.ctx).Debug().
272				Int("prev_text_len", len(previous)).
273				Msg("cef: text selection cleared")
274		}
275	} else if wv.ctx != nil {
276		logging.FromContext(wv.ctx).Debug().
277			Int("text_len", len(selectedText)).
278			Msg("cef: text selection changed")
279	}
280	if wv.engine != nil {
281		wv.scheduleSelectionUpdate(selectedText)
282	}
283}

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