detached 6a3405f

feat(omnibox): implement two-line row display with title and URL

commit
6a3405f1d1ad257a35caf574d35e398f483a20e9
author
brice · · 2025-12-16 18:24 UTC
parent
6990cc0
signature
unsigned
2 files · +56 -22
M internal/ui/component/omnibox.go +45 -22
@@ -170,8 +170,8 @@ func (o *Omnibox) resizeAndCenter(rowCount int) {
170170
171171 // Base height: header (~40px) + entry (~50px) at scale 1.0
172172 baseHeight := int(90 * o.uiScale)
173- // Row height: approximately 40px per row at scale 1.0
174- rowHeight := int(40 * o.uiScale)
173+ // Row height: approximately 50px per row at scale 1.0 (two lines: title + URL)
174+ rowHeight := int(50 * o.uiScale)
175175
176176 // Cap at max results
177177 if rowCount > omniboxMaxResults {
@@ -676,7 +676,7 @@ func (o *Omnibox) createFaviconImage(url, fallbackIcon string) *gtk.Image {
676676 return favicon
677677 }
678678
679-// createRowWithFavicon creates a ListBoxRow with favicon, title, and shortcut badge.
679+// createRowWithFavicon creates a ListBoxRow with favicon, title, URL, and shortcut badge.
680680 func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index int) *gtk.ListBoxRow {
681681 row := gtk.NewListBoxRow()
682682 if row == nil {
@@ -684,32 +684,54 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in
684684 }
685685 row.AddCssClass("omnibox-row")
686686
687- box := gtk.NewBox(gtk.OrientationHorizontalValue, 8)
688- if box == nil {
687+ hbox := gtk.NewBox(gtk.OrientationHorizontalValue, 8)
688+ if hbox == nil {
689689 return nil
690690 }
691- box.SetHexpand(true)
691+ hbox.SetHexpand(true)
692692
693- // Favicon image
693+ // Favicon image (vertically centered)
694694 if favicon := o.createFaviconImage(url, fallbackIcon); favicon != nil {
695- box.Append(&favicon.Widget)
695+ favicon.SetValign(gtk.AlignCenterValue)
696+ hbox.Append(&favicon.Widget)
696697 }
697698
698- // Title label
699- displayText := title
700- if displayText == "" {
701- displayText = url
699+ // Vertical box for title + URL
700+ textBox := gtk.NewBox(gtk.OrientationVerticalValue, 2)
701+ if textBox == nil {
702+ return nil
702703 }
703- label := gtk.NewLabel(nil)
704- if label != nil {
705- label.SetText(displayText)
706- label.AddCssClass("omnibox-suggestion-title")
707- label.SetHalign(gtk.AlignStartValue)
708- label.SetHexpand(true)
709- label.SetEllipsize(2) // PANGO_ELLIPSIZE_END
710- box.Append(&label.Widget)
704+ textBox.SetHexpand(true)
705+ textBox.SetValign(gtk.AlignCenterValue)
706+
707+ // Title label (or URL if no title)
708+ displayTitle := title
709+ if displayTitle == "" {
710+ displayTitle = url
711+ }
712+ titleLabel := gtk.NewLabel(nil)
713+ if titleLabel != nil {
714+ titleLabel.SetText(displayTitle)
715+ titleLabel.AddCssClass("omnibox-suggestion-title")
716+ titleLabel.SetHalign(gtk.AlignStartValue)
717+ titleLabel.SetEllipsize(2) // PANGO_ELLIPSIZE_END
718+ textBox.Append(&titleLabel.Widget)
719+ }
720+
721+ // URL label (only if title exists and differs from URL)
722+ if title != "" && title != url {
723+ urlLabel := gtk.NewLabel(nil)
724+ if urlLabel != nil {
725+ urlLabel.SetText(url)
726+ urlLabel.AddCssClass("omnibox-suggestion-url")
727+ urlLabel.SetHalign(gtk.AlignStartValue)
728+ urlLabel.SetEllipsize(2) // PANGO_ELLIPSIZE_END
729+ textBox.Append(&urlLabel.Widget)
730+ }
711731 }
712732
733+ hbox.Append(&textBox.Widget)
734+
713735 // Shortcut badge (Ctrl+1-9, Ctrl+0 for 10th)
714736 if index <= 9 {
715737 shortcutLabel := gtk.NewLabel(nil)
@@ -720,11 +742,12 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in
720742 shortcutLabel.SetText("Ctrl+0")
721743 }
722744 shortcutLabel.AddCssClass("omnibox-shortcut-badge")
723- box.Append(&shortcutLabel.Widget)
745+ shortcutLabel.SetValign(gtk.AlignCenterValue)
746+ hbox.Append(&shortcutLabel.Widget)
724747 }
725748 }
726749
727- row.SetChild(&box.Widget)
750+ row.SetChild(&hbox.Widget)
728751 return row
729752 }
730753
@@ -170,8 +170,8 @@ func (o *Omnibox) resizeAndCenter(rowCount int) {
170 170
171 // Base height: header (~40px) + entry (~50px) at scale 1.0171 // Base height: header (~40px) + entry (~50px) at scale 1.0
172 baseHeight := int(90 * o.uiScale)172 baseHeight := int(90 * o.uiScale)
173- // Row height: approximately 40px per row at scale 1.0173+ // Row height: approximately 50px per row at scale 1.0 (two lines: title + URL)
174- rowHeight := int(40 * o.uiScale)174+ rowHeight := int(50 * o.uiScale)
175 175
176 // Cap at max results176 // Cap at max results
177 if rowCount > omniboxMaxResults {177 if rowCount > omniboxMaxResults {
@@ -676,7 +676,7 @@ func (o *Omnibox) createFaviconImage(url, fallbackIcon string) *gtk.Image {
676 return favicon676 return favicon
677 }677 }
678 678
679-// createRowWithFavicon creates a ListBoxRow with favicon, title, and shortcut badge.679+// createRowWithFavicon creates a ListBoxRow with favicon, title, URL, and shortcut badge.
680 func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index int) *gtk.ListBoxRow {680 func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index int) *gtk.ListBoxRow {
681 row := gtk.NewListBoxRow()681 row := gtk.NewListBoxRow()
682 if row == nil {682 if row == nil {
@@ -684,32 +684,54 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in
684 }684 }
685 row.AddCssClass("omnibox-row")685 row.AddCssClass("omnibox-row")
686 686
687- box := gtk.NewBox(gtk.OrientationHorizontalValue, 8)687+ hbox := gtk.NewBox(gtk.OrientationHorizontalValue, 8)
688- if box == nil {688+ if hbox == nil {
689 return nil689 return nil
690 }690 }
691- box.SetHexpand(true)691+ hbox.SetHexpand(true)
692 692
693- // Favicon image693+ // Favicon image (vertically centered)
694 if favicon := o.createFaviconImage(url, fallbackIcon); favicon != nil {694 if favicon := o.createFaviconImage(url, fallbackIcon); favicon != nil {
695- box.Append(&favicon.Widget)695+ favicon.SetValign(gtk.AlignCenterValue)
696+ hbox.Append(&favicon.Widget)
696 }697 }
697 698
698- // Title label699+ // Vertical box for title + URL
699- displayText := title700+ textBox := gtk.NewBox(gtk.OrientationVerticalValue, 2)
700- if displayText == "" {701+ if textBox == nil {
701- displayText = url702+ return nil
702 }703 }
703- label := gtk.NewLabel(nil)704+ textBox.SetHexpand(true)
704- if label != nil {705+ textBox.SetValign(gtk.AlignCenterValue)
705- label.SetText(displayText)706+
706- label.AddCssClass("omnibox-suggestion-title")707+ // Title label (or URL if no title)
707- label.SetHalign(gtk.AlignStartValue)708+ displayTitle := title
708- label.SetHexpand(true)709+ if displayTitle == "" {
709- label.SetEllipsize(2) // PANGO_ELLIPSIZE_END710+ displayTitle = url
710- box.Append(&label.Widget)711+ }
712+ titleLabel := gtk.NewLabel(nil)
713+ if titleLabel != nil {
714+ titleLabel.SetText(displayTitle)
715+ titleLabel.AddCssClass("omnibox-suggestion-title")
716+ titleLabel.SetHalign(gtk.AlignStartValue)
717+ titleLabel.SetEllipsize(2) // PANGO_ELLIPSIZE_END
718+ textBox.Append(&titleLabel.Widget)
719+ }
720+
721+ // URL label (only if title exists and differs from URL)
722+ if title != "" && title != url {
723+ urlLabel := gtk.NewLabel(nil)
724+ if urlLabel != nil {
725+ urlLabel.SetText(url)
726+ urlLabel.AddCssClass("omnibox-suggestion-url")
727+ urlLabel.SetHalign(gtk.AlignStartValue)
728+ urlLabel.SetEllipsize(2) // PANGO_ELLIPSIZE_END
729+ textBox.Append(&urlLabel.Widget)
730+ }
711 }731 }
712 732
733+ hbox.Append(&textBox.Widget)
734+
713 // Shortcut badge (Ctrl+1-9, Ctrl+0 for 10th)735 // Shortcut badge (Ctrl+1-9, Ctrl+0 for 10th)
714 if index <= 9 {736 if index <= 9 {
715 shortcutLabel := gtk.NewLabel(nil)737 shortcutLabel := gtk.NewLabel(nil)
@@ -720,11 +742,12 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in
720 shortcutLabel.SetText("Ctrl+0")742 shortcutLabel.SetText("Ctrl+0")
721 }743 }
722 shortcutLabel.AddCssClass("omnibox-shortcut-badge")744 shortcutLabel.AddCssClass("omnibox-shortcut-badge")
723- box.Append(&shortcutLabel.Widget)745+ shortcutLabel.SetValign(gtk.AlignCenterValue)
746+ hbox.Append(&shortcutLabel.Widget)
724 }747 }
725 }748 }
726 749
727- row.SetChild(&box.Widget)750+ row.SetChild(&hbox.Widget)
728 return row751 return row
729 }752 }
730 753
M internal/ui/theme/css.go +11 -0
@@ -229,6 +229,17 @@ window.omnibox-window {
229229 font-weight: 500;
230230 }
231231
232+/* URL text below title */
233+.omnibox-suggestion-url {
234+ font-size: 0.75em;
235+ color: var(--muted);
236+ font-weight: 400;
237+}
238+
239+.omnibox-row:selected .omnibox-suggestion-url {
240+ color: var(--muted);
241+}
242+
232243 /* Keyboard shortcut badge */
233244 .omnibox-shortcut-badge {
234245 background-color: alpha(var(--muted, #909090), 0.3);
@@ -229,6 +229,17 @@ window.omnibox-window {
229 font-weight: 500;229 font-weight: 500;
230 }230 }
231 231
232+/* URL text below title */
233+.omnibox-suggestion-url {
234+ font-size: 0.75em;
235+ color: var(--muted);
236+ font-weight: 400;
237+}
238+
239+.omnibox-row:selected .omnibox-suggestion-url {
240+ color: var(--muted);
241+}
242+
232 /* Keyboard shortcut badge */243 /* Keyboard shortcut badge */
233 .omnibox-shortcut-badge {244 .omnibox-shortcut-badge {
234 background-color: alpha(var(--muted, #909090), 0.3);245 background-color: alpha(var(--muted, #909090), 0.3);

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