mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-28 23:42:51 -05:00
notifications: attempt to minimize rapid window creation/destruction
This commit is contained in:
@@ -175,7 +175,7 @@ PanelWindow {
|
|||||||
|
|
||||||
function getLeftMargin() {
|
function getLeftMargin() {
|
||||||
if (isTopCenter)
|
if (isTopCenter)
|
||||||
return (screen.width - implicitWidth) / 2;
|
return screen ? (screen.width - implicitWidth) / 2 : 0;
|
||||||
|
|
||||||
const popupPos = SettingsData.notificationPopupPosition;
|
const popupPos = SettingsData.notificationPopupPosition;
|
||||||
const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom;
|
const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom;
|
||||||
@@ -199,7 +199,8 @@ PanelWindow {
|
|||||||
return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
|
return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly property real dpr: CompositorService.getScreenScale(win.screen)
|
readonly property bool screenValid: win.screen && !_isDestroying
|
||||||
|
readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1
|
||||||
readonly property real alignedWidth: Theme.px(implicitWidth, dpr)
|
readonly property real alignedWidth: Theme.px(implicitWidth, dpr)
|
||||||
readonly property real alignedHeight: Theme.px(implicitHeight, dpr)
|
readonly property real alignedHeight: Theme.px(implicitHeight, dpr)
|
||||||
|
|
||||||
@@ -227,7 +228,7 @@ PanelWindow {
|
|||||||
id: bgShadowLayer
|
id: bgShadowLayer
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.snap(4, win.dpr)
|
anchors.margins: Theme.snap(4, win.dpr)
|
||||||
layer.enabled: true
|
layer.enabled: !win._isDestroying && win.screenValid
|
||||||
layer.smooth: false
|
layer.smooth: false
|
||||||
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
|
||||||
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
layer.textureMirroring: ShaderEffectSource.MirrorVertically
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import Quickshell
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
import qs.Services
|
||||||
|
|
||||||
QtObject {
|
QtObject {
|
||||||
@@ -12,6 +10,11 @@ QtObject {
|
|||||||
property int maxTargetNotifications: 4
|
property int maxTargetNotifications: 4
|
||||||
property var popupWindows: [] // strong refs to windows (live until exitFinished)
|
property var popupWindows: [] // strong refs to windows (live until exitFinished)
|
||||||
property var destroyingWindows: new Set()
|
property var destroyingWindows: new Set()
|
||||||
|
property var pendingDestroys: []
|
||||||
|
property int destroyDelayMs: 100
|
||||||
|
property var pendingCreates: []
|
||||||
|
property int createDelayMs: 50
|
||||||
|
property bool createBusy: false
|
||||||
property Component popupComponent
|
property Component popupComponent
|
||||||
|
|
||||||
popupComponent: Component {
|
popupComponent: Component {
|
||||||
@@ -25,7 +28,7 @@ QtObject {
|
|||||||
|
|
||||||
notificationConnections: Connections {
|
notificationConnections: Connections {
|
||||||
function onVisibleNotificationsChanged() {
|
function onVisibleNotificationsChanged() {
|
||||||
manager._sync(NotificationService.visibleNotifications)
|
manager._sync(NotificationService.visibleNotifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
target: NotificationService
|
target: NotificationService
|
||||||
@@ -33,239 +36,298 @@ QtObject {
|
|||||||
|
|
||||||
property Timer sweeper
|
property Timer sweeper
|
||||||
|
|
||||||
|
property Timer destroyTimer: Timer {
|
||||||
|
interval: destroyDelayMs
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: manager._processDestroyQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _processDestroyQueue() {
|
||||||
|
if (pendingDestroys.length === 0)
|
||||||
|
return;
|
||||||
|
const p = pendingDestroys.shift();
|
||||||
|
if (p && p.destroy) {
|
||||||
|
try {
|
||||||
|
p.destroy();
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
if (pendingDestroys.length > 0)
|
||||||
|
destroyTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleDestroy(p) {
|
||||||
|
if (!p)
|
||||||
|
return;
|
||||||
|
pendingDestroys.push(p);
|
||||||
|
if (!destroyTimer.running)
|
||||||
|
destroyTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
property Timer createTimer: Timer {
|
||||||
|
interval: createDelayMs
|
||||||
|
running: false
|
||||||
|
repeat: false
|
||||||
|
onTriggered: manager._processCreateQueue()
|
||||||
|
}
|
||||||
|
|
||||||
|
function _processCreateQueue() {
|
||||||
|
createBusy = false;
|
||||||
|
if (pendingCreates.length === 0)
|
||||||
|
return;
|
||||||
|
const wrapper = pendingCreates.shift();
|
||||||
|
if (wrapper)
|
||||||
|
_doInsertNewestAtTop(wrapper);
|
||||||
|
if (pendingCreates.length > 0) {
|
||||||
|
createBusy = true;
|
||||||
|
createTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _scheduleCreate(wrapper) {
|
||||||
|
if (!wrapper)
|
||||||
|
return;
|
||||||
|
pendingCreates.push(wrapper);
|
||||||
|
if (!createBusy) {
|
||||||
|
createBusy = true;
|
||||||
|
createTimer.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sweeper: Timer {
|
sweeper: Timer {
|
||||||
interval: 500
|
interval: 500
|
||||||
running: false
|
running: false
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
const toRemove = []
|
const toRemove = [];
|
||||||
for (const p of popupWindows) {
|
for (const p of popupWindows) {
|
||||||
if (!p) {
|
if (!p) {
|
||||||
toRemove.push(p)
|
toRemove.push(p);
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
const isZombie = p.status === Component.Null || (!p.visible && !p.exiting) || (!p.notificationData && !p._isDestroying) || (!p.hasValidData && !p._isDestroying)
|
const isZombie = p.status === Component.Null || (!p.visible && !p.exiting) || (!p.notificationData && !p._isDestroying) || (!p.hasValidData && !p._isDestroying);
|
||||||
if (isZombie) {
|
if (isZombie) {
|
||||||
toRemove.push(p)
|
toRemove.push(p);
|
||||||
if (p.forceExit) {
|
if (p.forceExit) {
|
||||||
p.forceExit()
|
p.forceExit();
|
||||||
} else if (p.destroy) {
|
} else if (p.destroy) {
|
||||||
try {
|
try {
|
||||||
p.destroy()
|
p.destroy();
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (toRemove.length) {
|
if (toRemove.length) {
|
||||||
popupWindows = popupWindows.filter(p => toRemove.indexOf(p) === -1)
|
popupWindows = popupWindows.filter(p => toRemove.indexOf(p) === -1);
|
||||||
const survivors = _active().sort((a, b) => a.screenY - b.screenY)
|
const survivors = _active().sort((a, b) => a.screenY - b.screenY);
|
||||||
for (let k = 0; k < survivors.length; ++k) {
|
for (let k = 0; k < survivors.length; ++k) {
|
||||||
survivors[k].screenY = topMargin + k * baseNotificationHeight
|
survivors[k].screenY = topMargin + k * baseNotificationHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (popupWindows.length === 0) {
|
if (popupWindows.length === 0) {
|
||||||
sweeper.stop()
|
sweeper.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _hasWindowFor(w) {
|
function _hasWindowFor(w) {
|
||||||
return popupWindows.some(p => p && p.notificationData === w && !p._isDestroying && p.status !== Component.Null)
|
return popupWindows.some(p => p && p.notificationData === w && !p._isDestroying && p.status !== Component.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isValidWindow(p) {
|
function _isValidWindow(p) {
|
||||||
return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData
|
return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _canMakeRoomFor(wrapper) {
|
function _canMakeRoomFor(wrapper) {
|
||||||
const activeWindows = _active()
|
const activeWindows = _active();
|
||||||
if (activeWindows.length < maxTargetNotifications) {
|
if (activeWindows.length < maxTargetNotifications) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
if (!wrapper || !wrapper.notification) {
|
if (!wrapper || !wrapper.notification) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
const incomingUrgency = wrapper.notification.urgency || 0
|
const incomingUrgency = wrapper.notification.urgency || 0;
|
||||||
for (const p of activeWindows) {
|
for (const p of activeWindows) {
|
||||||
if (!p.notificationData || !p.notificationData.notification) {
|
if (!p.notificationData || !p.notificationData.notification) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
const existingUrgency = p.notificationData.notification.urgency || 0
|
const existingUrgency = p.notificationData.notification.urgency || 0;
|
||||||
if (existingUrgency < incomingUrgency) {
|
if (existingUrgency < incomingUrgency) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
if (existingUrgency === incomingUrgency) {
|
if (existingUrgency === incomingUrgency) {
|
||||||
const timer = p.notificationData.timer
|
const timer = p.notificationData.timer;
|
||||||
if (timer && !timer.running) {
|
if (timer && !timer.running) {
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _makeRoomForNew(wrapper) {
|
function _makeRoomForNew(wrapper) {
|
||||||
const activeWindows = _active()
|
const activeWindows = _active();
|
||||||
if (activeWindows.length < maxTargetNotifications) {
|
if (activeWindows.length < maxTargetNotifications) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const toRemove = _selectPopupToRemove(activeWindows, wrapper)
|
const toRemove = _selectPopupToRemove(activeWindows, wrapper);
|
||||||
if (toRemove && !toRemove.exiting) {
|
if (toRemove && !toRemove.exiting) {
|
||||||
toRemove.notificationData.removedByLimit = true
|
toRemove.notificationData.removedByLimit = true;
|
||||||
toRemove.notificationData.popup = false
|
toRemove.notificationData.popup = false;
|
||||||
if (toRemove.notificationData.timer) {
|
if (toRemove.notificationData.timer) {
|
||||||
toRemove.notificationData.timer.stop()
|
toRemove.notificationData.timer.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _selectPopupToRemove(activeWindows, incomingWrapper) {
|
function _selectPopupToRemove(activeWindows, incomingWrapper) {
|
||||||
const incomingUrgency = (incomingWrapper && incomingWrapper.notification) ? incomingWrapper.notification.urgency || 0 : 0
|
const incomingUrgency = (incomingWrapper && incomingWrapper.notification) ? incomingWrapper.notification.urgency || 0 : 0;
|
||||||
const sortedWindows = activeWindows.slice().sort((a, b) => {
|
const sortedWindows = activeWindows.slice().sort((a, b) => {
|
||||||
const aUrgency = (a.notificationData && a.notificationData.notification) ? a.notificationData.notification.urgency || 0 : 0
|
const aUrgency = (a.notificationData && a.notificationData.notification) ? a.notificationData.notification.urgency || 0 : 0;
|
||||||
const bUrgency = (b.notificationData && b.notificationData.notification) ? b.notificationData.notification.urgency || 0 : 0
|
const bUrgency = (b.notificationData && b.notificationData.notification) ? b.notificationData.notification.urgency || 0 : 0;
|
||||||
if (aUrgency !== bUrgency) {
|
if (aUrgency !== bUrgency) {
|
||||||
return aUrgency - bUrgency
|
return aUrgency - bUrgency;
|
||||||
}
|
}
|
||||||
const aTimer = a.notificationData && a.notificationData.timer
|
const aTimer = a.notificationData && a.notificationData.timer;
|
||||||
const bTimer = b.notificationData && b.notificationData.timer
|
const bTimer = b.notificationData && b.notificationData.timer;
|
||||||
const aRunning = aTimer && aTimer.running
|
const aRunning = aTimer && aTimer.running;
|
||||||
const bRunning = bTimer && bTimer.running
|
const bRunning = bTimer && bTimer.running;
|
||||||
if (aRunning !== bRunning) {
|
if (aRunning !== bRunning) {
|
||||||
return aRunning ? 1 : -1
|
return aRunning ? 1 : -1;
|
||||||
}
|
}
|
||||||
return b.screenY - a.screenY
|
return b.screenY - a.screenY;
|
||||||
})
|
});
|
||||||
return sortedWindows[0]
|
return sortedWindows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _sync(newWrappers) {
|
function _sync(newWrappers) {
|
||||||
for (const w of newWrappers) {
|
for (const w of newWrappers) {
|
||||||
if (w && !_hasWindowFor(w)) {
|
if (w && !_hasWindowFor(w)) {
|
||||||
insertNewestAtTop(w)
|
insertNewestAtTop(w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const p of popupWindows.slice()) {
|
for (const p of popupWindows.slice()) {
|
||||||
if (!_isValidWindow(p)) {
|
if (!_isValidWindow(p)) {
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
if (p.notificationData && newWrappers.indexOf(p.notificationData) === -1 && !p.exiting) {
|
if (p.notificationData && newWrappers.indexOf(p.notificationData) === -1 && !p.exiting) {
|
||||||
p.notificationData.removedByLimit = true
|
p.notificationData.removedByLimit = true;
|
||||||
p.notificationData.popup = false
|
p.notificationData.popup = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertNewestAtTop(wrapper) {
|
function insertNewestAtTop(wrapper) {
|
||||||
if (!wrapper) {
|
if (!wrapper)
|
||||||
return
|
return;
|
||||||
|
if (createBusy || pendingCreates.length > 0) {
|
||||||
|
_scheduleCreate(wrapper);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
_doInsertNewestAtTop(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _doInsertNewestAtTop(wrapper) {
|
||||||
|
if (!wrapper)
|
||||||
|
return;
|
||||||
for (const p of popupWindows) {
|
for (const p of popupWindows) {
|
||||||
if (!_isValidWindow(p)) {
|
if (!_isValidWindow(p))
|
||||||
continue
|
continue;
|
||||||
}
|
if (p.exiting)
|
||||||
if (p.exiting) {
|
continue;
|
||||||
continue
|
p.screenY = p.screenY + baseNotificationHeight;
|
||||||
}
|
|
||||||
p.screenY = p.screenY + baseNotificationHeight
|
|
||||||
}
|
}
|
||||||
const notificationId = wrapper && wrapper.notification ? wrapper.notification.id : ""
|
const notificationId = wrapper && wrapper.notification ? wrapper.notification.id : "";
|
||||||
const win = popupComponent.createObject(null, {
|
const win = popupComponent.createObject(null, {
|
||||||
"notificationData": wrapper,
|
"notificationData": wrapper,
|
||||||
"notificationId": notificationId,
|
"notificationId": notificationId,
|
||||||
"screenY": topMargin,
|
"screenY": topMargin,
|
||||||
"screen": manager.modelData
|
"screen": manager.modelData
|
||||||
})
|
});
|
||||||
if (!win) {
|
if (!win)
|
||||||
return
|
return;
|
||||||
}
|
|
||||||
if (!win.hasValidData) {
|
if (!win.hasValidData) {
|
||||||
win.destroy()
|
win.destroy();
|
||||||
return
|
return;
|
||||||
}
|
|
||||||
popupWindows.push(win)
|
|
||||||
if (!sweeper.running) {
|
|
||||||
sweeper.start()
|
|
||||||
}
|
}
|
||||||
|
popupWindows.push(win);
|
||||||
|
createBusy = true;
|
||||||
|
createTimer.restart();
|
||||||
|
if (!sweeper.running)
|
||||||
|
sweeper.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _active() {
|
function _active() {
|
||||||
return popupWindows.filter(p => _isValidWindow(p) && p.notificationData && p.notificationData.popup && !p.exiting)
|
return popupWindows.filter(p => _isValidWindow(p) && p.notificationData && p.notificationData.popup && !p.exiting);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _bottom() {
|
function _bottom() {
|
||||||
let b = null
|
let b = null;
|
||||||
let maxY = -1
|
let maxY = -1;
|
||||||
for (const p of _active()) {
|
for (const p of _active()) {
|
||||||
if (p.screenY > maxY) {
|
if (p.screenY > maxY) {
|
||||||
maxY = p.screenY
|
maxY = p.screenY;
|
||||||
b = p
|
b = p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return b
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _onPopupEntered(p) {}
|
function _onPopupEntered(p) {
|
||||||
|
}
|
||||||
|
|
||||||
function _onPopupExitFinished(p) {
|
function _onPopupExitFinished(p) {
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const windowId = p.toString()
|
const windowId = p.toString();
|
||||||
if (destroyingWindows.has(windowId)) {
|
if (destroyingWindows.has(windowId)) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
destroyingWindows.add(windowId)
|
destroyingWindows.add(windowId);
|
||||||
const i = popupWindows.indexOf(p)
|
const i = popupWindows.indexOf(p);
|
||||||
if (i !== -1) {
|
if (i !== -1) {
|
||||||
popupWindows.splice(i, 1)
|
popupWindows.splice(i, 1);
|
||||||
popupWindows = popupWindows.slice()
|
popupWindows = popupWindows.slice();
|
||||||
}
|
}
|
||||||
if (NotificationService.releaseWrapper && p.notificationData) {
|
if (NotificationService.releaseWrapper && p.notificationData) {
|
||||||
NotificationService.releaseWrapper(p.notificationData)
|
NotificationService.releaseWrapper(p.notificationData);
|
||||||
}
|
}
|
||||||
Qt.callLater(() => {
|
_scheduleDestroy(p);
|
||||||
if (p && p.destroy) {
|
Qt.callLater(() => destroyingWindows.delete(windowId));
|
||||||
try {
|
const survivors = _active().sort((a, b) => a.screenY - b.screenY);
|
||||||
p.destroy()
|
|
||||||
} catch (e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Qt.callLater(() => destroyingWindows.delete(windowId))
|
|
||||||
})
|
|
||||||
const survivors = _active().sort((a, b) => a.screenY - b.screenY)
|
|
||||||
for (let k = 0; k < survivors.length; ++k) {
|
for (let k = 0; k < survivors.length; ++k) {
|
||||||
survivors[k].screenY = topMargin + k * baseNotificationHeight
|
survivors[k].screenY = topMargin + k * baseNotificationHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cleanupAllWindows() {
|
function cleanupAllWindows() {
|
||||||
sweeper.stop()
|
sweeper.stop();
|
||||||
|
destroyTimer.stop();
|
||||||
|
createTimer.stop();
|
||||||
|
pendingDestroys = [];
|
||||||
|
pendingCreates = [];
|
||||||
|
createBusy = false;
|
||||||
for (const p of popupWindows.slice()) {
|
for (const p of popupWindows.slice()) {
|
||||||
if (p) {
|
if (p) {
|
||||||
try {
|
try {
|
||||||
if (p.forceExit) {
|
if (p.forceExit) {
|
||||||
p.forceExit()
|
p.forceExit();
|
||||||
} else if (p.destroy) {
|
} else if (p.destroy) {
|
||||||
p.destroy()
|
p.destroy();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
popupWindows = []
|
popupWindows = [];
|
||||||
destroyingWindows.clear()
|
destroyingWindows.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
onPopupWindowsChanged: {
|
onPopupWindowsChanged: {
|
||||||
if (popupWindows.length > 0 && !sweeper.running) {
|
if (popupWindows.length > 0 && !sweeper.running) {
|
||||||
sweeper.start()
|
sweeper.start();
|
||||||
} else if (popupWindows.length === 0 && sweeper.running) {
|
} else if (popupWindows.length === 0 && sweeper.running) {
|
||||||
sweeper.stop()
|
sweeper.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user