[gnome-shell] dash: Improve DND to dash and allow reordering



commit b59daac6f307360cecf2a1f52428cd74b363e80a
Author: Florian Müllner <fmuellner gnome org>
Date:   Mon Nov 8 02:51:02 2010 +0100

    dash: Improve DND to dash and allow reordering
    
    Show a positional indicator where a new favorite will be added and
    make the favorites re-orderable. Also allow the removal of favorites
    using drag-and-drop according to the mockups.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=634948

 data/Makefile.am                |    1 +
 data/theme/dash-placeholder.svg |   84 ++++++++++++++++
 data/theme/gnome-shell.css      |   16 +++
 js/ui/appFavorites.js           |   25 ++++-
 js/ui/dash.js                   |  207 ++++++++++++++++++++++++++++++++++++---
 js/ui/overview.js               |    2 +
 6 files changed, 318 insertions(+), 17 deletions(-)
---
diff --git a/data/Makefile.am b/data/Makefile.am
index e17b0e7..8c04198 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -25,6 +25,7 @@ dist_theme_DATA =				\
 	theme/close-window.svg			\
 	theme/close.svg				\
 	theme/corner-ripple.png			\
+	theme/dash-placeholder.svg		\
 	theme/dialog-error.svg			\
 	theme/gnome-shell.css			\
 	theme/mosaic-view-active.svg		\
diff --git a/data/theme/dash-placeholder.svg b/data/theme/dash-placeholder.svg
new file mode 100644
index 0000000..cbae148
--- /dev/null
+++ b/data/theme/dash-placeholder.svg
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   width="76"
+   height="27"
+   id="svg11252"
+   version="1.1">
+  <defs
+     id="defs11254">
+    <radialGradient
+       xlink:href="#linearGradient39563-4-2"
+       id="radialGradient68155-2-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.3486842,0,317.8421)"
+       cx="49"
+       cy="488"
+       fx="49"
+       fy="488"
+       r="38" />
+    <linearGradient
+       id="linearGradient39563-4-2">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop39565-1-4" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop39567-7-9" />
+    </linearGradient>
+    <radialGradient
+       xlink:href="#linearGradient39573-6-1"
+       id="radialGradient68157-0-8"
+       gradientUnits="userSpaceOnUse"
+       cx="50.5"
+       cy="487.5"
+       fx="50.5"
+       fy="487.5"
+       r="10.5" />
+    <linearGradient
+       id="linearGradient39573-6-1">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop39575-5-6" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0;"
+         offset="1"
+         id="stop39577-1-2" />
+    </linearGradient>
+  </defs>
+  <g
+     id="layer1"
+     transform="translate(-337,-518.86218)">
+    <g
+       id="g99967"
+       style="display:inline"
+       transform="translate(326,44.862171)">
+      <rect
+         style="opacity:0.49375;color:#000000;fill:url(#radialGradient68155-2-3);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect99969"
+         width="76"
+         height="2"
+         x="11"
+         y="487"
+         rx="0"
+         ry="0" />
+      <path
+         style="opacity:0.43125;color:#000000;fill:url(#radialGradient68157-0-8);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="path99971"
+         d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
+         transform="matrix(1.2857143,0,0,1.2857143,-14.428572,-139.28571)" />
+      <path
+         transform="matrix(0.43589747,0,0,0.43589747,28.487179,275)"
+         d="M 61,487.5 C 61,493.29899 56.29899,498 50.5,498 44.70101,498 40,493.29899 40,487.5 40,481.70101 44.70101,477 50.5,477 c 5.79899,0 10.5,4.70101 10.5,10.5 z"
+         id="path99973"
+         style="color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+  </g>
+</svg>
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 785b290..e5ab750 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -341,6 +341,11 @@ StTooltip StLabel {
     width: 60px;
 }
 
+.dash-placeholder {
+    background-image: url("dash-placeholder.svg");
+    height: 27px;
+}
+
 #viewSelector {
     spacing: 16px;
 }
@@ -502,7 +507,17 @@ StTooltip StLabel {
     padding: 6px 12px;
 }
 
+.remove-favorite-icon {
+    color: #a0a0a0;
+}
+
+.remove-favorite-icon:hover {
+    color: white;
+    -st-shadow: black 0px 2px 2px;
+}
+
 .app-well-app > .overview-icon,
