v0.13.0 976176a

helpers.go RawHistoryFolder

1package webkit
2
3import (
4	"github.com/diamondburned/gotk4/pkg/gdk/v4"
5	"github.com/diamondburned/gotk4/pkg/gio/v2"
6	"github.com/diamondburned/gotk4/pkg/glib/v2"
7	"github.com/diamondburned/gotk4/pkg/gtk/v4"
8)
9
10// IsNativeAvailable returns true if the native GTK/WebKit libraries are available
11// In gotk4, this is always true if the import succeeded
12func IsNativeAvailable() bool {
13	return true
14}
15
16// IdleAdd adds a function to be called in an idle callback
17// Returns an idle source ID that can be used with IdleRemove
18func IdleAdd(fn func() bool) uint {
19	return uint(glib.IdleAdd(fn))
20}
21
22// IdleRemove removes an idle callback by its source ID
23func IdleRemove(sourceID uint) {
24	glib.SourceRemove(glib.SourceHandle(sourceID))
25}
26
27// WidgetIsValid checks if a widget pointer is valid (non-nil)
28func WidgetIsValid(w gtk.Widgetter) bool {
29	return w != nil
30}
31
32// WidgetSetMargin sets all margins for a widget
33func WidgetSetMargin(w gtk.Widgetter, margin int) {
34	if widget := getWidget(w); widget != nil {
35		widget.SetMarginStart(margin)
36		widget.SetMarginEnd(margin)
37		widget.SetMarginTop(margin)
38		widget.SetMarginBottom(margin)
39	}
40}
41
42// WidgetSetMarginTop sets the top margin for a widget
43func WidgetSetMarginTop(w gtk.Widgetter, margin int) {
44	if widget := getWidget(w); widget != nil {
45		widget.SetMarginTop(margin)
46	}
47}
48
49// WidgetSetMarginBottom sets the bottom margin for a widget
50func WidgetSetMarginBottom(w gtk.Widgetter, margin int) {
51	if widget := getWidget(w); widget != nil {
52		widget.SetMarginBottom(margin)
53	}
54}
55
56// WidgetSetMarginStart sets the start margin for a widget
57func WidgetSetMarginStart(w gtk.Widgetter, margin int) {
58	if widget := getWidget(w); widget != nil {
59		widget.SetMarginStart(margin)
60	}
61}
62
63// WidgetSetMarginEnd sets the end margin for a widget
64func WidgetSetMarginEnd(w gtk.Widgetter, margin int) {
65	if widget := getWidget(w); widget != nil {
66		widget.SetMarginEnd(margin)
67	}
68}
69
70// WidgetAddCSSClass adds a CSS class to a widget
71func WidgetAddCSSClass(w gtk.Widgetter, className string) {
72	if widget := getWidget(w); widget != nil {
73		widget.AddCSSClass(className)
74	}
75}
76
77// WidgetRemoveCSSClass removes a CSS class from a widget
78func WidgetRemoveCSSClass(w gtk.Widgetter, className string) {
79	if widget := getWidget(w); widget != nil {
80		widget.RemoveCSSClass(className)
81	}
82}
83
84// WidgetHasCSSClass checks if a widget has a CSS class
85func WidgetHasCSSClass(w gtk.Widgetter, className string) bool {
86	if widget := getWidget(w); widget != nil {
87		return widget.HasCSSClass(className)
88	}
89	return false
90}
91
92// WidgetSetHExpand sets whether the widget should expand horizontally
93func WidgetSetHExpand(w gtk.Widgetter, expand bool) {
94	if widget := getWidget(w); widget != nil {
95		widget.SetHExpand(expand)
96	}
97}
98
99// WidgetSetVExpand sets whether the widget should expand vertically
100func WidgetSetVExpand(w gtk.Widgetter, expand bool) {
101	if widget := getWidget(w); widget != nil {
102		widget.SetVExpand(expand)
103	}
104}
105
106// WidgetRealizeInContainer realizes a widget within a container
107func WidgetRealizeInContainer(w gtk.Widgetter, container gtk.Widgetter) {
108	if widget := getWidget(w); widget != nil {
109		widget.Realize()
110	}
111}
112
113// WidgetRealize realizes a widget
114func WidgetRealize(w gtk.Widgetter) {
115	if widget := getWidget(w); widget != nil {
116		widget.Realize()
117	}
118}
119
120// WidgetGetParent returns the parent widget
121func WidgetGetParent(w gtk.Widgetter) gtk.Widgetter {
122	if widget := getWidget(w); widget != nil {
123		return widget.Parent()
124	}
125	return nil
126}
127
128// WidgetUnparent removes a widget from its parent
129func WidgetUnparent(w gtk.Widgetter) {
130	if widget := getWidget(w); widget != nil {
131		widget.Unparent()
132	}
133}
134
135// WidgetQueueAllocate queues an allocate operation
136func WidgetQueueAllocate(w gtk.Widgetter) {
137	if widget := getWidget(w); widget != nil {
138		widget.QueueAllocate()
139	}
140}
141
142// WidgetQueueDraw queues a redraw operation
143func WidgetQueueDraw(w gtk.Widgetter) {
144	if widget := getWidget(w); widget != nil {
145		widget.QueueDraw()
146	}
147}
148
149// WidgetGetAllocation returns the widget's allocated area
150func WidgetGetAllocation(w gtk.Widgetter) (x, y, width, height int) {
151	if widget := getWidget(w); widget != nil {
152		alloc := widget.Allocation()
153		return alloc.X(), alloc.Y(), alloc.Width(), alloc.Height()
154	}
155	return 0, 0, 0, 0
156}
157
158// WidgetGetBounds returns the widget's bounds relative to its parent
159func WidgetGetBounds(w gtk.Widgetter) (x, y, width, height float64) {
160	if widget := getWidget(w); widget != nil {
161		parent := widget.Parent()
162		if parent != nil {
163			rect, ok := widget.ComputeBounds(parent)
164			if ok && rect != nil {
165				// Get bounds directly from graphene.Rect
166				return float64(rect.X()), float64(rect.Y()),
167					float64(rect.Width()), float64(rect.Height())
168			}
169		}
170	}
171	return 0, 0, 0, 0
172}
173
174// WidgetSetFocusChild sets the focus child of a widget
175func WidgetSetFocusChild(w gtk.Widgetter, child gtk.Widgetter) {
176	if widget := getWidget(w); widget != nil {
177		widget.SetFocusChild(child)
178	}
179}
180
181// WidgetAddFocusController adds a focus event controller to a widget
182func WidgetAddFocusController(w gtk.Widgetter, onEnter, onLeave func()) *gtk.EventControllerFocus {
183	if widget := getWidget(w); widget != nil {
184		controller := gtk.NewEventControllerFocus()
185		if onEnter != nil {
186			controller.ConnectEnter(onEnter)
187		}
188		if onLeave != nil {
189			controller.ConnectLeave(onLeave)
190		}
191		widget.AddController(controller)
192		return controller
193	}
194	return nil
195}
196
197// WidgetRemoveFocusController removes a focus event controller from a widget
198func WidgetRemoveFocusController(w gtk.Widgetter, controller *gtk.EventControllerFocus) {
199	if widget := getWidget(w); widget != nil && controller != nil {
200		widget.RemoveController(controller)
201	}
202}
203
204// WidgetAddHoverHandler registers a hover callback for the given widget and
205// returns a controller that can be used to detach it later.
206func WidgetAddHoverHandler(w gtk.Widgetter, fn func()) *gtk.EventControllerMotion {
207	if widget := getWidget(w); widget != nil && fn != nil {
208		motion := gtk.NewEventControllerMotion()
209		motion.SetPropagationPhase(gtk.PhaseCapture)
210
211		// Connect to enter signal - triggered when mouse enters the widget
212		motion.ConnectEnter(func(x, y float64) {
213			fn()
214		})
215
216		widget.AddController(motion)
217		return motion
218	}
219	return nil
220}
221
222// WidgetRemoveHoverHandler removes a previously registered hover controller
223func WidgetRemoveHoverHandler(w gtk.Widgetter, controller *gtk.EventControllerMotion) {
224	if widget := getWidget(w); widget != nil && controller != nil {
225		widget.RemoveController(controller)
226	}
227}
228
229// WidgetAddController adds an event controller to a widget
230func WidgetAddController(w gtk.Widgetter, controller gtk.EventControllerer) {
231	if widget := getWidget(w); widget != nil && controller != nil {
232		widget.AddController(controller)
233	}
234}
235
236// WidgetAllocation returns the widget's allocation (alias for WidgetGetAllocation)
237func WidgetAllocation(w gtk.Widgetter) (x, y, width, height int) {
238	return WidgetGetAllocation(w)
239}
240
241// PrefersDarkTheme checks if the system prefers dark theme
242func PrefersDarkTheme() bool {
243	// First try to read GNOME's color-scheme setting (GTK4+)
244	// Check if the schema exists before attempting to create GSettings object
245	// to avoid panics on non-GNOME systems (e.g., Sway, Hyprland, etc.)
246	var colorScheme string
247	schemaSource := gio.SettingsSchemaSourceGetDefault()
248	if schemaSource != nil {
249		schema := schemaSource.Lookup("org.gnome.desktop.interface", true)
250		if schema != nil {
251			if gnomeSettings := gio.NewSettings("org.gnome.desktop.interface"); gnomeSettings != nil {
252				colorScheme = gnomeSettings.String("color-scheme")
253			}
254		}
255	}
256
257	switch colorScheme {
258	case "prefer-dark":
259		return true
260	case "prefer-light":
261		return false
262	}
263
264	// Fallback to GTK's application-level preference
265	settings := gtk.SettingsGetDefault()
266	if settings != nil {
267		obj := settings.Object
268		if obj != nil {
269			prop := obj.ObjectProperty("gtk-application-prefer-dark-theme")
270			if v, ok := prop.(bool); ok && v {
271				return true
272			}
273		}
274	}
275	return false
276}
277
278// AddCSSProvider adds a CSS provider for styling
279func AddCSSProvider(css string) error {
280	provider := gtk.NewCSSProvider()
281	provider.LoadFromString(css)
282
283	gtk.StyleContextAddProviderForDisplay(
284		gdk.DisplayGetDefault(),
285		provider,
286		gtk.STYLE_PROVIDER_PRIORITY_APPLICATION,
287	)
288
289	return nil
290}

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