diff --git a/js/ui/search.js b/js/ui/search.js index 47cb0e6de..5b6a2c28f 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -112,6 +112,43 @@ function SearchProvider(title) { SearchProvider.prototype = { _init: function(title) { this.title = title; + this.searchSystem = null; + this.searchAsync = false; + }, + + _asyncCancelled: function() { + }, + + startAsync: function() { + this.searchAsync = true; + }, + + tryCancelAsync: function() { + if (!this.searchAsync) + return; + this._asyncCancelled(); + this.searchAsync = false; + }, + + /** + * addItems: + * @items: an array of result identifier strings representing + * items which match the last given search terms. + * + * This should be used for something that requires a bit more + * logic; it's designed to be an asyncronous way to add a result + * to the current search. + */ + addItems: function(items) { + if (!this.searchSystem) + throw new Error('Search provider not registered'); + + if (!items.length) + return; + + this.tryCancelAsync(); + + this.searchSystem.addProviderItems(this, items); }, /** @@ -315,6 +352,7 @@ SearchSystem.prototype = { }, registerProvider: function (provider) { + provider.searchSystem = this; this._providers.push(provider); }, @@ -331,12 +369,23 @@ SearchSystem.prototype = { this._previousResults = []; }, + addProviderItems: function(provider, items) { + this.emit('search-updated', provider, items); + }, + updateSearch: function(searchString) { searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, ''); if (searchString == '') - return []; + return; let terms = searchString.split(/\s+/); + this.updateSearchResults(terms); + }, + + updateSearchResults: function(terms) { + if (!terms) + return; + let isSubSearch = terms.length == this._previousTerms.length; if (isSubSearch) { for (let i = 0; i < terms.length; i++) { @@ -349,12 +398,12 @@ SearchSystem.prototype = { let results = []; if (isSubSearch) { - for (let i = 0; i < this._previousResults.length; i++) { + for (let i = 0; i < this._providers.length; i++) { let [provider, previousResults] = this._previousResults[i]; + provider.tryCancelAsync(); try { let providerResults = provider.getSubsearchResultSet(previousResults, terms); - if (providerResults.length > 0) - results.push([provider, providerResults]); + results.push([provider, providerResults]); } catch (error) { global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); } @@ -362,10 +411,10 @@ SearchSystem.prototype = { } else { for (let i = 0; i < this._providers.length; i++) { let provider = this._providers[i]; + provider.tryCancelAsync(); try { let providerResults = provider.getInitialResultSet(terms); - if (providerResults.length > 0) - results.push([provider, providerResults]); + results.push([provider, providerResults]); } catch (error) { global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); } @@ -374,8 +423,7 @@ SearchSystem.prototype = { this._previousTerms = terms; this._previousResults = results; - - return results; - } + this.emit('search-completed', results); + }, }; Signals.addSignalMethods(SearchSystem.prototype); diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 580f410be..04ef64cdf 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -189,6 +189,8 @@ function SearchResults(searchSystem, openSearchSystem) { SearchResults.prototype = { _init: function(searchSystem, openSearchSystem) { this._searchSystem = searchSystem; + this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults)); + this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults)); this._openSearchSystem = openSearchSystem; this.actor = new St.BoxLayout({ name: 'searchResults', @@ -223,9 +225,11 @@ SearchResults.prototype = { this._selectedProvider = -1; this._providers = this._searchSystem.getProviders(); this._providerMeta = []; - for (let i = 0; i < this._providers.length; i++) + this._providerMetaResults = {}; + for (let i = 0; i < this._providers.length; i++) { this.createProviderMeta(this._providers[i]); - + this._providerMetaResults[this.providers[i].title] = []; + } this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' }); this.actor.add(this._searchProvidersBox); @@ -305,6 +309,12 @@ SearchResults.prototype = { } }, + _clearDisplayForProvider: function(index) { + let meta = this._providerMeta[index]; + meta.resultDisplay.clear(); + meta.actor.hide(); + }, + reset: function() { this._searchSystem.reset(); this._statusText.hide(); @@ -319,15 +329,24 @@ SearchResults.prototype = { this._statusText.show(); }, + doSearch: function (searchString) { + this._searchSystem.updateSearch(searchString); + }, + _metaForProvider: function(provider) { return this._providerMeta[this._providers.indexOf(provider)]; }, - updateSearch: function (searchString) { - let results = this._searchSystem.updateSearch(searchString); - - this._clearDisplay(); + _updateCurrentResults: function(searchSystem, provider, results) { + let terms = searchSystem.getTerms(); + let meta = this._metaForProvider(provider); + meta.resultDisplay.clear(); + meta.actor.show(); + meta.resultDisplay.renderResults(results, terms); + return true; + }, + _updateResults: function(searchSystem, results) { if (results.length == 0) { this._statusText.set_text(_("No matching results.")); this._statusText.show(); @@ -337,7 +356,7 @@ SearchResults.prototype = { this._statusText.hide(); } - let terms = this._searchSystem.getTerms(); + let terms = searchSystem.getTerms(); this._openSearchSystem.setSearchTerms(terms); // To avoid CSS transitions causing flickering @@ -349,9 +368,15 @@ SearchResults.prototype = { for (let i = 0; i < results.length; i++) { let [provider, providerResults] = results[i]; - let meta = this._metaForProvider(provider); - meta.actor.show(); - meta.resultDisplay.renderResults(providerResults, terms); + if (providerResults.length == 0) { + this._clearDisplayForProvider(i); + } else { + this._providerMetaResults[provider.title] = providerResults; + this._clearDisplayForProvider(i); + let meta = this._metaForProvider(provider); + meta.actor.show(); + meta.resultDisplay.renderResults(providerResults, terms); + } } if (this._selectedOpenSearchButton == -1) diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js index 53b349c77..b33139f57 100644 --- a/js/ui/viewSelector.js +++ b/js/ui/viewSelector.js @@ -297,7 +297,7 @@ SearchTab.prototype = { _doSearch: function () { this._searchTimeoutId = 0; let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, ''); - this._searchResults.updateSearch(text); + this._searchResults.doSearch(text); return false; }