shift-shell/kwin/effects/shift-tile-animations/contents/code/main.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

155 lines
5.3 KiB
JavaScript
Raw Normal View History

// SPDX-FileCopyrightText: 2026 Marco Allegretti
// SPDX-License-Identifier: EUPL-1.2
/*global effect, effects, animate, animationTime, cancel, Effect, QEasingCurve */
"use strict";
class ShiftTileAnimationsEffect {
constructor() {
effect.configChanged.connect(this.loadConfig.bind(this));
effect.animationEnded.connect(this.cleanupWindow.bind(this));
effects.windowAdded.connect(this.manage.bind(this));
effects.windowClosed.connect(this.cleanupWindow.bind(this));
this.loadConfig();
for (const window of effects.stackingOrder) {
this.manage(window);
}
}
loadConfig() {
this.baseDuration = effect.readConfig("Duration", 185) || 185;
this.minimumDelta = effect.readConfig("MinimumDelta", 3) || 3;
}
manage(window) {
if (!window || window.shiftTileAnimationsManaged) {
return;
}
window.shiftTileAnimationsManaged = true;
window.shiftTileUserMoveResize = false;
window.windowFrameGeometryChanged.connect(this.onWindowFrameGeometryChanged.bind(this));
window.windowStartUserMovedResized.connect(this.onWindowStartUserMovedResized.bind(this));
window.windowFinishUserMovedResized.connect(this.onWindowFinishUserMovedResized.bind(this));
}
eligibleWindow(window) {
return window
&& !effects.hasActiveFullScreenEffect
&& window.visible
&& !window.deleted
&& window.managed
&& window.normalWindow
&& !window.fullScreen
&& !window.desktopWindow
&& !window.dock
&& !window.popup
&& !window.popupWindow
&& !window.outline;
}
validGeometry(geometry) {
return geometry && geometry.width > 0 && geometry.height > 0;
}
movedEnough(oldGeometry, newGeometry) {
return Math.abs(oldGeometry.x - newGeometry.x) >= this.minimumDelta
|| Math.abs(oldGeometry.y - newGeometry.y) >= this.minimumDelta
|| Math.abs(oldGeometry.width - newGeometry.width) >= this.minimumDelta
|| Math.abs(oldGeometry.height - newGeometry.height) >= this.minimumDelta;
}
durationFor(oldGeometry, newGeometry) {
const oldCenterX = oldGeometry.x + oldGeometry.width / 2;
const oldCenterY = oldGeometry.y + oldGeometry.height / 2;
const newCenterX = newGeometry.x + newGeometry.width / 2;
const newCenterY = newGeometry.y + newGeometry.height / 2;
const distanceX = newCenterX - oldCenterX;
const distanceY = newCenterY - oldCenterY;
const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
const resize = Math.abs(newGeometry.width - oldGeometry.width) + Math.abs(newGeometry.height - oldGeometry.height);
const travelAllowance = Math.min(80, Math.round(Math.max(distance / 12, resize / 18)));
return animationTime(Math.max(135, Math.min(260, this.baseDuration + travelAllowance)));
}
cancelWindowAnimation(window) {
if (!window || window.shiftTileMoveAnimation === undefined) {
return;
}
cancel(window.shiftTileMoveAnimation);
window.shiftTileMoveAnimation = undefined;
window.setData(Effect.WindowForceBlurRole, null);
}
cleanupWindow(window) {
if (!window) {
return;
}
window.shiftTileMoveAnimation = undefined;
window.setData(Effect.WindowForceBlurRole, null);
}
onWindowStartUserMovedResized(window) {
if (!window) {
return;
}
window.shiftTileUserMoveResize = true;
this.cancelWindowAnimation(window);
}
onWindowFinishUserMovedResized(window) {
if (window) {
window.shiftTileUserMoveResize = false;
}
}
onWindowFrameGeometryChanged(window, oldGeometry) {
if (!this.eligibleWindow(window) || window.shiftTileUserMoveResize) {
return;
}
const newGeometry = window.geometry;
if (!this.validGeometry(oldGeometry) || !this.validGeometry(newGeometry) || !this.movedEnough(oldGeometry, newGeometry)) {
return;
}
this.cancelWindowAnimation(window);
window.setData(Effect.WindowForceBlurRole, true);
window.shiftTileMoveAnimation = animate({
window: window,
duration: this.durationFor(oldGeometry, newGeometry),
keepAlive: false,
animations: [{
type: Effect.Size,
from: {
value1: oldGeometry.width,
value2: oldGeometry.height
},
to: {
value1: newGeometry.width,
value2: newGeometry.height
},
curve: QEasingCurve.OutCubic
}, {
type: Effect.Translation,
from: {
value1: oldGeometry.x - newGeometry.x - (newGeometry.width / 2 - oldGeometry.width / 2),
value2: oldGeometry.y - newGeometry.y - (newGeometry.height / 2 - oldGeometry.height / 2)
},
to: {
value1: 0,
value2: 0
},
curve: QEasingCurve.OutCubic
}]
});
}
}
new ShiftTileAnimationsEffect();