File indexing completed on 2024-11-10 04:57:12
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2015 Thomas Lübking <thomas.luebking@gmail.com> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 /*global effect, effects, animate, animationTime, Effect, QEasingCurve */ 0010 0011 "use strict"; 0012 0013 var badBadWindowsEffect = { 0014 duration: animationTime(250), 0015 showingDesktop: false, 0016 loadConfig: function () { 0017 badBadWindowsEffect.duration = animationTime(250); 0018 }, 0019 setShowingDesktop: function (showing) { 0020 badBadWindowsEffect.showingDesktop = showing; 0021 }, 0022 offToCorners: function (showing, frozenTime) { 0023 if (typeof frozenTime === "undefined") { 0024 frozenTime = -1; 0025 } 0026 var stackingOrder = effects.stackingOrder; 0027 var screenGeo = effects.virtualScreenGeometry; 0028 var xOffset = screenGeo.width / 16; 0029 var yOffset = screenGeo.height / 16; 0030 if (showing) { 0031 var closestWindows = [ undefined, undefined, undefined, undefined ]; 0032 var movedWindowsCount = 0; 0033 for (var i = 0; i < stackingOrder.length; ++i) { 0034 var w = stackingOrder[i]; 0035 0036 if (!w.hiddenByShowDesktop) { 0037 continue; 0038 } 0039 0040 // ignore invisible windows and such that do not have to be restored 0041 if (!w.visible) { 0042 if (w.offToCornerId) { 0043 // if it was visible when the effect was activated delete its animation data 0044 cancel(w.offToCornerId); 0045 delete w.offToCornerId; 0046 delete w.apertureCorner; 0047 } 0048 continue; 0049 } 0050 0051 // Don't touch docks 0052 if (w.dock) { 0053 continue; 0054 } 0055 0056 // calculate the corner distances 0057 var geo = w.geometry; 0058 var dl = geo.x + geo.width - screenGeo.x; 0059 var dr = screenGeo.x + screenGeo.width - geo.x; 0060 var dt = geo.y + geo.height - screenGeo.y; 0061 var db = screenGeo.y + screenGeo.height - geo.y; 0062 w.apertureDistances = [ dl + dt, dr + dt, dr + db, dl + db ]; 0063 movedWindowsCount += 1; 0064 0065 // if this window is the closest one to any corner, set it as preferred there 0066 var nearest = 0; 0067 for (var j = 1; j < 4; ++j) { 0068 if (w.apertureDistances[j] < w.apertureDistances[nearest] || 0069 (w.apertureDistances[j] == w.apertureDistances[nearest] && closestWindows[j] === undefined)) { 0070 nearest = j; 0071 } 0072 } 0073 if (closestWindows[nearest] === undefined || 0074 closestWindows[nearest].apertureDistances[nearest] > w.apertureDistances[nearest]) 0075 closestWindows[nearest] = w; 0076 } 0077 0078 // second pass, select corners 0079 0080 // 1st off, move the nearest windows to their nearest corners 0081 // this will ensure that if there's only on window in the lower right 0082 // it won't be moved out to the upper left 0083 var movedWindowsDec = [ 0, 0, 0, 0 ]; 0084 for (var i = 0; i < 4; ++i) { 0085 if (closestWindows[i] === undefined) 0086 continue; 0087 closestWindows[i].apertureCorner = i; 0088 delete closestWindows[i].apertureDistances; 0089 movedWindowsDec[i] = 1; 0090 } 0091 0092 // 2nd, distribute the remainders according to their preferences 0093 // this doesn't exactly have heapsort performance ;-) 0094 movedWindowsCount = Math.floor((movedWindowsCount + 3) / 4); 0095 for (var i = 0; i < 4; ++i) { 0096 for (var j = 0; j < movedWindowsCount - movedWindowsDec[i]; ++j) { 0097 var bestWindow = undefined; 0098 for (var k = 0; k < stackingOrder.length; ++k) { 0099 if (stackingOrder[k].apertureDistances === undefined) 0100 continue; 0101 if (bestWindow === undefined || 0102 stackingOrder[k].apertureDistances[i] < bestWindow.apertureDistances[i]) 0103 bestWindow = stackingOrder[k]; 0104 } 0105 if (bestWindow === undefined) 0106 break; 0107 bestWindow.apertureCorner = i; 0108 delete bestWindow.apertureDistances; 0109 } 0110 } 0111 0112 } 0113 0114 // actually re/move windows from/to assigned corners 0115 for (var i = 0; i < stackingOrder.length; ++i) { 0116 var w = stackingOrder[i]; 0117 if (w.apertureCorner === undefined && w.offToCornerId === undefined) 0118 continue; 0119 0120 if (w.dock) { 0121 continue; 0122 } 0123 0124 var anchor, tx, ty; 0125 var geo = w.geometry; 0126 if (w.apertureCorner == 1 || w.apertureCorner == 2) { 0127 tx = screenGeo.x + screenGeo.width - xOffset; 0128 anchor = Effect.Left; 0129 } else { 0130 tx = xOffset; 0131 anchor = Effect.Right; 0132 } 0133 if (w.apertureCorner > 1) { 0134 ty = screenGeo.y + screenGeo.height - yOffset; 0135 anchor |= Effect.Top; 0136 } else { 0137 ty = yOffset; 0138 anchor |= Effect.Bottom; 0139 } 0140 0141 if (showing) { 0142 if (!w.offToCornerId || !freezeInTime(w.offToCornerId, frozenTime)) { 0143 0144 w.offToCornerId = set({ 0145 window: w, 0146 duration: badBadWindowsEffect.duration, 0147 curve: QEasingCurve.InOutCubic, 0148 animations: [{ 0149 type: Effect.Position, 0150 targetAnchor: anchor, 0151 to: { value1: tx, value2: ty }, 0152 frozenTime: frozenTime 0153 },{ 0154 type: Effect.Opacity, 0155 to: 0.0, 0156 frozenTime: frozenTime 0157 }] 0158 }); 0159 } 0160 } else { 0161 // Reset if the window has become invisible in the meantime 0162 if (!w.visible) { 0163 cancel(w.offToCornerId); 0164 delete w.offToCornerId; 0165 delete w.apertureCorner; 0166 // This if the window was invisible and has become visible in the meantime 0167 } else if (!w.offToCornerId || !redirect(w.offToCornerId, Effect.Backward) || !freezeInTime(w.offToCornerId, frozenTime)) { 0168 animate({ 0169 window: w, 0170 duration: badBadWindowsEffect.duration, 0171 curve: QEasingCurve.InOutCubic, 0172 animations: [{ 0173 type: Effect.Position, 0174 sourceAnchor: anchor, 0175 gesture: true, 0176 from: { value1: tx, value2: ty } 0177 },{ 0178 type: Effect.Opacity, 0179 from: 0.0 0180 }] 0181 }); 0182 } 0183 } 0184 } 0185 }, 0186 animationEnded: function (w, a, meta) { 0187 // After the animation that closes the effect, reset all the parameters 0188 if (!badBadWindowsEffect.showingDesktop) { 0189 cancel(w.offToCornerId); 0190 delete w.offToCornerId; 0191 delete w.apertureCorner; 0192 } 0193 }, 0194 realtimeScreenEdgeCallback: function (border, deltaProgress, effectScreen) { 0195 if (!deltaProgress || !effectScreen) { 0196 badBadWindowsEffect.offToCorners(badBadWindowsEffect.showingDesktop, -1); 0197 return; 0198 } 0199 let time = 0; 0200 0201 switch (border) { 0202 case KWin.ElectricTop: 0203 case KWin.ElectricBottom: 0204 time = Math.min(1, (Math.abs(deltaProgress.height) / (effectScreen.geometry.height / 2))) * badBadWindowsEffect.duration; 0205 break; 0206 case KWin.ElectricLeft: 0207 case KWin.ElectricRight: 0208 time = Math.min(1, (Math.abs(deltaProgress.width) / (effectScreen.geometry.width / 2))) * badBadWindowsEffect.duration; 0209 break; 0210 default: 0211 return; 0212 } 0213 if (badBadWindowsEffect.showingDesktop) { 0214 time = badBadWindowsEffect.duration - time; 0215 } 0216 0217 badBadWindowsEffect.offToCorners(true, time) 0218 }, 0219 init: function () { 0220 badBadWindowsEffect.loadConfig(); 0221 effects.showingDesktopChanged.connect(badBadWindowsEffect.setShowingDesktop); 0222 effects.showingDesktopChanged.connect(badBadWindowsEffect.offToCorners); 0223 effect.animationEnded.connect(badBadWindowsEffect.animationEnded); 0224 0225 let edges = effect.touchEdgesForAction("show-desktop"); 0226 0227 for (let i in edges) { 0228 let edge = parseInt(edges[i]); 0229 registerRealtimeScreenEdge(edge, badBadWindowsEffect.realtimeScreenEdgeCallback); 0230 } 0231 } 0232 }; 0233 0234 badBadWindowsEffect.init();