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
This commit is contained in:
Jonas Dreßler 2020-06-01 14:41:25 +02:00 committed by Georges Basile Stavracas Neto
parent b3aab7f401
commit 090057d2df

View File

@ -34,61 +34,134 @@ function _interpolate(start, end, step) {
return start + (end - start) * step; return start + (end - start) * step;
} }
var WindowCloneLayout = GObject.registerClass( var WindowCloneLayout = GObject.registerClass({
class WindowCloneLayout extends Clutter.LayoutManager { Properties: {
_init(boundingBox) { '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(); super._init();
this._boundingBox = boundingBox; this._container = null;
this._boundingBox = new Clutter.ActorBox();
this._windows = new Map();
} }
get boundingBox() { _layoutChanged() {
return this._boundingBox; let frameRect;
}
set boundingBox(b) { for (const windowInfo of this._windows.values()) {
this._boundingBox = b; 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);
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(); this.layout_changed();
} }
vfunc_set_container(container) {
this._container = container;
}
vfunc_get_preferred_height(_container, _forWidth) { vfunc_get_preferred_height(_container, _forWidth) {
return [0, this._boundingBox.height]; return [0, this._boundingBox.get_height()];
} }
vfunc_get_preferred_width(_container, _forHeight) { vfunc_get_preferred_width(_container, _forHeight) {
return [0, this._boundingBox.width]; return [0, this._boundingBox.get_width()];
} }
vfunc_allocate(container, box) { vfunc_allocate(container, box) {
// If the scale isn't 1, we weren't allocated our preferred size // If the scale isn't 1, we weren't allocated our preferred size
// and have to scale the children allocations accordingly. // and have to scale the children allocations accordingly.
const scaleX = box.get_width() / this._boundingBox.width; const scaleX = box.get_width() / this._boundingBox.get_width();
const scaleY = box.get_height() / this._boundingBox.height; const scaleY = box.get_height() / this._boundingBox.get_height();
const childBox = new Clutter.ActorBox(); const childBox = new Clutter.ActorBox();
container.get_children().forEach(child => { for (const child of container) {
let realWindow; if (!child.visible)
if (child == container._windowClone) continue;
realWindow = container.realWindow;
else
realWindow = child.source;
const bufferRect = realWindow.meta_window.get_buffer_rect(); const windowInfo = this._windows.get(child);
childBox.set_origin( if (windowInfo) {
bufferRect.x - this._boundingBox.x, const bufferRect = windowInfo.metaWindow.get_buffer_rect();
bufferRect.y - this._boundingBox.y); childBox.set_origin(
bufferRect.x - this._boundingBox.x1,
bufferRect.y - this._boundingBox.y1);
const [, , natWidth, natHeight] = child.get_preferred_size(); const [, , natWidth, natHeight] = child.get_preferred_size();
childBox.set_size(natWidth, natHeight); childBox.set_size(natWidth, natHeight);
childBox.x1 *= scaleX; childBox.x1 *= scaleX;
childBox.x2 *= scaleX; childBox.x2 *= scaleX;
childBox.y1 *= scaleY; childBox.y1 *= scaleY;
childBox.y2 *= scaleY; childBox.y2 *= scaleY;
child.allocate(childBox); child.allocate(childBox);
} else {
child.allocate_preferred_size();
}
}
}
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.set_offscreen_redirect(Clutter.OffscreenRedirect.AUTOMATIC_FOR_OPACITY);
this.add_child(this._windowClone); this.layout_manager.addWindow(this._windowClone, this.metaWindow);
this._delegate = this; this._delegate = this;
@ -144,13 +217,7 @@ var WindowClone = GObject.registerClass({
this._windowClone._posChangedId = this.metaWindow.connect('position-changed', this._windowClone._posChangedId = this.metaWindow.connect('position-changed',
this._computeBoundingBox.bind(this)); this._computeBoundingBox.bind(this));
this._windowClone._destroyId = this._windowClone._destroyId =
this.realWindow.connect('destroy', () => { this.realWindow.connect('destroy', () => this.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._updateAttachedDialogs(); this._updateAttachedDialogs();
this._computeBoundingBox(); this._computeBoundingBox();
@ -235,15 +302,12 @@ var WindowClone = GObject.registerClass({
this._onMetaWindowSizeChanged.bind(this)); this._onMetaWindowSizeChanged.bind(this));
clone._posChangedId = metaWin.connect('position-changed', clone._posChangedId = metaWin.connect('position-changed',
this._onMetaWindowSizeChanged.bind(this)); this._onMetaWindowSizeChanged.bind(this));
clone._destroyId = realWin.connect('destroy', () => { clone._destroyId = realWin.connect('destroy',
clone.destroy(); this._onMetaWindowSizeChanged.bind(this));
this._onMetaWindowSizeChanged();
});
Shell.util_set_hidden_from_pick(clone, true); Shell.util_set_hidden_from_pick(clone, true);
this.add_child(clone); this.layout_manager.addWindow(clone, metaWin);
} }
_updateAttachedDialogs() { _updateAttachedDialogs() {
@ -282,7 +346,6 @@ var WindowClone = GObject.registerClass({
// Convert from a MetaRectangle to a native JS object // Convert from a MetaRectangle to a native JS object
this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height }; this._boundingBox = { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
this.layout_manager.boundingBox = rect;
} }
get windowCenter() { get windowCenter() {