fix: stabilize omnibox ghost rendering
- commit
- 9cc8eeebfe5a3b021de7e3e30f9861d8f5b905ac
- author
- brice · · 2026-01-26 04:29 UTC
- parent
- 92a7937
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
2 files · +188 -130
M internal/ui/component/omnibox.go +188 -123
| @@ -25,8 +25,11 @@ import ( | |||
| 25 | 25 | ) | |
| 26 | 26 | ||
| 27 | 27 | const ( | |
| 28 | - | debounceDelayMs = 50 | |
| 29 | - | endBoxSpacing = 6 | |
| 28 | + | debounceDelayMs = 50 | |
| 29 | + | endBoxSpacing = 6 | |
| 30 | + | defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)" | |
| 31 | + | ghostPositionRetryMs = 16 | |
| 32 | + | ghostPositionMaxAttempts = 5 | |
| 30 | 33 | ) | |
| 31 | 34 | ||
| 32 | 35 | // ViewMode distinguishes history search from favorites display. | |
| @@ -488,7 +491,7 @@ func (o *Omnibox) initEntry() error { | |||
| 488 | 491 | o.entry.AddCssClass("omnibox-entry") | |
| 489 | 492 | o.entry.SetHexpand(true) | |
| 490 | 493 | ||
| 491 | - | placeholder := "Search history or enter URL… (! lists bangs)" | |
| 494 | + | placeholder := defaultOmniboxPlaceholder | |
| 492 | 495 | o.entry.SetPlaceholderText(&placeholder) | |
| 493 | 496 | ||
| 494 | 497 | // Create overlay for ghost text | |
| @@ -834,77 +837,102 @@ func (o *Omnibox) setGhostText(originalInput, suffix, fullText string) { | |||
| 834 | 837 | o.hasGhostText = true | |
| 835 | 838 | o.mu.Unlock() | |
| 836 | 839 | ||
| 837 | - | var cb glib.SourceFunc = func(uintptr) bool { | |
| 838 | - | if o.ghostLabel == nil || o.entry == nil { | |
| 839 | - | return false | |
| 840 | - | } | |
| 840 | + | var schedulePosition func(attempt int) | |
| 841 | + | schedulePosition = func(attempt int) { | |
| 842 | + | var cb glib.SourceFunc = func(uintptr) bool { | |
| 843 | + | if o.ghostLabel == nil || o.entry == nil { | |
| 844 | + | return false | |
| 845 | + | } | |
| 841 | 846 | ||
| 842 | - | // Check if input has changed since the autocomplete query was made | |
| 843 | - | // This check runs on GTK main thread where entry.GetText() is reliable | |
| 844 | - | // Skip this check when originalInput is empty (row selection replaces full input) | |
| 845 | - | if originalInput != "" && o.entry.GetText() != originalInput { | |
| 846 | - | // Input changed, skip this stale result - a newer query will handle it | |
| 847 | - | return false | |
| 848 | - | } | |
| 847 | + | // Check if input has changed since the autocomplete query was made | |
| 848 | + | // This check runs on GTK main thread where entry.GetText() is reliable | |
| 849 | + | // Skip this check when originalInput is empty (row selection replaces full input) | |
| 850 | + | if originalInput != "" && o.entry.GetText() != originalInput { | |
| 851 | + | // Input changed, skip this stale result - a newer query will handle it | |
| 852 | + | return false | |
| 853 | + | } | |
| 849 | 854 | ||
| 850 | - | // Get the entry's Pango context and create a layout to measure text width | |
| 851 | - | pangoCtx := o.entry.GetPangoContext() | |
| 852 | - | if pangoCtx == nil { | |
| 853 | - | return false | |
| 854 | - | } | |
| 855 | + | if attempt < ghostPositionMaxAttempts { | |
| 856 | + | entryWidth := o.entry.GetAllocatedWidth() | |
| 857 | + | overlayWidth := 0 | |
| 858 | + | if o.entryOverlay != nil { | |
| 859 | + | overlayWidth = o.entryOverlay.GetAllocatedWidth() | |
| 860 | + | } | |
| 861 | + | if entryWidth <= 0 || overlayWidth <= 0 { | |
| 862 | + | retryCb := glib.SourceFunc(func(uintptr) bool { | |
| 863 | + | schedulePosition(attempt + 1) | |
| 864 | + | return false | |
| 865 | + | }) | |
| 866 | + | glib.TimeoutAdd(ghostPositionRetryMs, &retryCb, 0) | |
| 867 | + | return false | |
| 868 | + | } | |
| 869 | + | } | |
| 870 | + | ||
| 871 | + | // Get the entry's Pango context and create a layout to measure text width | |
| 872 | + | pangoCtx := o.entry.GetPangoContext() | |
| 873 | + | if pangoCtx == nil { | |
| 874 | + | return false | |
| 875 | + | } | |
| 855 | 876 | ||
| 856 | - | // Create layout to measure the input text width | |
| 857 | - | layout := pango.NewLayout(pangoCtx) | |
| 858 | - | layout.SetText(originalInput, len(originalInput)) | |
| 859 | - | ||
| 860 | - | // Get the width in pixels | |
| 861 | - | var widthPx, heightPx int | |
| 862 | - | layout.GetPixelSize(&widthPx, &heightPx) | |
| 863 | - | ||
| 864 | - | // Set the ghost label text (just the suffix) and position it horizontally | |
| 865 | - | o.ghostLabel.SetText(suffix) | |
| 866 | - | ||
| 867 | - | marginStart := widthPx | |
| 868 | - | delegate := o.entry.GetDelegate() | |
| 869 | - | if delegate != nil && o.entryOverlay != nil { | |
| 870 | - | textWidget := gtk.TextNewFromInternalPtr(delegate.GoPointer()) | |
| 871 | - | if textWidget != nil { | |
| 872 | - | var textBounds graphene.Rect | |
| 873 | - | if textWidget.ComputeBounds(&o.entryOverlay.Widget, &textBounds) { | |
| 874 | - | cursorPos := uint(utf8.RuneCountInString(originalInput)) | |
| 875 | - | var strongRect, weakRect graphene.Rect | |
| 876 | - | textWidget.ComputeCursorExtents(cursorPos, &strongRect, &weakRect) | |
| 877 | - | cursorX := float64(textBounds.GetX() + strongRect.GetX()) | |
| 878 | - | marginStart = int(math.Round(cursorX)) | |
| 877 | + | // Create layout to measure the input text width | |
| 878 | + | layout := pango.NewLayout(pangoCtx) | |
| 879 | + | layout.SetText(originalInput, len(originalInput)) | |
| 880 | + | ||
| 881 | + | // Get the width in pixels | |
| 882 | + | var widthPx, heightPx int | |
| 883 | + | layout.GetPixelSize(&widthPx, &heightPx) | |
| 884 | + | ||
| 885 | + | // Set the ghost label text (just the suffix) and position it horizontally | |
| 886 | + | o.ghostLabel.SetText(suffix) | |
| 887 | + | ||
| 888 | + | marginStart := widthPx | |
| 889 | + | delegate := o.entry.GetDelegate() | |
| 890 | + | if delegate != nil && o.entryOverlay != nil { | |
| 891 | + | textWidget := gtk.TextNewFromInternalPtr(delegate.GoPointer()) | |
| 892 | + | if textWidget != nil { | |
| 893 | + | var textBounds graphene.Rect | |
| 894 | + | if textWidget.ComputeBounds(&o.entryOverlay.Widget, &textBounds) { | |
| 895 | + | textStart := float64(textBounds.GetX()) | |
| 896 | + | cursorPos := uint(utf8.RuneCountInString(originalInput)) | |
| 897 | + | var strongRect, weakRect graphene.Rect | |
| 898 | + | textWidget.ComputeCursorExtents(cursorPos, &strongRect, &weakRect) | |
| 899 | + | cursorX := float64(textBounds.GetX() + strongRect.GetX()) | |
| 900 | + | marginStart = int(math.Round(cursorX)) | |
| 901 | + | if cursorX < textStart { | |
| 902 | + | marginStart = int(math.Round(textStart)) | |
| 903 | + | } | |
| 904 | + | } | |
| 879 | 905 | } | |
| 880 | 906 | } | |
| 881 | - | } | |
| 882 | 907 | ||
| 883 | - | // Clamp horizontal position to entry bounds | |
| 884 | - | if marginStart < 0 { | |
| 885 | - | marginStart = 0 | |
| 886 | - | } | |
| 887 | - | if o.entryOverlay != nil { | |
| 888 | - | var entryBounds graphene.Rect | |
| 889 | - | if o.entry.ComputeBounds(&o.entryOverlay.Widget, &entryBounds) { | |
| 890 | - | minStart := int(math.Round(float64(entryBounds.GetX()))) | |
| 891 | - | if marginStart < minStart { | |
| 892 | - | marginStart = minStart | |
| 908 | + | // Clamp horizontal position to entry bounds | |
| 909 | + | if marginStart < 0 { | |
| 910 | + | marginStart = 0 | |
| 911 | + | } | |
| 912 | + | if o.entryOverlay != nil { | |
| 913 | + | var entryBounds graphene.Rect | |
| 914 | + | if o.entry.ComputeBounds(&o.entryOverlay.Widget, &entryBounds) { | |
| 915 | + | minStart := int(math.Round(float64(entryBounds.GetX()))) | |
| 916 | + | if marginStart < minStart { | |
| 917 | + | marginStart = minStart | |
| 918 | + | } | |
| 893 | 919 | } | |
| 894 | 920 | } | |
| 895 | - | } | |
| 896 | 921 | ||
| 897 | - | o.ghostLabel.SetMarginStart(marginStart) | |
| 898 | - | // Vertical positioning handled by CSS (.omnibox-ghost) to scale correctly with UI | |
| 899 | - | o.ghostLabel.SetVisible(true) | |
| 922 | + | o.ghostLabel.SetMarginStart(marginStart) | |
| 923 | + | // Vertical positioning handled by CSS (.omnibox-ghost) to scale correctly with UI | |
| 924 | + | o.ghostLabel.SetVisible(true) | |
| 900 | 925 | ||
| 901 | - | // Hide placeholder text so it doesn't show through ghost text | |
| 902 | - | emptyPlaceholder := "" | |
| 903 | - | o.entry.SetPlaceholderText(&emptyPlaceholder) | |
| 926 | + | // Hide placeholder text so it doesn't show through ghost text | |
| 927 | + | emptyPlaceholder := "" | |
| 928 | + | o.entry.SetPlaceholderText(&emptyPlaceholder) | |
| 904 | 929 | ||
| 905 | - | return false | |
| 930 | + | return false | |
| 931 | + | } | |
| 932 | + | glib.IdleAdd(&cb, 0) | |
| 906 | 933 | } | |
| 907 | - | glib.IdleAdd(&cb, 0) | |
| 934 | + | ||
| 935 | + | schedulePosition(0) | |
| 908 | 936 | } | |
| 909 | 937 | ||
| 910 | 938 | // clearGhostText hides the ghost completion text. | |
| @@ -926,7 +954,7 @@ func (o *Omnibox) clearGhostText() { | |||
| 926 | 954 | } | |
| 927 | 955 | // Restore placeholder text | |
| 928 | 956 | if o.entry != nil { | |
| 929 | - | placeholder := "Search history or enter URL… (! lists bangs)" | |
| 957 | + | placeholder := defaultOmniboxPlaceholder | |
| 930 | 958 | o.entry.SetPlaceholderText(&placeholder) | |
| 931 | 959 | } | |
| 932 | 960 | return false | |
| @@ -934,6 +962,36 @@ func (o *Omnibox) clearGhostText() { | |||
| 934 | 962 | glib.IdleAdd(&cb, 0) | |
| 935 | 963 | } | |
| 936 | 964 | ||
| 965 | + | // clearGhostTextIfInput clears ghost text only if the entry text matches expectedInput. | |
| 966 | + | // This prevents stale background results from wiping newer ghost text. | |
| 967 | + | func (o *Omnibox) clearGhostTextIfInput(expectedInput string) { | |
| 968 | + | if o.entry == nil { | |
| 969 | + | return | |
| 970 | + | } | |
| 971 | + | ||
| 972 | + | var cb glib.SourceFunc = func(uintptr) bool { | |
| 973 | + | if o.entry == nil { | |
| 974 | + | return false | |
| 975 | + | } | |
| 976 | + | if o.entry.GetText() != expectedInput { | |
| 977 | + | return false | |
| 978 | + | } | |
| 979 | + | o.mu.Lock() | |
| 980 | + | o.ghostSuffix = "" | |
| 981 | + | o.ghostFullText = "" | |
| 982 | + | o.hasGhostText = false | |
| 983 | + | o.mu.Unlock() | |
| 984 | + | if o.ghostLabel != nil { | |
| 985 | + | o.ghostLabel.SetVisible(false) | |
| 986 | + | o.ghostLabel.SetText("") | |
| 987 | + | } | |
| 988 | + | placeholder := defaultOmniboxPlaceholder | |
| 989 | + | o.entry.SetPlaceholderText(&placeholder) | |
| 990 | + | return false | |
| 991 | + | } | |
| 992 | + | glib.IdleAdd(&cb, 0) | |
| 993 | + | } | |
| 994 | + | ||
| 937 | 995 | // acceptGhostCompletion accepts the ghost text and fills the input. | |
| 938 | 996 | func (o *Omnibox) acceptGhostCompletion() { | |
| 939 | 997 | o.mu.Lock() | |
| @@ -962,75 +1020,40 @@ func (o *Omnibox) acceptGhostCompletion() { | |||
| 962 | 1020 | o.mu.Unlock() | |
| 963 | 1021 | } | |
| 964 | 1022 | ||
| 965 | - | // updateGhostFromSuggestion updates ghost text based on the current autocomplete suggestion. | |
| 966 | - | // The query parameter is the search query that was used, ensuring we don't use stale input. | |
| 967 | - | func (o *Omnibox) updateGhostFromSuggestion(query string) { | |
| 968 | - | if o.autocompleteUC == nil { | |
| 969 | - | return | |
| 970 | - | } | |
| 971 | - | ||
| 972 | - | o.mu.RLock() | |
| 973 | - | bangMode := o.bangMode | |
| 974 | - | o.mu.RUnlock() | |
| 975 | - | ||
| 976 | - | // Use the query that triggered this search, not realInput which may have changed | |
| 977 | - | userInput := query | |
| 978 | - | ||
| 979 | - | // Don't show ghost text in bang mode (it has its own completion) | |
| 980 | - | if bangMode || userInput == "" { | |
| 981 | - | o.clearGhostText() | |
| 982 | - | return | |
| 983 | - | } | |
| 984 | - | ||
| 985 | - | go func() { | |
| 986 | - | output := o.autocompleteUC.GetSuggestion(o.ctx, usecase.GetSuggestionInput{Input: userInput}) | |
| 987 | - | if !output.Found || output.Suggestion == nil { | |
| 988 | - | o.clearGhostText() | |
| 989 | - | return | |
| 990 | - | } | |
| 991 | - | // Pass the original input so setGhostText can verify it's still current | |
| 992 | - | o.setGhostText(userInput, output.Suggestion.Suffix, output.Suggestion.FullText) | |
| 993 | - | }() | |
| 994 | - | } | |
| 995 | - | ||
| 996 | 1023 | // updateGhostFromURL updates ghost text based on a specific URL (from row selection). | |
| 997 | 1024 | // When a row is selected via arrow keys, show the URL as ghost text even if | |
| 998 | 1025 | // the user's input isn't a prefix - this shows what Tab would fill in. | |
| 999 | - | func (o *Omnibox) updateGhostFromURL(targetURL string) { | |
| 1026 | + | ||
| 1027 | + | func (o *Omnibox) updateGhostFromURL(userInput, targetURL string) bool { | |
| 1000 | 1028 | if targetURL == "" { | |
| 1001 | - | o.clearGhostText() | |
| 1002 | - | return | |
| 1029 | + | return false | |
| 1003 | 1030 | } | |
| 1004 | 1031 | ||
| 1005 | - | o.mu.RLock() | |
| 1006 | - | userInput := o.realInput | |
| 1007 | - | o.mu.RUnlock() | |
| 1008 | - | ||
| 1009 | 1032 | // Strip protocol from URL for cleaner display | |
| 1010 | 1033 | displayURL := autocomplete.StripProtocol(targetURL) | |
| 1011 | 1034 | ||
| 1012 | - | // Try to compute proper suffix if input is a prefix of the URL | |
| 1013 | - | if o.autocompleteUC != nil && userInput != "" { | |
| 1014 | - | output := o.autocompleteUC.GetSuggestionForURL(o.ctx, userInput, targetURL) | |
| 1015 | - | if output.Found && output.Suggestion != nil { | |
| 1016 | - | o.setGhostText(userInput, output.Suggestion.Suffix, output.Suggestion.FullText) | |
| 1017 | - | return | |
| 1018 | - | } | |
| 1035 | + | // Only show full URL as ghost text when input is empty (initial state) | |
| 1036 | + | if userInput == "" { | |
| 1037 | + | o.setGhostText("", displayURL, displayURL) | |
| 1038 | + | return true | |
| 1019 | 1039 | } | |
| 1020 | 1040 | ||
| 1021 | - | // If user has typed something but it's not a prefix match, don't show ghost text | |
| 1022 | - | // This prevents confusing ghost text when scrolling through history | |
| 1023 | - | if userInput != "" { | |
| 1024 | - | o.clearGhostText() | |
| 1025 | - | return | |
| 1041 | + | // Try to compute proper suffix if input is a prefix of the URL | |
| 1042 | + | if suffix, matchedURL, ok := autocomplete.ComputeURLCompletionSuffix(userInput, targetURL); ok { | |
| 1043 | + | o.setGhostText(userInput, suffix, matchedURL) | |
| 1044 | + | return true | |
| 1026 | 1045 | } | |
| 1027 | 1046 | ||
| 1028 | - | // Only show full URL as ghost text when input is empty (initial state) | |
| 1029 | - | o.setGhostText("", displayURL, displayURL) | |
| 1047 | + | return false | |
| 1030 | 1048 | } | |
| 1031 | 1049 | ||
| 1032 | 1050 | // updateGhostFromSelection updates ghost text based on the currently selected row. | |
| 1033 | 1051 | func (o *Omnibox) updateGhostFromSelection() { | |
| 1052 | + | o.updateGhostFromSelectionWithInput(o.entry.GetText()) | |
| 1053 | + | } | |
| 1054 | + | ||
| 1055 | + | // updateGhostFromSelectionWithInput updates ghost text based on selected row and input. | |
| 1056 | + | func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | |
| 1034 | 1057 | o.mu.RLock() | |
| 1035 | 1058 | idx := o.selectedIndex | |
| 1036 | 1059 | mode := o.viewMode | |
| @@ -1041,6 +1064,12 @@ func (o *Omnibox) updateGhostFromSelection() { | |||
| 1041 | 1064 | ||
| 1042 | 1065 | // No ghost text in bang mode | |
| 1043 | 1066 | if bangMode { | |
| 1067 | + | o.clearGhostTextIfInput(entryText) | |
| 1068 | + | return | |
| 1069 | + | } | |
| 1070 | + | ||
| 1071 | + | if entryText != "" { | |
| 1072 | + | o.resolveGhostCompletion(entryText, mode, suggestions, favorites) | |
| 1044 | 1073 | return | |
| 1045 | 1074 | } | |
| 1046 | 1075 | ||
| @@ -1056,11 +1085,49 @@ func (o *Omnibox) updateGhostFromSelection() { | |||
| 1056 | 1085 | } | |
| 1057 | 1086 | ||
| 1058 | 1087 | if targetURL == "" { | |
| 1059 | - | o.clearGhostText() | |
| 1088 | + | o.clearGhostTextIfInput(entryText) | |
| 1089 | + | return | |
| 1090 | + | } | |
| 1091 | + | ||
| 1092 | + | if o.updateGhostFromURL(entryText, targetURL) { | |
| 1060 | 1093 | return | |
| 1061 | 1094 | } | |
| 1062 | 1095 | ||
| 1063 | - | o.updateGhostFromURL(targetURL) | |
| 1096 | + | o.clearGhostTextIfInput(entryText) | |
| 1097 | + | } | |
| 1098 | + | ||
| 1099 | + | func (o *Omnibox) resolveGhostCompletion(entryText string, mode ViewMode, suggestions []Suggestion, favorites []Favorite) { | |
| 1100 | + | if o.autocompleteUC == nil { | |
| 1101 | + | o.clearGhostTextIfInput(entryText) | |
| 1102 | + | return | |
| 1103 | + | } | |
| 1104 | + | ||
| 1105 | + | visibleURLs := make([]string, 0, len(suggestions)) | |
| 1106 | + | if mode == ViewModeHistory { | |
| 1107 | + | for _, s := range suggestions { | |
| 1108 | + | if s.URL != "" { | |
| 1109 | + | visibleURLs = append(visibleURLs, s.URL) | |
| 1110 | + | } | |
| 1111 | + | } | |
| 1112 | + | } else { | |
| 1113 | + | for _, f := range favorites { | |
| 1114 | + | if f.URL != "" { | |
| 1115 | + | visibleURLs = append(visibleURLs, f.URL) | |
| 1116 | + | } | |
| 1117 | + | } | |
| 1118 | + | } | |
| 1119 | + | ||
| 1120 | + | go func() { | |
| 1121 | + | suggestion := o.autocompleteUC.ResolveCompletion(o.ctx, entryText, usecase.CompletionOptions{ | |
| 1122 | + | VisibleURLs: visibleURLs, | |
| 1123 | + | AllowBangs: false, | |
| 1124 | + | }) | |
| 1125 | + | if suggestion == nil { | |
| 1126 | + | o.clearGhostTextIfInput(entryText) | |
| 1127 | + | return | |
| 1128 | + | } | |
| 1129 | + | o.setGhostText(entryText, suggestion.Suffix, suggestion.FullText) | |
| 1130 | + | }() | |
| 1064 | 1131 | } | |
| 1065 | 1132 | ||
| 1066 | 1133 | // performSearch executes the search based on current view mode and query. | |
| @@ -1380,10 +1447,9 @@ func (o *Omnibox) updateSuggestions(suggestions []Suggestion, query string) { | |||
| 1380 | 1447 | // Select first item if available and update ghost text | |
| 1381 | 1448 | if rowCount > 0 { | |
| 1382 | 1449 | o.selectIndex(0) | |
| 1383 | - | // Update ghost text from first suggestion | |
| 1384 | - | o.updateGhostFromSuggestion(query) | |
| 1450 | + | o.updateGhostFromSelectionWithInput(query) | |
| 1385 | 1451 | } else { | |
| 1386 | - | o.clearGhostText() | |
| 1452 | + | o.clearGhostTextIfInput(query) | |
| 1387 | 1453 | } | |
| 1388 | 1454 | } | |
| 1389 | 1455 | ||
| @@ -1409,10 +1475,9 @@ func (o *Omnibox) updateFavorites(favorites []Favorite, query string) { | |||
| 1409 | 1475 | // Select first item if available and update ghost text | |
| 1410 | 1476 | if rowCount > 0 { | |
| 1411 | 1477 | o.selectIndex(0) | |
| 1412 | - | // Update ghost text from first favorite | |
| 1413 | - | o.updateGhostFromSuggestion(query) | |
| 1478 | + | o.updateGhostFromSelectionWithInput(query) | |
| 1414 | 1479 | } else { | |
| 1415 | - | o.clearGhostText() | |
| 1480 | + | o.clearGhostTextIfInput(query) | |
| 1416 | 1481 | } | |
| 1417 | 1482 | } | |
| 1418 | 1483 | ||
| @@ -25,8 +25,11 @@ import ( | |||||
| 25 | ) | 25 | ) | ||
| 26 | 26 | ||||
| 27 | const ( | 27 | const ( | ||
| 28 | - | debounceDelayMs = 50 | 28 | + | debounceDelayMs = 50 |
| 29 | - | endBoxSpacing = 6 | 29 | + | endBoxSpacing = 6 |
| 30 | + | defaultOmniboxPlaceholder = "Search history or enter URL… (! lists bangs)" | |||
| 31 | + | ghostPositionRetryMs = 16 | |||
| 32 | + | ghostPositionMaxAttempts = 5 | |||
| 30 | ) | 33 | ) | ||
| 31 | 34 | ||||
| 32 | // ViewMode distinguishes history search from favorites display. | 35 | // ViewMode distinguishes history search from favorites display. | ||
| @@ -488,7 +491,7 @@ func (o *Omnibox) initEntry() error { | |||||
| 488 | o.entry.AddCssClass("omnibox-entry") | 491 | o.entry.AddCssClass("omnibox-entry") | ||
| 489 | o.entry.SetHexpand(true) | 492 | o.entry.SetHexpand(true) | ||
| 490 | 493 | ||||
| 491 | - | placeholder := "Search history or enter URL… (! lists bangs)" | 494 | + | placeholder := defaultOmniboxPlaceholder |
| 492 | o.entry.SetPlaceholderText(&placeholder) | 495 | o.entry.SetPlaceholderText(&placeholder) | ||
| 493 | 496 | ||||
| 494 | // Create overlay for ghost text | 497 | // Create overlay for ghost text | ||
| @@ -834,77 +837,102 @@ func (o *Omnibox) setGhostText(originalInput, suffix, fullText string) { | |||||
| 834 | o.hasGhostText = true | 837 | o.hasGhostText = true | ||
| 835 | o.mu.Unlock() | 838 | o.mu.Unlock() | ||
| 836 | 839 | ||||
| 837 | - | var cb glib.SourceFunc = func(uintptr) bool { | 840 | + | var schedulePosition func(attempt int) |
| 838 | - | if o.ghostLabel == nil || o.entry == nil { | 841 | + | schedulePosition = func(attempt int) { |
| 839 | - | return false | 842 | + | var cb glib.SourceFunc = func(uintptr) bool { |
| 840 | - | } | 843 | + | if o.ghostLabel == nil || o.entry == nil { |
| 844 | + | return false | |||
| 845 | + | } | |||
| 841 | 846 | ||||
| 842 | - | // Check if input has changed since the autocomplete query was made | 847 | + | // Check if input has changed since the autocomplete query was made |
| 843 | - | // This check runs on GTK main thread where entry.GetText() is reliable | 848 | + | // This check runs on GTK main thread where entry.GetText() is reliable |
| 844 | - | // Skip this check when originalInput is empty (row selection replaces full input) | 849 | + | // Skip this check when originalInput is empty (row selection replaces full input) |
| 845 | - | if originalInput != "" && o.entry.GetText() != originalInput { | 850 | + | if originalInput != "" && o.entry.GetText() != originalInput { |
| 846 | - | // Input changed, skip this stale result - a newer query will handle it | 851 | + | // Input changed, skip this stale result - a newer query will handle it |
| 847 | - | return false | 852 | + | return false |
| 848 | - | } | 853 | + | } |
| 849 | 854 | ||||
| 850 | - | // Get the entry's Pango context and create a layout to measure text width | 855 | + | if attempt < ghostPositionMaxAttempts { |
| 851 | - | pangoCtx := o.entry.GetPangoContext() | 856 | + | entryWidth := o.entry.GetAllocatedWidth() |
| 852 | - | if pangoCtx == nil { | 857 | + | overlayWidth := 0 |
| 853 | - | return false | 858 | + | if o.entryOverlay != nil { |
| 854 | - | } | 859 | + | overlayWidth = o.entryOverlay.GetAllocatedWidth() |
| 860 | + | } | |||
| 861 | + | if entryWidth <= 0 || overlayWidth <= 0 { | |||
| 862 | + | retryCb := glib.SourceFunc(func(uintptr) bool { | |||
| 863 | + | schedulePosition(attempt + 1) | |||
| 864 | + | return false | |||
| 865 | + | }) | |||
| 866 | + | glib.TimeoutAdd(ghostPositionRetryMs, &retryCb, 0) | |||
| 867 | + | return false | |||
| 868 | + | } | |||
| 869 | + | } | |||
| 870 | + | ||||
| 871 | + | // Get the entry's Pango context and create a layout to measure text width | |||
| 872 | + | pangoCtx := o.entry.GetPangoContext() | |||
| 873 | + | if pangoCtx == nil { | |||
| 874 | + | return false | |||
| 875 | + | } | |||
| 855 | 876 | ||||
| 856 | - | // Create layout to measure the input text width | 877 | + | // Create layout to measure the input text width |
| 857 | - | layout := pango.NewLayout(pangoCtx) | 878 | + | layout := pango.NewLayout(pangoCtx) |
| 858 | - | layout.SetText(originalInput, len(originalInput)) | 879 | + | layout.SetText(originalInput, len(originalInput)) |
| 859 | - | 880 | + | ||
| 860 | - | // Get the width in pixels | 881 | + | // Get the width in pixels |
| 861 | - | var widthPx, heightPx int | 882 | + | var widthPx, heightPx int |
| 862 | - | layout.GetPixelSize(&widthPx, &heightPx) | 883 | + | layout.GetPixelSize(&widthPx, &heightPx) |
| 863 | - | 884 | + | ||
| 864 | - | // Set the ghost label text (just the suffix) and position it horizontally | 885 | + | // Set the ghost label text (just the suffix) and position it horizontally |
| 865 | - | o.ghostLabel.SetText(suffix) | 886 | + | o.ghostLabel.SetText(suffix) |
| 866 | - | 887 | + | ||
| 867 | - | marginStart := widthPx | 888 | + | marginStart := widthPx |
| 868 | - | delegate := o.entry.GetDelegate() | 889 | + | delegate := o.entry.GetDelegate() |
| 869 | - | if delegate != nil && o.entryOverlay != nil { | 890 | + | if delegate != nil && o.entryOverlay != nil { |
| 870 | - | textWidget := gtk.TextNewFromInternalPtr(delegate.GoPointer()) | 891 | + | textWidget := gtk.TextNewFromInternalPtr(delegate.GoPointer()) |
| 871 | - | if textWidget != nil { | 892 | + | if textWidget != nil { |
| 872 | - | var textBounds graphene.Rect | 893 | + | var textBounds graphene.Rect |
| 873 | - | if textWidget.ComputeBounds(&o.entryOverlay.Widget, &textBounds) { | 894 | + | if textWidget.ComputeBounds(&o.entryOverlay.Widget, &textBounds) { |
| 874 | - | cursorPos := uint(utf8.RuneCountInString(originalInput)) | 895 | + | textStart := float64(textBounds.GetX()) |
| 875 | - | var strongRect, weakRect graphene.Rect | 896 | + | cursorPos := uint(utf8.RuneCountInString(originalInput)) |
| 876 | - | textWidget.ComputeCursorExtents(cursorPos, &strongRect, &weakRect) | 897 | + | var strongRect, weakRect graphene.Rect |
| 877 | - | cursorX := float64(textBounds.GetX() + strongRect.GetX()) | 898 | + | textWidget.ComputeCursorExtents(cursorPos, &strongRect, &weakRect) |
| 878 | - | marginStart = int(math.Round(cursorX)) | 899 | + | cursorX := float64(textBounds.GetX() + strongRect.GetX()) |
| 900 | + | marginStart = int(math.Round(cursorX)) | |||
| 901 | + | if cursorX < textStart { | |||
| 902 | + | marginStart = int(math.Round(textStart)) | |||
| 903 | + | } | |||
| 904 | + | } | |||
| 879 | } | 905 | } | ||
| 880 | } | 906 | } | ||
| 881 | - | } | |||
| 882 | 907 | ||||
| 883 | - | // Clamp horizontal position to entry bounds | 908 | + | // Clamp horizontal position to entry bounds |
| 884 | - | if marginStart < 0 { | 909 | + | if marginStart < 0 { |
| 885 | - | marginStart = 0 | 910 | + | marginStart = 0 |
| 886 | - | } | 911 | + | } |
| 887 | - | if o.entryOverlay != nil { | 912 | + | if o.entryOverlay != nil { |
| 888 | - | var entryBounds graphene.Rect | 913 | + | var entryBounds graphene.Rect |
| 889 | - | if o.entry.ComputeBounds(&o.entryOverlay.Widget, &entryBounds) { | 914 | + | if o.entry.ComputeBounds(&o.entryOverlay.Widget, &entryBounds) { |
| 890 | - | minStart := int(math.Round(float64(entryBounds.GetX()))) | 915 | + | minStart := int(math.Round(float64(entryBounds.GetX()))) |
| 891 | - | if marginStart < minStart { | 916 | + | if marginStart < minStart { |
| 892 | - | marginStart = minStart | 917 | + | marginStart = minStart |
| 918 | + | } | |||
| 893 | } | 919 | } | ||
| 894 | } | 920 | } | ||
| 895 | - | } | |||
| 896 | 921 | ||||
| 897 | - | o.ghostLabel.SetMarginStart(marginStart) | 922 | + | o.ghostLabel.SetMarginStart(marginStart) |
| 898 | - | // Vertical positioning handled by CSS (.omnibox-ghost) to scale correctly with UI | 923 | + | // Vertical positioning handled by CSS (.omnibox-ghost) to scale correctly with UI |
| 899 | - | o.ghostLabel.SetVisible(true) | 924 | + | o.ghostLabel.SetVisible(true) |
| 900 | 925 | ||||
| 901 | - | // Hide placeholder text so it doesn't show through ghost text | 926 | + | // Hide placeholder text so it doesn't show through ghost text |
| 902 | - | emptyPlaceholder := "" | 927 | + | emptyPlaceholder := "" |
| 903 | - | o.entry.SetPlaceholderText(&emptyPlaceholder) | 928 | + | o.entry.SetPlaceholderText(&emptyPlaceholder) |
| 904 | 929 | ||||
| 905 | - | return false | 930 | + | return false |
| 931 | + | } | |||
| 932 | + | glib.IdleAdd(&cb, 0) | |||
| 906 | } | 933 | } | ||
| 907 | - | glib.IdleAdd(&cb, 0) | 934 | + | |
| 935 | + | schedulePosition(0) | |||
| 908 | } | 936 | } | ||
| 909 | 937 | ||||
| 910 | // clearGhostText hides the ghost completion text. | 938 | // clearGhostText hides the ghost completion text. | ||
| @@ -926,7 +954,7 @@ func (o *Omnibox) clearGhostText() { | |||||
| 926 | } | 954 | } | ||
| 927 | // Restore placeholder text | 955 | // Restore placeholder text | ||
| 928 | if o.entry != nil { | 956 | if o.entry != nil { | ||
| 929 | - | placeholder := "Search history or enter URL… (! lists bangs)" | 957 | + | placeholder := defaultOmniboxPlaceholder |
| 930 | o.entry.SetPlaceholderText(&placeholder) | 958 | o.entry.SetPlaceholderText(&placeholder) | ||
| 931 | } | 959 | } | ||
| 932 | return false | 960 | return false | ||
| @@ -934,6 +962,36 @@ func (o *Omnibox) clearGhostText() { | |||||
| 934 | glib.IdleAdd(&cb, 0) | 962 | glib.IdleAdd(&cb, 0) | ||
| 935 | } | 963 | } | ||
| 936 | 964 | ||||
| 965 | + | // clearGhostTextIfInput clears ghost text only if the entry text matches expectedInput. | |||
| 966 | + | // This prevents stale background results from wiping newer ghost text. | |||
| 967 | + | func (o *Omnibox) clearGhostTextIfInput(expectedInput string) { | |||
| 968 | + | if o.entry == nil { | |||
| 969 | + | return | |||
| 970 | + | } | |||
| 971 | + | ||||
| 972 | + | var cb glib.SourceFunc = func(uintptr) bool { | |||
| 973 | + | if o.entry == nil { | |||
| 974 | + | return false | |||
| 975 | + | } | |||
| 976 | + | if o.entry.GetText() != expectedInput { | |||
| 977 | + | return false | |||
| 978 | + | } | |||
| 979 | + | o.mu.Lock() | |||
| 980 | + | o.ghostSuffix = "" | |||
| 981 | + | o.ghostFullText = "" | |||
| 982 | + | o.hasGhostText = false | |||
| 983 | + | o.mu.Unlock() | |||
| 984 | + | if o.ghostLabel != nil { | |||
| 985 | + | o.ghostLabel.SetVisible(false) | |||
| 986 | + | o.ghostLabel.SetText("") | |||
| 987 | + | } | |||
| 988 | + | placeholder := defaultOmniboxPlaceholder | |||
| 989 | + | o.entry.SetPlaceholderText(&placeholder) | |||
| 990 | + | return false | |||
| 991 | + | } | |||
| 992 | + | glib.IdleAdd(&cb, 0) | |||
| 993 | + | } | |||
| 994 | + | ||||
| 937 | // acceptGhostCompletion accepts the ghost text and fills the input. | 995 | // acceptGhostCompletion accepts the ghost text and fills the input. | ||
| 938 | func (o *Omnibox) acceptGhostCompletion() { | 996 | func (o *Omnibox) acceptGhostCompletion() { | ||
| 939 | o.mu.Lock() | 997 | o.mu.Lock() | ||
| @@ -962,75 +1020,40 @@ func (o *Omnibox) acceptGhostCompletion() { | |||||
| 962 | o.mu.Unlock() | 1020 | o.mu.Unlock() | ||
| 963 | } | 1021 | } | ||
| 964 | 1022 | ||||
| 965 | - | // updateGhostFromSuggestion updates ghost text based on the current autocomplete suggestion. | |||
| 966 | - | // The query parameter is the search query that was used, ensuring we don't use stale input. | |||
| 967 | - | func (o *Omnibox) updateGhostFromSuggestion(query string) { | |||
| 968 | - | if o.autocompleteUC == nil { | |||
| 969 | - | return | |||
| 970 | - | } | |||
| 971 | - | ||||
| 972 | - | o.mu.RLock() | |||
| 973 | - | bangMode := o.bangMode | |||
| 974 | - | o.mu.RUnlock() | |||
| 975 | - | ||||
| 976 | - | // Use the query that triggered this search, not realInput which may have changed | |||
| 977 | - | userInput := query | |||
| 978 | - | ||||
| 979 | - | // Don't show ghost text in bang mode (it has its own completion) | |||
| 980 | - | if bangMode || userInput == "" { | |||
| 981 | - | o.clearGhostText() | |||
| 982 | - | return | |||
| 983 | - | } | |||
| 984 | - | ||||
| 985 | - | go func() { | |||
| 986 | - | output := o.autocompleteUC.GetSuggestion(o.ctx, usecase.GetSuggestionInput{Input: userInput}) | |||
| 987 | - | if !output.Found || output.Suggestion == nil { | |||
| 988 | - | o.clearGhostText() | |||
| 989 | - | return | |||
| 990 | - | } | |||
| 991 | - | // Pass the original input so setGhostText can verify it's still current | |||
| 992 | - | o.setGhostText(userInput, output.Suggestion.Suffix, output.Suggestion.FullText) | |||
| 993 | - | }() | |||
| 994 | - | } | |||
| 995 | - | ||||
| 996 | // updateGhostFromURL updates ghost text based on a specific URL (from row selection). | 1023 | // updateGhostFromURL updates ghost text based on a specific URL (from row selection). | ||
| 997 | // When a row is selected via arrow keys, show the URL as ghost text even if | 1024 | // When a row is selected via arrow keys, show the URL as ghost text even if | ||
| 998 | // the user's input isn't a prefix - this shows what Tab would fill in. | 1025 | // the user's input isn't a prefix - this shows what Tab would fill in. | ||
| 999 | - | func (o *Omnibox) updateGhostFromURL(targetURL string) { | 1026 | + | |
| 1027 | + | func (o *Omnibox) updateGhostFromURL(userInput, targetURL string) bool { | |||
| 1000 | if targetURL == "" { | 1028 | if targetURL == "" { | ||
| 1001 | - | o.clearGhostText() | 1029 | + | return false |
| 1002 | - | return | |||
| 1003 | } | 1030 | } | ||
| 1004 | 1031 | ||||
| 1005 | - | o.mu.RLock() | |||
| 1006 | - | userInput := o.realInput | |||
| 1007 | - | o.mu.RUnlock() | |||
| 1008 | - | ||||
| 1009 | // Strip protocol from URL for cleaner display | 1032 | // Strip protocol from URL for cleaner display | ||
| 1010 | displayURL := autocomplete.StripProtocol(targetURL) | 1033 | displayURL := autocomplete.StripProtocol(targetURL) | ||
| 1011 | 1034 | ||||
| 1012 | - | // Try to compute proper suffix if input is a prefix of the URL | 1035 | + | // Only show full URL as ghost text when input is empty (initial state) |
| 1013 | - | if o.autocompleteUC != nil && userInput != "" { | 1036 | + | if userInput == "" { |
| 1014 | - | output := o.autocompleteUC.GetSuggestionForURL(o.ctx, userInput, targetURL) | 1037 | + | o.setGhostText("", displayURL, displayURL) |
| 1015 | - | if output.Found && output.Suggestion != nil { | 1038 | + | return true |
| 1016 | - | o.setGhostText(userInput, output.Suggestion.Suffix, output.Suggestion.FullText) | |||
| 1017 | - | return | |||
| 1018 | - | } | |||
| 1019 | } | 1039 | } | ||
| 1020 | 1040 | ||||
| 1021 | - | // If user has typed something but it's not a prefix match, don't show ghost text | 1041 | + | // Try to compute proper suffix if input is a prefix of the URL |
| 1022 | - | // This prevents confusing ghost text when scrolling through history | 1042 | + | if suffix, matchedURL, ok := autocomplete.ComputeURLCompletionSuffix(userInput, targetURL); ok { |
| 1023 | - | if userInput != "" { | 1043 | + | o.setGhostText(userInput, suffix, matchedURL) |
| 1024 | - | o.clearGhostText() | 1044 | + | return true |
| 1025 | - | return | |||
| 1026 | } | 1045 | } | ||
| 1027 | 1046 | ||||
| 1028 | - | // Only show full URL as ghost text when input is empty (initial state) | 1047 | + | return false |
| 1029 | - | o.setGhostText("", displayURL, displayURL) | |||
| 1030 | } | 1048 | } | ||
| 1031 | 1049 | ||||
| 1032 | // updateGhostFromSelection updates ghost text based on the currently selected row. | 1050 | // updateGhostFromSelection updates ghost text based on the currently selected row. | ||
| 1033 | func (o *Omnibox) updateGhostFromSelection() { | 1051 | func (o *Omnibox) updateGhostFromSelection() { | ||
| 1052 | + | o.updateGhostFromSelectionWithInput(o.entry.GetText()) | |||
| 1053 | + | } | |||
| 1054 | + | ||||
| 1055 | + | // updateGhostFromSelectionWithInput updates ghost text based on selected row and input. | |||
| 1056 | + | func (o *Omnibox) updateGhostFromSelectionWithInput(entryText string) { | |||
| 1034 | o.mu.RLock() | 1057 | o.mu.RLock() | ||
| 1035 | idx := o.selectedIndex | 1058 | idx := o.selectedIndex | ||
| 1036 | mode := o.viewMode | 1059 | mode := o.viewMode | ||
| @@ -1041,6 +1064,12 @@ func (o *Omnibox) updateGhostFromSelection() { | |||||
| 1041 | 1064 | ||||
| 1042 | // No ghost text in bang mode | 1065 | // No ghost text in bang mode | ||
| 1043 | if bangMode { | 1066 | if bangMode { | ||
| 1067 | + | o.clearGhostTextIfInput(entryText) | |||
| 1068 | + | return | |||
| 1069 | + | } | |||
| 1070 | + | ||||
| 1071 | + | if entryText != "" { | |||
| 1072 | + | o.resolveGhostCompletion(entryText, mode, suggestions, favorites) | |||
| 1044 | return | 1073 | return | ||
| 1045 | } | 1074 | } | ||
| 1046 | 1075 | ||||
| @@ -1056,11 +1085,49 @@ func (o *Omnibox) updateGhostFromSelection() { | |||||
| 1056 | } | 1085 | } | ||
| 1057 | 1086 | ||||
| 1058 | if targetURL == "" { | 1087 | if targetURL == "" { | ||
| 1059 | - | o.clearGhostText() | 1088 | + | o.clearGhostTextIfInput(entryText) |
| 1089 | + | return | |||
| 1090 | + | } | |||
| 1091 | + | ||||
| 1092 | + | if o.updateGhostFromURL(entryText, targetURL) { | |||
| 1060 | return | 1093 | return | ||
| 1061 | } | 1094 | } | ||
| 1062 | 1095 | ||||
| 1063 | - | o.updateGhostFromURL(targetURL) | 1096 | + | o.clearGhostTextIfInput(entryText) |
| 1097 | + | } | |||
| 1098 | + | ||||
| 1099 | + | func (o *Omnibox) resolveGhostCompletion(entryText string, mode ViewMode, suggestions []Suggestion, favorites []Favorite) { | |||
| 1100 | + | if o.autocompleteUC == nil { | |||
| 1101 | + | o.clearGhostTextIfInput(entryText) | |||
| 1102 | + | return | |||
| 1103 | + | } | |||
| 1104 | + | ||||
| 1105 | + | visibleURLs := make([]string, 0, len(suggestions)) | |||
| 1106 | + | if mode == ViewModeHistory { | |||
| 1107 | + | for _, s := range suggestions { | |||
| 1108 | + | if s.URL != "" { | |||
| 1109 | + | visibleURLs = append(visibleURLs, s.URL) | |||
| 1110 | + | } | |||
| 1111 | + | } | |||
| 1112 | + | } else { | |||
| 1113 | + | for _, f := range favorites { | |||
| 1114 | + | if f.URL != "" { | |||
| 1115 | + | visibleURLs = append(visibleURLs, f.URL) | |||
| 1116 | + | } | |||
| 1117 | + | } | |||
| 1118 | + | } | |||
| 1119 | + | ||||
| 1120 | + | go func() { | |||
| 1121 | + | suggestion := o.autocompleteUC.ResolveCompletion(o.ctx, entryText, usecase.CompletionOptions{ | |||
| 1122 | + | VisibleURLs: visibleURLs, | |||
| 1123 | + | AllowBangs: false, | |||
| 1124 | + | }) | |||
| 1125 | + | if suggestion == nil { | |||
| 1126 | + | o.clearGhostTextIfInput(entryText) | |||
| 1127 | + | return | |||
| 1128 | + | } | |||
| 1129 | + | o.setGhostText(entryText, suggestion.Suffix, suggestion.FullText) | |||
| 1130 | + | }() | |||
| 1064 | } | 1131 | } | ||
| 1065 | 1132 | ||||
| 1066 | // performSearch executes the search based on current view mode and query. | 1133 | // performSearch executes the search based on current view mode and query. | ||
| @@ -1380,10 +1447,9 @@ func (o *Omnibox) updateSuggestions(suggestions []Suggestion, query string) { | |||||
| 1380 | // Select first item if available and update ghost text | 1447 | // Select first item if available and update ghost text | ||
| 1381 | if rowCount > 0 { | 1448 | if rowCount > 0 { | ||
| 1382 | o.selectIndex(0) | 1449 | o.selectIndex(0) | ||
| 1383 | - | // Update ghost text from first suggestion | 1450 | + | o.updateGhostFromSelectionWithInput(query) |
| 1384 | - | o.updateGhostFromSuggestion(query) | |||
| 1385 | } else { | 1451 | } else { | ||
| 1386 | - | o.clearGhostText() | 1452 | + | o.clearGhostTextIfInput(query) |
| 1387 | } | 1453 | } | ||
| 1388 | } | 1454 | } | ||
| 1389 | 1455 | ||||
| @@ -1409,10 +1475,9 @@ func (o *Omnibox) updateFavorites(favorites []Favorite, query string) { | |||||
| 1409 | // Select first item if available and update ghost text | 1475 | // Select first item if available and update ghost text | ||
| 1410 | if rowCount > 0 { | 1476 | if rowCount > 0 { | ||
| 1411 | o.selectIndex(0) | 1477 | o.selectIndex(0) | ||
| 1412 | - | // Update ghost text from first favorite | 1478 | + | o.updateGhostFromSelectionWithInput(query) |
| 1413 | - | o.updateGhostFromSuggestion(query) | |||
| 1414 | } else { | 1479 | } else { | ||
| 1415 | - | o.clearGhostText() | 1480 | + | o.clearGhostTextIfInput(query) |
| 1416 | } | 1481 | } | ||
| 1417 | } | 1482 | } | ||
| 1418 | 1483 | ||||
M internal/ui/component/workspace_view.go +0 -7
| @@ -213,15 +213,8 @@ func (wv *WorkspaceView) setActivePaneIDInternal(paneID entity.PaneID) error { | |||
| 213 | 213 | wv.logger.Debug(). | |
| 214 | 214 | Str("from_pane", string(currentActiveID)). | |
| 215 | 215 | Str("to_pane", string(paneID)). | |
| 216 | - | Bool("omnibox_visible", wv.omnibox != nil). | |
| 217 | - | Str("omnibox_pane", string(wv.omniboxPaneID)). | |
| 218 | 216 | Msg("active pane changing") | |
| 219 | 217 | ||
| 220 | - | // Destroy omnibox if active pane is changing | |
| 221 | - | if currentActiveID != paneID && wv.omnibox != nil { | |
| 222 | - | wv.logger.Debug().Msg("destroying omnibox due to pane change") | |
| 223 | - | wv.hideOmniboxInternal() | |
| 224 | - | } | |
| 225 | 218 | // Destroy find bar if active pane is changing | |
| 226 | 219 | if currentActiveID != paneID && wv.findBar != nil { | |
| 227 | 220 | wv.hideFindBarInternal() | |
| @@ -213,15 +213,8 @@ func (wv *WorkspaceView) setActivePaneIDInternal(paneID entity.PaneID) error { | |||||
| 213 | wv.logger.Debug(). | 213 | wv.logger.Debug(). | ||
| 214 | Str("from_pane", string(currentActiveID)). | 214 | Str("from_pane", string(currentActiveID)). | ||
| 215 | Str("to_pane", string(paneID)). | 215 | Str("to_pane", string(paneID)). | ||
| 216 | - | Bool("omnibox_visible", wv.omnibox != nil). | |||
| 217 | - | Str("omnibox_pane", string(wv.omniboxPaneID)). | |||
| 218 | Msg("active pane changing") | 216 | Msg("active pane changing") | ||
| 219 | 217 | ||||
| 220 | - | // Destroy omnibox if active pane is changing | |||
| 221 | - | if currentActiveID != paneID && wv.omnibox != nil { | |||
| 222 | - | wv.logger.Debug().Msg("destroying omnibox due to pane change") | |||
| 223 | - | wv.hideOmniboxInternal() | |||
| 224 | - | } | |||
| 225 | // Destroy find bar if active pane is changing | 218 | // Destroy find bar if active pane is changing | ||
| 226 | if currentActiveID != paneID && wv.findBar != nil { | 219 | if currentActiveID != paneID && wv.findBar != nil { | ||
| 227 | wv.hideFindBarInternal() | 220 | wv.hideFindBarInternal() | ||