diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 23bdb1e06..d189340fb 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -190,6 +190,13 @@ const GridSearchResults = new Lang.Class({ return; let targetActor = this._grid.getItemAtIndex(this.selectionIndex); targetActor._delegate.activate(); + }, + + getFirstResult: function() { + if (this.getVisibleResultCount() > 0) + return this._grid.getItemAtIndex(0)._delegate; + else + return null; } }); @@ -245,6 +252,9 @@ const SearchResults = new Lang.Class({ this._openSearchProviders = []; this._openSearchSystem.connect('changed', Lang.bind(this, this._updateOpenSearchProviderButtons)); this._updateOpenSearchProviderButtons(); + + this._highlightDefault = false; + this._defaultResult = null; }, _updateOpenSearchProviderButtons: function() { @@ -284,6 +294,16 @@ const SearchResults = new Lang.Class({ button.set_child(bin); provider.actor = button; + button.setSelected = function(selected) { + if (selected) + button.add_style_pseudo_class('selected'); + else + button.remove_style_pseudo_class('selected'); + }; + button.activate = Lang.bind(this, function() { + this._openSearchSystem.activateResult(provider.id); + }); + this._searchProvidersBox.add(button); }, @@ -364,17 +384,31 @@ const SearchResults = new Lang.Class({ if (this._selectedOpenSearchButton > -1 || this._selectedProvider > -1) return; + let newDefaultResult = null; + for (let i = 0; i < this._providerMeta.length; i++) { let meta = this._providerMeta[i]; if (meta.hasPendingResults) return; - if (meta.actor.visible) + let firstResult = meta.resultDisplay.getFirstResult(); + if (firstResult && firstResult.actor.visible) { + newDefaultResult = firstResult; break; // select this one! + } } - this.selectDown(false); - this._initialSelectionSet = true; + if (!newDefaultResult) + newDefaultResult = this._searchProvidersBox.get_first_child(); + + if (newDefaultResult != this._defaultResult) { + if (this._defaultResult) + this._defaultResult.setSelected(false); + if (newDefaultResult) + newDefaultResult.setSelected(this._highlightDefault); + + this._defaultResult = newDefaultResult; + } }, _updateCurrentResults: function(searchSystem, results) { @@ -428,11 +462,9 @@ const SearchResults = new Lang.Class({ let terms = searchSystem.getTerms(); this._openSearchSystem.setSearchTerms(terms); - // To avoid CSS transitions causing flickering - // of the selection when the first search result - // stays the same, we hide the content while - // filling in the results and setting the initial - // selection. + // To avoid CSS transitions causing flickering when the first search + // result stays the same, we hide the content while filling in the + // results. this._content.hide(); for (let i = 0; i < results.length; i++) { @@ -535,5 +567,27 @@ const SearchResults = new Lang.Class({ let resultDisplay = meta.resultDisplay; resultDisplay.activateSelected(); Main.overview.hide(); + }, + + activateDefault: function() { + if (this._defaultResult && this._defaultResult.actor.visible) + this._defaultResult.activate(); + }, + + highlightDefault: function(highlight) { + this._highlightDefault = highlight; + if (this._defaultResult) + this._defaultResult.setSelected(highlight); + }, + + navigateFocus: function(direction) { + if (direction == Gtk.DirectionType.TAB_FORWARD && this._defaultResult) { + // The default result appears focused, so navigate directly to the + // next result. + this.actor.navigate_focus(null, direction, false); + this.actor.navigate_focus(global.stage.key_focus, direction, false); + } else { + this.actor.navigate_focus(null, direction, false); + } } }); diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 363d4df26..8826556c7 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -133,14 +133,14 @@ const SearchTab = new Lang.Class({ this._text.connect('text-changed', Lang.bind(this, this._onTextChanged)); this._text.connect('key-press-event', Lang.bind(this, function (o, e) { // We can't connect to 'activate' here because search providers - // might want to do something with the modifiers in activateSelected. + // might want to do something with the modifiers in activateDefault. let symbol = e.get_key_symbol(); if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { if (this._searchTimeoutId > 0) { Mainloop.source_remove(this._searchTimeoutId); this._doSearch(); } - this._searchResults.activateSelected(); + this._searchResults.activateDefault(); return true; } return false; @@ -152,6 +152,13 @@ const SearchTab = new Lang.Class({ this._capturedEventId = 0; + this._text.connect('key-focus-in', Lang.bind(this, function() { + this._searchResults.highlightDefault(true); + })); + this._text.connect('key-focus-out', Lang.bind(this, function() { + this._searchResults.highlightDefault(false); + })); + // Since the entry isn't inside the results container we install this // dummy widget as the last results container child so that we can // include the entry in the keynav tab path... @@ -271,30 +278,18 @@ const SearchTab = new Lang.Class({ _onKeyPress: function(entry, event) { let symbol = event.get_key_symbol(); - if (symbol == Clutter.Up) { - if (!this.active) - return true; - this._searchResults.selectUp(false); - - return true; - } else if (symbol == Clutter.Down) { - if (!this.active) - return true; - - this._searchResults.selectDown(false); - return true; - } else if (symbol == Clutter.Escape) { + if (symbol == Clutter.Escape) { if (this._isActivated()) { this._reset(); return true; } } else if (this.active) { if (symbol == Clutter.Tab) { - this._searchResults.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); + this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD); return true; } else if (symbol == Clutter.ISO_Left_Tab) { this._focusTrap.can_focus = false; - this._searchResults.actor.navigate_focus(null, Gtk.DirectionType.TAB_BACKWARD, false); + this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD); this._focusTrap.can_focus = true; return true; }