diff --git a/animator.js b/animator.js index e3d19be..ccfe84c 100644 --- a/animator.js +++ b/animator.js @@ -61,6 +61,7 @@ export let Animator = class { } let count = dock._icons.length; + let createdResources = false; if (dock.renderArea.get_children().length == 0) { this._renderers = []; this._dots = []; @@ -80,6 +81,7 @@ export let Animator = class { renderer.visible = false; target.add_child(renderer); this._renderers.push(renderer); + createdResources = true; // dot let dots = new DockItemDotsOverlay(new Dot(DOT_CANVAS_SIZE)); @@ -100,6 +102,15 @@ export let Animator = class { this._badges[i].visible = false; } + // Newly attached actors do not have a stage allocation until the next + // frame. Mutating their scale/position immediately floods Mutter with + // "needs an allocation" warnings and can leave stale geometry after the + // display wakes. Give Clutter one frame to allocate them first. + if (createdResources) { + this._target.queue_relayout(); + return false; + } + return true; } diff --git a/dock.js b/dock.js index d3202ad..d07b05c 100644 --- a/dock.js +++ b/dock.js @@ -963,23 +963,25 @@ export let Dock = GObject.registerClass( } _snapToContainerEdge(container, child, edge = true) { - child.x = container.width / 2 - child.width / 2; - child.y = container.height / 2 - child.height / 2; + let x = container.width / 2 - child.width / 2; + let y = container.height / 2 - child.height / 2; if (edge) { if (this.isVertical()) { if (this._position == DockPosition.LEFT) { - child.x = 0; + x = 0; } else { - child.x = container.width - child.width; + x = container.width - child.width; } } else { if (this._position == DockPosition.TOP) { - child.y = 0; + y = 0; } else { - child.y = container.height - child.height; + y = container.height - child.height; } } } + if (child.x !== x) child.x = x; + if (child.y !== y) child.y = y; } layout() { @@ -1105,24 +1107,28 @@ export let Dock = GObject.registerClass( } this._icons.forEach((icon) => { - icon.width = Math.floor(iconSizeSpaced * scaleFactor); - icon.height = Math.floor(iconSizeSpaced * scaleFactor); + let actorSize = Math.floor(iconSizeSpaced * scaleFactor); + if (icon.width !== actorSize) icon.width = actorSize; + if (icon.height !== actorSize) icon.height = actorSize; if (icon.style != iconStyle) { icon.style = iconStyle; } }); - //! check with multi-monitor and scaled displays - this.x = m.x; - this.y = m.y; - this.width = m.width; - this.height = m.height; - // reorient and reposition the dash - this.dash.last_child.layout_manager.orientation = vertical; - this.dash._box.layout_manager.orientation = vertical; + let orientation = vertical + ? Clutter.Orientation.VERTICAL + : Clutter.Orientation.HORIZONTAL; + if (this.dash.last_child.layout_manager.orientation !== orientation) { + this.dash.last_child.layout_manager.orientation = orientation; + } + if (this.dash._box.layout_manager.orientation !== orientation) { + this.dash._box.layout_manager.orientation = orientation; + } if (this._extraIcons) { - this._extraIcons.layout_manager.orientation = vertical; + if (this._extraIcons.layout_manager.orientation !== orientation) { + this._extraIcons.layout_manager.orientation = orientation; + } } // hug the edge @@ -1137,14 +1143,30 @@ export let Dock = GObject.registerClass( // computation derived from animation scale let magnify = this.extension.animation_magnify * 1.8; let fp = iconSize * 2 + iconSize * (0.6 * (1 + magnify)); + + // Compute the final monitor-relative geometry before committing actor + // properties. Assigning the full monitor size and then the dock size on + // every animation frame continuously invalidates Clutter allocations. + let dockWidth = vertical ? fp * scaleFactor : m.width; + let dockHeight = vertical ? m.height : fp * scaleFactor; + let dockX = m.x + (m.width - dockWidth) / 2; + let dockY = m.y + (m.height - dockHeight) / 2; if (vertical) { - this.width = fp * scaleFactor; + dockX = + this._position == DockPosition.LEFT + ? m.x + : m.x + m.width - dockWidth; } else { - this.height = fp * scaleFactor; + dockY = + this._position == DockPosition.TOP + ? m.y + : m.y + m.height - dockHeight; } - this._snapToContainerEdge(m, this, true); - this.x += m.x; - this.y += m.y; + + if (this.width !== dockWidth) this.width = dockWidth; + if (this.height !== dockHeight) this.height = dockHeight; + if (this.x !== dockX) this.x = dockX; + if (this.y !== dockY) this.y = dockY; this._snapToContainerEdge(this, this.dash, true); this._iconSizeScaledDown = iconSize; @@ -1154,20 +1176,22 @@ export let Dock = GObject.registerClass( //! add scaleFactor? let dwellHeight = 2; if (vertical) { - this.dwell.width = dwellHeight; - this.dwell.height = this.height; - this.dwell.x = m.x; - this.dwell.y = this.y; + if (this.dwell.width !== dwellHeight) this.dwell.width = dwellHeight; + if (this.dwell.height !== this.height) this.dwell.height = this.height; + if (this.dwell.x !== m.x) this.dwell.x = m.x; + if (this.dwell.y !== this.y) this.dwell.y = this.y; if (this._position == DockPosition.RIGHT) { - this.dwell.x = m.x + m.width - dwellHeight; + let dwellX = m.x + m.width - dwellHeight; + if (this.dwell.x !== dwellX) this.dwell.x = dwellX; } } else { - this.dwell.width = this.width; - this.dwell.height = dwellHeight; - this.dwell.x = this.x; - this.dwell.y = this.y + this.height - this.dwell.height; + if (this.dwell.width !== this.width) this.dwell.width = this.width; + if (this.dwell.height !== dwellHeight) this.dwell.height = dwellHeight; + if (this.dwell.x !== this.x) this.dwell.x = this.x; + let dwellY = this.y + this.height - dwellHeight; + if (this.dwell.y !== dwellY) this.dwell.y = dwellY; if (this._position == DockPosition.TOP) { - this.dwell.y = this.y; + if (this.dwell.y !== this.y) this.dwell.y = this.y; } } diff --git a/integrations.js b/integrations.js index a0815e2..fc96b76 100644 --- a/integrations.js +++ b/integrations.js @@ -209,6 +209,37 @@ export const Integrations = class { return; } + // Display blank/unblank briefly leaves the blur actors without an + // allocation. Do not derive or apply clip geometry during that window; + // Mutter rejects NaN clips and can keep the actor tree in a relayout + // loop afterwards. + let allocationActors = [ + dock, + dock._background, + dock.renderArea, + bms.first_child, + meta_background, + ]; + if ( + allocationActors.some( + (actor) => !actor?.has_allocation || !actor.has_allocation() + ) + ) { + return; + } + + let setFiniteClip = (x, y, width, height) => { + if ( + ![x, y, width, height].every(Number.isFinite) || + width <= 0 || + height <= 0 + ) { + return false; + } + bms.first_child.set_clip(x, y, width, height); + return true; + }; + // bottom layout switch (dock._position) { case 'left': @@ -217,7 +248,7 @@ export const Integrations = class { bms.y = 0; bms.first_child.x = 0; bms.first_child.y = 0; - bms.first_child.set_clip( + setFiniteClip( bg_offset_x, bg_offset_y, dock._background.width - (dock.extension.border_thickness && 0), @@ -229,7 +260,7 @@ export const Integrations = class { bms.y = 0; bms.first_child.x = -meta_background.width + rw; bms.first_child.y = 0; - bms.first_child.set_clip( + setFiniteClip( -bms.first_child.x + bg_offset_x, 0 + bg_offset_y, dock._background.width - (dock.extension.border_thickness && 0), @@ -242,7 +273,7 @@ export const Integrations = class { bms.y = 0; bms.first_child.x = 0; bms.first_child.y = -meta_background.height + rh; - bms.first_child.set_clip( + setFiniteClip( 0 + bg_offset_x, -bms.first_child.y + bg_offset_y, dock._background.width - (dock.extension.border_thickness && 0),