[gnome-shell/gbsneto/icon-grid-part3: 10/12] appIcon: Make AppIcon a drop target



commit f8a10a7e84605390a40324f8811910aae523c950
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri Jul 12 19:32:54 2019 -0300

    appIcon: Make AppIcon a drop target
    
    Because the Dash icons are not drop targets themselves,
    add a tiny DashIcon class, which is an AppDisplay.AppIcon
    subclass, and disable all DND drop code from it.
    
    Show a folder preview when dragging an app icon over another
    app icon.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/671

 js/ui/appDisplay.js | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/dash.js       |  28 ++++++++++++--
 2 files changed, 128 insertions(+), 3 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 2b3417d790..b5252cbc9b 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1751,6 +1751,9 @@ var AppIcon = class AppIcon {
 
         this.actor._delegate = this;
 
+        this._hasDndHover = false;
+        this._folderPreviewId = 0;
+
         // Get the isDraggable property without passing it on to the BaseIcon:
         let appIconParams = Params.parse(iconParams, { isDraggable: true }, true);
         let isDraggable = appIconParams['isDraggable'];
@@ -1791,6 +1794,9 @@ var AppIcon = class AppIcon {
             });
         }
 
+        Main.overview.connect('item-drag-begin', this._onDragBegin.bind(this));
+        Main.overview.connect('item-drag-end', this._onDragEnd.bind(this));
+
         this.actor.connect('destroy', this._onDestroy.bind(this));
 
         this._menuTimeoutId = 0;
@@ -1801,6 +1807,10 @@ var AppIcon = class AppIcon {
     }
 
     _onDestroy() {
+        if (this._folderPreviewId > 0) {
+            GLib.source_remove(this._folderPreviewId);
+            this._folderPreviewId = 0;
+        }
         if (this._stateChangedId > 0)
             this.app.disconnect(this._stateChangedId);
         if (this._draggable && this._dragging) {
@@ -2000,6 +2010,99 @@ var AppIcon = class AppIcon {
             opacity: 255
         });
     }
+
+    _showFolderPreview() {
+        // HACK!!!
+        this.icon.label.opacity = 0;
+        this.icon._iconBin.ease({
+            scale_x: FOLDER_SUBICON_FRACTION,
+            scale_y: FOLDER_SUBICON_FRACTION
+        });
+    }
+
+    _hideFolderPreview() {
+        // HACK!!!
+        this.icon.label.opacity = 255;
+        this.icon._iconBin.ease({
+            scale_x: 1.0,
+            scale_y: 1.0
+        });
+    }
+
+    _canAccept(source) {
+        let view = _getViewFromIcon(source);
+
+        return source != this &&
+               (source instanceof AppIcon) &&
+               (view instanceof AllView);
+    }
+
+    _setHoveringByDnd(hovering) {
+        if (hovering) {
+            if (this._folderPreviewId > 0)
+                return;
+
+            this._folderPreviewId =
+                GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
+                    this.actor.add_style_pseudo_class('drop');
+                    this._showFolderPreview();
+                    this._folderPreviewId = 0;
+                    return GLib.SOURCE_REMOVE;
+                });
+        } else {
+            if (this._folderPreviewId > 0) {
+                GLib.source_remove(this._folderPreviewId);
+                this._folderPreviewId = 0;
+            }
+            this._hideFolderPreview();
+            this.actor.remove_style_pseudo_class('drop');
+        }
+    }
+
+    _onDragBegin() {
+        this._dragMonitor = {
+            dragMotion: this._onDragMotion.bind(this),
+        };
+        DND.addDragMonitor(this._dragMonitor);
+    }
+
+    _onDragMotion(dragEvent) {
+        let target = dragEvent.targetActor;
+        let isHovering = target == this.actor || this.actor.contains(target);
+        let canDrop = this._canAccept(dragEvent.source);
+        let hasDndHover = isHovering && canDrop;
+
+        if (this._hasDndHover != hasDndHover) {
+            this._setHoveringByDnd(hasDndHover);
+            this._hasDndHover = hasDndHover;
+        }
+
+        return DND.DragMotionResult.CONTINUE;
+    }
+
+    _onDragEnd() {
+        this.actor.remove_style_pseudo_class('drop');
+        DND.removeDragMonitor(this._dragMonitor);
+    }
+
+    handleDragOver(source) {
+        if (source == this)
+            return DND.DragMotionResult.NO_DROP;
+
+        if (!this._canAccept(source))
+            return DND.DragMotionResult.CONTINUE;
+
+        return DND.DragMotionResult.MOVE_DROP;
+    }
+
+    acceptDrop(source) {
+        this._setHoveringByDnd(false);
+
+        if (!this._canAccept(source))
+            return false;
+
+        return true;
+    }
 };
 Signals.addSignalMethods(AppIcon.prototype);
 
diff --git a/js/ui/dash.js b/js/ui/dash.js
index b7ed5874c2..c866dabbfa 100644
--- a/js/ui/dash.js
+++ b/js/ui/dash.js
@@ -24,6 +24,30 @@ function getAppFromSource(source) {
     }
 }
 
+var DashIcon = class DashIcon extends AppDisplay.AppIcon {
+    constructor(app) {
+        super(app, {
+            setSizeManually: true,
+            showLabel: false
+        });
+    }
+
+    // Disable all DnD methods
+    _onDragBegin() {
+    }
+
+    _onDragEnd() {
+    }
+
+    handleDragOver() {
+        return DND.DragMotionResult.CONTINUE;
+    }
+
+    acceptDrop() {
+        return false;
+    }
+};
+
 // A container like StBin, but taking the child's scale into account
 // when requesting a size
 var DashItemContainer = GObject.registerClass(
@@ -450,9 +474,7 @@ var Dash = class Dash {
     }
 
     _createAppItem(app) {
-        let appIcon = new AppDisplay.AppIcon(app,
-                                             { setSizeManually: true,
-                                               showLabel: false });
+        let appIcon = new DashIcon(app);
 
         appIcon.connect('menu-state-changed',
                         (appIcon, opened) => {


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