diff options
| author | nippy <you@example.com> | 2026-04-18 13:49:56 +0200 |
|---|---|---|
| committer | nippy <you@example.com> | 2026-04-18 13:49:56 +0200 |
| commit | 5d94c0a7d44a2255b81815a52a7056a94a39842d (patch) | |
| tree | 759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml | |
| parent | e79cdf210b267f21a186255ce1a4d50029439d54 (diff) | |
| download | RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip | |
update Raveos themes
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml')
| -rw-r--r-- | raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml new file mode 100644 index 0000000..02bfc0f --- /dev/null +++ b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml @@ -0,0 +1,239 @@ +import QtQuick +import qs.Common +import qs.Services + +QtObject { + id: manager + + property var modelData + property int topMargin: 0 + readonly property bool compactMode: SettingsData.notificationCompactMode + readonly property real cardPadding: compactMode ? Theme.notificationCardPaddingCompact : Theme.notificationCardPadding + readonly property real popupIconSize: compactMode ? Theme.notificationIconSizeCompact : Theme.notificationIconSizeNormal + readonly property real actionButtonHeight: compactMode ? 20 : 24 + readonly property real contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS + readonly property real popupSpacing: compactMode ? 0 : Theme.spacingXS + readonly property real collapsedContentHeight: Math.max(popupIconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2)) + readonly property int baseNotificationHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing + popupSpacing + property var popupWindows: [] + property var destroyingWindows: new Set() + property var pendingDestroys: [] + property int destroyDelayMs: 100 + property Component popupComponent + + popupComponent: Component { + NotificationPopup { + onExitFinished: manager._onPopupExitFinished(this) + onPopupHeightChanged: manager._onPopupHeightChanged(this) + } + } + + property Connections notificationConnections + + notificationConnections: Connections { + function onVisibleNotificationsChanged() { + manager._sync(NotificationService.visibleNotifications); + } + + target: NotificationService + } + + 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(); + } + + sweeper: Timer { + interval: 500 + running: false + repeat: true + onTriggered: { + const toRemove = []; + for (const p of popupWindows) { + if (!p) { + toRemove.push(p); + continue; + } + const isZombie = p.status === Component.Null || (!p.visible && !p.exiting) || (!p.notificationData && !p._isDestroying) || (!p.hasValidData && !p._isDestroying); + if (isZombie) { + toRemove.push(p); + if (p.forceExit) { + p.forceExit(); + } else if (p.destroy) { + try { + p.destroy(); + } catch (e) {} + } + } + } + if (toRemove.length) { + popupWindows = popupWindows.filter(p => toRemove.indexOf(p) === -1); + _repositionAll(); + } + if (popupWindows.length === 0) + sweeper.stop(); + } + } + + function _hasWindowFor(w) { + return popupWindows.some(p => p && p.notificationData === w && !p._isDestroying && p.status !== Component.Null); + } + + function _isValidWindow(p) { + return p && p.status !== Component.Null && !p._isDestroying && p.hasValidData; + } + + function _isFocusedScreen() { + if (!SettingsData.notificationFocusedMonitor) + return true; + const focused = CompositorService.getFocusedScreen(); + return focused && manager.modelData && focused.name === manager.modelData.name; + } + + function _sync(newWrappers) { + for (const p of popupWindows.slice()) { + if (!_isValidWindow(p) || p.exiting) + continue; + if (p.notificationData && newWrappers.indexOf(p.notificationData) === -1) { + p.notificationData.removedByLimit = true; + p.notificationData.popup = false; + } + } + for (const w of newWrappers) { + if (w && !_hasWindowFor(w) && _isFocusedScreen()) + _insertAtTop(w); + } + } + + function _popupHeight(p) { + return (p.alignedHeight || p.implicitHeight || (baseNotificationHeight - popupSpacing)) + popupSpacing; + } + + function _insertAtTop(wrapper) { + if (!wrapper) + return; + const notificationId = wrapper?.notification ? wrapper.notification.id : ""; + const win = popupComponent.createObject(null, { + "notificationData": wrapper, + "notificationId": notificationId, + "screenY": topMargin, + "screen": manager.modelData + }); + if (!win) + return; + if (!win.hasValidData) { + win.destroy(); + return; + } + popupWindows.unshift(win); + _repositionAll(); + if (!sweeper.running) + sweeper.start(); + } + + function _repositionAll() { + const active = popupWindows.filter(p => _isValidWindow(p) && p.notificationData?.popup && !p.exiting); + + const pinnedSlots = []; + for (const p of active) { + if (!p.hovered) + continue; + pinnedSlots.push({ + y: p.screenY, + end: p.screenY + _popupHeight(p) + }); + } + pinnedSlots.sort((a, b) => a.y - b.y); + + let currentY = topMargin; + for (const win of active) { + if (win.hovered) + continue; + for (const slot of pinnedSlots) { + if (currentY >= slot.y - 1 && currentY < slot.end) + currentY = slot.end; + } + win.screenY = currentY; + currentY += _popupHeight(win); + } + } + + function _onPopupHeightChanged(p) { + if (!p || p.exiting || p._isDestroying) + return; + if (popupWindows.indexOf(p) === -1) + return; + _repositionAll(); + } + + function _onPopupExitFinished(p) { + if (!p) + return; + const windowId = p.toString(); + if (destroyingWindows.has(windowId)) + return; + destroyingWindows.add(windowId); + const i = popupWindows.indexOf(p); + if (i !== -1) { + popupWindows.splice(i, 1); + popupWindows = popupWindows.slice(); + } + if (NotificationService.releaseWrapper && p.notificationData) + NotificationService.releaseWrapper(p.notificationData); + _scheduleDestroy(p); + Qt.callLater(() => destroyingWindows.delete(windowId)); + _repositionAll(); + } + + function cleanupAllWindows() { + sweeper.stop(); + destroyTimer.stop(); + pendingDestroys = []; + for (const p of popupWindows.slice()) { + if (p) { + try { + if (p.forceExit) { + p.forceExit(); + } else if (p.destroy) { + p.destroy(); + } + } catch (e) {} + } + } + popupWindows = []; + destroyingWindows.clear(); + } + + onPopupWindowsChanged: { + if (popupWindows.length > 0 && !sweeper.running) { + sweeper.start(); + } else if (popupWindows.length === 0 && sweeper.running) { + sweeper.stop(); + } + } +} |