+.remove-favorite > .overview-icon,
 .search-result-content > .overview-icon {
     border-radius: 4px;
     padding: 4px;
@@ -523,6 +538,7 @@ StTooltip StLabel {
 }
 
 .app-well-app:hover > .overview-icon,
+.remove-favorite:hover > .overview-icon,
 .search-result-content:hover > .overview-icon {
     background: rgba(255,255,255,0.33);
     text-shadow: black 0px 2px 2px;
diff --git a/js/ui/appFavorites.js b/js/ui/appFavorites.js
index 9c050ee..a3e83c4 100644
--- a/js/ui/appFavorites.js
+++ b/js/ui/appFavorites.js
@@ -63,7 +63,7 @@ AppFavorites.prototype = {
         return appId in this._favorites;
     },
 
-    _addFavorite: function(appId) {
+    _addFavorite: function(appId, pos) {
         if (appId in this._favorites)
             return false;
 
@@ -73,14 +73,17 @@ AppFavorites.prototype = {
             return false;
 
         let ids = this._getIds();
-        ids.push(appId);
+        if (pos == -1)
+            ids.push(appId);
+        else
+            ids.splice(pos, 0, appId);
         global.settings.set_strv(this.FAVORITE_APPS_KEY, ids);
         this._favorites[appId] = app;
         return true;
     },
 
-    addFavorite: function(appId) {
-        if (!this._addFavorite(appId))
+    addFavoriteAtPos: function(appId, pos) {
+        if (!this._addFavorite(appId, pos))
             return;
 
         let app = Shell.AppSystem.get_default().get_app(appId);
@@ -90,6 +93,15 @@ AppFavorites.prototype = {
         }));
     },
 
+    addFavorite: function(appId) {
+        this.addFavoriteAtPos(appId, -1);
+    },
+
+    moveFavoriteToPos: function(appId, pos) {
+        this._removeFavorite(appId);
+        this._addFavorite(appId, pos);
+    },
+
     _removeFavorite: function(appId) {
         if (!appId in this._favorites)
             return false;
@@ -100,13 +112,16 @@ AppFavorites.prototype = {
     },
 
     removeFavorite: function(appId) {
+        let ids = this._getIds();
+        let pos = ids.indexOf(appId);
+
         let app = this._favorites[appId];
         if (!this._removeFavorite(appId))
             return;
 
         Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
                                          Lang.bind(this, function () {
-            this._addFavorite(appId);
+            this._addFavorite(appId, pos);
         }));
     }
 };
diff --git a/js/ui/dash.js b/js/ui/dash.js
index 1f0a534..39440cf 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -1,8 +1,8 @@
 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 
-const Mainloop = imports.mainloop;
 const Signals = imports.signals;
 const Lang = imports.lang;
+const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 const St = imports.gi.St;
 const Gettext = imports.gettext.domain('gnome-shell');
@@ -11,10 +11,67 @@ const _ = Gettext.gettext;
 const AppDisplay = imports.ui.appDisplay;
 const AppFavorites = imports.ui.appFavorites;
 const DND = imports.ui.dnd;
+const IconGrid = imports.ui.iconGrid;
 const Main = imports.ui.main;
 const Workspace = imports.ui.workspace;
 
 
+function RemoveFavoriteIcon() {
+    this._init();
+}
+
+RemoveFavoriteIcon.prototype = {
+    _init: function() {
+        this.actor = new St.Bin({ style_class: 'remove-favorite' });
+        this._iconActor = null;
+        this.icon = new IconGrid.BaseIcon(_("Remove"),
+                                           { setSizeManually: true,
+                                             createIcon: Lang.bind(this, this._createIcon) });
+        this.actor.set_child(this.icon.actor);
+        this.actor._delegate = this;
+    },
+
+    _createIcon: function(size) {
+        this._iconActor = new St.Icon({ icon_name: 'user-trash',
+                                        style_class: 'remove-favorite-icon',
+                                        icon_size: size });
+        return this._iconActor;
+    },
+
+    setHover: function(hovered) {
+        this.actor.set_hover(hovered);
+        if (this._iconActor)
+            this._iconActor.set_hover(hovered);
+    },
+
+    // Rely on the dragged item being a favorite
+    handleDragOver: function(source, actor, x, y, time) {
+        return DND.DragMotionResult.MOVE_DROP;
+    },
+
+    acceptDrop: function(source, actor, x, y, time) {
+        let app = null;
+        if (source instanceof AppDisplay.AppWellIcon) {
+            let appSystem = Shell.AppSystem.get_default();
+            app = appSystem.get_app(source.getId());
+        } else if (source instanceof Workspace.WindowClone) {
+            let tracker = Shell.WindowTracker.get_default();
+            app = tracker.get_window_app(source.metaWindow);
+        }
+
+        let id = app.get_id();
+
+        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
+            function () {
+                AppFavorites.getAppFavorites().removeFavorite(id);
+                return false;
+            }));
+
+        return true;
+    }
+};
+
+
 function Dash() {
     this._init();
 }
