summaryrefslogtreecommitdiff
path: root/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup
diff options
context:
space:
mode:
Diffstat (limited to 'raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup')
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopup.qml1164
-rw-r--r--raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml239
2 files changed, 0 insertions, 1403 deletions
diff --git a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopup.qml b/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopup.qml
deleted file mode 100644
index 8c9f7f4..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopup.qml
+++ /dev/null
@@ -1,1164 +0,0 @@
-import QtQuick
-import QtQuick.Controls
-import Quickshell
-import Quickshell.Wayland
-import Quickshell.Services.Notifications
-import qs.Common
-import qs.Services
-import qs.Widgets
-
-PanelWindow {
- id: win
-
- WindowBlur {
- targetWindow: win
- blurX: content.x + content.cardInset + swipeTx.x + tx.x
- blurY: content.y + content.cardInset + swipeTx.y + tx.y
- blurWidth: !win._finalized ? Math.max(0, content.width - content.cardInset * 2) : 0
- blurHeight: !win._finalized ? Math.max(0, content.height - content.cardInset * 2) : 0
- blurRadius: Theme.cornerRadius
- }
-
- WlrLayershell.namespace: "dms:notification-popup"
-
- required property var notificationData
- required property string notificationId
- readonly property bool hasValidData: notificationData && notificationData.notification
- readonly property alias hovered: cardHoverHandler.hovered
- property int screenY: 0
- property bool exiting: false
- property bool _isDestroying: false
- property bool _finalized: false
- property real _lastReportedAlignedHeight: -1
- property real _storedTopMargin: 0
- property real _storedBottomMargin: 0
- readonly property string clearText: I18n.tr("Dismiss")
- property bool descriptionExpanded: false
- readonly property bool hasExpandableBody: (notificationData?.htmlBody || "").replace(/<[^>]*>/g, "").trim().length > 0
- onDescriptionExpandedChanged: {
- popupHeightChanged();
- }
- onImplicitHeightChanged: {
- const aligned = Theme.px(implicitHeight, dpr);
- if (Math.abs(aligned - _lastReportedAlignedHeight) < 0.5)
- return;
- _lastReportedAlignedHeight = aligned;
- popupHeightChanged();
- }
-
- 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 contentSpacing: compactMode ? Theme.spacingXS : Theme.spacingS
- readonly property real contentBottomClearance: 8
- readonly property real actionButtonHeight: compactMode ? 20 : 24
- readonly property real collapsedContentHeight: Math.max(popupIconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2)) + contentBottomClearance
- readonly property real privacyCollapsedContentHeight: Math.max(popupIconSize, Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2) + contentBottomClearance
- readonly property real basePopupHeight: cardPadding * 2 + collapsedContentHeight + actionButtonHeight + contentSpacing
- readonly property real basePopupHeightPrivacy: cardPadding * 2 + privacyCollapsedContentHeight + actionButtonHeight + contentSpacing
-
- signal entered
- signal exitStarted
- signal exitFinished
- signal popupHeightChanged
-
- function startExit() {
- if (exiting || _isDestroying) {
- return;
- }
- exiting = true;
- exitStarted();
- exitAnim.restart();
- exitWatchdog.restart();
- if (NotificationService.removeFromVisibleNotifications)
- NotificationService.removeFromVisibleNotifications(win.notificationData);
- }
-
- function forceExit() {
- if (_isDestroying) {
- return;
- }
- _isDestroying = true;
- exiting = true;
- visible = false;
- exitWatchdog.stop();
- finalizeExit("forced");
- }
-
- function finalizeExit(reason) {
- if (_finalized) {
- return;
- }
-
- _finalized = true;
- _isDestroying = true;
- exitWatchdog.stop();
- wrapperConn.enabled = false;
- wrapperConn.target = null;
- win.exitFinished();
- }
-
- visible: !_finalized
- WlrLayershell.layer: {
- const envLayer = Quickshell.env("DMS_NOTIFICATION_LAYER");
- if (envLayer) {
- switch (envLayer) {
- case "bottom":
- return WlrLayershell.Bottom;
- case "overlay":
- return WlrLayershell.Overlay;
- case "background":
- return WlrLayershell.Background;
- case "top":
- return WlrLayershell.Top;
- }
- }
-
- if (!notificationData)
- return WlrLayershell.Top;
-
- SettingsData.notificationOverlayEnabled;
-
- const shouldUseOverlay = (SettingsData.notificationOverlayEnabled) || (notificationData.urgency === NotificationUrgency.Critical);
-
- return shouldUseOverlay ? WlrLayershell.Overlay : WlrLayershell.Top;
- }
- WlrLayershell.exclusiveZone: -1
- WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
- color: "transparent"
- readonly property real contentImplicitWidth: screen ? Math.min(400, Math.max(320, screen.width * 0.23)) : 380
- readonly property real contentImplicitHeight: {
- if (SettingsData.notificationPopupPrivacyMode && !descriptionExpanded)
- return basePopupHeightPrivacy;
- if (!descriptionExpanded)
- return basePopupHeight;
- const bodyTextHeight = bodyText.contentHeight || 0;
- const collapsedBodyHeight = Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2);
- if (bodyTextHeight > collapsedBodyHeight + 2)
- return basePopupHeight + bodyTextHeight - collapsedBodyHeight;
- return basePopupHeight;
- }
- implicitWidth: contentImplicitWidth + (windowShadowPad * 2)
- implicitHeight: contentImplicitHeight + (windowShadowPad * 2)
-
- Behavior on implicitHeight {
- enabled: !exiting && !_isDestroying
- NumberAnimation {
- id: implicitHeightAnim
- duration: descriptionExpanded ? Theme.notificationExpandDuration : Theme.notificationCollapseDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.emphasized
- }
- }
-
- onHasValidDataChanged: {
- if (!hasValidData && !exiting && !_isDestroying) {
- forceExit();
- }
- }
- Component.onCompleted: {
- _lastReportedAlignedHeight = Theme.px(implicitHeight, dpr);
- _storedTopMargin = getTopMargin();
- _storedBottomMargin = getBottomMargin();
- if (SettingsData.notificationPopupPrivacyMode)
- descriptionExpanded = false;
- if (hasValidData) {
- Qt.callLater(() => enterX.restart());
- } else {
- forceExit();
- }
- }
- onNotificationDataChanged: {
- if (!_isDestroying) {
- if (SettingsData.notificationPopupPrivacyMode)
- descriptionExpanded = false;
- wrapperConn.target = win.notificationData || null;
- notificationConn.target = (win.notificationData && win.notificationData.notification && win.notificationData.notification.Retainable) || null;
- }
- }
- onEntered: {
- if (!_isDestroying) {
- enterDelay.start();
- }
- }
- Component.onDestruction: {
- _isDestroying = true;
- exitWatchdog.stop();
- if (notificationData && notificationData.timer) {
- notificationData.timer.stop();
- }
- }
-
- property bool isTopCenter: SettingsData.notificationPopupPosition === -1
- property bool isBottomCenter: SettingsData.notificationPopupPosition === SettingsData.Position.BottomCenter
- property bool isCenterPosition: isTopCenter || isBottomCenter
- readonly property real maxPopupShadowBlurPx: Math.max((Theme.elevationLevel3 && Theme.elevationLevel3.blurPx !== undefined) ? Theme.elevationLevel3.blurPx : 12, (Theme.elevationLevel4 && Theme.elevationLevel4.blurPx !== undefined) ? Theme.elevationLevel4.blurPx : 16)
- readonly property real maxPopupShadowOffsetXPx: Math.max(Math.abs(Theme.elevationOffsetX(Theme.elevationLevel3)), Math.abs(Theme.elevationOffsetX(Theme.elevationLevel4)))
- readonly property real maxPopupShadowOffsetYPx: Math.max(Math.abs(Theme.elevationOffsetY(Theme.elevationLevel3, 6)), Math.abs(Theme.elevationOffsetY(Theme.elevationLevel4, 8)))
- readonly property real windowShadowPad: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled ? Theme.snap(Math.max(16, maxPopupShadowBlurPx + Math.max(maxPopupShadowOffsetXPx, maxPopupShadowOffsetYPx) + 8), dpr) : 0
-
- anchors.top: true
- anchors.left: true
- anchors.bottom: false
- anchors.right: false
-
- mask: contentInputMask
-
- Region {
- id: contentInputMask
- item: contentMaskRect
- }
-
- Item {
- id: contentMaskRect
- visible: false
- x: content.x
- y: content.y
- width: alignedWidth
- height: alignedHeight
- }
-
- margins {
- top: getWindowTopMargin()
- bottom: 0
- left: getWindowLeftMargin()
- right: 0
- }
-
- function getBarInfo() {
- if (!screen)
- return {
- topBar: 0,
- bottomBar: 0,
- leftBar: 0,
- rightBar: 0
- };
- return SettingsData.getAdjacentBarInfo(screen, SettingsData.notificationPopupPosition, {
- id: "notification-popup",
- screenPreferences: [screen.name],
- autoHide: false
- });
- }
-
- function getTopMargin() {
- const popupPos = SettingsData.notificationPopupPosition;
- const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
- if (!isTop)
- return 0;
-
- const barInfo = getBarInfo();
- const base = barInfo.topBar > 0 ? barInfo.topBar : Theme.popupDistance;
- return base + screenY;
- }
-
- function getBottomMargin() {
- const popupPos = SettingsData.notificationPopupPosition;
- const isBottom = isBottomCenter || popupPos === SettingsData.Position.Bottom || popupPos === SettingsData.Position.Right;
- if (!isBottom)
- return 0;
-
- const barInfo = getBarInfo();
- const base = barInfo.bottomBar > 0 ? barInfo.bottomBar : Theme.popupDistance;
- return base + screenY;
- }
-
- function getLeftMargin() {
- if (isCenterPosition)
- return screen ? (screen.width - alignedWidth) / 2 : 0;
-
- const popupPos = SettingsData.notificationPopupPosition;
- const isLeft = popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom;
- if (!isLeft)
- return 0;
-
- const barInfo = getBarInfo();
- return barInfo.leftBar > 0 ? barInfo.leftBar : Theme.popupDistance;
- }
-
- function getRightMargin() {
- if (isCenterPosition)
- return 0;
-
- const popupPos = SettingsData.notificationPopupPosition;
- const isRight = popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Right;
- if (!isRight)
- return 0;
-
- const barInfo = getBarInfo();
- return barInfo.rightBar > 0 ? barInfo.rightBar : Theme.popupDistance;
- }
-
- function getContentX() {
- if (!screen)
- return 0;
-
- const popupPos = SettingsData.notificationPopupPosition;
- const barLeft = getLeftMargin();
- const barRight = getRightMargin();
-
- if (isCenterPosition)
- return Theme.snap((screen.width - alignedWidth) / 2, dpr);
- if (popupPos === SettingsData.Position.Left || popupPos === SettingsData.Position.Bottom)
- return Theme.snap(barLeft, dpr);
- return Theme.snap(screen.width - alignedWidth - barRight, dpr);
- }
-
- function getContentY() {
- if (!screen)
- return 0;
-
- const popupPos = SettingsData.notificationPopupPosition;
- const barTop = getTopMargin();
- const barBottom = getBottomMargin();
- const isTop = isTopCenter || popupPos === SettingsData.Position.Top || popupPos === SettingsData.Position.Left;
- if (isTop)
- return Theme.snap(barTop, dpr);
- return Theme.snap(screen.height - alignedHeight - barBottom, dpr);
- }
-
- function getWindowLeftMargin() {
- if (!screen)
- return 0;
- return Theme.snap(getContentX() - windowShadowPad, dpr);
- }
-
- function getWindowTopMargin() {
- if (!screen)
- return 0;
- return Theme.snap(getContentY() - windowShadowPad, dpr);
- }
-
- readonly property bool screenValid: win.screen && !_isDestroying
- readonly property real dpr: screenValid ? CompositorService.getScreenScale(win.screen) : 1
- readonly property real alignedWidth: Theme.px(Math.max(0, implicitWidth - (windowShadowPad * 2)), dpr)
- readonly property real alignedHeight: Theme.px(Math.max(0, implicitHeight - (windowShadowPad * 2)), dpr)
-
- Item {
- id: content
-
- x: Theme.snap(windowShadowPad, dpr)
- y: Theme.snap(windowShadowPad, dpr)
- width: alignedWidth
- height: alignedHeight
- visible: !win._finalized
- scale: cardHoverHandler.hovered ? 1.01 : 1.0
- transformOrigin: Item.Center
-
- Behavior on scale {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- property real swipeOffset: 0
- readonly property real dismissThreshold: isCenterPosition ? height * 0.4 : width * 0.35
- readonly property real swipeFadeStartRatio: 0.75
- readonly property real swipeTravelDistance: isCenterPosition ? height : width
- readonly property real swipeFadeStartOffset: swipeTravelDistance * swipeFadeStartRatio
- readonly property real swipeFadeDistance: Math.max(1, swipeTravelDistance - swipeFadeStartOffset)
- readonly property bool swipeActive: swipeDragHandler.active
- property bool swipeDismissing: false
-
- readonly property bool shadowsAllowed: Theme.elevationEnabled && SettingsData.notificationPopupShadowEnabled
- readonly property var elevLevel: cardHoverHandler.hovered ? Theme.elevationLevel4 : Theme.elevationLevel3
- readonly property real cardInset: Theme.snap(4, win.dpr)
- readonly property real shadowRenderPadding: shadowsAllowed ? Theme.snap(Math.max(16, shadowBlurPx + Math.max(Math.abs(shadowOffsetX), Math.abs(shadowOffsetY)) + 8), win.dpr) : 0
- property real shadowBlurPx: shadowsAllowed ? (elevLevel && elevLevel.blurPx !== undefined ? elevLevel.blurPx : 12) : 0
- property real shadowOffsetX: shadowsAllowed ? Theme.elevationOffsetX(elevLevel) : 0
- property real shadowOffsetY: shadowsAllowed ? Theme.elevationOffsetY(elevLevel, 6) : 0
-
- Behavior on shadowBlurPx {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- Behavior on shadowOffsetX {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- Behavior on shadowOffsetY {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- ElevationShadow {
- id: bgShadowLayer
- anchors.fill: parent
- anchors.margins: -content.shadowRenderPadding
- level: content.elevLevel
- fallbackOffset: 6
- shadowBlurPx: content.shadowBlurPx
- shadowOffsetX: content.shadowOffsetX
- shadowOffsetY: content.shadowOffsetY
- shadowColor: content.shadowsAllowed && content.elevLevel ? Theme.elevationShadowColor(content.elevLevel) : "transparent"
- shadowEnabled: !win._isDestroying && win.screenValid && content.shadowsAllowed
- layer.textureSize: Qt.size(Math.round(width * win.dpr), Math.round(height * win.dpr))
- layer.textureMirroring: ShaderEffectSource.MirrorVertically
-
- sourceRect.anchors.fill: undefined
- sourceRect.x: content.shadowRenderPadding + content.cardInset
- sourceRect.y: content.shadowRenderPadding + content.cardInset
- sourceRect.width: Math.max(0, content.width - (content.cardInset * 2))
- sourceRect.height: Math.max(0, content.height - (content.cardInset * 2))
- sourceRect.radius: Theme.cornerRadius
- sourceRect.color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
- sourceRect.border.color: notificationData && notificationData.urgency === NotificationUrgency.Critical ? Theme.withAlpha(Theme.primary, 0.3) : Theme.withAlpha(Theme.outline, 0.08)
- sourceRect.border.width: notificationData && notificationData.urgency === NotificationUrgency.Critical ? 2 : 0
-
- Rectangle {
- x: bgShadowLayer.sourceRect.x
- y: bgShadowLayer.sourceRect.y
- width: bgShadowLayer.sourceRect.width
- height: bgShadowLayer.sourceRect.height
- radius: bgShadowLayer.sourceRect.radius
- visible: notificationData && notificationData.urgency === NotificationUrgency.Critical
- opacity: 1
- clip: true
-
- gradient: Gradient {
- orientation: Gradient.Horizontal
-
- GradientStop {
- position: 0
- color: Theme.primary
- }
-
- GradientStop {
- position: 0.02
- color: Theme.primary
- }
-
- GradientStop {
- position: 0.021
- color: "transparent"
- }
- }
- }
- }
-
- Rectangle {
- anchors.fill: parent
- anchors.margins: content.cardInset
- radius: Theme.cornerRadius
- color: "transparent"
- border.color: BlurService.borderColor
- border.width: BlurService.borderWidth
- z: 100
- }
-
- Item {
- id: backgroundContainer
- anchors.fill: parent
- anchors.margins: content.cardInset
- clip: true
-
- HoverHandler {
- id: cardHoverHandler
- }
-
- Connections {
- target: cardHoverHandler
- function onHoveredChanged() {
- if (!notificationData || win.exiting || win._isDestroying)
- return;
- if (cardHoverHandler.hovered) {
- if (notificationData.timer)
- notificationData.timer.stop();
- } else if (notificationData.popup && notificationData.timer) {
- notificationData.timer.restart();
- }
- }
- }
-
- LayoutMirroring.enabled: I18n.isRtl
- LayoutMirroring.childrenInherit: true
-
- Item {
- id: notificationContent
-
- readonly property real expandedTextHeight: bodyText.contentHeight || 0
- readonly property real collapsedBodyHeight: Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2)
- readonly property real effectiveCollapsedHeight: (SettingsData.notificationPopupPrivacyMode && !descriptionExpanded) ? win.privacyCollapsedContentHeight : win.collapsedContentHeight
- readonly property real extraHeight: (descriptionExpanded && expandedTextHeight > collapsedBodyHeight + 2) ? (expandedTextHeight - collapsedBodyHeight) : 0
-
- anchors.top: parent.top
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.topMargin: cardPadding
- anchors.leftMargin: Theme.spacingL
- anchors.rightMargin: Theme.spacingL + Theme.notificationHoverRevealMargin
- height: effectiveCollapsedHeight + extraHeight
- clip: SettingsData.notificationPopupPrivacyMode && !descriptionExpanded
-
- DankCircularImage {
- id: iconContainer
-
- readonly property string rawImage: notificationData?.image || ""
- readonly property string iconFromImage: {
- if (rawImage.startsWith("image://icon/"))
- return rawImage.substring(13);
- return "";
- }
- readonly property bool imageHasSpecialPrefix: {
- const icon = iconFromImage;
- return icon.startsWith("material:") || icon.startsWith("svg:") || icon.startsWith("unicode:") || icon.startsWith("image:");
- }
- readonly property bool hasNotificationImage: rawImage !== "" && !rawImage.startsWith("image://icon/")
- readonly property bool needsImagePersist: hasNotificationImage && rawImage.startsWith("image://qsimage/") && !notificationData.persistedImagePath
-
- width: popupIconSize
- height: popupIconSize
- anchors.left: parent.left
- anchors.top: parent.top
- anchors.topMargin: {
- if (SettingsData.notificationPopupPrivacyMode && !descriptionExpanded) {
- const headerSummary = Theme.fontSizeSmall * 1.2 + Theme.fontSizeMedium * 1.2;
- return Math.max(0, headerSummary / 2 - popupIconSize / 2);
- }
- if (descriptionExpanded)
- return Math.max(0, Theme.fontSizeSmall * 1.2 + (Theme.fontSizeMedium * 1.2 + Theme.fontSizeSmall * 1.2 * (compactMode ? 1 : 2)) / 2 - popupIconSize / 2);
- return Math.max(0, Theme.fontSizeSmall * 1.2 + (textContainer.height - Theme.fontSizeSmall * 1.2) / 2 - popupIconSize / 2);
- }
-
- imageSource: {
- if (!notificationData)
- return "";
- if (hasNotificationImage)
- return notificationData.cleanImage || "";
- if (imageHasSpecialPrefix)
- return "";
- const appIcon = notificationData.appIcon;
- if (!appIcon)
- return iconFromImage ? Paths.resolveIconUrl(iconFromImage) : "";
- if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://") || appIcon.includes("/"))
- return appIcon;
- if (appIcon.startsWith("material:") || appIcon.startsWith("svg:") || appIcon.startsWith("unicode:") || appIcon.startsWith("image:"))
- return "";
- return Paths.resolveIconPath(appIcon);
- }
-
- hasImage: hasNotificationImage
- fallbackIcon: {
- if (imageHasSpecialPrefix)
- return iconFromImage;
- return notificationData?.appIcon || iconFromImage || "";
- }
- fallbackText: {
- const appName = notificationData?.appName || "?";
- return appName.charAt(0).toUpperCase();
- }
-
- onImageStatusChanged: {
- if (imageStatus === Image.Ready && needsImagePersist) {
- const cachePath = NotificationService.getImageCachePath(notificationData);
- saveImageToFile(cachePath);
- }
- }
-
- onImageSaved: filePath => {
- if (!notificationData)
- return;
- notificationData.persistedImagePath = filePath;
- const wrapperId = notificationData.notification?.id?.toString() || "";
- if (wrapperId)
- NotificationService.updateHistoryImage(wrapperId, filePath);
- }
- }
-
- Column {
- id: textContainer
-
- anchors.left: iconContainer.right
- anchors.leftMargin: Theme.spacingM
- anchors.right: parent.right
- anchors.top: parent.top
- spacing: Theme.notificationContentSpacing
-
- Row {
- id: headerRow
- width: parent.width
- spacing: Theme.spacingXS
- visible: headerAppNameText.text.length > 0 || headerTimeText.text.length > 0
-
- StyledText {
- id: headerAppNameText
- text: notificationData ? (notificationData.appName || "") : ""
- color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Font.Normal
- elide: Text.ElideRight
- maximumLineCount: 1
- width: Math.min(implicitWidth, parent.width - headerSeparator.implicitWidth - headerTimeText.implicitWidth - parent.spacing * 2)
- }
-
- StyledText {
- id: headerSeparator
- text: (headerAppNameText.text.length > 0 && headerTimeText.text.length > 0) ? " • " : ""
- color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Font.Normal
- }
-
- StyledText {
- id: headerTimeText
- text: notificationData ? (notificationData.timeStr || "") : ""
- color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Font.Normal
- }
- }
-
- StyledText {
- text: notificationData ? (notificationData.summary || "") : ""
- color: Theme.surfaceText
- font.pixelSize: Theme.fontSizeMedium
- font.weight: Font.Medium
- width: parent.width
- elide: Text.ElideRight
- horizontalAlignment: Text.AlignLeft
- maximumLineCount: 1
- visible: text.length > 0
- }
-
- StyledText {
- id: bodyText
- property bool hasMoreText: truncated
-
- text: notificationData ? (notificationData.htmlBody || "") : ""
- color: Theme.surfaceVariantText
- font.pixelSize: Theme.fontSizeSmall
- width: parent.width
- elide: descriptionExpanded ? Text.ElideNone : Text.ElideRight
- horizontalAlignment: Text.AlignLeft
- maximumLineCount: descriptionExpanded ? -1 : (compactMode ? 1 : 2)
- wrapMode: Text.WrapAtWordBoundaryOrAnywhere
- visible: text.length > 0
- opacity: (SettingsData.notificationPopupPrivacyMode && !descriptionExpanded) ? 0 : 1
- linkColor: Theme.primary
- onLinkActivated: link => Qt.openUrlExternally(link)
-
- MouseArea {
- anchors.fill: parent
- cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : (bodyText.hasMoreText || descriptionExpanded) ? Qt.PointingHandCursor : Qt.ArrowCursor
-
- onClicked: mouse => {
- if (!parent.hoveredLink && (bodyText.hasMoreText || descriptionExpanded))
- win.descriptionExpanded = !win.descriptionExpanded;
- }
-
- propagateComposedEvents: true
- onPressed: mouse => {
- if (parent.hoveredLink)
- mouse.accepted = false;
- }
- onReleased: mouse => {
- if (parent.hoveredLink)
- mouse.accepted = false;
- }
- }
- }
-
- StyledText {
- text: I18n.tr("Message Content", "notification privacy mode placeholder")
- color: Theme.surfaceVariantText
- font.pixelSize: Theme.fontSizeSmall
- width: parent.width
- visible: SettingsData.notificationPopupPrivacyMode && !descriptionExpanded && win.hasExpandableBody
- }
- }
- }
-
- DankActionButton {
- id: closeButton
-
- anchors.right: parent.right
- anchors.top: parent.top
- anchors.topMargin: cardPadding
- anchors.rightMargin: Theme.spacingL
- iconName: "close"
- iconSize: compactMode ? 14 : 16
- buttonSize: compactMode ? 20 : 24
- z: 15
-
- onClicked: {
- if (notificationData && !win.exiting)
- notificationData.popup = false;
- }
- }
-
- DankActionButton {
- id: expandButton
-
- anchors.right: closeButton.left
- anchors.rightMargin: Theme.spacingXS
- anchors.top: parent.top
- anchors.topMargin: cardPadding
- iconName: descriptionExpanded ? "expand_less" : "expand_more"
- iconSize: compactMode ? 14 : 16
- buttonSize: compactMode ? 20 : 24
- z: 15
- visible: SettingsData.notificationPopupPrivacyMode && win.hasExpandableBody
-
- onClicked: {
- if (win.hasExpandableBody)
- win.descriptionExpanded = !win.descriptionExpanded;
- }
- }
-
- Row {
- visible: cardHoverHandler.hovered
- opacity: visible ? 1 : 0
- anchors.right: clearButton.visible ? clearButton.left : parent.right
- anchors.rightMargin: clearButton.visible ? contentSpacing : Theme.spacingL
- anchors.top: notificationContent.bottom
- anchors.topMargin: contentSpacing
- spacing: contentSpacing
- z: 20
-
- Behavior on opacity {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
-
- Repeater {
- model: notificationData ? (notificationData.actions || []) : []
-
- Rectangle {
- property bool isHovered: false
-
- width: Math.max(actionText.implicitWidth + Theme.spacingM, Theme.notificationActionMinWidth)
- height: actionButtonHeight
- radius: Theme.notificationButtonCornerRadius
- color: isHovered ? Theme.withAlpha(Theme.primary, Theme.stateLayerHover) : "transparent"
-
- StyledText {
- id: actionText
-
- text: modelData.text || "Open"
- color: parent.isHovered ? Theme.primary : Theme.surfaceVariantText
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Font.Medium
- anchors.centerIn: parent
- elide: Text.ElideRight
- }
-
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- cursorShape: Qt.PointingHandCursor
- acceptedButtons: Qt.LeftButton
- onEntered: parent.isHovered = true
- onExited: parent.isHovered = false
- onClicked: {
- if (modelData && modelData.invoke)
- modelData.invoke();
- if (notificationData && !win.exiting)
- notificationData.popup = false;
- }
- }
- }
- }
- }
-
- Rectangle {
- id: clearButton
-
- property bool isHovered: false
- readonly property int actionCount: notificationData ? (notificationData.actions || []).length : 0
-
- visible: actionCount < 3 && cardHoverHandler.hovered
- opacity: visible ? 1 : 0
- Behavior on opacity {
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Theme.standardEasing
- }
- }
- anchors.right: parent.right
- anchors.rightMargin: Theme.spacingL
- anchors.top: notificationContent.bottom
- anchors.topMargin: contentSpacing
- width: Math.max(clearTextLabel.implicitWidth + Theme.spacingM, Theme.notificationActionMinWidth)
- height: actionButtonHeight
- radius: Theme.notificationButtonCornerRadius
- color: isHovered ? Theme.withAlpha(Theme.primary, Theme.stateLayerHover) : "transparent"
- z: 20
-
- StyledText {
- id: clearTextLabel
-
- text: win.clearText
- color: clearButton.isHovered ? Theme.primary : Theme.surfaceVariantText
- font.pixelSize: Theme.fontSizeSmall
- font.weight: Font.Medium
- anchors.centerIn: parent
- }
-
- MouseArea {
- anchors.fill: parent
- hoverEnabled: true
- cursorShape: Qt.PointingHandCursor
- acceptedButtons: Qt.LeftButton
- onEntered: clearButton.isHovered = true
- onExited: clearButton.isHovered = false
- onClicked: {
- if (notificationData && !win.exiting)
- NotificationService.dismissNotification(notificationData);
- }
- }
- }
-
- MouseArea {
- id: cardHoverArea
-
- anchors.fill: parent
- hoverEnabled: true
- acceptedButtons: Qt.LeftButton | Qt.RightButton
- cursorShape: Qt.PointingHandCursor
- propagateComposedEvents: true
- z: -1
- onClicked: mouse => {
- if (!notificationData || win.exiting)
- return;
- if (mouse.button === Qt.RightButton) {
- popupContextMenu.popup();
- } else if (mouse.button === Qt.LeftButton) {
- const canExpand = bodyText.hasMoreText || win.descriptionExpanded || (SettingsData.notificationPopupPrivacyMode && win.hasExpandableBody);
- if (canExpand) {
- win.descriptionExpanded = !win.descriptionExpanded;
- } else if (notificationData.actions && notificationData.actions.length > 0) {
- notificationData.actions[0].invoke();
- NotificationService.dismissNotification(notificationData);
- } else {
- notificationData.popup = false;
- }
- }
- }
- }
- }
-
- DragHandler {
- id: swipeDragHandler
- target: null
- xAxis.enabled: !isCenterPosition
- yAxis.enabled: isCenterPosition
-
- onActiveChanged: {
- if (active || win.exiting || content.swipeDismissing)
- return;
-
- if (Math.abs(content.swipeOffset) > content.dismissThreshold) {
- content.swipeDismissing = true;
- swipeDismissAnim.start();
- } else {
- content.swipeOffset = 0;
- }
- }
-
- onTranslationChanged: {
- if (win.exiting)
- return;
-
- const raw = isCenterPosition ? translation.y : translation.x;
- if (isTopCenter) {
- content.swipeOffset = Math.min(0, raw);
- } else if (isBottomCenter) {
- content.swipeOffset = Math.max(0, raw);
- } else {
- const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
- content.swipeOffset = isLeft ? Math.min(0, raw) : Math.max(0, raw);
- }
- }
- }
-
- opacity: {
- const swipeAmount = Math.abs(content.swipeOffset);
- if (swipeAmount <= content.swipeFadeStartOffset)
- return 1;
- const fadeProgress = (swipeAmount - content.swipeFadeStartOffset) / content.swipeFadeDistance;
- return Math.max(0, 1 - fadeProgress);
- }
-
- Behavior on opacity {
- enabled: !content.swipeActive
- NumberAnimation {
- duration: Theme.shortDuration
- }
- }
-
- Behavior on swipeOffset {
- enabled: !content.swipeActive && !content.swipeDismissing
- NumberAnimation {
- duration: Theme.notificationExitDuration
- easing.type: Theme.standardEasing
- }
- }
-
- NumberAnimation {
- id: swipeDismissAnim
- target: content
- property: "swipeOffset"
- to: isTopCenter ? -content.height : isBottomCenter ? content.height : (SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom ? -content.width : content.width)
- duration: Theme.notificationExitDuration
- easing.type: Easing.OutCubic
- onStopped: {
- NotificationService.dismissNotification(notificationData);
- win.forceExit();
- }
- }
-
- transform: [
- Translate {
- id: swipeTx
- x: isCenterPosition ? 0 : content.swipeOffset
- y: isCenterPosition ? content.swipeOffset : 0
- },
- Translate {
- id: tx
- x: {
- if (isCenterPosition)
- return 0;
- const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
- return isLeft ? -Anims.slidePx : Anims.slidePx;
- }
- y: isTopCenter ? -Anims.slidePx : isBottomCenter ? Anims.slidePx : 0
- }
- ]
- }
-
- NumberAnimation {
- id: enterX
-
- target: tx
- property: isCenterPosition ? "y" : "x"
- from: {
- if (isTopCenter)
- return -Anims.slidePx;
- if (isBottomCenter)
- return Anims.slidePx;
- const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
- return isLeft ? -Anims.slidePx : Anims.slidePx;
- }
- to: 0
- duration: Theme.notificationEnterDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: isCenterPosition ? Theme.expressiveCurves.standardDecel : Theme.expressiveCurves.emphasizedDecel
- onStopped: {
- if (!win.exiting && !win._isDestroying) {
- if (isCenterPosition) {
- if (Math.abs(tx.y) < 0.5)
- win.entered();
- } else {
- if (Math.abs(tx.x) < 0.5)
- win.entered();
- }
- }
- }
- }
-
- ParallelAnimation {
- id: exitAnim
-
- onStopped: finalizeExit("animStopped")
-
- PropertyAnimation {
- target: tx
- property: isCenterPosition ? "y" : "x"
- from: 0
- to: {
- if (isTopCenter)
- return -Anims.slidePx;
- if (isBottomCenter)
- return Anims.slidePx;
- const isLeft = SettingsData.notificationPopupPosition === SettingsData.Position.Left || SettingsData.notificationPopupPosition === SettingsData.Position.Bottom;
- return isLeft ? -Anims.slidePx : Anims.slidePx;
- }
- duration: Theme.notificationExitDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.emphasizedAccel
- }
-
- NumberAnimation {
- target: content
- property: "opacity"
- from: 1
- to: 0
- duration: Theme.notificationExitDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.standardAccel
- }
-
- NumberAnimation {
- target: content
- property: "scale"
- from: 1
- to: 0.98
- duration: Theme.notificationExitDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.emphasizedAccel
- }
- }
-
- Connections {
- id: wrapperConn
-
- function onPopupChanged() {
- if (!win.notificationData || win._isDestroying)
- return;
- if (!win.notificationData.popup && !win.exiting)
- startExit();
- }
-
- target: win.notificationData || null
- ignoreUnknownSignals: true
- enabled: !win._isDestroying
- }
-
- Connections {
- id: notificationConn
-
- function onDropped() {
- if (!win._isDestroying && !win.exiting)
- forceExit();
- }
-
- target: (win.notificationData && win.notificationData.notification && win.notificationData.notification.Retainable) || null
- ignoreUnknownSignals: true
- enabled: !win._isDestroying
- }
-
- Timer {
- id: enterDelay
-
- interval: 160
- repeat: false
- onTriggered: {
- if (notificationData && notificationData.timer && !exiting && !_isDestroying)
- notificationData.timer.start();
- }
- }
-
- Timer {
- id: exitWatchdog
-
- interval: 600
- repeat: false
- onTriggered: finalizeExit("watchdog")
- }
-
- Behavior on screenY {
- id: screenYAnim
-
- enabled: !exiting && !_isDestroying
-
- NumberAnimation {
- duration: Theme.shortDuration
- easing.type: Easing.BezierSpline
- easing.bezierCurve: Theme.expressiveCurves.standardDecel
- }
- }
-
- Menu {
- id: popupContextMenu
- width: 220
- contentHeight: 130
- margins: -1
- popupType: Popup.Window
- closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
-
- background: Rectangle {
- color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
- radius: Theme.cornerRadius
- border.width: 0
- border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
- }
-
- MenuItem {
- id: setNotificationRulesItem
- text: I18n.tr("Set notification rules")
-
- contentItem: StyledText {
- text: parent.text
- font.pixelSize: Theme.fontSizeSmall
- color: Theme.surfaceText
- leftPadding: Theme.spacingS
- verticalAlignment: Text.AlignVCenter
- }
-
- background: Rectangle {
- color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
- radius: Theme.cornerRadius / 2
- }
-
- onTriggered: {
- const appName = notificationData?.appName || "";
- const desktopEntry = notificationData?.desktopEntry || "";
- SettingsData.addNotificationRuleForNotification(appName, desktopEntry);
- PopoutService.openSettingsWithTab("notifications");
- }
- }
-
- MenuItem {
- id: muteUnmuteItem
- readonly property bool isMuted: SettingsData.isAppMuted(notificationData?.appName || "", notificationData?.desktopEntry || "")
- text: isMuted ? I18n.tr("Unmute popups for %1").arg(notificationData?.appName || I18n.tr("this app")) : I18n.tr("Mute popups for %1").arg(notificationData?.appName || I18n.tr("this app"))
-
- contentItem: StyledText {
- text: parent.text
- font.pixelSize: Theme.fontSizeSmall
- color: Theme.surfaceText
- leftPadding: Theme.spacingS
- verticalAlignment: Text.AlignVCenter
- }
-
- background: Rectangle {
- color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
- radius: Theme.cornerRadius / 2
- }
-
- onTriggered: {
- const appName = notificationData?.appName || "";
- const desktopEntry = notificationData?.desktopEntry || "";
- if (isMuted) {
- SettingsData.removeMuteRuleForApp(appName, desktopEntry);
- } else {
- SettingsData.addMuteRuleForApp(appName, desktopEntry);
- if (notificationData && !exiting)
- NotificationService.dismissNotification(notificationData);
- }
- }
- }
-
- MenuItem {
- text: I18n.tr("Dismiss")
-
- contentItem: StyledText {
- text: parent.text
- font.pixelSize: Theme.fontSizeSmall
- color: Theme.surfaceText
- leftPadding: Theme.spacingS
- verticalAlignment: Text.AlignVCenter
- }
-
- background: Rectangle {
- color: parent.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
- radius: Theme.cornerRadius / 2
- }
-
- onTriggered: {
- if (notificationData && !exiting)
- NotificationService.dismissNotification(notificationData);
- }
- }
- }
-}
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
deleted file mode 100644
index 02bfc0f..0000000
--- a/raveos-theme/hyprland/theme-data/DankMaterialShell/quickshell/Modules/Notifications/Popup/NotificationPopupManager.qml
+++ /dev/null
@@ -1,239 +0,0 @@
-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();
- }
- }
-}