File indexing completed on 2024-11-10 04:57:06

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2018, 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 "use strict";
0011 
0012 const blacklist = [
0013     // The logout screen has to be animated only by the logout effect.
0014     "ksmserver ksmserver",
0015     "ksmserver-logout-greeter ksmserver-logout-greeter",
0016 
0017     // KDE Plasma splash screen has to be animated only by the login effect.
0018     "ksplashqml ksplashqml",
0019 
0020     // Spectacle needs to be blacklisted in order to stay out of its own screenshots.
0021     "spectacle spectacle", // x11
0022     "spectacle org.kde.spectacle", // wayland
0023 ];
0024 
0025 class ScaleEffect {
0026     constructor() {
0027         effect.configChanged.connect(this.loadConfig.bind(this));
0028         effect.animationEnded.connect(this.cleanupForcedRoles.bind(this));
0029         effects.windowAdded.connect(this.slotWindowAdded.bind(this));
0030         effects.windowClosed.connect(this.slotWindowClosed.bind(this));
0031         effects.windowDataChanged.connect(this.slotWindowDataChanged.bind(this));
0032 
0033         this.loadConfig();
0034     }
0035 
0036     loadConfig() {
0037         const defaultDuration = 200;
0038         const duration = effect.readConfig("Duration", defaultDuration) || defaultDuration;
0039         this.duration = animationTime(duration);
0040         this.inScale = effect.readConfig("InScale", 0.8);
0041         this.outScale = effect.readConfig("OutScale", 0.8);
0042     }
0043 
0044     static isScaleWindow(window) {
0045         // We don't want to animate most of plasmashell's windows, yet, some
0046         // of them we want to, for example, Task Manager Settings window.
0047         // The problem is that all those window share single window class.
0048         // So, the only way to decide whether a window should be animated is
0049         // to use a heuristic: if a window has decoration, then it's most
0050         // likely a dialog or a settings window so we have to animate it.
0051         if (window.windowClass == "plasmashell plasmashell"
0052                 || window.windowClass == "plasmashell org.kde.plasmashell") {
0053             return window.hasDecoration;
0054         }
0055 
0056         if (blacklist.indexOf(window.windowClass) != -1) {
0057             return false;
0058         }
0059 
0060         if (window.hasDecoration) {
0061             return true;
0062         }
0063 
0064         // Don't animate combobox popups, tooltips, popup menus, etc.
0065         if (window.popupWindow) {
0066             return false;
0067         }
0068 
0069         // Dont't animate the outline and the screenlocker as it looks bad.
0070         if (window.lockScreen || window.outline) {
0071             return false;
0072         }
0073 
0074         // Override-redirect windows are usually used for user interface
0075         // concepts that are not expected to be animated by this effect.
0076         if (!window.managed) {
0077             return false;
0078         }
0079 
0080         return window.normalWindow || window.dialog;
0081     }
0082 
0083     setupForcedRoles(window) {
0084         window.setData(Effect.WindowForceBackgroundContrastRole, true);
0085         window.setData(Effect.WindowForceBlurRole, true);
0086     }
0087 
0088     cleanupForcedRoles(window) {
0089         window.setData(Effect.WindowForceBackgroundContrastRole, null);
0090         window.setData(Effect.WindowForceBlurRole, null);
0091     }
0092 
0093     slotWindowAdded(window) {
0094         if (effects.hasActiveFullScreenEffect) {
0095             return;
0096         }
0097         if (!ScaleEffect.isScaleWindow(window)) {
0098             return;
0099         }
0100         if (!window.visible) {
0101             return;
0102         }
0103         if (effect.isGrabbed(window, Effect.WindowAddedGrabRole)) {
0104             return;
0105         }
0106         this.setupForcedRoles(window);
0107         window.scaleInAnimation = animate({
0108             window: window,
0109             curve: QEasingCurve.OutCubic,
0110             duration: this.duration,
0111             animations: [
0112                 {
0113                     type: Effect.Scale,
0114                     from: this.inScale
0115                 },
0116                 {
0117                     type: Effect.Opacity,
0118                     from: 0
0119                 }
0120             ]
0121         });
0122     }
0123 
0124     slotWindowClosed(window) {
0125         if (effects.hasActiveFullScreenEffect) {
0126             return;
0127         }
0128         if (!ScaleEffect.isScaleWindow(window)) {
0129             return;
0130         }
0131         if (!window.visible || window.skipsCloseAnimation) {
0132             return;
0133         }
0134         if (effect.isGrabbed(window, Effect.WindowClosedGrabRole)) {
0135             return;
0136         }
0137         if (window.scaleInAnimation) {
0138             cancel(window.scaleInAnimation);
0139             delete window.scaleInAnimation;
0140         }
0141         this.setupForcedRoles(window);
0142         window.scaleOutAnimation = animate({
0143             window: window,
0144             curve: QEasingCurve.InCubic,
0145             duration: this.duration,
0146             animations: [
0147                 {
0148                     type: Effect.Scale,
0149                     to: this.outScale
0150                 },
0151                 {
0152                     type: Effect.Opacity,
0153                     to: 0
0154                 }
0155             ]
0156         });
0157     }
0158 
0159     slotWindowDataChanged(window, role) {
0160         if (role == Effect.WindowAddedGrabRole) {
0161             if (window.scaleInAnimation && effect.isGrabbed(window, role)) {
0162                 cancel(window.scaleInAnimation);
0163                 delete window.scaleInAnimation;
0164                 this.cleanupForcedRoles(window);
0165             }
0166         } else if (role == Effect.WindowClosedGrabRole) {
0167             if (window.scaleOutAnimation && effect.isGrabbed(window, role)) {
0168                 cancel(window.scaleOutAnimation);
0169                 delete window.scaleOutAnimation;
0170                 this.cleanupForcedRoles(window);
0171             }
0172         }
0173     }
0174 }
0175 
0176 new ScaleEffect();