From 8507d3c4e4a3ef4046473efc6857ff8cdd248400 Mon Sep 17 00:00:00 2001 From: Tanner Doshier Date: Thu, 9 Aug 2012 16:52:36 -0500 Subject: [PATCH] searchDisplay, and others: Switch from provider title to provider icon Display a '+' icon on the provider icon if there are more results that are hidden. If the provider icon is clicked, ask the provider to launch itself and perform a search with the current terms. https://bugzilla.gnome.org/show_bug.cgi?id=681797 --- data/org.gnome.ShellSearchProvider.xml | 20 ++++++ js/ui/remoteSearch.js | 8 +++ js/ui/search.js | 16 +++++ js/ui/searchDisplay.js | 95 ++++++++++++++++++++++++-- 4 files changed, 133 insertions(+), 6 deletions(-) diff --git a/data/org.gnome.ShellSearchProvider.xml b/data/org.gnome.ShellSearchProvider.xml index 558e6620c..4bf30760f 100644 --- a/data/org.gnome.ShellSearchProvider.xml +++ b/data/org.gnome.ShellSearchProvider.xml @@ -143,5 +143,25 @@ + + + + + + Called when the user clicks on the provider icon. The provider + application should open and run the active search term itself. + + + + + + + + The current search term(s). + + + + + diff --git a/js/ui/remoteSearch.js b/js/ui/remoteSearch.js index 35176f4fb..2db7583b2 100644 --- a/js/ui/remoteSearch.js +++ b/js/ui/remoteSearch.js @@ -27,6 +27,9 @@ const SearchProviderIface = + + + ; var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface); @@ -112,6 +115,7 @@ const RemoteSearchProvider = new Lang.Class({ this.parent(title.toUpperCase()); this._cancellable = new Gio.Cancellable(); + this.icon = icon; }, createIcon: function(size, meta) { @@ -196,6 +200,10 @@ const RemoteSearchProvider = new Lang.Class({ activateResult: function(id) { this._proxy.ActivateResultRemote(id); + }, + + launchSearch: function(terms) { + this._proxy.LaunchSearchRemote(terms); } }); diff --git a/js/ui/search.js b/js/ui/search.js index 22f53d913..fec3f2fb7 100644 --- a/js/ui/search.js +++ b/js/ui/search.js @@ -163,6 +163,16 @@ const SearchProvider = new Lang.Class({ */ activateResult: function(id) { throw new Error('Not implemented'); + }, + + /** + * launchSearch: + * @terms: Current search terms + * + * Called when the user clicks the provider icon. + */ + launchSearch: function(terms) { + throw new Error('Not implemented'); } }); Signals.addSignalMethods(SearchProvider.prototype); @@ -261,5 +271,11 @@ const SearchSystem = new Lang.Class({ } } }, + + launchSearch: function(provider) { + let terms = this.getTerms(); + provider.launchSearch(terms); + Main.overview.toggle(); + } }); Signals.addSignalMethods(SearchSystem.prototype); diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js index 5a914bd99..f0ac119c3 100644 --- a/js/ui/searchDisplay.js +++ b/js/ui/searchDisplay.js @@ -7,6 +7,7 @@ const Meta = imports.gi.Meta; const St = imports.gi.St; const Atk = imports.gi.Atk; +const AppDisplay = imports.ui.appDisplay; const DND = imports.ui.dnd; const IconGrid = imports.ui.iconGrid; const Main = imports.ui.main; @@ -111,8 +112,8 @@ const GridSearchResults = new Lang.Class({ this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS, xAlign: St.Align.START }); this.actor = new St.Bin({ x_align: St.Align.START }); - this.actor.set_child(this._grid.actor); + this._width = 0; this.actor.connect('notify::width', Lang.bind(this, function() { this._width = this.actor.width; @@ -130,6 +131,7 @@ const GridSearchResults = new Lang.Class({ }, getResultsForDisplay: function() { + this._grid.actor.ensure_style(); let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount(); let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit() - alreadyVisible; @@ -143,6 +145,10 @@ const GridSearchResults = new Lang.Class({ return this._grid.visibleItemsCount(); }, + hasMoreResults: function() { + return this._notDisplayedResult.length > 0; + }, + setResults: function(results, terms) { // copy the lists this._notDisplayedResult = results.slice(0); @@ -220,21 +226,32 @@ const SearchResults = new Lang.Class({ }, createProviderMeta: function(provider) { - let providerBox = new St.BoxLayout({ style_class: 'search-section', - vertical: true }); - let title = new St.Label({ style_class: 'search-section-header', - text: provider.title }); - providerBox.add(title, { x_fill: false, x_align: St.Align.START }); + let providerBox = new St.BoxLayout({ style_class: 'search-section' }); + let isAppsProvider = (provider instanceof AppDisplay.AppSearchProvider); + + let providerIcon; + if (!isAppsProvider) { + providerIcon = new ProviderIcon(provider); + providerIcon.connect('launch-search', Lang.bind(this, function(providerIcon) { + this._searchSystem.launchSearch(providerIcon.provider); + })); + providerBox.add(providerIcon.actor, { x_fill: false, + y_fill: false, + x_align: St.Align.START, + y_align: St.Align.START }); + } let resultDisplayBin = new St.Bin({ style_class: 'search-section-results', x_fill: true, y_fill: true }); providerBox.add(resultDisplayBin, { expand: true }); + let resultDisplay = new GridSearchResults(provider); resultDisplayBin.set_child(resultDisplay.actor); this._providerMeta.push({ provider: provider, actor: providerBox, + icon: providerIcon, resultDisplay: resultDisplay }); this._content.add(providerBox); }, @@ -341,6 +358,9 @@ const SearchResults = new Lang.Class({ meta.resultDisplay.setResults(providerResults, terms); let results = meta.resultDisplay.getResultsForDisplay(); + if (meta.icon) + meta.icon.moreIcon.visible = meta.resultDisplay.hasMoreResults(); + provider.getResultMetas(results, Lang.bind(this, function(metas) { this._clearDisplayForProvider(provider); meta.actor.show(); @@ -393,3 +413,66 @@ const SearchResults = new Lang.Class({ } } }); + +const ProviderIcon = new Lang.Class({ + Name: 'ProviderIcon', + + PROVIDER_ICON_SIZE: 48, + + MORE_ICON_SIZE: 16, + + _init: function(provider) { + this.provider = provider; + + this.actor = new St.Button({ style_class: 'search-section-icon-bin', + reactive: true, + can_focus: true, + track_hover: true }); + this.actor.connect('clicked', Lang.bind(this, this._onIconClicked)); + + this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() }); + this.actor.set_child(this._content); + + let rtl = (this.actor.get_text_direction() == Clutter.TextDirection.RTL); + + this.moreIcon = new St.Icon({ style_class: 'search-section-icon-more', + icon_size: this.MORE_ICON_SIZE, + icon_name: 'list-add-symbolic', + visible: false, + x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END, + y_align: Clutter.ActorAlign.END, + // HACK: without these, ClutterBinLayout + // ignores alignment properties on the actor + x_expand: true, + y_expand: true }); + + this._iconBin = new St.Bin({ style_class: 'search-section-icon', + width: this.PROVIDER_ICON_SIZE, + height: this.PROVIDER_ICON_SIZE }); + + this._content.add_actor(this._iconBin); + this._content.add_actor(this.moreIcon); + + this._createProviderIcon(); + }, + + _createProviderIcon: function() { + let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE }); + + if (this.provider.icon) + icon.gicon = this.provider.icon; + else + icon.icon_name = 'application-x-executable'; + + this._iconBin.set_child(icon); + }, + + activate: function() { + this.emit('launch-search'); + }, + + _onIconClicked: function(actor) { + this.activate(); + } +}); +Signals.addSignalMethods(ProviderIcon.prototype);