[gnome-shell/overlay-design02] Replace main AppDisplay with AppWell



commit e57b7ec3359ce39e4fa360c744883fb37a069a49
Author: Colin Walters <walters verbum org>
Date:   Thu Jun 25 17:45:06 2009 -0400

    Replace main AppDisplay with AppWell
    
    This is a start towards implementing the 02 overlay design.  The
    default applications has moved into GConf.  We keep around an AppDisplay
    instance for handling the right side behavior.

 js/misc/appInfo.js  |   68 +++++++++---------
 js/ui/appDisplay.js |  197 +++++++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/overlay.js    |   58 ++--------------
 3 files changed, 235 insertions(+), 88 deletions(-)
---
diff --git a/js/misc/appInfo.js b/js/misc/appInfo.js
index 32f9bd0..61b36bd 100644
--- a/js/misc/appInfo.js
+++ b/js/misc/appInfo.js
@@ -7,32 +7,6 @@ const Shell = imports.gi.Shell;
 
 const Main = imports.ui.main;
 
-// TODO - move this into GConf once we're not a plugin anymore
-// but have taken over metacity
-// This list is taken from GNOME Online popular applications
-// http://online.gnome.org/applications
-// but with nautilus removed (since it should already be running)
-// and evince, totem, and gnome-file-roller removed (since they're
-// usually started by opening documents, not by opening the app
-// directly)
-const DEFAULT_APPLICATIONS = [
-    'mozilla-firefox.desktop',
-    'gnome-terminal.desktop',
-    'evolution.desktop',
-    'gedit.desktop',
-    'mozilla-thunderbird.desktop',
-    'rhythmbox.desktop',
-    'epiphany.desktop',
-    'xchat.desktop',
-    'openoffice.org-1.9-writer.desktop',
-    'emacs.desktop',
-    'gnome-system-monitor.desktop',
-    'openoffice.org-1.9-calc.desktop',
-    'eclipse.desktop',
-    'openoffice.org-1.9-impress.desktop',
-    'vncviewer.desktop'
-];
-
 function AppInfo(appId) {
     this._init(appId);
 }