@@ -24,6 +81,11 @@ Dash.prototype = {
         this._menus = [];
         this._menuDisplays = [];
         this._maxHeight = -1;
+        this._iconSize = 48;
+
+        this._dragPlaceholder = null;
+        this._dragPlaceholderPos = -1;
+        this._favRemoveTarget = null;
 
         this._favorites = [];
 
@@ -50,6 +112,75 @@ Dash.prototype = {
         this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
     },
 
+    show: function() {
+        this._itemDragBeginId = Main.overview.connect('item-drag-begin',
+            Lang.bind(this, this._onDragBegin));
+        this._itemDragEndId = Main.overview.connect('item-drag-end',
+            Lang.bind(this, this._onDragEnd));
+        this._windowDragBeginId = Main.overview.connect('window-drag-begin',
+            Lang.bind(this, this._onDragBegin));
+        this._windowDragEndId = Main.overview.connect('window-drag-end',
+            Lang.bind(this, this._onDragEnd));
+    },
+
+    hide: function() {
+        Main.overview.disconnect(this._itemDragBeginId);
+        Main.overview.disconnect(this._itemDragEndId);
+        Main.overview.disconnect(this._windowDragBeginId);
+        Main.overview.disconnect(this._windowDragEndId);
+    },
+
+    _onDragBegin: function() {
+        this._dragMonitor = {
+            dragMotion: Lang.bind(this, this._onDragMotion)
+        };
+        DND.addDragMonitor(this._dragMonitor);
+    },
+
+    _onDragEnd: function() {
+        this._clearDragPlaceholder();
+        if (this._favRemoveTarget) {
+            this._favRemoveTarget.actor.destroy();
+            this._favRemoveTarget = null;
+        }
+        DND.removeMonitor(this._dragMonitor);
+    },
+
+    _onDragMotion: function(dragEvent) {
+        let app = null;
+        if (dragEvent.source instanceof AppDisplay.AppWellIcon)
+            app = this._appSystem.get_app(dragEvent.source.getId());
+        else if (dragEvent.source instanceof Workspace.WindowClone)
+            app = this._tracker.get_window_app(dragEvent.source.metaWindow);
+        else
+            return DND.DragMotionResult.CONTINUE;
+
+        let id = app.get_id();
+
+        let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
+
+        let srcIsFavorite = (id in favorites);
+
+        if (srcIsFavorite && this._favRemoveTarget == null) {
+                this._favRemoveTarget = new RemoveFavoriteIcon();
+                this._favRemoveTarget.icon.setIconSize(this._iconSize);
+                this._box.add(this._favRemoveTarget.actor);
+        }
+
+        let favRemoveHovered = false;
+        if (this._favRemoveTarget)
+            favRemoveHovered =
+                this._favRemoveTarget.actor.contains(dragEvent.targetActor);
+
+        if (!this._box.contains(dragEvent.targetActor) || favRemoveHovered)
+            this._clearDragPlaceholder();
+
+        if (this._favRemoveTarget)
+            this._favRemoveTarget.setHover(favRemoveHovered);
+
+        return DND.DragMotionResult.CONTINUE;
+    },
+
     _appIdListToHash: function(apps) {
         let ids = {};
         for (let i = 0; i < apps.length; i++)
@@ -61,6 +192,19 @@ Dash.prototype = {
         Main.queueDeferredWork(this._workId);
     },
 
+    _addApp: function(app) {
+        let display = new AppDisplay.AppWellIcon(app);
+        display._draggable.connect('drag-begin',
+                                   Lang.bind(this, function() {
+                                       display.actor.opacity = 50;
+                                   }));
+        display._draggable.connect('drag-end',
+                                   Lang.bind(this, function() {
+                                       display.actor.opacity = 255;
+                                   }));
+        this._box.add(display.actor);
+    },
+
     _redisplay: function () {
         this._box.hide();
         this._box.remove_all();
@@ -74,16 +218,14 @@ Dash.prototype = {
 
         for (let id in favorites) {
             let app = favorites[id];
-            let display = new AppDisplay.AppWellIcon(app);
-            this._box.add(display.actor);
+            this._addApp(app);
         }
 
         for (let i = 0; i < running.length; i++) {
             let app = running[i];
             if (app.get_id() in favorites)
                 continue;
-            let display = new AppDisplay.AppWellIcon(app);
-            this._box.add(display.actor);
+            this._addApp(app);
         }
 
         let children = this._box.get_children();
@@ -112,6 +254,14 @@ Dash.prototype = {
         this._box.show();
     },
 
+    _clearDragPlaceholder: function() {
+        if (this._dragPlaceholder) {
+            this._dragPlaceholder.destroy();
+            this._dragPlaceholder = null;
+            this._dragPlaceholderPos = -1;
+        }
+    },
+
     handleDragOver : function(source, actor, x, y, time) {
         let app = null;
         if (source instanceof AppDisplay.AppWellIcon)
@@ -123,6 +273,28 @@ Dash.prototype = {
         if (app == null || app.is_transient())
             return DND.DragMotionResult.NO_DROP;
 
+        let numFavorites = AppFavorites.getAppFavorites().getFavorites().length;
+        let numChildren = this._box.get_children().length;
+        let boxHeight = this._box.height;
+
+        // Keep the placeholder out of the index calculation; assuming that
+        // the remove target has the same size as "normal" items, we don't
+        // need to do the same adjustment there.
+        if (this._dragPlaceholder) {
+            boxHeight -= this._dragPlaceholder.height;
+            numChildren--;
+        }
+
+        let pos = Math.round(y * numChildren / boxHeight);
+
+        if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
+            this._dragPlaceholderPos = pos;
+            if (this._dragPlaceholder)
+                this._dragPlaceholder.destroy();
+            this._dragPlaceholder = new St.Bin({ style_class: 'dash-placeholder' });
+            this._box.insert_actor(this._dragPlaceholder, pos);
+        }
+
         let id = app.get_id();
 
         let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
@@ -130,7 +302,7 @@ Dash.prototype = {
         let srcIsFavorite = (id in favorites);
 
         if (srcIsFavorite)
-            return DND.DragMotionResult.NO_DROP;
+            return DND.DragMotionResult.MOVE_DROP;
 
         return DND.DragMotionResult.COPY_DROP;
     },
@@ -155,14 +327,25 @@ Dash.prototype = {
 
         let srcIsFavorite = (id in favorites);
 
-        if (srcIsFavorite) {
-            return false;
-        } else {
-            Mainloop.idle_add(Lang.bind(this, function () {
-                AppFavorites.getAppFavorites().addFavorite(id);
+        let favPos = 0;
+        let children = this._box.get_children();
+        for (let i = 0; i < this._dragPlaceholderPos; i++) {
+            let childId = children[i]._delegate.app.get_id();
+            if (childId == id)
+                continue;
+            if (childId in favorites)
+                favPos++;
+        }
+
+        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
+            function () {
+                let appFavorites = AppFavorites.getAppFavorites();
+                if (srcIsFavorite)
+                    appFavorites.moveFavoriteToPos(id, favPos);
+                else
+                    appFavorites.addFavoriteAtPos(id, favPos);
                 return false;
             }));
-        }
 
         return true;
     }
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 61c29c9..64c3035 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -302,6 +302,7 @@ Overview.prototype = {
 
         this.viewSelector.show();
         this._workspacesDisplay.show();
+        this._dash.show();
 
         this.workspaces = this._workspacesDisplay.workspacesView;
         this._group.add_actor(this.workspaces.actor);
@@ -434,6 +435,7 @@ Overview.prototype = {
 
         this._workspacesDisplay.hide();
         this.viewSelector.hide();
+        this._dash.hide();
 
         this._desktopFade.hide();
         this._background.hide();



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