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) { | |||
| 170 | 170 | ||
| 171 | 171 | // Base height: header (~40px) + entry (~50px) at scale 1.0 | |
| 172 | 172 | 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) | |
| 175 | 175 | ||
| 176 | 176 | // Cap at max results | |
| 177 | 177 | if rowCount > omniboxMaxResults { | |
| @@ -676,7 +676,7 @@ func (o *Omnibox) createFaviconImage(url, fallbackIcon string) *gtk.Image { | |||
| 676 | 676 | 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 | 680 | func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index int) *gtk.ListBoxRow { | |
| 681 | 681 | row := gtk.NewListBoxRow() | |
| 682 | 682 | if row == nil { | |
| @@ -684,32 +684,54 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in | |||
| 684 | 684 | } | |
| 685 | 685 | row.AddCssClass("omnibox-row") | |
| 686 | 686 | ||
| 687 | - | box := gtk.NewBox(gtk.OrientationHorizontalValue, 8) | |
| 688 | - | if box == nil { | |
| 687 | + | hbox := gtk.NewBox(gtk.OrientationHorizontalValue, 8) | |
| 688 | + | if hbox == nil { | |
| 689 | 689 | return nil | |
| 690 | 690 | } | |
| 691 | - | box.SetHexpand(true) | |
| 691 | + | hbox.SetHexpand(true) | |
| 692 | 692 | ||
| 693 | - | // Favicon image | |
| 693 | + | // Favicon image (vertically centered) | |
| 694 | 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 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 | |
| 702 | 703 | } | |
| 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 | + | } | |
| 711 | 731 | } | |
| 712 | 732 | ||
| 733 | + | hbox.Append(&textBox.Widget) | |
| 734 | + | ||
| 713 | 735 | // Shortcut badge (Ctrl+1-9, Ctrl+0 for 10th) | |
| 714 | 736 | if index <= 9 { | |
| 715 | 737 | shortcutLabel := gtk.NewLabel(nil) | |
| @@ -720,11 +742,12 @@ func (o *Omnibox) createRowWithFavicon(url, title, fallbackIcon string, index in | |||
| 720 | 742 | shortcutLabel.SetText("Ctrl+0") | |
| 721 | 743 | } | |
| 722 | 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 | 751 | return row | |
| 729 | 752 | } | |
| 730 | 753 | ||
| @@ -170,8 +170,8 @@ func (o *Omnibox) resizeAndCenter(rowCount int) { | |||||
| 170 | 170 | ||||
| 171 | // Base height: header (~40px) + entry (~50px) at scale 1.0 | 171 | // 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.0 | 173 | + | // 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 results | 176 | // 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 favicon | 676 | 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 nil | 689 | return nil | ||
| 690 | } | 690 | } | ||
| 691 | - | box.SetHexpand(true) | 691 | + | hbox.SetHexpand(true) |
| 692 | 692 | ||||
| 693 | - | // Favicon image | 693 | + | // 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 label | 699 | + | // Vertical box for title + URL |
| 699 | - | displayText := title | 700 | + | textBox := gtk.NewBox(gtk.OrientationVerticalValue, 2) |
| 700 | - | if displayText == "" { | 701 | + | if textBox == nil { |
| 701 | - | displayText = url | 702 | + | 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_END | 710 | + | 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 row | 751 | return row | ||
| 729 | } | 752 | } | ||
| 730 | 753 | ||||
M internal/ui/theme/css.go +11 -0
| @@ -229,6 +229,17 @@ window.omnibox-window { | |||
| 229 | 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 | 243 | /* Keyboard shortcut badge */ | |
| 233 | 244 | .omnibox-shortcut-badge { | |
| 234 | 245 | 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); | ||