[gnome-shell] workspaceThumbnail: Make it horizontal



commit 3ad7b85e259dffd6958816361a866a31310a9382
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Mon Jan 25 20:52:17 2021 -0300

    workspaceThumbnail: Make it horizontal
    
    Allocate workspace thumbnails horizontally. This requires introducing code
    to handle the RTL direction. Do a small rewrite of the DnD hover method to
    be simultaneously simpler and easier to follow, and work correctly on RTL.
    
    Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1593>

 js/ui/workspaceThumbnail.js | 285 +++++++++++++++++++++++++-------------------
 1 file changed, 162 insertions(+), 123 deletions(-)
---
diff --git a/js/ui/workspaceThumbnail.js b/js/ui/workspaceThumbnail.js
index 431f46eb60..a30cc0a73b 100644
--- a/js/ui/workspaceThumbnail.js
+++ b/js/ui/workspaceThumbnail.js
@@ -627,9 +627,11 @@ var ThumbnailsBox = GObject.registerClass({
     },
 }, class ThumbnailsBox extends St.Widget {
     _init(scrollAdjustment) {
-        super._init({ reactive: true,
-                      style_class: 'workspace-thumbnails',
-                      request_mode: Clutter.RequestMode.WIDTH_FOR_HEIGHT });
+        super._init({
+            style_class: 'workspace-thumbnails',
+            reactive: true,
+            x_align: Clutter.ActorAlign.CENTER,
+        });
 
         this._delegate = this;
 
@@ -723,11 +725,11 @@ var ThumbnailsBox = GObject.registerClass({
     }
 
     _activateThumbnailAtPoint(stageX, stageY, time) {
-        let [r_, x_, y] = this.transform_stage_point(stageX, stageY);
+        const [r_, x] = this.transform_stage_point(stageX, stageY);
 
-        let thumbnail = this._thumbnails.find(t => {
-            let [, h] = t.get_transformed_size();
-            return y >= t.y && y <= t.y + h;
+        const thumbnail = this._thumbnails.find(t => {
+            const [w] = t.get_transformed_size();
+            return x >= t.x && x <= t.x + w;
         });
         if (thumbnail)
             thumbnail.activate(time);
@@ -792,6 +794,57 @@ var ThumbnailsBox = GObject.registerClass({
         this.queue_relayout();
     }
 
+    _getPlaceholderTarget(index, spacing, rtl) {
+        const workspace = this._thumbnails[index];
+
+        let targetX1;
+        let targetX2;
+
+        if (rtl) {
+            const [r_, w] = workspace.get_transformed_size();
+            const baseX = workspace.x + w;
+            targetX1 = baseX - WORKSPACE_CUT_SIZE;
+            targetX2 = baseX + spacing + WORKSPACE_CUT_SIZE;
+        } else {
+            targetX1 = workspace.x - spacing - WORKSPACE_CUT_SIZE;
+            targetX2 = workspace.x + WORKSPACE_CUT_SIZE;
+        }
+
+        if (index === 0) {
+            if (rtl)
+                targetX2 -= spacing + WORKSPACE_CUT_SIZE;
+            else
+                targetX1 += spacing + WORKSPACE_CUT_SIZE;
+        }
+
+        if (index === this._dropPlaceholderPos) {
+            const placeholderWidth = this._dropPlaceholder.get_width() + spacing;
+            if (rtl)
+                targetX2 += placeholderWidth;
+            else
+                targetX1 -= placeholderWidth;
+        }
+
+        return [targetX1, targetX2];
+    }
+
+    _withinWorkspace(x, index, rtl) {
+        const length = this._thumbnails.length;
+        const workspace = this._thumbnails[index];
+
+        let workspaceX1 = workspace.x + WORKSPACE_CUT_SIZE;
+        let workspaceX2 = workspace.x + workspace.width - WORKSPACE_CUT_SIZE;
+
+        if (index === length - 1) {
+            if (rtl)
+                workspaceX1 -= WORKSPACE_CUT_SIZE;
+            else
+                workspaceX2 += WORKSPACE_CUT_SIZE;
+        }
+
+        return x > workspaceX1 && x <= workspaceX2;
+    }
+
     // Draggable target interface
     handleDragOver(source, actor, x, y, time) {
         if (!source.metaWindow &&
@@ -800,41 +853,30 @@ var ThumbnailsBox = GObject.registerClass({
             source != Main.xdndHandler)
             return DND.DragMotionResult.CONTINUE;
 
+        const rtl = Clutter.get_default_text_direction() === Clutter.TextDirection.RTL;
         let canCreateWorkspaces = Meta.prefs_get_dynamic_workspaces();
         let spacing = this.get_theme_node().get_length('spacing');
 
         this._dropWorkspace = -1;
         let placeholderPos = -1;
-        let targetBase;
-        if (this._dropPlaceholderPos == 0)
-            targetBase = this._dropPlaceholder.y;
-        else
-            targetBase = this._thumbnails[0].y;
-        let targetTop = targetBase - spacing - WORKSPACE_CUT_SIZE;
         let length = this._thumbnails.length;
         for (let i = 0; i < length; i++) {
-            // Allow the reorder target to have a 10px "cut" into
-            // each side of the thumbnail, to make dragging onto the
-            // placeholder easier
-            let [, h] = this._thumbnails[i].get_transformed_size();
-            let targetBottom = targetBase + WORKSPACE_CUT_SIZE;
-            let nextTargetBase = targetBase + h + spacing;
-            let nextTargetTop =  nextTargetBase - spacing - (i == length - 1 ? 0 : WORKSPACE_CUT_SIZE);
-
-            // Expand the target to include the placeholder, if it exists.
-            if (i == this._dropPlaceholderPos)
-                targetBottom += this._dropPlaceholder.get_height();
-
-            if (y > targetTop && y <= targetBottom && source != Main.xdndHandler && canCreateWorkspaces) {
-                placeholderPos = i;
-                break;
-            } else if (y > targetBottom && y <= nextTargetTop) {
-                this._dropWorkspace = i;
-                break;
+            const index = rtl ? length - i - 1 : i;
+
+            if (canCreateWorkspaces && source !== Main.xdndHandler) {
+                const [targetStart, targetEnd] =
+                    this._getPlaceholderTarget(index, spacing, rtl);
+
+                if (x > targetStart && x <= targetEnd) {
+                    placeholderPos = index;
+                    break;
+                }
             }
 
-            targetBase = nextTargetBase;
-            targetTop = nextTargetTop;
+            if (this._withinWorkspace(x, index, rtl)) {
+                this._dropWorkspace = index;
+                break;
+            }
         }
 
         if (this._dropPlaceholderPos != placeholderPos) {
@@ -1156,40 +1198,39 @@ var ThumbnailsBox = GObject.registerClass({
         this._stateUpdateQueued = true;
     }
 
-    vfunc_get_preferred_height(_forWidth) {
-        // Note that for getPreferredWidth/Height we cheat a bit and skip propagating
-        // the size request to our children because we know how big they are and know
-        // that the actors aren't depending on the virtual functions being called.
+    vfunc_get_preferred_height(forWidth) {
         let workspaceManager = global.workspace_manager;
         let themeNode = this.get_theme_node();
 
+        forWidth = themeNode.adjust_for_width(forWidth);
+
         let spacing = themeNode.get_length('spacing');
         let nWorkspaces = workspaceManager.n_workspaces;
         let totalSpacing = (nWorkspaces - 1) * spacing;
 
-        let naturalHeight = totalSpacing + nWorkspaces * this._porthole.height * MAX_THUMBNAIL_SCALE;
+        const avail = forWidth - totalSpacing;
+
+        let scale = (avail / nWorkspaces) / this._porthole.width;
+        scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
 
-        return themeNode.adjust_preferred_height(totalSpacing, naturalHeight);
+        const height = Math.round(this._porthole.height * scale);
+        return themeNode.adjust_preferred_height(height, height);
     }
 
-    vfunc_get_preferred_width(forHeight) {
+    vfunc_get_preferred_width(_forHeight) {
+        // Note that for getPreferredHeight/Width we cheat a bit and skip propagating
+        // the size request to our children because we know how big they are and know
+        // that the actors aren't depending on the virtual functions being called.
         let workspaceManager = global.workspace_manager;
         let themeNode = this.get_theme_node();
 
-        forHeight = themeNode.adjust_for_height(forHeight);
-
         let spacing = themeNode.get_length('spacing');
         let nWorkspaces = workspaceManager.n_workspaces;
         let totalSpacing = (nWorkspaces - 1) * spacing;
 
-        let avail = forHeight - totalSpacing;
-
-        let scale = (avail / nWorkspaces) / this._porthole.height;
-        scale = Math.min(scale, MAX_THUMBNAIL_SCALE);
-
-        let width = Math.round(this._porthole.width * scale);
-
-        return themeNode.adjust_preferred_width(width, width);
+        const naturalWidth =
+            totalSpacing + nWorkspaces * this._porthole.width * MAX_THUMBNAIL_SCALE;
+        return themeNode.adjust_preferred_width(totalSpacing, naturalWidth);
     }
 
     _updatePorthole() {
@@ -1216,17 +1257,18 @@ var ThumbnailsBox = GObject.registerClass({
 
         box = themeNode.get_content_box(box);
 
-        let portholeWidth = this._porthole.width;
-        let portholeHeight = this._porthole.height;
-        let spacing = themeNode.get_length('spacing');
+        const portholeWidth = this._porthole.width;
+        const portholeHeight = this._porthole.height;
+        const spacing = themeNode.get_length('spacing');
 
         // Compute the scale we'll need once everything is updated
         let nWorkspaces = workspaceManager.n_workspaces;
         let totalSpacing = (nWorkspaces - 1) * spacing;
-        let avail = (box.y2 - box.y1) - totalSpacing;
+        const availableWidth = (box.get_width() - totalSpacing) / nWorkspaces;
 
-        let newScale = (avail / nWorkspaces) / portholeHeight;
-        newScale = Math.min(newScale, MAX_THUMBNAIL_SCALE);
+        const hScale = availableWidth / portholeWidth;
+        const vScale = box.get_height() / portholeHeight;
+        const newScale = Math.min(hScale, vScale);
 
         if (newScale != this._targetScale) {
             if (this._targetScale > 0) {
@@ -1242,24 +1284,19 @@ var ThumbnailsBox = GObject.registerClass({
             this._queueUpdateStates();
         }
 
-        let thumbnailHeight = portholeHeight * this._scale;
-        let thumbnailWidth = Math.round(portholeWidth * this._scale);
-        let roundedHScale = thumbnailWidth / portholeWidth;
-
-        let slideOffset; // X offset when thumbnail is fully slid offscreen
-        if (rtl)
-            slideOffset = -(thumbnailWidth + themeNode.get_padding(St.Side.LEFT));
-        else
-            slideOffset = thumbnailWidth + themeNode.get_padding(St.Side.RIGHT);
+        const ratio = portholeWidth / portholeHeight;
+        const thumbnailHeight = Math.round(portholeHeight * this._scale);
+        const thumbnailWidth = Math.round(thumbnailHeight * ratio);
+        const roundedVScale = thumbnailHeight / portholeHeight;
 
         let indicatorValue = this._scrollAdjustment.value;
         let indicatorUpperWs = Math.ceil(indicatorValue);
         let indicatorLowerWs = Math.floor(indicatorValue);
 
-        let indicatorLowerY1 = 0;
-        let indicatorLowerY2 = 0;
-        let indicatorUpperY1 = 0;
-        let indicatorUpperY2 = 0;
+        let indicatorLowerX1 = 0;
+        let indicatorLowerX2 = 0;
+        let indicatorUpperX1 = 0;
+        let indicatorUpperX2 = 0;
 
         let indicatorThemeNode = this._indicator.get_theme_node();
         let indicatorTopFullBorder = indicatorThemeNode.get_padding(St.Side.TOP) + 
indicatorThemeNode.get_border_width(St.Side.TOP);
@@ -1267,7 +1304,7 @@ var ThumbnailsBox = GObject.registerClass({
         let indicatorLeftFullBorder = indicatorThemeNode.get_padding(St.Side.LEFT) + 
indicatorThemeNode.get_border_width(St.Side.LEFT);
         let indicatorRightFullBorder = indicatorThemeNode.get_padding(St.Side.RIGHT) + 
indicatorThemeNode.get_border_width(St.Side.RIGHT);
 
-        let y = box.y1;
+        let x = box.x1;
 
         if (this._dropPlaceholderPos == -1) {
             this._dropPlaceholder.allocate_preferred_size(
@@ -1281,82 +1318,84 @@ var ThumbnailsBox = GObject.registerClass({
         let childBox = new Clutter.ActorBox();
 
         for (let i = 0; i < this._thumbnails.length; i++) {
-            let thumbnail = this._thumbnails[i];
-
+            const thumbnail = this._thumbnails[i];
             if (i > 0)
-                y += spacing - Math.round(thumbnail.collapse_fraction * spacing);
-
-            let x1, x2;
-            if (rtl) {
-                x1 = box.x1 + slideOffset * thumbnail.slide_position;
-                x2 = x1 + thumbnailWidth;
-            } else {
-                x1 = box.x2 - thumbnailWidth + slideOffset * thumbnail.slide_position;
-                x2 = x1 + thumbnailWidth;
-            }
+                x += spacing - Math.round(thumbnail.collapse_fraction * spacing);
+
+            const y1 = box.y1;
+            const y2 = y1 + thumbnailHeight;
+
+            if (i === this._dropPlaceholderPos) {
+                const [, placeholderWidth] = this._dropPlaceholder.get_preferred_width(-1);
+                childBox.y1 = y1;
+                childBox.y2 = y2;
+
+                if (rtl) {
+                    childBox.x2 = box.x2 - Math.round(x);
+                    childBox.x1 = box.x2 - Math.round(x + placeholderWidth);
+                } else {
+                    childBox.x1 = Math.round(x);
+                    childBox.x2 = Math.round(x + placeholderWidth);
+                }
 
-            if (i == this._dropPlaceholderPos) {
-                let [, placeholderHeight] = this._dropPlaceholder.get_preferred_height(-1);
-                childBox.x1 = x1;
-                childBox.x2 = x2;
-                childBox.y1 = Math.round(y);
-                childBox.y2 = Math.round(y + placeholderHeight);
                 this._dropPlaceholder.allocate(childBox);
+
                 Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
                     this._dropPlaceholder.show();
                 });
-                y += placeholderHeight + spacing;
+                x += placeholderWidth + spacing;
             }
 
-            // We might end up with thumbnailHeight being something like 99.33
-            // pixels. To make this work and not end up with a gap at the bottom,
-            // we need some thumbnails to be 99 pixels and some 100 pixels height;
+            // We might end up with thumbnailWidth being something like 99.33
+            // pixels. To make this work and not end up with a gap at the end,
+            // we need some thumbnails to be 99 pixels and some 100 pixels width;
             // we compute an actual scale separately for each thumbnail.
-            let y1 = Math.round(y);
-            let y2 = Math.round(y + thumbnailHeight);
-            let roundedVScale = (y2 - y1) / portholeHeight;
-
-            if (i === indicatorUpperWs) {
-                indicatorUpperY1 = y1;
-                indicatorUpperY2 = y2;
-            }
-            if (i === indicatorLowerWs) {
-                indicatorLowerY1 = y1;
-                indicatorLowerY2 = y2;
-            }
+            const x1 = Math.round(x);
+            const x2 = Math.round(x + thumbnailWidth);
+            const roundedHScale = (x2 - x1) / portholeWidth;
 
             // Allocating a scaled actor is funny - x1/y1 correspond to the origin
             // of the actor, but x2/y2 are increased by the *unscaled* size.
-            childBox.x1 = x1;
-            childBox.x2 = x1 + portholeWidth;
+            if (rtl) {
+                childBox.x2 = box.x2 - x1;
+                childBox.x1 = box.x2 - (x1 + portholeWidth);
+            } else {
+                childBox.x1 = x1;
+                childBox.x2 = x1 + portholeWidth;
+            }
             childBox.y1 = y1;
             childBox.y2 = y1 + portholeHeight;
 
             thumbnail.set_scale(roundedHScale, roundedVScale);
             thumbnail.allocate(childBox);
 
+            if (i === indicatorUpperWs) {
+                indicatorUpperX1 = childBox.x1;
+                indicatorUpperX2 = childBox.x2;
+            }
+            if (i === indicatorLowerWs) {
+                indicatorLowerX1 = childBox.x1;
+                indicatorLowerX2 = childBox.x2;
+            }
+
             // We round the collapsing portion so that we don't get thumbnails resizing
             // during an animation due to differences in rounded, but leave the uncollapsed
             // portion unrounded so that non-animating we end up with the right total
-            y += thumbnailHeight - Math.round(thumbnailHeight * thumbnail.collapse_fraction);
+            x += thumbnailWidth - Math.round(thumbnailWidth * thumbnail.collapse_fraction);
         }
 
-        if (rtl) {
-            childBox.x1 = box.x1;
-            childBox.x2 = box.x1 + thumbnailWidth;
-        } else {
-            childBox.x1 = box.x2 - thumbnailWidth;
-            childBox.x2 = box.x2;
-        }
-        let indicatorY1 = indicatorLowerY1 +
-            (indicatorUpperY1 - indicatorLowerY1) * (indicatorValue % 1);
-        let indicatorY2 = indicatorLowerY2 +
-            (indicatorUpperY2 - indicatorLowerY2) * (indicatorValue % 1);
-
-        childBox.x1 -= indicatorLeftFullBorder;
-        childBox.x2 += indicatorRightFullBorder;
-        childBox.y1 = indicatorY1 - indicatorTopFullBorder;
-        childBox.y2 = indicatorY2 + indicatorBottomFullBorder;
+        childBox.y1 = box.y1;
+        childBox.y2 = box.y1 + thumbnailHeight;
+
+        const indicatorX1 = indicatorLowerX1 +
+            (indicatorUpperX1 - indicatorLowerX1) * (indicatorValue % 1);
+        const indicatorX2 = indicatorLowerX2 +
+            (indicatorUpperX2 - indicatorLowerX2) * (indicatorValue % 1);
+
+        childBox.x1 = indicatorX1 - indicatorLeftFullBorder;
+        childBox.x2 = indicatorX2 + indicatorRightFullBorder;
+        childBox.y1 -= indicatorTopFullBorder;
+        childBox.y2 += indicatorBottomFullBorder;
         this._indicator.allocate(childBox);
     }
 });


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