[gnome-shell/wip/re-search-v2: 4/26] searchDisplay: Add ListSearchResult and ListSearchResults



commit 1bb09fd0f13a2722b26ea30fa84a43502836f3c7
Author: Tanner Doshier <doshitan gmail com>
Date:   Wed Aug 1 20:48:10 2012 -0500

    searchDisplay: Add ListSearchResult and ListSearchResults
    
    These are for all search results except apps (and Wanda).
    We also simplify a bit the packing of search results, which removes some
    ugly code in navigateFocus() where we needed to call
    st_widget_navigate_focus() twice, since the grid icon was composed by
    two nested boxes, both focusable.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=681797

 data/theme/gnome-shell.css |   70 +++++++++-----
 js/ui/appDisplay.js        |    6 -
 js/ui/search.js            |    3 -
 js/ui/searchDisplay.js     |  236 +++++++++++++++++++++++++++++++------------
 js/ui/wanda.js             |    3 +-
 5 files changed, 218 insertions(+), 100 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index fd3121e..19645d7 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -49,7 +49,7 @@ stage {
 .switcher-list, 
 .app-well-app > .overview-icon,
 .show-apps > .overview-icon,
-.search-result-content > .overview-icon {
+.grid-search-result .overview-icon {
     font-size: 9pt;
     font-weight: bold;
 }
@@ -760,7 +760,7 @@ StScrollBar StButton#vhandle:active {
 
 #searchResultsContent {
     padding-right: 20px;
-    spacing: 36px;
+    spacing: 16px;
 }
 
 #searchResultsContent:rtl {
@@ -768,29 +768,32 @@ StScrollBar StButton#vhandle:active {
     padding-left: 20px;
 }
 
