[gnome-shell] workspace: Track windows in WindowClone layout manager



commit 090057d2df231072df2bcf1c6bc5780b7c84eb48
Author: Jonas Dreßler <verdre v0yd nl>
Date:   Mon Jun 1 14:41:25 2020 +0200

    workspace: Track windows in WindowClone layout manager
    
    Move the tracking of the bounding box and all the layout related things
    out of the WindowClone class and into the layout manager. This allows
    the layout manager to keep track of its windows itself and simply notify
    the new bounding-box property in case that box changes.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1297

 js/ui/workspace.js | 153 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 108 insertions(+), 45 deletions(-)
---
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 00a15b36a8..509172f7e6 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -34,61 +34,134 @@ function _interpolate(start, end, step) {
     return start + (end - start) * step;
 }
 
-var WindowCloneLayout = GObject.registerClass(
-class WindowCloneLayout extends Clutter.LayoutManager {
-    _init(boundingBox) {
+var WindowCloneLayout = GObject.registerClass({
+    Properties: {
+        'bounding-box': GObject.ParamSpec.boxed(
+            'bounding-box', 'Bounding box', 'Bounding box',
+            GObject.ParamFlags.READABLE,
+            Clutter.ActorBox.$gtype),
+    },
+}, class WindowCloneLayout extends Clutter.LayoutManager {
+    _init() {
         super._init();
 
-        this._boundingBox = boundingBox;
+        this._container = null;
+        this._boundingBox = new Clutter.ActorBox();
+        this._windows = new Map();
     }
 
-    get boundingBox() {
-        return this._boundingBox;
-    }
+    _layoutChanged() {
+        let frameRect;
+
+        for (const windowInfo of this._windows.values()) {
+            const frame = windowInfo.metaWindow.get_frame_rect();
+            frameRect = frameRect ? frameRect.union(frame) : frame;
+        }
+
+        if (!frameRect)
+            frameRect = new Meta.Rectangle();
+
+        const oldBox = this._boundingBox.copy();
+        this._boundingBox.set_origin(frameRect.x, frameRect.y);
+        this._boundingBox.set_size(frameRect.width, frameRect.height);
 
-    set boundingBox(b) {
-        this._boundingBox = b;
+        if (!this._boundingBox.equal(oldBox))
+            this.notify('bounding-box');
+
+        // Always call layout_changed(), a size or position change of an
+        // attached dialog might not affect the boundingBox
         this.layout_changed();
     }
 
+    vfunc_set_container(container) {
+        this._container = container;
+    }
+
     vfunc_get_preferred_height(_container, _forWidth) {
-        return [0, this._boundingBox.height];
+        return [0, this._boundingBox.get_height()];
     }
 
     vfunc_get_preferred_width(_container, _forHeight) {
-        return [0, this._boundingBox.width];
+        return [0, this._boundingBox.get_width()];
     }
 
     vfunc_allocate(container, box) {
         // If the scale isn't 1, we weren't allocated our preferred size
         // and have to scale the children allocations accordingly.
-        const scaleX = box.get_width() / this._boundingBox.width;
-        const scaleY = box.get_height() / this._boundingBox.height;
+        const scaleX = box.get_width() / this._boundingBox.get_width();
+        const scaleY = box.get_height() / this._boundingBox.get_height();
 
         const childBox = new Clutter.ActorBox();
 
-        container.get_children().forEach(child => {
-            let realWindow;
-            if (child == container._windowClone)
-                realWindow = container.realWindow;
-            else
-                realWindow = child.source;
+        for (const child of container) {
+            if (!child.visible)
+                continue;
 
-            const bufferRect = realWindow.meta_window.get_buffer_rect();
-            childBox.set_origin(
-                bufferRect.x - this._boundingBox.x,
-                bufferRect.y - this._boundingBox.y);
+            const windowInfo = this._windows.get(child);
+            if (windowInfo) {
+                const bufferRect = windowInfo.metaWindow.get_buffer_rect();
+                childBox.set_origin(
+                    bufferRect.x - this._boundingBox.x1,
+                    bufferRect.y - this._boundingBox.y1);
 
-            const [, , natWidth, natHeight] = child.get_preferred_size();
-            childBox.set_size(natWidth, natHeight);
+                const [, , natWidth, natHeight] = child.get_preferred_size();
+                childBox.set_size(natWidth, natHeight);
 
-            childBox.x1 *= scaleX;
-            childBox.x2 *= scaleX;
-            childBox.y1 *= scaleY;
-            childBox.y2 *= scaleY;
+                childBox.x1 *= scaleX;
+                childBox.x2 *= scaleX;
+                childBox.y1 *= scaleY;
+                childBox.y2 *= scaleY;
+
+                child.allocate(childBox);
+            } else {
+                child.allocate_preferred_size();
+            }
+        }
+    }
 
-            child.allocate(childBox);
+    addWindow(clone, metaWindow) {
+        if (this._windows.has(clone))
+            return;
+
+        const windowActor = metaWindow.get_compositor_private();
+
+        this._windows.set(clone, {
+            metaWindow,
+            windowActor,
+            sizeChangedId: metaWindow.connect('size-changed', () =>
+                this._layoutChanged()),
+            positionChangedId: metaWindow.connect('position-changed', () =>
+                this._layoutChanged()),
+            destroyId: windowActor.connect('destroy', () =>
+                clone.destroy()),
+            cloneDestroyId: clone.connect('destroy', () =>
+                this.removeWindow(clone)),
         });
+
+        this._container.add_child(clone);
+
+        this._layoutChanged();
+    }
+
+    removeWindow(clone) {
+        const windowInfo = this._windows.get(clone);
+        if (!windowInfo)
+            return;
+
+        windowInfo.metaWindow.disconnect(windowInfo.sizeChangedId);
+        windowInfo.metaWindow.disconnect(windowInfo.positionChangedId);
+        windowInfo.windowActor.disconnect(windowInfo.destroyId);
+        clone.disconnect(windowInfo.cloneDestroyId);
+
+        this._windows.delete(clone);
+        this._container.remove_child(clone);
+
+        this._layoutChanged();
+    }
+
+    // eslint-disable-next-line camelcase
+    get bounding_box() {
+        return this._boundingBox;
     }
 });
 
@@ -130,7 +203,7 @@ var WindowClone = GObject.registerClass({
 
         this.set_offscreen_redirect(Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
 
-        this.add_child(this._windowClone);
+        this.layout_manager.addWindow(this._windowClone, this.metaWindow);
 
         this._delegate = this;
 
@@ -144,13 +217,7 @@ var WindowClone = GObject.registerClass({
         this._windowClone._posChangedId = this.metaWindow.connect('position-changed',
             this._computeBoundingBox.bind(this));
         this._windowClone._destroyId =
-            this.realWindow.connect('destroy', () => {
-                // First destroy the clone and then destroy everything
-                // This will ensure that we never see it in the
-                // _disconnectSignals loop
-                this._windowClone.destroy();
-                this.destroy();
-            });
+            this.realWindow.connect('destroy', () => this.destroy());
 
         this._updateAttachedDialogs();
         this._computeBoundingBox();
@@ -235,15 +302,12 @@ var WindowClone = GObject.registerClass({
             this._onMetaWindowSizeChanged.bind(this));
         clone._posChangedId = metaWin.connect('position-changed',
             this._onMetaWindowSizeChanged.bind(this));
-        clone._destroyId = realWin.connect('destroy', () => {
-            clone.destroy();
-
-            this._onMetaWindowSizeChanged();
-        });
+        clone._destroyId = realWin.connect('destroy',
+            this._onMetaWindowSizeChanged.bind(this));
 
         Shell.util_set_hidden_from_pick(clone, true);
 
-        this.add_child(clone);
+        this.layout_manager.addWindow(clone, metaWin);
     }
 
     _updateAttachedDialogs() {
@@ -282,7 +346,6 @@ var WindowClone = GObject.registerClass({
 
         // Convert from a MetaRectangle to a native JS object
         this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
-        this.layout_manager.boundingBox = rect;
     }
 
     get windowCenter() {


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