detached 2740349

fix(omnibox): size standalone results from monitor height (#463)

* fix(omnibox): size standalone results from monitor height

The standalone layer-shell window is sized by its content, so its own
allocation never reflected the space available on screen and the
omnibox always showed 10 rows. Use the monitor height instead.

Read GdkMonitor geometry through a C-layout struct: puregotk's
gdk.Rectangle declares Go int fields while GdkRectangle holds C ints.

* chore(deps): bump puregotk to v0.7.5

puregotk v0.7.5 generates gint/guint record fields as int32/uint32, so
gdk.Rectangle now matches GdkRectangle. Drop the C-layout workaround in
the omnibox monitor lookup and convert Rectangle fields in the gtkmenu
popover anchor and WebKit popup geometry.
commit
274034900c232929a8a8c12c07b72b13f347aa45
author
bnema · · 2026-10-04 07:52 UTC
committer
GitHub · · 2026-10-04 07:52 UTC
parent
a9d1a98
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
10 files · +59 -13
M go.mod +3 -1
@@ -11,7 +11,7 @@ require (
1111 github.com/bnema/purego-pipewire v0.1.6
1212 github.com/bnema/purego-sqlite v0.1.5
1313 github.com/bnema/purego-webp v0.2.1
14- github.com/bnema/puregotk v0.7.4
14+ github.com/bnema/puregotk v0.7.5
1515 github.com/charmbracelet/bubbles v1.0.0
1616 github.com/charmbracelet/bubbletea v1.3.10
1717 github.com/charmbracelet/lipgloss v1.1.0
@@ -47,6 +47,7 @@ require (
4747 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
4848 github.com/fatih/color v1.19.0 // indirect
4949 github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
50+ github.com/google/go-cmp v0.7.0 // indirect
5051 github.com/inconshreveable/mousetrap v1.1.0 // indirect
5152 github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
5253 github.com/mattn/go-colorable v0.1.14 // indirect
@@ -77,6 +78,7 @@ require (
7778 golang.org/x/text v0.42.0 // indirect
7879 golang.org/x/tools v0.49.0 // indirect
7980 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
81+ mvdan.cc/gofumpt v0.9.2 // indirect
8082 )
8183
8284 tool github.com/a-h/templ/cmd/templ
@@ -11,7 +11,7 @@ require (
11 github.com/bnema/purego-pipewire v0.1.611 github.com/bnema/purego-pipewire v0.1.6
12 github.com/bnema/purego-sqlite v0.1.512 github.com/bnema/purego-sqlite v0.1.5
13 github.com/bnema/purego-webp v0.2.113 github.com/bnema/purego-webp v0.2.1
14- github.com/bnema/puregotk v0.7.414+ github.com/bnema/puregotk v0.7.5
15 github.com/charmbracelet/bubbles v1.0.015 github.com/charmbracelet/bubbles v1.0.0
16 github.com/charmbracelet/bubbletea v1.3.1016 github.com/charmbracelet/bubbletea v1.3.10
17 github.com/charmbracelet/lipgloss v1.1.017 github.com/charmbracelet/lipgloss v1.1.0
@@ -47,6 +47,7 @@ require (
47 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect47 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
48 github.com/fatih/color v1.19.0 // indirect48 github.com/fatih/color v1.19.0 // indirect
49 github.com/go-viper/mapstructure/v2 v2.5.0 // indirect49 github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
50+ github.com/google/go-cmp v0.7.0 // indirect
50 github.com/inconshreveable/mousetrap v1.1.0 // indirect51 github.com/inconshreveable/mousetrap v1.1.0 // indirect
51 github.com/lucasb-eyer/go-colorful v1.4.0 // indirect52 github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
52 github.com/mattn/go-colorable v0.1.14 // indirect53 github.com/mattn/go-colorable v0.1.14 // indirect
@@ -77,6 +78,7 @@ require (
77 golang.org/x/text v0.42.0 // indirect78 golang.org/x/text v0.42.0 // indirect
78 golang.org/x/tools v0.49.0 // indirect79 golang.org/x/tools v0.49.0 // indirect
79 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect80 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
81+ mvdan.cc/gofumpt v0.9.2 // indirect
80 )82 )
81 83
82 tool github.com/a-h/templ/cmd/templ84 tool github.com/a-h/templ/cmd/templ
M go.sum +4 -0
@@ -24,6 +24,8 @@ github.com/bnema/purego-webp v0.2.1 h1:/4TU7++xyc6WnHzqRPg5bYYiIoKnxYaf/7haau0q5
2424 github.com/bnema/purego-webp v0.2.1/go.mod h1:TYOQvozzcPup3zJQ58YmiCB4MkroeiyvvTP/MGWm+zI=
2525 github.com/bnema/puregotk v0.7.4 h1:EL2EIwceNoDudd2OcgT0Tuuus2kmZrNuU6luODESh4g=
2626 github.com/bnema/puregotk v0.7.4/go.mod h1:BLcNL9jse2WqX3bRZCVhx4scE9xzWT26OazdHFzL5mo=
27+github.com/bnema/puregotk v0.7.5 h1:g7RGgrNKQUMkxtZ9T7ocMQYtDOopqT+4JuyZeH0KuCI=
28+github.com/bnema/puregotk v0.7.5/go.mod h1:BLcNL9jse2WqX3bRZCVhx4scE9xzWT26OazdHFzL5mo=
2729 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
2830 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
2931 github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
@@ -178,3 +180,5 @@ modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
178180 modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
179181 modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
180182 modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
183+mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4=
184+mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s=
@@ -24,6 +24,8 @@ github.com/bnema/purego-webp v0.2.1 h1:/4TU7++xyc6WnHzqRPg5bYYiIoKnxYaf/7haau0q5
24 github.com/bnema/purego-webp v0.2.1/go.mod h1:TYOQvozzcPup3zJQ58YmiCB4MkroeiyvvTP/MGWm+zI=24 github.com/bnema/purego-webp v0.2.1/go.mod h1:TYOQvozzcPup3zJQ58YmiCB4MkroeiyvvTP/MGWm+zI=
25 github.com/bnema/puregotk v0.7.4 h1:EL2EIwceNoDudd2OcgT0Tuuus2kmZrNuU6luODESh4g=25 github.com/bnema/puregotk v0.7.4 h1:EL2EIwceNoDudd2OcgT0Tuuus2kmZrNuU6luODESh4g=
26 github.com/bnema/puregotk v0.7.4/go.mod h1:BLcNL9jse2WqX3bRZCVhx4scE9xzWT26OazdHFzL5mo=26 github.com/bnema/puregotk v0.7.4/go.mod h1:BLcNL9jse2WqX3bRZCVhx4scE9xzWT26OazdHFzL5mo=
27+github.com/bnema/puregotk v0.7.5 h1:g7RGgrNKQUMkxtZ9T7ocMQYtDOopqT+4JuyZeH0KuCI=
28+github.com/bnema/puregotk v0.7.5/go.mod h1:BLcNL9jse2WqX3bRZCVhx4scE9xzWT26OazdHFzL5mo=
27 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=29 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
28 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=30 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
29 github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=31 github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc=
@@ -178,3 +180,5 @@ modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
178 modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=180 modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
179 modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=181 modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
180 modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=182 modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
183+mvdan.cc/gofumpt v0.9.2 h1:zsEMWL8SVKGHNztrx6uZrXdp7AX8r421Vvp23sz7ik4=
184+mvdan.cc/gofumpt v0.9.2/go.mod h1:iB7Hn+ai8lPvofHd9ZFGVg2GOr8sBUw1QUWjNbmIL/s=
M internal/infrastructure/gtkmenu/renderer.go +1 -1
@@ -172,7 +172,7 @@ func PopoverPointingRect(
172172 destX, destY = tx, ty
173173 }
174174 }
175- return &gdk.Rectangle{X: int(destX), Y: int(destY), Width: 1, Height: 1}
175+ return &gdk.Rectangle{X: int32(destX), Y: int32(destY), Width: 1, Height: 1}
176176 }
177177
178178 func createPopoverHost(parent *gtk.Widget, x, y int32) (*gtk.MenuButton, *gtk.Overlay) {
@@ -172,7 +172,7 @@ func PopoverPointingRect(
172 destX, destY = tx, ty172 destX, destY = tx, ty
173 }173 }
174 }174 }
175- return &gdk.Rectangle{X: int(destX), Y: int(destY), Width: 1, Height: 1}175+ return &gdk.Rectangle{X: int32(destX), Y: int32(destY), Width: 1, Height: 1}
176 }176 }
177 177
178 func createPopoverHost(parent *gtk.Widget, x, y int32) (*gtk.MenuButton, *gtk.Overlay) {178 func createPopoverHost(parent *gtk.Widget, x, y int32) (*gtk.MenuButton, *gtk.Overlay) {
M internal/infrastructure/gtkmenu/renderer_test.go +6 -6
@@ -24,10 +24,10 @@ func TestPopoverPointingRect(t *testing.T) {
2424 return 24, 48, true
2525 })
2626
27- require.Equal(t, 24, rect.X)
28- require.Equal(t, 48, rect.Y)
29- require.Equal(t, 1, rect.Width)
30- require.Equal(t, 1, rect.Height)
27+ require.Equal(t, int32(24), rect.X)
28+ require.Equal(t, int32(48), rect.Y)
29+ require.Equal(t, int32(1), rect.Width)
30+ require.Equal(t, int32(1), rect.Height)
3131 })
3232
3333 t.Run("falls back to raw coordinates when translation fails", func(t *testing.T) {
@@ -35,8 +35,8 @@ func TestPopoverPointingRect(t *testing.T) {
3535 return srcX, srcY, false
3636 })
3737
38- require.Equal(t, 10, rect.X)
39- require.Equal(t, 20, rect.Y)
38+ require.Equal(t, int32(10), rect.X)
39+ require.Equal(t, int32(20), rect.Y)
4040 })
4141 }
4242
@@ -24,10 +24,10 @@ func TestPopoverPointingRect(t *testing.T) {
24 return 24, 48, true24 return 24, 48, true
25 })25 })
26 26
27- require.Equal(t, 24, rect.X)27+ require.Equal(t, int32(24), rect.X)
28- require.Equal(t, 48, rect.Y)28+ require.Equal(t, int32(48), rect.Y)
29- require.Equal(t, 1, rect.Width)29+ require.Equal(t, int32(1), rect.Width)
30- require.Equal(t, 1, rect.Height)30+ require.Equal(t, int32(1), rect.Height)
31 })31 })
32 32
33 t.Run("falls back to raw coordinates when translation fails", func(t *testing.T) {33 t.Run("falls back to raw coordinates when translation fails", func(t *testing.T) {
@@ -35,8 +35,8 @@ func TestPopoverPointingRect(t *testing.T) {
35 return srcX, srcY, false35 return srcX, srcY, false
36 })36 })
37 37
38- require.Equal(t, 10, rect.X)38+ require.Equal(t, int32(10), rect.X)
39- require.Equal(t, 20, rect.Y)39+ require.Equal(t, int32(20), rect.Y)
40 })40 })
41 }41 }
42 42
M internal/infrastructure/webkit/webview.go +3 -3
@@ -593,10 +593,10 @@ func (wv *WebView) ResolvePopupFeatures() dto.PopupFeatures {
593593 if props == nil {
594594 return dto.PopupFeatures{State: dto.PopupFeaturesUnknown}
595595 }
596- geometry := &gdk.Rectangle{}
597- props.GetGeometry(geometry)
596+ var geometry gdk.Rectangle
597+ props.GetGeometry(&geometry)
598598 return popupFeaturesFromWindowProperties(
599- geometry.X, geometry.Y, geometry.Width, geometry.Height,
599+ int(geometry.X), int(geometry.Y), int(geometry.Width), int(geometry.Height),
600600 props.GetToolbarVisible(), props.GetLocationbarVisible(), props.GetResizable(),
601601 )
602602 }
@@ -593,10 +593,10 @@ func (wv *WebView) ResolvePopupFeatures() dto.PopupFeatures {
593 if props == nil {593 if props == nil {
594 return dto.PopupFeatures{State: dto.PopupFeaturesUnknown}594 return dto.PopupFeatures{State: dto.PopupFeaturesUnknown}
595 }595 }
596- geometry := &gdk.Rectangle{}596+ var geometry gdk.Rectangle
597- props.GetGeometry(geometry)597+ props.GetGeometry(&geometry)
598 return popupFeaturesFromWindowProperties(598 return popupFeaturesFromWindowProperties(
599- geometry.X, geometry.Y, geometry.Width, geometry.Height,599+ int(geometry.X), int(geometry.Y), int(geometry.Width), int(geometry.Height),
600 props.GetToolbarVisible(), props.GetLocationbarVisible(), props.GetResizable(),600 props.GetToolbarVisible(), props.GetLocationbarVisible(), props.GetResizable(),
601 )601 )
602 }602 }
M internal/ui/component/modal_sizing.go +4 -0
@@ -17,6 +17,7 @@ type ModalSizeConfig struct {
1717 FixedWidth int // Optional fixed width in pixels (overrides WidthPct/MaxWidth)
1818 FixedTopMargin int // Optional fixed top margin in pixels
1919 UseFixedTopMargin bool // When true, FixedTopMargin overrides TopMarginPct
20+ UseMonitorHeight bool // When true, row limits use the monitor height (standalone window sized by its content)
2021 }
2122
2223 // RowHeightDefaults holds default (unscaled) row heights for list-based modals.
@@ -141,6 +142,9 @@ func ResolveModalSizeConfig(cfg, defaults ModalSizeConfig) ModalSizeConfig {
141142 resolved.FixedTopMargin = cfg.FixedTopMargin
142143 resolved.UseFixedTopMargin = true
143144 }
145+ if cfg.UseMonitorHeight {
146+ resolved.UseMonitorHeight = true
147+ }
144148
145149 return resolved
146150 }
@@ -17,6 +17,7 @@ type ModalSizeConfig struct {
17 FixedWidth int // Optional fixed width in pixels (overrides WidthPct/MaxWidth)17 FixedWidth int // Optional fixed width in pixels (overrides WidthPct/MaxWidth)
18 FixedTopMargin int // Optional fixed top margin in pixels18 FixedTopMargin int // Optional fixed top margin in pixels
19 UseFixedTopMargin bool // When true, FixedTopMargin overrides TopMarginPct19 UseFixedTopMargin bool // When true, FixedTopMargin overrides TopMarginPct
20+ UseMonitorHeight bool // When true, row limits use the monitor height (standalone window sized by its content)
20 }21 }
21 22
22 // RowHeightDefaults holds default (unscaled) row heights for list-based modals.23 // RowHeightDefaults holds default (unscaled) row heights for list-based modals.
@@ -141,6 +142,9 @@ func ResolveModalSizeConfig(cfg, defaults ModalSizeConfig) ModalSizeConfig {
141 resolved.FixedTopMargin = cfg.FixedTopMargin142 resolved.FixedTopMargin = cfg.FixedTopMargin
142 resolved.UseFixedTopMargin = true143 resolved.UseFixedTopMargin = true
143 }144 }
145+ if cfg.UseMonitorHeight {
146+ resolved.UseMonitorHeight = true
147+ }
144 148
145 return resolved149 return resolved
146 }150 }
M internal/ui/component/modal_sizing_test.go +2 -0
@@ -117,6 +117,7 @@ func TestResolveModalSizeConfig_MergesDefaultsAndOverrides(t *testing.T) {
117117 FixedWidth: 800,
118118 FixedTopMargin: 0,
119119 UseFixedTopMargin: true,
120+ UseMonitorHeight: true,
120121 }
121122
122123 resolved := ResolveModalSizeConfig(override, defaults)
@@ -128,6 +129,7 @@ func TestResolveModalSizeConfig_MergesDefaultsAndOverrides(t *testing.T) {
128129 assert.Equal(t, 800, resolved.FixedWidth)
129130 assert.True(t, resolved.UseFixedTopMargin)
130131 assert.Equal(t, 0, resolved.FixedTopMargin)
132+ assert.True(t, resolved.UseMonitorHeight)
131133 }
132134
133135 func TestScaleValue_DefaultScale(t *testing.T) {
@@ -117,6 +117,7 @@ func TestResolveModalSizeConfig_MergesDefaultsAndOverrides(t *testing.T) {
117 FixedWidth: 800,117 FixedWidth: 800,
118 FixedTopMargin: 0,118 FixedTopMargin: 0,
119 UseFixedTopMargin: true,119 UseFixedTopMargin: true,
120+ UseMonitorHeight: true,
120 }121 }
121 122
122 resolved := ResolveModalSizeConfig(override, defaults)123 resolved := ResolveModalSizeConfig(override, defaults)
@@ -128,6 +129,7 @@ func TestResolveModalSizeConfig_MergesDefaultsAndOverrides(t *testing.T) {
128 assert.Equal(t, 800, resolved.FixedWidth)129 assert.Equal(t, 800, resolved.FixedWidth)
129 assert.True(t, resolved.UseFixedTopMargin)130 assert.True(t, resolved.UseFixedTopMargin)
130 assert.Equal(t, 0, resolved.FixedTopMargin)131 assert.Equal(t, 0, resolved.FixedTopMargin)
132+ assert.True(t, resolved.UseMonitorHeight)
131 }133 }
132 134
133 func TestScaleValue_DefaultScale(t *testing.T) {135 func TestScaleValue_DefaultScale(t *testing.T) {
M internal/ui/component/omnibox.go +34 -2
@@ -576,13 +576,45 @@ func (o *Omnibox) setResultsContainerState(rowCount int) {
576576 }
577577 }
578578
579-// effectiveMaxRows returns the max visible rows adapted to the current parent pane height.
579+// effectiveMaxRows returns the max visible rows adapted to the available height:
580+// the parent pane, or the monitor when the omnibox sizes its own window.
580581 // Must be called on the GTK main thread.
581582 func (o *Omnibox) effectiveMaxRows() int {
582583 if o.parentOverlay == nil {
583584 return OmniboxListDefaults.MaxVisibleRows
584585 }
585- return EffectiveMaxRows(o.parentOverlay.GetAllocatedHeight(), o.estimateRowHeight(), o.sizeCfg, OmniboxListDefaults)
586+ height := o.parentOverlay.GetAllocatedHeight()
587+ if o.sizeCfg.UseMonitorHeight {
588+ height = widgetMonitorHeight(o.parentOverlay.GtkWidget())
589+ }
590+ return EffectiveMaxRows(height, o.estimateRowHeight(), o.sizeCfg, OmniboxListDefaults)
591+}
592+
593+// widgetMonitorHeight returns the logical height of the monitor showing the
594+// widget, or 0 when it is unknown.
595+func widgetMonitorHeight(widget *gtk.Widget) int {
596+ if widget == nil {
597+ return 0
598+ }
599+ native := widget.GetNative()
600+ if native == nil {
601+ return 0
602+ }
603+ surface := native.GetSurface()
604+ if surface == nil {
605+ return 0
606+ }
607+ display := surface.GetDisplay()
608+ if display == nil {
609+ return 0
610+ }
611+ monitor := display.GetMonitorAtSurface(surface)
612+ if monitor == nil {
613+ return 0
614+ }
615+ var geometry gdk.Rectangle
616+ monitor.GetGeometry(&geometry)
617+ return int(geometry.Height)
586618 }
587619
588620 // requestedDimensions returns the scale-aware modal width and top margin for
@@ -576,13 +576,45 @@ func (o *Omnibox) setResultsContainerState(rowCount int) {
576 }576 }
577 }577 }
578 578
579-// effectiveMaxRows returns the max visible rows adapted to the current parent pane height.579+// effectiveMaxRows returns the max visible rows adapted to the available height:
580+// the parent pane, or the monitor when the omnibox sizes its own window.
580 // Must be called on the GTK main thread.581 // Must be called on the GTK main thread.
581 func (o *Omnibox) effectiveMaxRows() int {582 func (o *Omnibox) effectiveMaxRows() int {
582 if o.parentOverlay == nil {583 if o.parentOverlay == nil {
583 return OmniboxListDefaults.MaxVisibleRows584 return OmniboxListDefaults.MaxVisibleRows
584 }585 }
585- return EffectiveMaxRows(o.parentOverlay.GetAllocatedHeight(), o.estimateRowHeight(), o.sizeCfg, OmniboxListDefaults)586+ height := o.parentOverlay.GetAllocatedHeight()
587+ if o.sizeCfg.UseMonitorHeight {
588+ height = widgetMonitorHeight(o.parentOverlay.GtkWidget())
589+ }
590+ return EffectiveMaxRows(height, o.estimateRowHeight(), o.sizeCfg, OmniboxListDefaults)
591+}
592+
593+// widgetMonitorHeight returns the logical height of the monitor showing the
594+// widget, or 0 when it is unknown.
595+func widgetMonitorHeight(widget *gtk.Widget) int {
596+ if widget == nil {
597+ return 0
598+ }
599+ native := widget.GetNative()
600+ if native == nil {
601+ return 0
602+ }
603+ surface := native.GetSurface()
604+ if surface == nil {
605+ return 0
606+ }
607+ display := surface.GetDisplay()
608+ if display == nil {
609+ return 0
610+ }
611+ monitor := display.GetMonitorAtSurface(surface)
612+ if monitor == nil {
613+ return 0
614+ }
615+ var geometry gdk.Rectangle
616+ monitor.GetGeometry(&geometry)
617+ return int(geometry.Height)
586 }618 }
587 619
588 // requestedDimensions returns the scale-aware modal width and top margin for620 // requestedDimensions returns the scale-aware modal width and top margin for
M internal/ui/standalone_omnibox.go +1 -0
@@ -206,6 +206,7 @@ func standaloneOmniboxSizeConfig() component.ModalSizeConfig {
206206 FixedWidth: component.OmniboxSizeDefaults.MaxWidth,
207207 FixedTopMargin: 0,
208208 UseFixedTopMargin: true,
209+ UseMonitorHeight: true,
209210 }, component.OmniboxSizeDefaults)
210211 }
211212
@@ -206,6 +206,7 @@ func standaloneOmniboxSizeConfig() component.ModalSizeConfig {
206 FixedWidth: component.OmniboxSizeDefaults.MaxWidth,206 FixedWidth: component.OmniboxSizeDefaults.MaxWidth,
207 FixedTopMargin: 0,207 FixedTopMargin: 0,
208 UseFixedTopMargin: true,208 UseFixedTopMargin: true,
209+ UseMonitorHeight: true,
209 }, component.OmniboxSizeDefaults)210 }, component.OmniboxSizeDefaults)
210 }211 }
211 212
M internal/ui/standalone_omnibox_test.go +1 -0
@@ -136,6 +136,7 @@ func TestStandaloneOmniboxSizeConfig_UsesFixedGeometry(t *testing.T) {
136136 FixedWidth: 800,
137137 FixedTopMargin: 0,
138138 UseFixedTopMargin: true,
139+ UseMonitorHeight: true,
139140 }) {
140141 t.Fatalf("unexpected standalone omnibox size config: %#v", cfg)
141142 }
@@ -136,6 +136,7 @@ func TestStandaloneOmniboxSizeConfig_UsesFixedGeometry(t *testing.T) {
136 FixedWidth: 800,136 FixedWidth: 800,
137 FixedTopMargin: 0,137 FixedTopMargin: 0,
138 UseFixedTopMargin: true,138 UseFixedTopMargin: true,
139+ UseMonitorHeight: true,
139 }) {140 }) {
140 t.Fatalf("unexpected standalone omnibox size config: %#v", cfg)141 t.Fatalf("unexpected standalone omnibox size config: %#v", cfg)
141 }142 }

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