[gnome-shell/T27795: 130/138] dnd: Refactor DnD code from AppIcon to a new base class ViewIcon



commit 23e0e43ef93fbfe4a80ab469e19c7ecbc047a6a6
Author: Mario Sanchez Prada <mario endlessm com>
Date:   Wed Feb 14 20:54:18 2018 +0000

    dnd: Refactor DnD code from AppIcon to a new base class ViewIcon
    
    This abstract class will keep the common code between AppIcon and
    FolderIcon, seeding the foundations for the rest of the DnD code.
    
    Additionally, make sure to include both getId() and getIcon() methods
    that will be useful to check when the icon's image has changed for
    and application or the desktop, which would require a redraw.
    
    https://phabricator.endlessm.com/T17661
    https://phabricator.endlessm.com/T18012

 js/ui/appDisplay.js | 328 ++++++++++++++++++++++++++++++++++------------------
 1 file changed, 218 insertions(+), 110 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index c33f9ac2fd..d5bc27de1d 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1232,37 +1232,188 @@ var FolderView = class FolderView extends BaseAppView {
     }
 };
 
-var FolderIcon = class FolderIcon {
-    constructor(dirInfo, parentView) {
-        this.name = '';
-        this._parentView = parentView;
+const ViewIconState = {
+    NORMAL: 0,
+    DND_PLACEHOLDER: 1,
+    NUM_STATES: 2
+};
 
-        this.id = dirInfo.get_id();
-        this._dirInfo = dirInfo;
-        this.actor = new St.Button({ style_class: 'app-well-app app-folder',
-                                     button_mask: St.ButtonMask.ONE,
-                                     toggle_mode: true,
-                                     can_focus: true,
-                                     x_fill: true,
-                                     y_fill: true });
+var ViewIcon = GObject.registerClass(
+class ViewIcon extends GObject.Object {
+    _init(buttonParams, iconParams) {
+        super._init();
+
+        buttonParams = Params.parse(buttonParams, {
+            style_class: 'app-well-app',
+            pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
+            button_mask: St.ButtonMask.ONE |
+                         St.ButtonMask.TWO |
+                         St.ButtonMask.THREE,
+            toggle_mode: false,
+            can_focus: true,
+            x_fill: true,
+            y_fill: true
+        }, true);
+
+        iconParams = Params.parse(iconParams, {
+            showLabel: true,
+        }, true);
+
+        // Might be changed once the createIcon() method is called.
+        this._iconSize = IconGrid.ICON_SIZE;
+        this._iconState = ViewIconState.NORMAL;
+
+        this.actor = new St.Button(buttonParams);
         this.actor._delegate = this;
-        // whether we need to update arrow side, position etc.
-        this._popupInvalidated = false;
+        this.actor.connect('destroy', this._onDestroy.bind(this));
 
-        this.icon = new IconGrid.BaseIcon('', {
-            createIcon: this._createIcon.bind(this),
-            setSizeManually: false,
-        });
-        this.actor.set_child(this.icon);
+        // Get the isDraggable property without passing it on to the BaseIcon:
+        let appIconParams = Params.parse(iconParams, {
+            isDraggable: true,
+        }, true);
+        let isDraggable = appIconParams['isDraggable'];
+        delete iconParams['isDraggable'];
+
+        this.icon = new IconGrid.BaseIcon(this.name, iconParams);
         this.actor.label_actor = this.icon.label;
 
-        this.view = new FolderView(this._dirInfo, parentView);
+        if (isDraggable) {
+            this._draggable = DND.makeDraggable(this.actor);
+            this._draggable.connect('drag-begin', () => {
+                this._dragging = true;
+                this.scaleAndFade();
+                this._removeMenuTimeout();
+                Main.overview.beginItemDrag(this);
+            });
+            this._draggable.connect('drag-cancelled', () => {
+                this._dragging = false;
+                Main.overview.cancelledItemDrag(this);
+            });
+            this._draggable.connect('drag-end', () => {
+                this._dragging = false;
+                this.undoScaleAndFade();
+                Main.overview.endItemDrag(this);
+            });
+        }
 
         this._itemDragBeginId = Main.overview.connect(
             'item-drag-begin', this._onDragBegin.bind(this));
         this._itemDragEndId = Main.overview.connect(
             'item-drag-end', this._onDragEnd.bind(this));
+    }
+
+    get id() {
+        return this._id;
+    }
+
+    get name() {
+        return this._name;
+    }
+
+    getIcon() {
+        throw new Error('Not implemented');
+    }
 
+    scaleAndFade() {
+        this.actor.reactive = false;
+        this.actor.ease({
+            scale_x: 0.75,
+            scale_y: 0.75,
+            opacity: 128
+        });
+    }
+
+    undoScaleAndFade() {
+        this.actor.reactive = true;
+        this.actor.ease({
+            scale_x: 1.0,
+            scale_y: 1.0,
+            opacity: 255
+        });
+    }
+
+    _onLabelUpdate() {
+        // Do nothing by default
+    }
+
+    _onLabelCancel() {
+        this.icon.actor.sync_hover();
+    }
+
+    _onDestroy() {
+        this.actor._delegate = null;
+    }
+
+    _createIconBase(iconSize) {
+        if (this._iconSize != iconSize)
+            this._iconSize = iconSize;
+
+        // Replace the original icon with an empty placeholder
+        if (this._iconState == ViewIconState.DND_PLACEHOLDER)
+            return new St.Icon({ icon_size: this._iconSize });
+
+        return this._createIconFunc(this._iconSize);
+    }
+
+    prepareForDrag() {
+        throw new Error('Not implemented');
+    }
+
+    getDragActor() {
+        // Each subclass creates the actor returned here in different ways
+        throw new Error('Not implemented');
+    }
+
+    // Returns the original actor that should align with the actor
+    // we show as the item is being dragged.
+    getDragActorSource() {
+        return this.icon.icon;
+    }
+
+    set iconState(iconState) {
+        if (this._iconState == iconState)
+            return;
+
+        this._iconState = iconState;
+        this.icon.reloadIcon();
+    }
+
+    get iconState() {
+        return this._iconState;
+    }
+});
+
+var FolderIcon = GObject.registerClass({
+    Signals: {
+        'apps-changed': {},
+        'name-changed': {},
+     },
+}, class FolderIcon extends ViewIcon {
+    _init(dirInfo, parentView) {
+        let buttonParams = {
+            button_mask: St.ButtonMask.ONE,
+            toggle_mode: true,
+        };
+        let iconParams = {
+            isDraggable: false,
+            createIcon: this._createIcon.bind(this),
+            setSizeManually: false,
+        };
+        this._parentView = parentView;
+
+        this._name = dirInfo.get_name();
+        this._id = dirInfo.get_id();
+        this._dirInfo = dirInfo;
+
+        super._init(buttonParams, iconParams);
+        this.actor.add_style_class_name('app-folder');
+        this.actor.set_child(this.icon);
+        this.actor.label_actor = this.icon.label;
+
+        this.view = new FolderView(this._dirInfo, parentView);
+
+        // whether we need to update arrow side, position etc.
+        this._popupInvalidated = false;
         this._popupTimeoutId = 0;
 
         this.actor.connect('leave-event', this._onLeaveEvent.bind(this));
@@ -1304,6 +1455,16 @@ var FolderIcon = class FolderIcon {
         this._openSpaceForPopup();
     }
 
+    getIcon() {
+        return this._dirInfo.get_icon();
+    }
+
+    getAppIds() {
+        return this.view.getAllItems().map(function(item) {
+            return item.id;
+        });
+    }
+
     getAppIds() {
         return this.view.getAllItems().map(item => item.id);
     }
@@ -1371,11 +1532,11 @@ var FolderIcon = class FolderIcon {
 
     _updateName() {
         let name = this._dirInfo.get_name();
-        if (this.name == name)
+        if (this._name == name)
             return;
 
-        this.name = name;
-        this.icon.label.text = this.name;
+        this._name = name;
+        this.icon.label.text = this._name;
         this.emit('name-changed');
     }
 
@@ -1534,8 +1695,10 @@ var FolderIcon = class FolderIcon {
             this.view.adaptToSize(width, height);
         this._popupInvalidated = true;
     }
-};
-Signals.addSignalMethods(FolderIcon.prototype);
+
+    prepareForDrag() {
+    }
+});
 
 var RenameFolderMenuItem = GObject.registerClass(
 class RenameFolderMenuItem extends PopupMenu.PopupBaseMenuItem {
@@ -1816,19 +1979,28 @@ class AppIconSourceActor extends MessageTray.SourceActor {
     }
 });
 
-var AppIcon = class AppIcon {
-    constructor(app, iconParams = {}) {
+var AppIcon = GObject.registerClass({
+    Signals: { 'menu-state-changed': { param_types: [GObject.TYPE_BOOLEAN] },
+               'sync-tooltip': {} },
+}, class AppDisplayIcon extends ViewIcon {
+    _init(app, iconParams = {}) {
         this.app = app;
-        this.id = app.get_id();
-        this.name = app.get_name();
+        this._id = app.get_id();
+        this._name = app.get_name();
+
+        let buttonParams = { button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO };
+        iconParams = Params.parse(iconParams, {
+            createIcon: this._createIcon.bind(this),
+            createExtraIcons: this._createExtraIcons.bind(this),
+        }, true);
+
+        // Get the showMenu property without passing it on to the BaseIcon:
+        let appIconParams = Params.parse(iconParams, { showMenu: true }, true);
 
-        this.actor = new St.Button({ style_class: 'app-well-app',
-                                     pivot_point: new Clutter.Point({ x: 0.5, y: 0.5 }),
-                                     reactive: true,
-                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
-                                     can_focus: true,
-                                     x_fill: true,
-                                     y_fill: true });
+        this._showMenu = appIconParams['showMenu'];
+        delete iconParams['showMenu'];
+
+        super._init(buttonParams, iconParams);
 
         this._dot = new St.Widget({ style_class: 'app-well-app-running-dot',
                                     layout_manager: new Clutter.BinLayout(),
@@ -1838,31 +2010,14 @@ var AppIcon = class AppIcon {
 
         this._iconContainer = new St.Widget({ layout_manager: new Clutter.BinLayout(),
                                               x_expand: true, y_expand: true });
+        this._iconContainer.add_child(this.icon);
 
         this.actor.set_child(this._iconContainer);
         this._iconContainer.add_child(this._dot);
 
-        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, showMenu: true }, true);
-        let isDraggable = appIconParams['isDraggable'];
-        delete iconParams['isDraggable'];
-
-        this._showMenu = appIconParams['showMenu'];
-        delete iconParams['showMenu'];
-
-        iconParams['createIcon'] = this._createIcon.bind(this);
-        iconParams['createExtraIcons'] = this._createExtraIcons.bind(this);
-        iconParams['setSizeManually'] = false;
-        this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
-        this._iconContainer.add_child(this.icon);
-
-        this.actor.label_actor = this.icon.label;
-
         this.actor.connect('leave-event', this._onLeaveEvent.bind(this));
         this.actor.connect('button-press-event', this._onButtonPress.bind(this));
         this.actor.connect('touch-event', this._onTouchEvent.bind(this));
@@ -1872,30 +2027,6 @@ var AppIcon = class AppIcon {
         this._menu = null;
         this._menuManager = new PopupMenu.PopupMenuManager(this.actor);
 
-        if (isDraggable) {
-            this._draggable = DND.makeDraggable(this.actor);
-            this._draggable.connect('drag-begin', () => {
-                this._dragging = true;
-                this.scaleAndFade();
-                this._removeMenuTimeout();
-                Main.overview.beginItemDrag(this);
-            });
-            this._draggable.connect('drag-cancelled', () => {
-                this._dragging = false;
-                Main.overview.cancelledItemDrag(this);
-            });
-            this._draggable.connect('drag-end', () => {
-                this._dragging = false;
-                this.undoScaleAndFade();
-                Main.overview.endItemDrag(this);
-            });
-        }
-
-        this._itemDragBeginId = Main.overview.connect(
-            'item-drag-begin', this._onDragBegin.bind(this));
-        this._itemDragEndId = Main.overview.connect(
-            'item-drag-end', this._onDragEnd.bind(this));
-
         this.actor.connect('destroy', this._onDestroy.bind(this));
 
         this._menuTimeoutId = 0;
@@ -1905,6 +2036,10 @@ var AppIcon = class AppIcon {
         this._updateRunningStyle();
     }
 
+    getIcon() {
+        return this.app.get_icon();
+    }
+
     _onDestroy() {
         Main.overview.disconnect(this._itemDragBeginId);
         Main.overview.disconnect(this._itemDragEndId);
@@ -1993,10 +2128,6 @@ var AppIcon = class AppIcon {
         this._menu.actor.navigate_focus(null, St.DirectionType.TAB_FORWARD, false);
     }
 
-    getId() {
-        return this.app.get_id();
-    }
-
     popupMenu() {
         this._removeMenuTimeout();
 
@@ -2087,40 +2218,18 @@ var AppIcon = class AppIcon {
         this.app.open_new_window(params.workspace);
     }
 
-    getDragActor() {
-        // This is a temporary change not to depend on the dash while
-        // we don't implement our own Drag'n'Drop mechanism in EOS.
-        return this.app.create_icon_texture(IconGrid.ICON_SIZE);
+    prepareForDrag() {
+        this._removeMenuTimeout();
     }
 
-    // Returns the original actor that should align with the actor
-    // we show as the item is being dragged.
-    getDragActorSource() {
-        return this.icon.icon;
+    getDragActor() {
+        return this.app.create_icon_texture(this._iconSize);
     }
 
     shouldShowTooltip() {
         return this.actor.hover && (!this._menu || !this._menu.isOpen);
     }
 
-    scaleAndFade() {
-        this.actor.reactive = false;
-        this.actor.ease({
-            scale_x: 0.75,
-            scale_y: 0.75,
-            opacity: 128
-        });
-    }
-
-    undoScaleAndFade() {
-        this.actor.reactive = true;
-        this.actor.ease({
-            scale_x: 1.0,
-            scale_y: 1.0,
-            opacity: 255
-        });
-    }
-
     _showFolderPreview() {
         this.icon.label.opacity = 0;
         this.icon.icon.ease({
@@ -2214,8 +2323,7 @@ var AppIcon = class AppIcon {
 
         return view.createFolder(apps);
     }
-};
-Signals.addSignalMethods(AppIcon.prototype);
+});
 
 var AppIconMenu = class AppIconMenu extends PopupMenu.PopupMenu {
     constructor(source) {


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