From ff3d32dd186fa39a80f6d7734654819b20ba0346 Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Fri, 12 Jul 2019 19:32:54 -0300 Subject: [PATCH] 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 | 101 ++++++++++++++++++++++++++++++++++++++++++++ js/ui/dash.js | 28 ++++++++++-- 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js index df608235d..ec1cb84c7 100644 --- a/js/ui/appDisplay.js +++ b/js/ui/appDisplay.js @@ -1743,6 +1743,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']; @@ -1783,6 +1786,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; @@ -1793,6 +1799,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) { @@ -1992,6 +2002,97 @@ var AppIcon = class AppIcon { opacity: 255 }); } + + _showFolderPreview() { + this.icon.label.opacity = 0; + this.icon.icon.ease({ + scale_x: FOLDER_SUBICON_FRACTION, + scale_y: FOLDER_SUBICON_FRACTION + }); + } + + _hideFolderPreview() { + this.icon.label.opacity = 255; + this.icon.icon.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 b7ed5874c..c866dabbf 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) => {