fix(omnibox): cap rendered rows to navigable results and scroll only on keyboard selection
- commit
- 36bf68a301736c1993ed5bbbe942607640520d76
- author
- brice · · 2026-10-10 01:06 UTC
- parent
- c89d890
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +98 -65
M internal/ui/component/modal_sizing_test.go +2 -3
| @@ -178,9 +178,8 @@ func TestOmniboxValign(t *testing.T) { | |||
| 178 | 178 | assert.Equal(t, gtk.AlignStartValue, omniboxValign(ModalSizeConfig{UseFixedTopMargin: true})) | |
| 179 | 179 | } | |
| 180 | 180 | ||
| 181 | - | func TestOmniboxListDefaults_ShowFiveScrollTen(t *testing.T) { | |
| 182 | - | assert.Equal(t, 5, OmniboxListDefaults.MaxVisibleRows) | |
| 183 | - | assert.Equal(t, 10, OmniboxListDefaults.MaxResults) | |
| 181 | + | func TestOmniboxListDefaults_ScrollableResults(t *testing.T) { | |
| 182 | + | assert.Less(t, OmniboxListDefaults.MaxVisibleRows, OmniboxListDefaults.MaxResults) | |
| 184 | 183 | assert.Less(t, OmniboxListDefaults.SmallMaxVisibleRows, OmniboxListDefaults.MaxVisibleRows) | |
| 185 | 184 | } | |
| 186 | 185 | ||
| @@ -178,9 +178,8 @@ func TestOmniboxValign(t *testing.T) { | |||||
| 178 | assert.Equal(t, gtk.AlignStartValue, omniboxValign(ModalSizeConfig{UseFixedTopMargin: true})) | 178 | assert.Equal(t, gtk.AlignStartValue, omniboxValign(ModalSizeConfig{UseFixedTopMargin: true})) | ||
| 179 | } | 179 | } | ||
| 180 | 180 | ||||
| 181 | - | func TestOmniboxListDefaults_ShowFiveScrollTen(t *testing.T) { | 181 | + | func TestOmniboxListDefaults_ScrollableResults(t *testing.T) { |
| 182 | - | assert.Equal(t, 5, OmniboxListDefaults.MaxVisibleRows) | 182 | + | assert.Less(t, OmniboxListDefaults.MaxVisibleRows, OmniboxListDefaults.MaxResults) |
| 183 | - | assert.Equal(t, 10, OmniboxListDefaults.MaxResults) | |||
| 184 | assert.Less(t, OmniboxListDefaults.SmallMaxVisibleRows, OmniboxListDefaults.MaxVisibleRows) | 183 | assert.Less(t, OmniboxListDefaults.SmallMaxVisibleRows, OmniboxListDefaults.MaxVisibleRows) | ||
| 185 | } | 184 | } | ||
| 186 | 185 | ||||
M internal/ui/component/omnibox.go +31 -34
| @@ -1512,7 +1512,7 @@ func (o *Omnibox) restoreEntryToRealInput() { | |||
| 1512 | 1512 | // updateGhostFromSelectionWithInput updates ghost text based on selected row and input. | |
| 1513 | 1513 | func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | |
| 1514 | 1514 | log := logging.FromContext(o.ctx) | |
| 1515 | - | maxVisible := OmniboxListDefaults.MaxResults | |
| 1515 | + | maxResults := OmniboxListDefaults.MaxResults | |
| 1516 | 1516 | o.mu.RLock() | |
| 1517 | 1517 | idx := o.selectedIndex | |
| 1518 | 1518 | mode := o.viewMode | |
| @@ -1540,10 +1540,10 @@ func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | |||
| 1540 | 1540 | return | |
| 1541 | 1541 | } | |
| 1542 | 1542 | ||
| 1543 | - | targetURL, hasExplicitSelection := selectedTargetURL(mode, idx, maxVisible, suggestions, favorites) | |
| 1543 | + | targetURL, hasExplicitSelection := selectedTargetURL(mode, idx, maxResults, suggestions, favorites) | |
| 1544 | 1544 | ||
| 1545 | 1545 | if entryText != "" { | |
| 1546 | - | o.resolveGhostCompletion(entryText, targetURL, hasExplicitSelection, mode, maxVisible, suggestions, favorites) | |
| 1546 | + | o.resolveGhostCompletion(entryText, targetURL, hasExplicitSelection, mode, maxResults, suggestions, favorites) | |
| 1547 | 1547 | return | |
| 1548 | 1548 | } | |
| 1549 | 1549 | ||
| @@ -1565,7 +1565,7 @@ func (o *Omnibox) resolveGhostCompletion( | |||
| 1565 | 1565 | entryText, selectedURL string, | |
| 1566 | 1566 | hasExplicitSelection bool, | |
| 1567 | 1567 | mode ViewMode, | |
| 1568 | - | maxVisible int, | |
| 1568 | + | maxResults int, | |
| 1569 | 1569 | suggestions []Suggestion, | |
| 1570 | 1570 | favorites []Favorite, | |
| 1571 | 1571 | ) { | |
| @@ -1575,7 +1575,7 @@ func (o *Omnibox) resolveGhostCompletion( | |||
| 1575 | 1575 | SelectedURL: selectedURL, | |
| 1576 | 1576 | HasExplicitSelection: hasExplicitSelection, | |
| 1577 | 1577 | Mode: mode, | |
| 1578 | - | MaxVisible: maxVisible, | |
| 1578 | + | MaxResults: maxResults, | |
| 1579 | 1579 | Suggestions: suggestions, | |
| 1580 | 1580 | Favorites: favorites, | |
| 1581 | 1581 | }) | |
| @@ -1987,6 +1987,11 @@ func (o *Omnibox) rebuildList() { | |||
| 1987 | 1987 | bangSuggestions := o.bangSuggestions | |
| 1988 | 1988 | o.mu.RUnlock() | |
| 1989 | 1989 | ||
| 1990 | + | limit := OmniboxListDefaults.MaxResults | |
| 1991 | + | bangSuggestions = bangSuggestions[:limitedCount(len(bangSuggestions), limit)] | |
| 1992 | + | suggestions = suggestions[:limitedCount(len(suggestions), limit)] | |
| 1993 | + | favorites = favorites[:limitedCount(len(favorites), limit)] | |
| 1994 | + | ||
| 1990 | 1995 | if bangMode { | |
| 1991 | 1996 | for i, b := range bangSuggestions { | |
| 1992 | 1997 | row := o.createBangRow(b, i) | |
| @@ -2267,17 +2272,22 @@ func (o *Omnibox) selectIndex(index int) { | |||
| 2267 | 2272 | row := o.listBox.GetRowAtIndex(index) | |
| 2268 | 2273 | if row != nil { | |
| 2269 | 2274 | o.listBox.SelectRow(row) | |
| 2270 | - | o.scrollRowIntoView(row) | |
| 2271 | 2275 | return | |
| 2272 | 2276 | } | |
| 2273 | 2277 | ||
| 2274 | 2278 | o.updateGhostFromSelection() | |
| 2275 | 2279 | } | |
| 2276 | 2280 | ||
| 2277 | - | // scrollRowIntoView scrolls the results list so the row is fully visible. | |
| 2278 | - | // The entry keeps keyboard focus, so GTK does not scroll the list itself. | |
| 2279 | - | func (o *Omnibox) scrollRowIntoView(row *gtk.ListBoxRow) { | |
| 2280 | - | if o.scrolledWin == nil || row == nil { | |
| 2281 | + | // scrollSelectionIntoView scrolls the results list so the row at index is | |
| 2282 | + | // fully visible. The entry keeps keyboard focus, so GTK does not scroll the | |
| 2283 | + | // list itself. Only keyboard navigation calls this: hover must not move rows | |
| 2284 | + | // under the pointer. | |
| 2285 | + | func (o *Omnibox) scrollSelectionIntoView(index int) { | |
| 2286 | + | if o.scrolledWin == nil || o.listBox == nil { | |
| 2287 | + | return | |
| 2288 | + | } | |
| 2289 | + | row := o.listBox.GetRowAtIndex(index) | |
| 2290 | + | if row == nil { | |
| 2281 | 2291 | return | |
| 2282 | 2292 | } | |
| 2283 | 2293 | adj := o.scrolledWin.GetVadjustment() | |
| @@ -2297,17 +2307,8 @@ func (o *Omnibox) scrollRowIntoView(row *gtk.ListBoxRow) { | |||
| 2297 | 2307 | func (o *Omnibox) selectNext() { | |
| 2298 | 2308 | o.mu.Lock() | |
| 2299 | 2309 | current := o.selectedIndex | |
| 2300 | - | mode := o.viewMode | |
| 2301 | - | bangMode := o.bangMode | |
| 2302 | - | maxVisible := OmniboxListDefaults.MaxResults | |
| 2303 | - | var maxIndex int | |
| 2304 | - | if bangMode { | |
| 2305 | - | maxIndex = visibleResultCount(len(o.bangSuggestions), maxVisible) - 1 | |
| 2306 | - | } else if mode == ViewModeHistory { | |
| 2307 | - | maxIndex = visibleResultCount(len(o.suggestions), maxVisible) - 1 | |
| 2308 | - | } else { | |
| 2309 | - | maxIndex = visibleResultCount(len(o.favorites), maxVisible) - 1 | |
| 2310 | - | } | |
| 2310 | + | maxIndex := navigableCount(o.bangMode, o.viewMode, | |
| 2311 | + | len(o.bangSuggestions), len(o.suggestions), len(o.favorites)) - 1 | |
| 2311 | 2312 | o.hasNavigated = true // User is navigating with arrow keys | |
| 2312 | 2313 | o.mu.Unlock() | |
| 2313 | 2314 | ||
| @@ -2320,23 +2321,15 @@ func (o *Omnibox) selectNext() { | |||
| 2320 | 2321 | newIndex = 0 // Wrap around | |
| 2321 | 2322 | } | |
| 2322 | 2323 | o.selectIndex(newIndex) | |
| 2324 | + | o.scrollSelectionIntoView(newIndex) | |
| 2323 | 2325 | } | |
| 2324 | 2326 | ||
| 2325 | 2327 | // selectPrevious moves selection up. | |
| 2326 | 2328 | func (o *Omnibox) selectPrevious() { | |
| 2327 | 2329 | o.mu.Lock() | |
| 2328 | 2330 | current := o.selectedIndex | |
| 2329 | - | mode := o.viewMode | |
| 2330 | - | bangMode := o.bangMode | |
| 2331 | - | maxVisible := OmniboxListDefaults.MaxResults | |
| 2332 | - | var maxIndex int | |
| 2333 | - | if bangMode { | |
| 2334 | - | maxIndex = visibleResultCount(len(o.bangSuggestions), maxVisible) - 1 | |
| 2335 | - | } else if mode == ViewModeHistory { | |
| 2336 | - | maxIndex = visibleResultCount(len(o.suggestions), maxVisible) - 1 | |
| 2337 | - | } else { | |
| 2338 | - | maxIndex = visibleResultCount(len(o.favorites), maxVisible) - 1 | |
| 2339 | - | } | |
| 2331 | + | maxIndex := navigableCount(o.bangMode, o.viewMode, | |
| 2332 | + | len(o.bangSuggestions), len(o.suggestions), len(o.favorites)) - 1 | |
| 2340 | 2333 | o.hasNavigated = true // User is navigating with arrow keys | |
| 2341 | 2334 | o.mu.Unlock() | |
| 2342 | 2335 | ||
| @@ -2349,6 +2342,7 @@ func (o *Omnibox) selectPrevious() { | |||
| 2349 | 2342 | newIndex = maxIndex // Wrap around | |
| 2350 | 2343 | } | |
| 2351 | 2344 | o.selectIndex(newIndex) | |
| 2345 | + | o.scrollSelectionIntoView(newIndex) | |
| 2352 | 2346 | } | |
| 2353 | 2347 | ||
| 2354 | 2348 | // selectAndNavigate selects an index and navigates to it. | |
| @@ -2361,14 +2355,14 @@ func (o *Omnibox) selectAndNavigate(index int) { | |||
| 2361 | 2355 | suggestions := o.suggestions | |
| 2362 | 2356 | favorites := o.favorites | |
| 2363 | 2357 | o.mu.RUnlock() | |
| 2364 | - | maxVisible := OmniboxListDefaults.MaxResults | |
| 2358 | + | maxResults := OmniboxListDefaults.MaxResults | |
| 2365 | 2359 | ||
| 2366 | 2360 | if bangMode { | |
| 2367 | 2361 | o.navigateToSelected() | |
| 2368 | 2362 | return | |
| 2369 | 2363 | } | |
| 2370 | 2364 | ||
| 2371 | - | targetURL := resolveTargetURLForSelection(mode, index, maxVisible, suggestions, favorites) | |
| 2365 | + | targetURL := resolveTargetURLForSelection(mode, index, maxResults, suggestions, favorites) | |
| 2372 | 2366 | if targetURL == "" { | |
| 2373 | 2367 | return | |
| 2374 | 2368 | } | |
| @@ -2833,6 +2827,9 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||
| 2833 | 2827 | Msg("omnibox geometry decision") | |
| 2834 | 2828 | ||
| 2835 | 2829 | o.mainBox.SetSizeRequest(width, -1) | |
| 2830 | + | if omniboxValign(o.sizeCfg) == gtk.AlignCenterValue { | |
| 2831 | + | marginTop = 0 // centered by GTK; a margin would push it off-center | |
| 2832 | + | } | |
| 2836 | 2833 | o.outerBox.SetMarginTop(marginTop) | |
| 2837 | 2834 | ||
| 2838 | 2835 | // Show the omnibox | |
| @@ -1512,7 +1512,7 @@ func (o *Omnibox) restoreEntryToRealInput() { | |||||
| 1512 | // updateGhostFromSelectionWithInput updates ghost text based on selected row and input. | 1512 | // updateGhostFromSelectionWithInput updates ghost text based on selected row and input. | ||
| 1513 | func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | 1513 | func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | ||
| 1514 | log := logging.FromContext(o.ctx) | 1514 | log := logging.FromContext(o.ctx) | ||
| 1515 | - | maxVisible := OmniboxListDefaults.MaxResults | 1515 | + | maxResults := OmniboxListDefaults.MaxResults |
| 1516 | o.mu.RLock() | 1516 | o.mu.RLock() | ||
| 1517 | idx := o.selectedIndex | 1517 | idx := o.selectedIndex | ||
| 1518 | mode := o.viewMode | 1518 | mode := o.viewMode | ||
| @@ -1540,10 +1540,10 @@ func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | |||||
| 1540 | return | 1540 | return | ||
| 1541 | } | 1541 | } | ||
| 1542 | 1542 | ||||
| 1543 | - | targetURL, hasExplicitSelection := selectedTargetURL(mode, idx, maxVisible, suggestions, favorites) | 1543 | + | targetURL, hasExplicitSelection := selectedTargetURL(mode, idx, maxResults, suggestions, favorites) |
| 1544 | 1544 | ||||
| 1545 | if entryText != "" { | 1545 | if entryText != "" { | ||
| 1546 | - | o.resolveGhostCompletion(entryText, targetURL, hasExplicitSelection, mode, maxVisible, suggestions, favorites) | 1546 | + | o.resolveGhostCompletion(entryText, targetURL, hasExplicitSelection, mode, maxResults, suggestions, favorites) |
| 1547 | return | 1547 | return | ||
| 1548 | } | 1548 | } | ||
| 1549 | 1549 | ||||
| @@ -1565,7 +1565,7 @@ func (o *Omnibox) resolveGhostCompletion( | |||||
| 1565 | entryText, selectedURL string, | 1565 | entryText, selectedURL string, | ||
| 1566 | hasExplicitSelection bool, | 1566 | hasExplicitSelection bool, | ||
| 1567 | mode ViewMode, | 1567 | mode ViewMode, | ||
| 1568 | - | maxVisible int, | 1568 | + | maxResults int, |
| 1569 | suggestions []Suggestion, | 1569 | suggestions []Suggestion, | ||
| 1570 | favorites []Favorite, | 1570 | favorites []Favorite, | ||
| 1571 | ) { | 1571 | ) { | ||
| @@ -1575,7 +1575,7 @@ func (o *Omnibox) resolveGhostCompletion( | |||||
| 1575 | SelectedURL: selectedURL, | 1575 | SelectedURL: selectedURL, | ||
| 1576 | HasExplicitSelection: hasExplicitSelection, | 1576 | HasExplicitSelection: hasExplicitSelection, | ||
| 1577 | Mode: mode, | 1577 | Mode: mode, | ||
| 1578 | - | MaxVisible: maxVisible, | 1578 | + | MaxResults: maxResults, |
| 1579 | Suggestions: suggestions, | 1579 | Suggestions: suggestions, | ||
| 1580 | Favorites: favorites, | 1580 | Favorites: favorites, | ||
| 1581 | }) | 1581 | }) | ||
| @@ -1987,6 +1987,11 @@ func (o *Omnibox) rebuildList() { | |||||
| 1987 | bangSuggestions := o.bangSuggestions | 1987 | bangSuggestions := o.bangSuggestions | ||
| 1988 | o.mu.RUnlock() | 1988 | o.mu.RUnlock() | ||
| 1989 | 1989 | ||||
| 1990 | + | limit := OmniboxListDefaults.MaxResults | |||
| 1991 | + | bangSuggestions = bangSuggestions[:limitedCount(len(bangSuggestions), limit)] | |||
| 1992 | + | suggestions = suggestions[:limitedCount(len(suggestions), limit)] | |||
| 1993 | + | favorites = favorites[:limitedCount(len(favorites), limit)] | |||
| 1994 | + | ||||
| 1990 | if bangMode { | 1995 | if bangMode { | ||
| 1991 | for i, b := range bangSuggestions { | 1996 | for i, b := range bangSuggestions { | ||
| 1992 | row := o.createBangRow(b, i) | 1997 | row := o.createBangRow(b, i) | ||
| @@ -2267,17 +2272,22 @@ func (o *Omnibox) selectIndex(index int) { | |||||
| 2267 | row := o.listBox.GetRowAtIndex(index) | 2272 | row := o.listBox.GetRowAtIndex(index) | ||
| 2268 | if row != nil { | 2273 | if row != nil { | ||
| 2269 | o.listBox.SelectRow(row) | 2274 | o.listBox.SelectRow(row) | ||
| 2270 | - | o.scrollRowIntoView(row) | |||
| 2271 | return | 2275 | return | ||
| 2272 | } | 2276 | } | ||
| 2273 | 2277 | ||||
| 2274 | o.updateGhostFromSelection() | 2278 | o.updateGhostFromSelection() | ||
| 2275 | } | 2279 | } | ||
| 2276 | 2280 | ||||
| 2277 | - | // scrollRowIntoView scrolls the results list so the row is fully visible. | 2281 | + | // scrollSelectionIntoView scrolls the results list so the row at index is |
| 2278 | - | // The entry keeps keyboard focus, so GTK does not scroll the list itself. | 2282 | + | // fully visible. The entry keeps keyboard focus, so GTK does not scroll the |
| 2279 | - | func (o *Omnibox) scrollRowIntoView(row *gtk.ListBoxRow) { | 2283 | + | // list itself. Only keyboard navigation calls this: hover must not move rows |
| 2280 | - | if o.scrolledWin == nil || row == nil { | 2284 | + | // under the pointer. |
| 2285 | + | func (o *Omnibox) scrollSelectionIntoView(index int) { | |||
| 2286 | + | if o.scrolledWin == nil || o.listBox == nil { | |||
| 2287 | + | return | |||
| 2288 | + | } | |||
| 2289 | + | row := o.listBox.GetRowAtIndex(index) | |||
| 2290 | + | if row == nil { | |||
| 2281 | return | 2291 | return | ||
| 2282 | } | 2292 | } | ||
| 2283 | adj := o.scrolledWin.GetVadjustment() | 2293 | adj := o.scrolledWin.GetVadjustment() | ||
| @@ -2297,17 +2307,8 @@ func (o *Omnibox) scrollRowIntoView(row *gtk.ListBoxRow) { | |||||
| 2297 | func (o *Omnibox) selectNext() { | 2307 | func (o *Omnibox) selectNext() { | ||
| 2298 | o.mu.Lock() | 2308 | o.mu.Lock() | ||
| 2299 | current := o.selectedIndex | 2309 | current := o.selectedIndex | ||
| 2300 | - | mode := o.viewMode | 2310 | + | maxIndex := navigableCount(o.bangMode, o.viewMode, |
| 2301 | - | bangMode := o.bangMode | 2311 | + | len(o.bangSuggestions), len(o.suggestions), len(o.favorites)) - 1 |
| 2302 | - | maxVisible := OmniboxListDefaults.MaxResults | |||
| 2303 | - | var maxIndex int | |||
| 2304 | - | if bangMode { | |||
| 2305 | - | maxIndex = visibleResultCount(len(o.bangSuggestions), maxVisible) - 1 | |||
| 2306 | - | } else if mode == ViewModeHistory { | |||
| 2307 | - | maxIndex = visibleResultCount(len(o.suggestions), maxVisible) - 1 | |||
| 2308 | - | } else { | |||
| 2309 | - | maxIndex = visibleResultCount(len(o.favorites), maxVisible) - 1 | |||
| 2310 | - | } | |||
| 2311 | o.hasNavigated = true // User is navigating with arrow keys | 2312 | o.hasNavigated = true // User is navigating with arrow keys | ||
| 2312 | o.mu.Unlock() | 2313 | o.mu.Unlock() | ||
| 2313 | 2314 | ||||
| @@ -2320,23 +2321,15 @@ func (o *Omnibox) selectNext() { | |||||
| 2320 | newIndex = 0 // Wrap around | 2321 | newIndex = 0 // Wrap around | ||
| 2321 | } | 2322 | } | ||
| 2322 | o.selectIndex(newIndex) | 2323 | o.selectIndex(newIndex) | ||
| 2324 | + | o.scrollSelectionIntoView(newIndex) | |||
| 2323 | } | 2325 | } | ||
| 2324 | 2326 | ||||
| 2325 | // selectPrevious moves selection up. | 2327 | // selectPrevious moves selection up. | ||
| 2326 | func (o *Omnibox) selectPrevious() { | 2328 | func (o *Omnibox) selectPrevious() { | ||
| 2327 | o.mu.Lock() | 2329 | o.mu.Lock() | ||
| 2328 | current := o.selectedIndex | 2330 | current := o.selectedIndex | ||
| 2329 | - | mode := o.viewMode | 2331 | + | maxIndex := navigableCount(o.bangMode, o.viewMode, |
| 2330 | - | bangMode := o.bangMode | 2332 | + | len(o.bangSuggestions), len(o.suggestions), len(o.favorites)) - 1 |
| 2331 | - | maxVisible := OmniboxListDefaults.MaxResults | |||
| 2332 | - | var maxIndex int | |||
| 2333 | - | if bangMode { | |||
| 2334 | - | maxIndex = visibleResultCount(len(o.bangSuggestions), maxVisible) - 1 | |||
| 2335 | - | } else if mode == ViewModeHistory { | |||
| 2336 | - | maxIndex = visibleResultCount(len(o.suggestions), maxVisible) - 1 | |||
| 2337 | - | } else { | |||
| 2338 | - | maxIndex = visibleResultCount(len(o.favorites), maxVisible) - 1 | |||
| 2339 | - | } | |||
| 2340 | o.hasNavigated = true // User is navigating with arrow keys | 2333 | o.hasNavigated = true // User is navigating with arrow keys | ||
| 2341 | o.mu.Unlock() | 2334 | o.mu.Unlock() | ||
| 2342 | 2335 | ||||
| @@ -2349,6 +2342,7 @@ func (o *Omnibox) selectPrevious() { | |||||
| 2349 | newIndex = maxIndex // Wrap around | 2342 | newIndex = maxIndex // Wrap around | ||
| 2350 | } | 2343 | } | ||
| 2351 | o.selectIndex(newIndex) | 2344 | o.selectIndex(newIndex) | ||
| 2345 | + | o.scrollSelectionIntoView(newIndex) | |||
| 2352 | } | 2346 | } | ||
| 2353 | 2347 | ||||
| 2354 | // selectAndNavigate selects an index and navigates to it. | 2348 | // selectAndNavigate selects an index and navigates to it. | ||
| @@ -2361,14 +2355,14 @@ func (o *Omnibox) selectAndNavigate(index int) { | |||||
| 2361 | suggestions := o.suggestions | 2355 | suggestions := o.suggestions | ||
| 2362 | favorites := o.favorites | 2356 | favorites := o.favorites | ||
| 2363 | o.mu.RUnlock() | 2357 | o.mu.RUnlock() | ||
| 2364 | - | maxVisible := OmniboxListDefaults.MaxResults | 2358 | + | maxResults := OmniboxListDefaults.MaxResults |
| 2365 | 2359 | ||||
| 2366 | if bangMode { | 2360 | if bangMode { | ||
| 2367 | o.navigateToSelected() | 2361 | o.navigateToSelected() | ||
| 2368 | return | 2362 | return | ||
| 2369 | } | 2363 | } | ||
| 2370 | 2364 | ||||
| 2371 | - | targetURL := resolveTargetURLForSelection(mode, index, maxVisible, suggestions, favorites) | 2365 | + | targetURL := resolveTargetURLForSelection(mode, index, maxResults, suggestions, favorites) |
| 2372 | if targetURL == "" { | 2366 | if targetURL == "" { | ||
| 2373 | return | 2367 | return | ||
| 2374 | } | 2368 | } | ||
| @@ -2833,6 +2827,9 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||||
| 2833 | Msg("omnibox geometry decision") | 2827 | Msg("omnibox geometry decision") | ||
| 2834 | 2828 | ||||
| 2835 | o.mainBox.SetSizeRequest(width, -1) | 2829 | o.mainBox.SetSizeRequest(width, -1) | ||
| 2830 | + | if omniboxValign(o.sizeCfg) == gtk.AlignCenterValue { | |||
| 2831 | + | marginTop = 0 // centered by GTK; a margin would push it off-center | |||
| 2832 | + | } | |||
| 2836 | o.outerBox.SetMarginTop(marginTop) | 2833 | o.outerBox.SetMarginTop(marginTop) | ||
| 2837 | 2834 | ||||
| 2838 | // Show the omnibox | 2835 | // Show the omnibox | ||
M internal/ui/component/omnibox_suggestions.go +35 -22
| @@ -19,7 +19,7 @@ type ghostCompletionRequest struct { | |||
| 19 | 19 | SelectedURL string | |
| 20 | 20 | HasExplicitSelection bool | |
| 21 | 21 | Mode ViewMode | |
| 22 | - | MaxVisible int | |
| 22 | + | MaxResults int | |
| 23 | 23 | Suggestions []Suggestion | |
| 24 | 24 | Favorites []Favorite | |
| 25 | 25 | } | |
| @@ -34,7 +34,7 @@ func ghostCompletion(req ghostCompletionRequest) (fullText, suffix string, ok bo | |||
| 34 | 34 | return "", "", false | |
| 35 | 35 | } | |
| 36 | 36 | fullText, suffix, ok = visibleGhostSuggestion( | |
| 37 | - | input, req.SelectedURL, req.HasExplicitSelection, req.Mode, req.MaxVisible, req.Suggestions, req.Favorites, | |
| 37 | + | input, req.SelectedURL, req.HasExplicitSelection, req.Mode, req.MaxResults, req.Suggestions, req.Favorites, | |
| 38 | 38 | ) | |
| 39 | 39 | if !ok { | |
| 40 | 40 | return "", "", false | |
| @@ -122,7 +122,7 @@ func visibleGhostSuggestion( | |||
| 122 | 122 | query, selectedURL string, | |
| 123 | 123 | hasExplicitSelection bool, | |
| 124 | 124 | mode ViewMode, | |
| 125 | - | maxVisible int, | |
| 125 | + | maxResults int, | |
| 126 | 126 | suggestions []Suggestion, | |
| 127 | 127 | favorites []Favorite, | |
| 128 | 128 | ) (fullText, suffix string, ok bool) { | |
| @@ -131,16 +131,16 @@ func visibleGhostSuggestion( | |||
| 131 | 131 | return fullText, suffix, ok | |
| 132 | 132 | } | |
| 133 | 133 | ||
| 134 | - | visibleURLs := visibleURLsForMode(mode, maxVisible, suggestions, favorites) | |
| 135 | - | suffix, fullText, ok = autocomplete.BestURLCompletion(query, visibleURLs) | |
| 134 | + | listedURLs := listedURLsForMode(mode, maxResults, suggestions, favorites) | |
| 135 | + | suffix, fullText, ok = autocomplete.BestURLCompletion(query, listedURLs) | |
| 136 | 136 | return fullText, suffix, ok | |
| 137 | 137 | } | |
| 138 | 138 | ||
| 139 | - | func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, favorites []Favorite) []string { | |
| 139 | + | func listedURLsForMode(mode ViewMode, maxResults int, suggestions []Suggestion, favorites []Favorite) []string { | |
| 140 | 140 | if mode == ViewModeHistory { | |
| 141 | - | visibleCount := visibleResultCount(len(suggestions), maxVisible) | |
| 142 | - | urls := make([]string, 0, visibleCount) | |
| 143 | - | for _, s := range suggestions[:visibleCount] { | |
| 141 | + | count := limitedCount(len(suggestions), maxResults) | |
| 142 | + | urls := make([]string, 0, count) | |
| 143 | + | for _, s := range suggestions[:count] { | |
| 144 | 144 | if s.URL != "" { | |
| 145 | 145 | urls = append(urls, s.URL) | |
| 146 | 146 | } | |
| @@ -148,9 +148,9 @@ func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, | |||
| 148 | 148 | return urls | |
| 149 | 149 | } | |
| 150 | 150 | ||
| 151 | - | visibleCount := visibleResultCount(len(favorites), maxVisible) | |
| 152 | - | urls := make([]string, 0, visibleCount) | |
| 153 | - | for _, f := range favorites[:visibleCount] { | |
| 151 | + | count := limitedCount(len(favorites), maxResults) | |
| 152 | + | urls := make([]string, 0, count) | |
| 153 | + | for _, f := range favorites[:count] { | |
| 154 | 154 | if f.URL != "" { | |
| 155 | 155 | urls = append(urls, f.URL) | |
| 156 | 156 | } | |
| @@ -158,11 +158,11 @@ func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, | |||
| 158 | 158 | return urls | |
| 159 | 159 | } | |
| 160 | 160 | ||
| 161 | - | func selectedTargetURL(mode ViewMode, idx, maxVisible int, suggestions []Suggestion, favorites []Favorite) (string, bool) { | |
| 161 | + | func selectedTargetURL(mode ViewMode, idx, maxResults int, suggestions []Suggestion, favorites []Favorite) (string, bool) { | |
| 162 | 162 | if idx < 0 { | |
| 163 | 163 | return "", false | |
| 164 | 164 | } | |
| 165 | - | return resolveTargetURLForSelection(mode, idx, maxVisible, suggestions, favorites), true | |
| 165 | + | return resolveTargetURLForSelection(mode, idx, maxResults, suggestions, favorites), true | |
| 166 | 166 | } | |
| 167 | 167 | ||
| 168 | 168 | func bangSuggestionTextAt(idx int, bangSuggestions []BangSuggestion) (string, bool) { | |
| @@ -172,14 +172,27 @@ func bangSuggestionTextAt(idx int, bangSuggestions []BangSuggestion) (string, bo | |||
| 172 | 172 | return "!" + bangSuggestions[idx].Key + " ", true | |
| 173 | 173 | } | |
| 174 | 174 | ||
| 175 | - | func visibleResultCount(total, maxVisible int) int { | |
| 175 | + | func limitedCount(total, maxResults int) int { | |
| 176 | 176 | if total <= 0 { | |
| 177 | 177 | return 0 | |
| 178 | 178 | } | |
| 179 | - | if maxVisible <= 0 || total < maxVisible { | |
| 179 | + | if maxResults <= 0 || total < maxResults { | |
| 180 | 180 | return total | |
| 181 | 181 | } | |
| 182 | - | return maxVisible | |
| 182 | + | return maxResults | |
| 183 | + | } | |
| 184 | + | ||
| 185 | + | // navigableCount returns how many rows of the active list are rendered and | |
| 186 | + | // reachable by keyboard: the active list length capped at MaxResults. | |
| 187 | + | func navigableCount(bangMode bool, mode ViewMode, bangs, suggestions, favorites int) int { | |
| 188 | + | total := favorites | |
| 189 | + | switch { | |
| 190 | + | case bangMode: | |
| 191 | + | total = bangs | |
| 192 | + | case mode == ViewModeHistory: | |
| 193 | + | total = suggestions | |
| 194 | + | } | |
| 195 | + | return limitedCount(total, OmniboxListDefaults.MaxResults) | |
| 183 | 196 | } | |
| 184 | 197 | ||
| 185 | 198 | func effectiveSearchQuery(entryText, realInput string, hasGhost bool) string { | |
| @@ -189,16 +202,16 @@ func effectiveSearchQuery(entryText, realInput string, hasGhost bool) string { | |||
| 189 | 202 | return entryText | |
| 190 | 203 | } | |
| 191 | 204 | ||
| 192 | - | func resolveTargetURLForSelection(mode ViewMode, idx, maxVisible int, suggestions []Suggestion, favorites []Favorite) string { | |
| 205 | + | func resolveTargetURLForSelection(mode ViewMode, idx, maxResults int, suggestions []Suggestion, favorites []Favorite) string { | |
| 193 | 206 | if mode == ViewModeHistory { | |
| 194 | - | visibleCount := visibleResultCount(len(suggestions), maxVisible) | |
| 195 | - | if idx >= 0 && idx < visibleCount { | |
| 207 | + | count := limitedCount(len(suggestions), maxResults) | |
| 208 | + | if idx >= 0 && idx < count { | |
| 196 | 209 | return suggestions[idx].URL | |
| 197 | 210 | } | |
| 198 | 211 | return "" | |
| 199 | 212 | } | |
| 200 | - | visibleCount := visibleResultCount(len(favorites), maxVisible) | |
| 201 | - | if idx >= 0 && idx < visibleCount { | |
| 213 | + | count := limitedCount(len(favorites), maxResults) | |
| 214 | + | if idx >= 0 && idx < count { | |
| 202 | 215 | return favorites[idx].URL | |
| 203 | 216 | } | |
| 204 | 217 | return "" | |
| @@ -19,7 +19,7 @@ type ghostCompletionRequest struct { | |||||
| 19 | SelectedURL string | 19 | SelectedURL string | ||
| 20 | HasExplicitSelection bool | 20 | HasExplicitSelection bool | ||
| 21 | Mode ViewMode | 21 | Mode ViewMode | ||
| 22 | - | MaxVisible int | 22 | + | MaxResults int |
| 23 | Suggestions []Suggestion | 23 | Suggestions []Suggestion | ||
| 24 | Favorites []Favorite | 24 | Favorites []Favorite | ||
| 25 | } | 25 | } | ||
| @@ -34,7 +34,7 @@ func ghostCompletion(req ghostCompletionRequest) (fullText, suffix string, ok bo | |||||
| 34 | return "", "", false | 34 | return "", "", false | ||
| 35 | } | 35 | } | ||
| 36 | fullText, suffix, ok = visibleGhostSuggestion( | 36 | fullText, suffix, ok = visibleGhostSuggestion( | ||
| 37 | - | input, req.SelectedURL, req.HasExplicitSelection, req.Mode, req.MaxVisible, req.Suggestions, req.Favorites, | 37 | + | input, req.SelectedURL, req.HasExplicitSelection, req.Mode, req.MaxResults, req.Suggestions, req.Favorites, |
| 38 | ) | 38 | ) | ||
| 39 | if !ok { | 39 | if !ok { | ||
| 40 | return "", "", false | 40 | return "", "", false | ||
| @@ -122,7 +122,7 @@ func visibleGhostSuggestion( | |||||
| 122 | query, selectedURL string, | 122 | query, selectedURL string, | ||
| 123 | hasExplicitSelection bool, | 123 | hasExplicitSelection bool, | ||
| 124 | mode ViewMode, | 124 | mode ViewMode, | ||
| 125 | - | maxVisible int, | 125 | + | maxResults int, |
| 126 | suggestions []Suggestion, | 126 | suggestions []Suggestion, | ||
| 127 | favorites []Favorite, | 127 | favorites []Favorite, | ||
| 128 | ) (fullText, suffix string, ok bool) { | 128 | ) (fullText, suffix string, ok bool) { | ||
| @@ -131,16 +131,16 @@ func visibleGhostSuggestion( | |||||
| 131 | return fullText, suffix, ok | 131 | return fullText, suffix, ok | ||
| 132 | } | 132 | } | ||
| 133 | 133 | ||||
| 134 | - | visibleURLs := visibleURLsForMode(mode, maxVisible, suggestions, favorites) | 134 | + | listedURLs := listedURLsForMode(mode, maxResults, suggestions, favorites) |
| 135 | - | suffix, fullText, ok = autocomplete.BestURLCompletion(query, visibleURLs) | 135 | + | suffix, fullText, ok = autocomplete.BestURLCompletion(query, listedURLs) |
| 136 | return fullText, suffix, ok | 136 | return fullText, suffix, ok | ||
| 137 | } | 137 | } | ||
| 138 | 138 | ||||
| 139 | - | func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, favorites []Favorite) []string { | 139 | + | func listedURLsForMode(mode ViewMode, maxResults int, suggestions []Suggestion, favorites []Favorite) []string { |
| 140 | if mode == ViewModeHistory { | 140 | if mode == ViewModeHistory { | ||
| 141 | - | visibleCount := visibleResultCount(len(suggestions), maxVisible) | 141 | + | count := limitedCount(len(suggestions), maxResults) |
| 142 | - | urls := make([]string, 0, visibleCount) | 142 | + | urls := make([]string, 0, count) |
| 143 | - | for _, s := range suggestions[:visibleCount] { | 143 | + | for _, s := range suggestions[:count] { |
| 144 | if s.URL != "" { | 144 | if s.URL != "" { | ||
| 145 | urls = append(urls, s.URL) | 145 | urls = append(urls, s.URL) | ||
| 146 | } | 146 | } | ||
| @@ -148,9 +148,9 @@ func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, | |||||
| 148 | return urls | 148 | return urls | ||
| 149 | } | 149 | } | ||
| 150 | 150 | ||||
| 151 | - | visibleCount := visibleResultCount(len(favorites), maxVisible) | 151 | + | count := limitedCount(len(favorites), maxResults) |
| 152 | - | urls := make([]string, 0, visibleCount) | 152 | + | urls := make([]string, 0, count) |
| 153 | - | for _, f := range favorites[:visibleCount] { | 153 | + | for _, f := range favorites[:count] { |
| 154 | if f.URL != "" { | 154 | if f.URL != "" { | ||
| 155 | urls = append(urls, f.URL) | 155 | urls = append(urls, f.URL) | ||
| 156 | } | 156 | } | ||
| @@ -158,11 +158,11 @@ func visibleURLsForMode(mode ViewMode, maxVisible int, suggestions []Suggestion, | |||||
| 158 | return urls | 158 | return urls | ||
| 159 | } | 159 | } | ||
| 160 | 160 | ||||
| 161 | - | func selectedTargetURL(mode ViewMode, idx, maxVisible int, suggestions []Suggestion, favorites []Favorite) (string, bool) { | 161 | + | func selectedTargetURL(mode ViewMode, idx, maxResults int, suggestions []Suggestion, favorites []Favorite) (string, bool) { |
| 162 | if idx < 0 { | 162 | if idx < 0 { | ||
| 163 | return "", false | 163 | return "", false | ||
| 164 | } | 164 | } | ||
| 165 | - | return resolveTargetURLForSelection(mode, idx, maxVisible, suggestions, favorites), true | 165 | + | return resolveTargetURLForSelection(mode, idx, maxResults, suggestions, favorites), true |
| 166 | } | 166 | } | ||
| 167 | 167 | ||||
| 168 | func bangSuggestionTextAt(idx int, bangSuggestions []BangSuggestion) (string, bool) { | 168 | func bangSuggestionTextAt(idx int, bangSuggestions []BangSuggestion) (string, bool) { | ||
| @@ -172,14 +172,27 @@ func bangSuggestionTextAt(idx int, bangSuggestions []BangSuggestion) (string, bo | |||||
| 172 | return "!" + bangSuggestions[idx].Key + " ", true | 172 | return "!" + bangSuggestions[idx].Key + " ", true | ||
| 173 | } | 173 | } | ||
| 174 | 174 | ||||
| 175 | - | func visibleResultCount(total, maxVisible int) int { | 175 | + | func limitedCount(total, maxResults int) int { |
| 176 | if total <= 0 { | 176 | if total <= 0 { | ||
| 177 | return 0 | 177 | return 0 | ||
| 178 | } | 178 | } | ||
| 179 | - | if maxVisible <= 0 || total < maxVisible { | 179 | + | if maxResults <= 0 || total < maxResults { |
| 180 | return total | 180 | return total | ||
| 181 | } | 181 | } | ||
| 182 | - | return maxVisible | 182 | + | return maxResults |
| 183 | + | } | |||
| 184 | + | ||||
| 185 | + | // navigableCount returns how many rows of the active list are rendered and | |||
| 186 | + | // reachable by keyboard: the active list length capped at MaxResults. | |||
| 187 | + | func navigableCount(bangMode bool, mode ViewMode, bangs, suggestions, favorites int) int { | |||
| 188 | + | total := favorites | |||
| 189 | + | switch { | |||
| 190 | + | case bangMode: | |||
| 191 | + | total = bangs | |||
| 192 | + | case mode == ViewModeHistory: | |||
| 193 | + | total = suggestions | |||
| 194 | + | } | |||
| 195 | + | return limitedCount(total, OmniboxListDefaults.MaxResults) | |||
| 183 | } | 196 | } | ||
| 184 | 197 | ||||
| 185 | func effectiveSearchQuery(entryText, realInput string, hasGhost bool) string { | 198 | func effectiveSearchQuery(entryText, realInput string, hasGhost bool) string { | ||
| @@ -189,16 +202,16 @@ func effectiveSearchQuery(entryText, realInput string, hasGhost bool) string { | |||||
| 189 | return entryText | 202 | return entryText | ||
| 190 | } | 203 | } | ||
| 191 | 204 | ||||
| 192 | - | func resolveTargetURLForSelection(mode ViewMode, idx, maxVisible int, suggestions []Suggestion, favorites []Favorite) string { | 205 | + | func resolveTargetURLForSelection(mode ViewMode, idx, maxResults int, suggestions []Suggestion, favorites []Favorite) string { |
| 193 | if mode == ViewModeHistory { | 206 | if mode == ViewModeHistory { | ||
| 194 | - | visibleCount := visibleResultCount(len(suggestions), maxVisible) | 207 | + | count := limitedCount(len(suggestions), maxResults) |
| 195 | - | if idx >= 0 && idx < visibleCount { | 208 | + | if idx >= 0 && idx < count { |
| 196 | return suggestions[idx].URL | 209 | return suggestions[idx].URL | ||
| 197 | } | 210 | } | ||
| 198 | return "" | 211 | return "" | ||
| 199 | } | 212 | } | ||
| 200 | - | visibleCount := visibleResultCount(len(favorites), maxVisible) | 213 | + | count := limitedCount(len(favorites), maxResults) |
| 201 | - | if idx >= 0 && idx < visibleCount { | 214 | + | if idx >= 0 && idx < count { |
| 202 | return favorites[idx].URL | 215 | return favorites[idx].URL | ||
| 203 | } | 216 | } | ||
| 204 | return "" | 217 | return "" | ||
M internal/ui/component/omnibox_suggestions_test.go +30 -6
| @@ -18,26 +18,26 @@ func TestGhostCompletion(t *testing.T) { | |||
| 18 | 18 | }{ | |
| 19 | 19 | { | |
| 20 | 20 | name: "host-like input completes to the domain, not the redirect URL", | |
| 21 | - | req: ghostCompletionRequest{EntryText: "goo", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | |
| 21 | + | req: ghostCompletionRequest{EntryText: "goo", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, | |
| 22 | 22 | wantFull: "google.com", | |
| 23 | 23 | wantSuffix: "gle.com", | |
| 24 | 24 | wantOK: true, | |
| 25 | 25 | }, | |
| 26 | 26 | { | |
| 27 | 27 | name: "leading whitespace is ignored", | |
| 28 | - | req: ghostCompletionRequest{EntryText: " gith", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | |
| 28 | + | req: ghostCompletionRequest{EntryText: " gith", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, | |
| 29 | 29 | wantFull: "github.com", | |
| 30 | 30 | wantSuffix: "ub.com", | |
| 31 | 31 | wantOK: true, | |
| 32 | 32 | }, | |
| 33 | 33 | { | |
| 34 | 34 | name: "hidden rows never complete", | |
| 35 | - | req: ghostCompletionRequest{EntryText: "gith", Mode: ViewModeHistory, MaxVisible: 1, Suggestions: history}, | |
| 35 | + | req: ghostCompletionRequest{EntryText: "gith", Mode: ViewModeHistory, MaxResults: 1, Suggestions: history}, | |
| 36 | 36 | wantOK: false, | |
| 37 | 37 | }, | |
| 38 | 38 | { | |
| 39 | 39 | name: "favorites mode uses favorites", | |
| 40 | - | req: ghostCompletionRequest{EntryText: "gitl", Mode: ViewModeFavorites, MaxVisible: 5, Favorites: favorites}, | |
| 40 | + | req: ghostCompletionRequest{EntryText: "gitl", Mode: ViewModeFavorites, MaxResults: 5, Favorites: favorites}, | |
| 41 | 41 | wantFull: "gitlab.com", | |
| 42 | 42 | wantSuffix: "ab.com", | |
| 43 | 43 | wantOK: true, | |
| @@ -46,7 +46,7 @@ func TestGhostCompletion(t *testing.T) { | |||
| 46 | 46 | name: "explicit selection completes from the selected URL, not the best match", | |
| 47 | 47 | req: ghostCompletionRequest{ | |
| 48 | 48 | EntryText: "gi", SelectedURL: "https://gitlab.com/", HasExplicitSelection: true, | |
| 49 | - | Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history, | |
| 49 | + | Mode: ViewModeHistory, MaxResults: 5, Suggestions: history, | |
| 50 | 50 | }, | |
| 51 | 51 | wantFull: "gitlab.com", | |
| 52 | 52 | wantSuffix: "tlab.com", | |
| @@ -54,7 +54,7 @@ func TestGhostCompletion(t *testing.T) { | |||
| 54 | 54 | }, | |
| 55 | 55 | { | |
| 56 | 56 | name: "blank input shows nothing", | |
| 57 | - | req: ghostCompletionRequest{EntryText: " ", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | |
| 57 | + | req: ghostCompletionRequest{EntryText: " ", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, | |
| 58 | 58 | wantOK: false, | |
| 59 | 59 | }, | |
| 60 | 60 | } | |
| @@ -69,3 +69,27 @@ func TestGhostCompletion(t *testing.T) { | |||
| 69 | 69 | }) | |
| 70 | 70 | } | |
| 71 | 71 | } | |
| 72 | + | ||
| 73 | + | func TestNavigableCount(t *testing.T) { | |
| 74 | + | limit := OmniboxListDefaults.MaxResults | |
| 75 | + | tests := []struct { | |
| 76 | + | name string | |
| 77 | + | bangMode bool | |
| 78 | + | mode ViewMode | |
| 79 | + | bangs, suggestions, favorites int | |
| 80 | + | want int | |
| 81 | + | }{ | |
| 82 | + | {"history beyond visible rows", false, ViewModeHistory, 0, limit, 30, limit}, | |
| 83 | + | {"favorites capped at results limit", false, ViewModeFavorites, 0, 3, 30, limit}, | |
| 84 | + | {"bangs win over view mode", true, ViewModeHistory, 2, 8, 0, 2}, | |
| 85 | + | {"empty list", false, ViewModeFavorites, 4, 4, 0, 0}, | |
| 86 | + | } | |
| 87 | + | for _, tt := range tests { | |
| 88 | + | t.Run(tt.name, func(t *testing.T) { | |
| 89 | + | got := navigableCount(tt.bangMode, tt.mode, tt.bangs, tt.suggestions, tt.favorites) | |
| 90 | + | if got != tt.want { | |
| 91 | + | t.Fatalf("navigableCount() = %d, want %d", got, tt.want) | |
| 92 | + | } | |
| 93 | + | }) | |
| 94 | + | } | |
| 95 | + | } | |
| @@ -18,26 +18,26 @@ func TestGhostCompletion(t *testing.T) { | |||||
| 18 | }{ | 18 | }{ | ||
| 19 | { | 19 | { | ||
| 20 | name: "host-like input completes to the domain, not the redirect URL", | 20 | name: "host-like input completes to the domain, not the redirect URL", | ||
| 21 | - | req: ghostCompletionRequest{EntryText: "goo", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | 21 | + | req: ghostCompletionRequest{EntryText: "goo", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, |
| 22 | wantFull: "google.com", | 22 | wantFull: "google.com", | ||
| 23 | wantSuffix: "gle.com", | 23 | wantSuffix: "gle.com", | ||
| 24 | wantOK: true, | 24 | wantOK: true, | ||
| 25 | }, | 25 | }, | ||
| 26 | { | 26 | { | ||
| 27 | name: "leading whitespace is ignored", | 27 | name: "leading whitespace is ignored", | ||
| 28 | - | req: ghostCompletionRequest{EntryText: " gith", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | 28 | + | req: ghostCompletionRequest{EntryText: " gith", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, |
| 29 | wantFull: "github.com", | 29 | wantFull: "github.com", | ||
| 30 | wantSuffix: "ub.com", | 30 | wantSuffix: "ub.com", | ||
| 31 | wantOK: true, | 31 | wantOK: true, | ||
| 32 | }, | 32 | }, | ||
| 33 | { | 33 | { | ||
| 34 | name: "hidden rows never complete", | 34 | name: "hidden rows never complete", | ||
| 35 | - | req: ghostCompletionRequest{EntryText: "gith", Mode: ViewModeHistory, MaxVisible: 1, Suggestions: history}, | 35 | + | req: ghostCompletionRequest{EntryText: "gith", Mode: ViewModeHistory, MaxResults: 1, Suggestions: history}, |
| 36 | wantOK: false, | 36 | wantOK: false, | ||
| 37 | }, | 37 | }, | ||
| 38 | { | 38 | { | ||
| 39 | name: "favorites mode uses favorites", | 39 | name: "favorites mode uses favorites", | ||
| 40 | - | req: ghostCompletionRequest{EntryText: "gitl", Mode: ViewModeFavorites, MaxVisible: 5, Favorites: favorites}, | 40 | + | req: ghostCompletionRequest{EntryText: "gitl", Mode: ViewModeFavorites, MaxResults: 5, Favorites: favorites}, |
| 41 | wantFull: "gitlab.com", | 41 | wantFull: "gitlab.com", | ||
| 42 | wantSuffix: "ab.com", | 42 | wantSuffix: "ab.com", | ||
| 43 | wantOK: true, | 43 | wantOK: true, | ||
| @@ -46,7 +46,7 @@ func TestGhostCompletion(t *testing.T) { | |||||
| 46 | name: "explicit selection completes from the selected URL, not the best match", | 46 | name: "explicit selection completes from the selected URL, not the best match", | ||
| 47 | req: ghostCompletionRequest{ | 47 | req: ghostCompletionRequest{ | ||
| 48 | EntryText: "gi", SelectedURL: "https://gitlab.com/", HasExplicitSelection: true, | 48 | EntryText: "gi", SelectedURL: "https://gitlab.com/", HasExplicitSelection: true, | ||
| 49 | - | Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history, | 49 | + | Mode: ViewModeHistory, MaxResults: 5, Suggestions: history, |
| 50 | }, | 50 | }, | ||
| 51 | wantFull: "gitlab.com", | 51 | wantFull: "gitlab.com", | ||
| 52 | wantSuffix: "tlab.com", | 52 | wantSuffix: "tlab.com", | ||
| @@ -54,7 +54,7 @@ func TestGhostCompletion(t *testing.T) { | |||||
| 54 | }, | 54 | }, | ||
| 55 | { | 55 | { | ||
| 56 | name: "blank input shows nothing", | 56 | name: "blank input shows nothing", | ||
| 57 | - | req: ghostCompletionRequest{EntryText: " ", Mode: ViewModeHistory, MaxVisible: 5, Suggestions: history}, | 57 | + | req: ghostCompletionRequest{EntryText: " ", Mode: ViewModeHistory, MaxResults: 5, Suggestions: history}, |
| 58 | wantOK: false, | 58 | wantOK: false, | ||
| 59 | }, | 59 | }, | ||
| 60 | } | 60 | } | ||
| @@ -69,3 +69,27 @@ func TestGhostCompletion(t *testing.T) { | |||||
| 69 | }) | 69 | }) | ||
| 70 | } | 70 | } | ||
| 71 | } | 71 | } | ||
| 72 | + | ||||
| 73 | + | func TestNavigableCount(t *testing.T) { | |||
| 74 | + | limit := OmniboxListDefaults.MaxResults | |||
| 75 | + | tests := []struct { | |||
| 76 | + | name string | |||
| 77 | + | bangMode bool | |||
| 78 | + | mode ViewMode | |||
| 79 | + | bangs, suggestions, favorites int | |||
| 80 | + | want int | |||
| 81 | + | }{ | |||
| 82 | + | {"history beyond visible rows", false, ViewModeHistory, 0, limit, 30, limit}, | |||
| 83 | + | {"favorites capped at results limit", false, ViewModeFavorites, 0, 3, 30, limit}, | |||
| 84 | + | {"bangs win over view mode", true, ViewModeHistory, 2, 8, 0, 2}, | |||
| 85 | + | {"empty list", false, ViewModeFavorites, 4, 4, 0, 0}, | |||
| 86 | + | } | |||
| 87 | + | for _, tt := range tests { | |||
| 88 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 89 | + | got := navigableCount(tt.bangMode, tt.mode, tt.bangs, tt.suggestions, tt.favorites) | |||
| 90 | + | if got != tt.want { | |||
| 91 | + | t.Fatalf("navigableCount() = %d, want %d", got, tt.want) | |||
| 92 | + | } | |||
| 93 | + | }) | |||
| 94 | + | } | |||
| 95 | + | } | |||