-.search-section-header {
-    padding: 4px 12px;
-    spacing: 4px;
-    color: #6f6f6f;
-    font-size: .8em;
+.search-section {
+    /* This should be equal to #searchResultsContent spacing */
+    spacing: 16px;
 }
 
-.search-statustext {
-    color: #efefef;
-    font-size: 2em;
-    font-weight: bold;
+.search-section-separator {
+    -gradient-height: 1px;
+    -gradient-start: rgba(255,255,255,0);
+    -gradient-end: rgba(255,255,255,0.5);
+    -margin-horizontal: 1.5em;
+    height: 1px;
 }
 
-.search-section-results {
-    padding: 6px;
+.search-section-content {
+    /* This is the space between the provider icon and the results container */
+    spacing: 32px;
 }
 
-.search-section-list-results {
-    spacing: 4px;
+.search-statustext {
+    color: #efefef;
+    font-size: 2em;
+    font-weight: bold;
 }
 
-.results-container {
-    spacing: 4px;
+.list-search-results {
+    spacing: 3px;
 }
 
 /* Text labels are an odd number of pixels tall. The uneven top and bottom
@@ -819,7 +822,7 @@ StScrollBar StButton#vhandle:active {
     -x-offset: 8px;
 }
 
-/* Application Launchers and Grid */
+/* Application Launchers, Grid and List results */
 
 .icon-grid {
     spacing: 36px;
@@ -871,6 +874,23 @@ StScrollBar StButton#vhandle:active {
     padding: 4px 8px;
 }
 
+.list-search-result-content {
+    spacing: 12px;
+    padding: 12px;
+}
+
+.list-search-result-title {
+    font-weight: bold;
+    font-size: 14pt;
+    color: white;
+}
+
+.list-search-result-description {
+    font-weight: bold;
+    font-size: 12pt;
+    color: #eeeeec;
+}
+
 .search-provider-icon-more {
     width: 16px;
     height: 16px;
@@ -880,7 +900,8 @@ StScrollBar StButton#vhandle:active {
 .app-well-app > .overview-icon,
 .show-apps > .overview-icon,
 .search-provider-icon,
-.search-result-content > .overview-icon {
+.list-search-result,
+.grid-search-result .overview-icon {
     border-radius: 4px;
     padding: 3px;
     border: 1px rgba(0,0,0,0);
@@ -897,7 +918,8 @@ StScrollBar StButton#vhandle:active {
 .app-well-app:hover > .overview-icon,
 .show-apps:hover > .overview-icon,
 .search-provider-icon:hover,
-.search-result-content:hover > .overview-icon {
+.list-search-result:hover,
+.grid-search-result:hover .overview-icon {
     background-color: rgba(255,255,255,0.1);
     text-shadow: black 0px 2px 2px;
     transition-duration: 100;
@@ -932,12 +954,14 @@ StScrollBar StButton#vhandle:active {
 }
 
 .app-well-app:focus > .overview-icon,
-.search-result-content:focus > .overview-icon,
+.grid-search-result:focus .overview-icon,
 .show-apps:focus > .overview-icon,
 .search-provider-icon:focus,
+.list-search-result:focus,
 .app-well-app:selected > .overview-icon,
-.search-result-content:selected > .overview-icon,
-.search-provider-icon:selected {
+.grid-search-result:selected .overview-icon,
+.search-provider-icon:selected,
+.list-search-result:selected {
     background-color: rgba(255,255,255,0.33);
 }
 
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 754864d..ac2860a 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -403,12 +403,6 @@ const SettingsSearchProvider = new Lang.Class({
         pref.activate();
     },
 
-    createResultActor: function (resultMeta, terms) {
-        let app = resultMeta['id'];
-        let icon = new AppWellIcon(app);
-        return icon.actor;
-    },
-
     launchSearch: function(terms) {
         // FIXME: this should be a remote search provider
         this.appInfo.launch([], global.create_app_launch_context());
diff --git a/js/ui/search.js b/js/ui/search.js
index 6bbbdab..827e34d 100644
--- a/js/ui/search.js
+++ b/js/ui/search.js
@@ -154,9 +154,6 @@ const SearchProvider = new Lang.Class({
      * Search providers may optionally override this to render a
      * particular serch result in a custom fashion.  The default
      * implementation will show the icon next to the name.
-     *
-     * The actor should be an instance of St.Widget, with the style class
-     * 'search-result-content'.
      */
     createResultActor: function(resultMeta, terms) {
         return null;
diff --git a/js/ui/searchDisplay.js b/js/ui/searchDisplay.js
index 6c0ecc1..4db34c5 100644
--- a/js/ui/searchDisplay.js
+++ b/js/ui/searchDisplay.js
@@ -11,10 +11,11 @@ const DND = imports.ui.dnd;
 const IconGrid = imports.ui.iconGrid;
 const Main = imports.ui.main;
 const Overview = imports.ui.overview;
+const Separator = imports.ui.separator;
 const Search = imports.ui.search;
 
-const MAX_SEARCH_RESULTS_ROWS = 1;
-
+const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
+const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 
 const SearchResult = new Lang.Class({
     Name: 'SearchResult',
@@ -23,21 +24,91 @@ const SearchResult = new Lang.Class({
         this.provider = provider;
         this.metaInfo = metaInfo;
         this.terms = terms;
-        this.actor = new St.Button({ style_class: 'search-result',
-                                     reactive: true,
+
+        this.actor = new St.Button({ reactive: true,
+                                     can_focus: true,
+                                     track_hover: true,
                                      x_align: St.Align.START,
                                      y_fill: true });
+
         this.actor._delegate = this;
+        this.actor.connect('clicked', Lang.bind(this, this.activate));
+    },
+
+    activate: function() {
+        this.provider.activateResult(this.metaInfo.id, this.terms);
+        Main.overview.toggle();
+    },
+
+    setSelected: function(selected) {
+        if (selected)
+            this.actor.add_style_pseudo_class('selected');
+        else
+            this.actor.remove_style_pseudo_class('selected');
+    }
+});
+
+const ListSearchResult = new Lang.Class({
+    Name: 'ListSearchResult',
+    Extends: SearchResult,
+
+    ICON_SIZE: 64,
+
+    _init: function(provider, metaInfo, terms) {
+        this.parent(provider, metaInfo, terms);
+
+        this.actor.style_class = 'list-search-result';
+        this.actor.x_fill = true;
+
+        let content = new St.BoxLayout({ style_class: 'list-search-result-content',
+                                         vertical: false });
+        this.actor.set_child(content);
+
+        // An icon for, or thumbnail of, content
+        let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
+        if (icon) {
+            content.add(icon);
+        }
+
+        let details = new St.BoxLayout({ vertical: true });
+        content.add(details, { x_fill: true,
+                               y_fill: false,
+                               x_align: St.Align.START,
+                               y_align: St.Align.MIDDLE });
+
+        let title = new St.Label({ style_class: 'list-search-result-title',
+                                   text: this.metaInfo['name'] })
+        details.add(title, { x_fill: false,
+                             y_fill: false,
+                             x_align: St.Align.START,
+                             y_align: St.Align.START });
+
+        // TODO: should highlight terms in the description here
+        if (this.metaInfo['description']) {
+            let description = new St.Label({ style_class: 'list-search-result-description',
+                                             text: '"' + this.metaInfo['description'] + '"' });
+            details.add(description, { x_fill: false,
+                                       y_fill: false,
+                                       x_align: St.Align.START,
+                                       y_align: St.Align.END });
+        }
+    }
+});
+
+const GridSearchResult = new Lang.Class({
+    Name: 'GridSearchResult',
+    Extends: SearchResult,
+
+    _init: function(provider, metaInfo, terms) {
+        this.parent(provider, metaInfo, terms);
+
+        this.actor.style_class = 'grid-search-result';
 
         let content = provider.createResultActor(metaInfo, terms);
         let dragSource = null;
 
         if (content == null) {
-            content = new St.Bin({ style_class: 'search-result-content',
-                                   reactive: true,
-                                   can_focus: true,
-                                   track_hover: true,
-                                   accessible_role: Atk.Role.PUSH_BUTTON });
+            content = new St.Bin();
             let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
                                              { createIcon: this.metaInfo['createIcon'] });
             content.set_child(icon.actor);
@@ -47,10 +118,8 @@ const SearchResult = new Lang.Class({
             if (content._delegate && content._delegate.getDragActorSource)
                 dragSource = content._delegate.getDragActorSource();
         }
-        this._content = content;
-        this.actor.set_child(content);
 
-        this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
+        this.actor.set_child(content);
 
         let draggable = DND.makeDraggable(this.actor);
         draggable.connect('drag-begin',
@@ -72,22 +141,6 @@ const SearchResult = new Lang.Class({
         this._dragActorSource = dragSource;
     },
 
-    setSelected: function(selected) {
-        if (selected)
-            this._content.add_style_pseudo_class('selected');
-        else
-            this._content.remove_style_pseudo_class('selected');
-    },
-
-    activate: function() {
-        this.provider.activateResult(this.metaInfo.id, this.terms);
-        Main.overview.toggle();
-    },
-
-    _onResultClicked: function(actor) {
-        this.activate();
-    },
-
     getDragActorSource: function() {
         return this._dragActorSource;
     },
@@ -104,6 +157,77 @@ const SearchResult = new Lang.Class({
     }
 });
 
+const ListSearchResults = new Lang.Class({
+    Name: 'ListSearchResults',
+    Extends: Search.SearchResultDisplay,
+
+    _init: function(provider) {
+        this.parent(provider);
+
+        this.actor = new St.BoxLayout({ style_class: 'search-section-content' });
+        this.providerIcon = new ProviderIcon(provider);
+        this.providerIcon.connect('clicked', Lang.bind(this,
+            function() {
+                provider.launchSearch(this._terms);
+                Main.overview.toggle();
+            }));
+
+        this.actor.add(this.providerIcon, { x_fill: false,
+                                            y_fill: false,
+                                            x_align: St.Align.START,
+                                            y_align: St.Align.START });
+
+        this._content = new St.BoxLayout({ style_class: 'list-search-results',
+                                           vertical: true });
+        this.actor.add_actor(this._content);
+
+        this._notDisplayedResult = [];
+        this._terms = [];
+        this._pendingClear = false;
+    },
+
+    getResultsForDisplay: function() {
+        let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
+        let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
+
+        let newResults = this._notDisplayedResult.splice(0, canDisplay);
+        return newResults;
+    },
+
+    getVisibleResultCount: function() {
+        return this._content.get_n_children();
+    },
+
+    hasMoreResults: function() {
+        return this._notDisplayedResult.length > 0;
+    },
+
+    setResults: function(results, terms) {
+        // copy the lists
+        this._notDisplayedResult = results.slice(0);
+        this._terms = terms.slice(0);
+        this._pendingClear = true;
+    },
+
+    renderResults: function(metas) {
+        for (let i = 0; i < metas.length; i++) {
+            let display = new ListSearchResult(this.provider, metas[i], this._terms);
+            this._content.add_actor(display.actor);
+        }
+    },
+
+    clear: function () {
+        this._content.destroy_all_children();
+        this._pendingClear = false;
+    },
+
+    getFirstResult: function() {
+        if (this.getVisibleResultCount() > 0)
+            return this._content.get_child_at_index(0)._delegate;
+        else
+            return null;
+    }
+});
 
 const GridSearchResults = new Lang.Class({
     Name: 'GridSearchResults',
@@ -112,22 +236,12 @@ const GridSearchResults = new Lang.Class({
     _init: function(provider, grid) {
         this.parent(provider);
 
-        this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
+        this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
                                                      xAlign: St.Align.START });
-        this.actor = new St.Bin({ x_align: St.Align.START });
+        this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
 
         this.actor.set_child(this._grid.actor);
-        this._width = 0;
-        this.actor.connect('notify::width', Lang.bind(this, function() {
-            this._width = this.actor.width;
-            Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
-                let results = this.getResultsForDisplay();
-                if (results.length == 0)
-                    return;
-
-                provider.getResultMetas(results, Lang.bind(this, this.renderResults));
-            }));
-        }));
+
         this._notDisplayedResult = [];
         this._terms = [];
         this._pendingClear = false;
@@ -135,12 +249,11 @@ const GridSearchResults = new Lang.Class({
 
     getResultsForDisplay: function() {
         let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
-        let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
+        let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
                          - alreadyVisible;
 
-        let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
-
-        return this._notDisplayedResult.splice(0, numResults);
+        let newResults = this._notDisplayedResult.splice(0, canDisplay);
+        return newResults;
     },
 
     getVisibleResultCount: function() {
@@ -160,7 +273,7 @@ const GridSearchResults = new Lang.Class({
 
     renderResults: function(metas) {
         for (let i = 0; i < metas.length; i++) {
-            let display = new SearchResult(this.provider, metas[i], this._terms);
+            let display = new GridSearchResult(this.provider, metas[i], this._terms);
             this._grid.addItem(display.actor);
         }
     },
@@ -229,29 +342,25 @@ const SearchResults = new Lang.Class({
     },
 
     createProviderMeta: function(provider) {
-        let providerBox = new St.BoxLayout({ style_class: 'search-section' });
+        let providerBox = new St.BoxLayout({ style_class: 'search-section',
+                                             vertical: true });
         let providerIcon = null;
+        let resultDisplay = null;
 
         if (provider.appInfo) {
-            providerIcon = new ProviderIcon(provider);
-            providerIcon.connect('clicked', Lang.bind(this,
-                function() {
-                    provider.launchSearch(this._searchSystem.getTerms());
-                    Main.overview.toggle();
-                }));
-
-            providerBox.add(providerIcon, { x_fill: false,
-                                            y_fill: false,
-                                            x_align: St.Align.START,
-                                            y_align: St.Align.START });
+            resultDisplay = new ListSearchResults(provider);
+            providerIcon = resultDisplay.providerIcon;
+        } else {
+            resultDisplay = new GridSearchResults(provider);
         }
 
-        let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
+        let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
                                             x_fill: true,
                                             y_fill: true });
         providerBox.add(resultDisplayBin, { expand: true });
-        let resultDisplay = new GridSearchResults(provider);
-        resultDisplayBin.set_child(resultDisplay.actor);
+
+        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
+        providerBox.add(separator.actor);
 
         this._providerMeta.push({ provider: provider,
                                   actor: providerBox,
@@ -412,11 +521,6 @@ const SearchResults = new Lang.Class({
 
         let from = this._defaultResult ? this._defaultResult.actor : null;
         this.actor.navigate_focus(from, direction, false);
-        if (this._defaultResult) {
-            // The default result appears focused, so navigate directly to the
-            // next result.
-            this.actor.navigate_focus(global.stage.key_focus, direction, false);
-        }
     }
 });
 
diff --git a/js/ui/wanda.js b/js/ui/wanda.js
index 846098b..692efd4 100644
--- a/js/ui/wanda.js
+++ b/js/ui/wanda.js
@@ -71,8 +71,7 @@ const WandaIconBin = new Lang.Class({
     Name: 'WandaIconBin',
 
     _init: function(fish, label, params) {
-        this.actor = new St.Bin({ style_class: 'search-result-content',
-                                  reactive: true,
+        this.actor = new St.Bin({ reactive: true,
                                   track_hover: true });
         this.icon = new WandaIcon(fish, label, params);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]