[gnome-shell/wip/carlosg/appgrid-navigation: 12/15] js/appDisplay: Implement side page previews while DnDing




commit 6969837ad2d2c5e68643585782cd0cc3fd8bd7d6
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Feb 3 12:51:17 2021 +0100

    js/appDisplay: Implement side page previews while DnDing
    
    When DnDing an icon, we show both previous/next page, and optionally
    a "placeholder" actor to allow creating new pages. These sides on the
    scrollview are drop targets themselves, allowing to drop an app onto
    the next/prev page without further navigation.
    
    Still, preserve the checks to maybe switch to prev/next page without
    finishing the DnD operation, for finer grained operations.

 data/theme/gnome-shell-sass/widgets/_app-grid.scss |  8 +++
 js/ui/appDisplay.js                                | 67 +++++++++++++++++++++-
 2 files changed, 72 insertions(+), 3 deletions(-)
---
diff --git a/data/theme/gnome-shell-sass/widgets/_app-grid.scss 
b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
index 051dbc239e..19c3ee5a4b 100644
--- a/data/theme/gnome-shell-sass/widgets/_app-grid.scss
+++ b/data/theme/gnome-shell-sass/widgets/_app-grid.scss
@@ -137,3 +137,11 @@ $app_grid_fg_color: #fff;
   border-radius: 99px;
   icon-size: $app_icon_size * 0.5;
 }
+
+.dnd-placeholder {
+  background: rgba(255,255,255,0.25);
+  width: 80px;
+
+  &:ltr { border-radius: 15px 0px 0px 15px; }
+  &:rtl { border-radius: 0px 15px 15px 0px; }
+}
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index d741016cce..6c2cb57997 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -55,6 +55,7 @@ var SidePages = {
     NONE: 0,
     PREVIOUS: 1 << 0,
     NEXT: 1 << 1,
+    EMPTY_PLACEHOLDER: 1 << 2,
 };
 
 function _getCategories(info) {
@@ -153,6 +154,7 @@ var BaseAppView = GObject.registerClass({
             enable_mouse_scrolling: false,
         });
         this._scrollView.set_policy(St.PolicyType.EXTERNAL, St.PolicyType.NEVER);
+        this._scrollView._delegate = this;
 
         this._canScroll = true; // limiting scrolling speed
         this._scrollTimeoutId = 0;
@@ -184,12 +186,33 @@ var BaseAppView = GObject.registerClass({
             this._scrollView.event(event, false);
         });
 
+        // "Empty page" DnD indicator
+        this._emptyPageIndicator = new St.Widget({
+            opacity: 0,
+            reactive: false,
+            visible: false,
+            x_expand: true,
+            y_expand: true,
+            x_align: Clutter.ActorAlign.END,
+            y_align: Clutter.ActorAlign.FILL,
+        });
+        this._emptyPageIndicator.add_style_class_name('dnd-placeholder');
+        this._emptyPageIndicator._delegate = this;
+
+        const scrollContainer = new St.Widget({
+            layout_manager: new Clutter.BinLayout(),
+            clip_to_allocation: true,
+            y_expand: true,
+        });
+        scrollContainer.add_child(this._scrollView);
+        scrollContainer.add_child(this._emptyPageIndicator);
+
         this._box = new St.BoxLayout({
             vertical: true,
             x_expand: true,
             y_expand: true,
         });
-        this._box.add_child(this._scrollView);
+        this._box.add_child(scrollContainer);
         this._box.add_child(this._pageIndicators);
 
         // Swipe
@@ -574,6 +597,7 @@ var BaseAppView = GObject.registerClass({
             dragMotion: this._onDragMotion.bind(this),
         };
         DND.addDragMonitor(this._dragMonitor);
+        this._slideSidePages(SidePages.PREVIOUS | SidePages.NEXT | SidePages.EMPTY_PLACEHOLDER);
     }
 
     _onDragMotion(dragEvent) {
@@ -590,7 +614,15 @@ var BaseAppView = GObject.registerClass({
 
         this._maybeMoveItem(dragEvent);
 
-        return DND.DragMotionResult.CONTINUE;
+        this._dropPage = this._pageForCoords(dragEvent.x, dragEvent.y);
+        if (!this._dropPage)
+            return DND.DragMotionResult.CONTINUE;
+
+        if (this._dropPage === SidePages.NEXT || this._grid.currentPage !== 0)
+            return DND.DragMotionResult.MOVE_DROP;
+
+        delete this._dropPage;
+        return DND.DragMotionResult.NO_DROP;
     }
 
     _onDragEnd() {
@@ -600,12 +632,15 @@ var BaseAppView = GObject.registerClass({
         }
 
         this._resetOvershoot();
+        this._slideSidePages(SidePages.NONE);
+        delete this._dropPage;
     }
 
     _onDragCancelled() {
         // At this point, the positions aren't stored yet, thus _redisplay()
         // will move all items to their original positions
         this._redisplay();
+        this._slideSidePages(SidePages.NONE);
     }
 
     _canAccept(source) {
@@ -629,6 +664,25 @@ var BaseAppView = GObject.registerClass({
 
             this._moveItem(source, page, position);
             this._removeDelayedMove();
+        } else if (this._dropPage) {
+            let page = this._grid.currentPage;
+
+            while (true) {
+                page = this._dropPage === SidePages.NEXT
+                    ? page + 1 : page - 1;
+                if (page < 0 || page >= this._grid.nPages)
+                    break;
+
+                const items = this._grid.getItemsAtPage(page);
+                const itemsPerPage =
+                      this._grid.layout_manager.rowsPerPage * this._grid.layout_manager.columnsPerPage;
+
+                if (items.length < itemsPerPage)
+                    break;
+            }
+
+            this._moveItem(source, Math.max(page, 0), -1);
+            this.goToPage(page);
         }
 
         return true;
@@ -983,7 +1037,13 @@ var BaseAppView = GObject.registerClass({
             let translationX = (1 - adjustment.value) * 100 * multiplier;
             translationX = rtl ? -translationX : translationX;
             const nextPage = this._grid.currentPage + page;
-            if (nextPage >= 0 &&
+            if ((state & SidePages.EMPTY_PLACEHOLDER) !== 0 &&
+                page > 0 &&
+                nextPage === this._grid.nPages) {
+                this._emptyPageIndicator.visible = true;
+                this._emptyPageIndicator.translation_x = translationX;
+                this._emptyPageIndicator.opacity = adjustment.value * 255;
+            } else if (nextPage >= 0 &&
                 nextPage < this._grid.nPages - 1) {
                 const items = this._grid.layout_manager.getItemsAtPage(nextPage);
                 items.forEach(item => (item.translation_x = translationX));
@@ -1037,6 +1097,7 @@ var BaseAppView = GObject.registerClass({
                     mode: Clutter.AnimationMode.EASE_OUT_QUAD,
                     onComplete: () => {
                         this._teardownPagePreview(1);
+                        this._emptyPageIndicator.visible = false;
                         this._syncClip();
                         this._updateFadeForNavigation();
                     },


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