@@ -41,8 +15,9 @@ AppInfo.prototype = {
     _init : function(appId) {
         this.appId = appId;
         this._gAppInfo = Gio.DesktopAppInfo.new(appId);
-        if (!this._gAppInfo)
+        if (!this._gAppInfo) {
             throw new Error('Unknown appId ' + appId);
+        }
 
         this.id = this._gAppInfo.get_id();
         this.name = this._gAppInfo.get_name();
@@ -120,16 +95,39 @@ function getMostUsedApps(count) {
         }
     }
 
+    let favs = getFavorites();
     // Fill the list with default applications it's not full yet
-    for (let i = 0; i < DEFAULT_APPLICATIONS.length && matches.length <= count; i++) {
-        let appId = DEFAULT_APPLICATIONS[i];
-        if (alreadyAdded[appId])
-            continue;
-
-        let appInfo = getAppInfo(appId);
-        if (appInfo)
-            matches.push(appInfo);
+    for (let i = 0; i < favs.length && favs.length <= count; i++) {
+        matches.push(favs[i]);
     }
 
     return matches;
 }
+
+function _idListToInfos(ids) {
+    let infos = [];
+    for (let i = 0; i < ids.length; i++) {
+        let display = getAppInfo(ids[i]);
+        if (display == null)
+            continue;
+        infos.push(display);
+    }
+    return infos;
+}
+
+function getFavorites() {
+    let system = Shell.AppSystem.get_default();
+
+    return _idListToInfos(system.get_favorites());
+}
+
+function getRunning() {
+    let monitor = Shell.AppMonitor.get_default();
+    let basename = function (n) {
+        let i = n.lastIndexOf('/');
+        if (i < 0)
+            return n;
+        return n.substring(i+1);
+    }
+    return _idListToInfos(monitor.get_running_app_ids().map(basename));
+}
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 1f2a6b7..3729b14 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -5,16 +5,21 @@ const Clutter = imports.gi.Clutter;
 const Pango = imports.gi.Pango;
 const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
+const Tidy = imports.gi.Tidy;
 const Shell = imports.gi.Shell;
 const Lang = imports.lang;
 const Signals = imports.signals;
+const Mainloop = imports.mainloop;
 
 const AppInfo = imports.misc.appInfo;
+const DND = imports.ui.dnd;
 const GenericDisplay = imports.ui.genericDisplay;
 
 const ENTERED_MENU_COLOR = new Clutter.Color();
 ENTERED_MENU_COLOR.from_pixel(0x00ff0022);
 
+const APP_ICON_SIZE = 48;
+
 const MENU_ICON_SIZE = 24;
 const MENU_SPACING = 15;
 
@@ -147,6 +152,7 @@ MenuItem.prototype = {
 }
 Signals.addSignalMethods(MenuItem.prototype);
 
+
 /* This class represents a display containing a collection of application items.
  * The applications are sorted based on their popularity by default, and based on
  * their name if some search filter is applied.
@@ -428,3 +434,194 @@ AppDisplay.prototype = {
 };
 
 Signals.addSignalMethods(AppDisplay.prototype);
+
+function WellDisplayItem(appInfo, isFavorite) {
+    this._init(appInfo, isFavorite);
+}
+
+WellDisplayItem.prototype = {
+    _init : function(appInfo, isFavorite) {
+        this.appInfo = appInfo;
+
+        this.isFavorite = isFavorite;
+
+        this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+                                   width: APP_ICON_SIZE,
+                                   reactive: true });
+        this.actor._delegate = this;
+        this.actor.connect('button-release-event', Lang.bind(this, function (b, e) {
+            this.launch();
+            this.emit('activated');
+        }));
+
+        let draggable = DND.makeDraggable(this.actor);
+
+        this._icon = appInfo.getIcon(APP_ICON_SIZE);
+
+        this.actor.append(this._icon, Big.BoxPackFlags.NONE);
+
+        let count = Shell.AppMonitor.get_default().get_window_count(appInfo.appId);
+
+        this._name = new Clutter.Text({ color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+                                        font_name: "Sans 12px",
+                                        ellipsize: Pango.EllipsizeMode.END,
+                                        text: appInfo.name });
+        if (count > 0) {
+            let runningBox = new Big.Box({ /* border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+                                           border: 1,
+                                           padding: 1 */ });
+            runningBox.append(this._name, Big.BoxPackFlags.EXPAND);
+            this.actor.append(runningBox, Big.BoxPackFlags.NONE);
+        } else {
+            this.actor.append(this._name, Big.BoxPackFlags.NONE);
+        }
+    },
+
+    // Opens an application represented by this display item.
+    launch : function() {
+        this.appInfo.launch();
+    },
+
+    // Draggable interface - FIXME deduplicate with GenericDisplay
+    getDragActor: function(stageX, stageY) {
+        this.dragActor = new Clutter.Clone({ source: this._icon });
+        [this.dragActor.width, this.dragActor.height] = this._icon.get_transformed_size();
+
+        // If the user dragged from the icon itself, then position
+        // the dragActor over the original icon. Otherwise center it
+        // around the pointer
+        let [iconX, iconY] = this._icon.get_transformed_position();
+        let [iconWidth, iconHeight] = this._icon.get_transformed_size();
+        if (stageX > iconX && stageX <= iconX + iconWidth &&
+            stageY > iconY && stageY <= iconY + iconHeight)
+            this.dragActor.set_position(iconX, iconY);
+        else
+            this.dragActor.set_position(stageX - this.dragActor.width / 2, stageY - this.dragActor.height / 2);
+        return this.dragActor;
+    },
+
+    // Returns the original icon that is being used as a source for the cloned texture
+    // that represents the item as it is being dragged.
+    getDragActorSource: function() {
+        return this._icon;
+    }
+};
+
+Signals.addSignalMethods(WellDisplayItem.prototype);
+
+function WellArea(width, isFavorite) {
+    this._init(width, isFavorite);
+}
+
+WellArea.prototype = {
+    _init : function(width, isFavorite) {
+        this.isFavorite = isFavorite;
+
+        this.actor = new Tidy.Grid({ width: width });
+        this.actor._delegate = this;
+    },
+
+    redisplay: function (infos) {
+        let children;
+
+        children = this.actor.get_children();
+        children.forEach(Lang.bind(this, function (v) {
+            this.actor.remove_actor(v);
+            v.destroy();
+        }));
+
+        for (let i = 0; i < infos.length; i++) {
+            let display = new WellDisplayItem(infos[i], this.isFavorite);
+            display.connect('activated', Lang.bind(this, function (display) {
+                this.emit('activated', display);
+            }));
+            this.actor.add_actor(display.actor);
+        };
+    },
+
+    // Draggable target interface
+    acceptDrop : function(source, actor, x, y, time) {
+        let global = Shell.Global.get();
+
+        if (!(source instanceof WellDisplayItem)) {
+            return false;
+        }
+
+        let appSystem = Shell.AppSystem.get_default();
+        let id = source.appInfo.appId;
+        if (source.isFavorite && (!this.isFavorite)) {
+            Mainloop.idle_add(function () {
+                appSystem.remove_favorite(id);
+            });
+        } else if ((!source.isFavorite) && this.isFavorite) {
+            Mainloop.idle_add(function () {
+                appSystem.add_favorite(id);
+            });
+        }
+
+        return true;
+    }
+}
+
+Signals.addSignalMethods(WellArea.prototype);
+
+function AppWell(width) {
+    this._init(width);
+}
+
+AppWell.prototype = {
+    _init : function(width) {
+        this._menus = [];
+        this._menuDisplays = [];
+
+        this.actor = new Big.Box({ orientation: Big.BoxOrientation.VERTICAL,
+                                   width: width });
+
+        this._appSystem = Shell.AppSystem.get_default();
+        this._appMonitor = Shell.AppMonitor.get_default();
+
+        this._appSystem.connect('changed', Lang.bind(this, function(appSys) {
+            this._redisplay();
+        }));
+        this._appMonitor.connect('changed', Lang.bind(this, function(monitor) {
+            this._redisplay();
+        }));
+
+        this._favoritesArea = new WellArea(width, true);
+        this._favoritesArea.connect('activated', Lang.bind(this, function (a, display) {
+            this.emit('activated');
+        }));
+        this.actor.append(this._favoritesArea.actor, Big.BoxPackFlags.NONE);
+
+        this._runningBox = new Big.Box({ border_color: GenericDisplay.ITEM_DISPLAY_NAME_COLOR,
+                                         border: 1,
+                                         corner_radius: 3,
+                                         padding: GenericDisplay.PREVIEW_BOX_PADDING });
+        this._runningArea = new WellArea(width, false);
+        this._runningArea.connect('activated', Lang.bind(this, function (a, display) {
+            this.emit('activated');
+        }));
+        this._runningBox.append(this._runningArea.actor, Big.BoxPackFlags.EXPAND);
+        this.actor.append(this._runningBox, Big.BoxPackFlags.NONE);
+
+        this._redisplay();
+    },
+
+    _redisplay: function() {
+        let arrayToObject = function(a) {
+            let o = {};
+            for (let i = 0; i < a.length; i++)
+                o[a[i]] = 1;
+            return o;
+        };
+        let favorites = AppInfo.getFavorites();
+        let favoriteIds = arrayToObject(favorites.map(function (e) { return e.appId; }));
+        let running = AppInfo.getRunning().filter(function (e) {
+            return !(e.appId in favoriteIds);
+        });
+        this._favoritesArea.redisplay(favorites);
+        this._runningArea.redisplay(running);
+    }
+};
+
+Signals.addSignalMethods(AppWell.prototype);
diff --git a/js/ui/overlay.js b/js/ui/overlay.js
index fdfda5a..03078ff 100644
--- a/js/ui/overlay.js
+++ b/js/ui/overlay.js
@@ -283,7 +283,6 @@ Dash.prototype = {
         this._searchEntry.entry.connect('activate', function (se) {
             // only one of the displays will have an item selected, so it's ok to
             // call activateSelected() on all of them
-            me._appDisplay.activateSelected();
             me._docDisplay.activateSelected();
             me._resultsAppsSection.display.activateSelected();
             me._resultsDocsSection.display.activateSelected();
@@ -329,11 +328,9 @@ Dash.prototype = {
         
         this._appsContent = new Big.Box({ orientation: Big.BoxOrientation.HORIZONTAL });
         this._appsSection.append(this._appsContent, Big.BoxPackFlags.EXPAND);
-        this._appDisplay = new AppDisplay.AppDisplay(this._displayWidth, this._itemDisplayHeight / 2, DASH_COLUMNS, DASH_SECTION_PADDING);
-        let sideArea = this._appDisplay.getSideArea();
-        sideArea.hide();
-        this._appsContent.append(sideArea, Big.BoxPackFlags.NONE);
-        this._appsContent.append(this._appDisplay.actor, Big.BoxPackFlags.EXPAND);
+        this._appWell = new AppDisplay.AppWell(this._displayWidth);
+        this._appWell.actor.show();
+        this._appsContent.append(this._appWell.actor, Big.BoxPackFlags.EXPAND);
 
         let moreAppsBox = new Big.Box({x_align: Big.BoxAlignment.END});
         this._moreAppsLink = new Link.Link({ color: DASH_TEXT_COLOR,
@@ -444,13 +441,12 @@ Dash.prototype = {
         let itemDetailsAvailableWidth = this._detailsWidth - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
         let itemDetailsAvailableHeight = detailsHeight - DASH_SECTION_PADDING * 2 - DASH_BORDER_WIDTH * 2;
 
-        this._appDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
         this._docDisplay.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
         this._resultsAppsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
         this._resultsDocsSection.display.setAvailableDimensionsForItemDetails(itemDetailsAvailableWidth, itemDetailsAvailableHeight);
 
         /* Proxy the activated signals */
-        this._appDisplay.connect('activated', function(appDisplay) {
+        this._appWell.connect('activated', function(well) {
             me.emit('activated');
         });
         this._docDisplay.connect('activated', function(docDisplay) {
@@ -462,27 +458,7 @@ Dash.prototype = {
         this._resultsDocsSection.display.connect('activated', function(resultsDocsDisplay) {
             me.emit('activated');
         });
-        this._appDisplay.connect('selected', function(appDisplay) {
-            // We allow clicking on any item to select it, so if an 
-            // item in the app display is selected, we need to make sure that
-            // no item in the doc display has the selection.
-            me._docDisplay.unsetSelected();
-            me._resultsDocsSection.display.unsetSelected();
-            me._resultsAppsSection.display.unsetSelected();
-            if (me._firstSelectAfterOverlayShow) {
-                me._firstSelectAfterOverlayShow = false;
-            } else if (!me._detailsShowing()) { 
-                me._detailsPane.show();
-                me.emit('panes-displayed');
-            }
-            me._detailsContent.remove_all();
-            me._detailsContent.append(me._appDisplay.selectedItemDetails, Big.BoxPackFlags.NONE); 
-        });
         this._docDisplay.connect('selected', function(docDisplay) {
-            // We allow clicking on any item to select it, so if an 
-            // item in the doc display is selected, we need to make sure that
-            // no item in the app display has the selection.
-            me._appDisplay.unsetSelected(); 
             me._resultsDocsSection.display.unsetSelected();
             me._resultsAppsSection.display.unsetSelected();
             if (!me._detailsShowing()) { 
@@ -493,7 +469,6 @@ Dash.prototype = {
             me._detailsContent.append(me._docDisplay.selectedItemDetails, Big.BoxPackFlags.NONE); 
         });
         this._resultsDocsSection.display.connect('selected', function(resultsDocDisplay) {
-            me._appDisplay.unsetSelected(); 
             me._docDisplay.unsetSelected();
             me._resultsAppsSection.display.unsetSelected();
             if (!me._detailsShowing()) { 
@@ -504,7 +479,6 @@ Dash.prototype = {
             me._detailsContent.append(me._resultsDocsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
         });
         this._resultsAppsSection.display.connect('selected', function(resultsAppDisplay) {
-            me._appDisplay.unsetSelected(); 
             me._docDisplay.unsetSelected();
             me._resultsDocsSection.display.unsetSelected();
             if (!me._detailsShowing()) { 
@@ -514,12 +488,6 @@ Dash.prototype = {
             me._detailsContent.remove_all();
             me._detailsContent.append(me._resultsAppsSection.display.selectedItemDetails, Big.BoxPackFlags.NONE);
         });
-        this._appDisplay.connect('redisplayed', function(appDisplay) {
-            me._ensureItemSelected();
-        });
-        this._docDisplay.connect('redisplayed', function(docDisplay) {
-            me._ensureItemSelected();
-        });
 
         this._moreAppsLink.connect('clicked',
             function(o, event) {
@@ -543,7 +511,6 @@ Dash.prototype = {
     show: function() {
         let global = Shell.Global.get();
 
-        this._appDisplay.show();
         this._appsContent.show();
         this._docDisplay.show();
         global.stage.set_key_focus(this._searchEntry.entry);
@@ -567,22 +534,6 @@ Dash.prototype = {
         this._unsetSearchMode();
     },
 
-    // Ensures that one of the displays has the selection if neither owns it after the
-    // latest redisplay. This can be applicable if the display that earlier had the
-    // selection no longer has any items, or if their is a single section being shown 
-    // in the expanded view and it went from having no matching items to having some.
-    // We first try to place the selection in the applications section, because it is
-    // displayed above the documents section.
-    _ensureItemSelected: function() { 
-        if (!this._appDisplay.hasSelected() && !this._docDisplay.hasSelected()) {
-            if (this._appDisplay.hasItems()) { 
-                this._appDisplay.selectFirstItem();
-            } else if (this._docDisplay.hasItems()) {
-                this._docDisplay.selectFirstItem();
-            }
-        }
-    },
-
     // Sets the 'More' mode for browsing applications.
     _setMoreAppsMode: function() {
         if (this._moreAppsMode)
@@ -811,6 +762,7 @@ Overlay.prototype = {
     // the item on any workspace.
     handleDragOver : function(source, actor, x, y, time) {
         if (source instanceof GenericDisplay.GenericDisplayItem) {
+            log("unsetting more mode");
             this._dash.unsetMoreMode();
             return true;
         }



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