From c1510b547645de5e8f70f6be99a0ba894b797241 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 14:51:22 -0500 Subject: notifs/toasts: reworked notifications and toasts and how they display and work together. see pull request comment. --- modules/drawers/Drawers.qml | 1 + modules/drawers/Interactions.qml | 46 +++++++++++++++++++++ modules/drawers/Panels.qml | 88 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) (limited to 'modules/drawers') diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml index 2ba79a4..5337917 100644 --- a/modules/drawers/Drawers.qml +++ b/modules/drawers/Drawers.qml @@ -140,6 +140,7 @@ Variants { property bool dashboard property bool utilities property bool sidebar + property bool notifications Component.onCompleted: Visibilities.load(scope.modelData, this) } diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 9579b15..10190a4 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -198,6 +198,52 @@ CustomMouseArea { utilitiesShortcutActive = false; } + // Show notifications panel on hover + // Try using inTopPanel first (works when panel is visible), fallback to corner detection only when panel is collapsed + const panelHeight = panels.notifications.height || panels.notifications.implicitHeight || 0; + const panelWidth = panels.notifications.width || panels.notifications.implicitWidth || Config.notifs.sizes.width; + const panelX = bar.implicitWidth + panels.notifications.x; + const isPanelCollapsed = panelHeight < 10; // Consider collapsed if height is very small + + let showNotifications = inTopPanel(panels.notifications, x, y); + + // Only use fallback corner detection when panel is collapsed + if (!showNotifications && isPanelCollapsed) { + // Use panel's actual width and position for fallback, with some padding + const cornerPadding = Config.border.rounding || 20; + showNotifications = x >= panelX - cornerPadding && + x <= panelX + panelWidth + cornerPadding && + y < Config.border.thickness + cornerPadding; + } + + // Check if mouse is over the clear all button area + // Button is positioned to the left of the notification panel + if (!showNotifications && panels.notifications.height > 0 && panels.clearAllButton && panels.clearAllButton.visible) { + const buttonX = bar.implicitWidth + panels.clearAllButton.x; + const buttonY = Config.border.thickness + panels.clearAllButton.y; + const buttonWidth = panels.clearAllButton.width; + const buttonHeight = panels.clearAllButton.height; + + const inButtonArea = x >= buttonX && + x <= buttonX + buttonWidth && + y >= buttonY && + y <= buttonY + buttonHeight; + + if (inButtonArea) { + showNotifications = true; + } + } + + // Show or hide notification panel based on hover + if (panels.notifications.content) { + if (showNotifications) { + panels.notifications.content.show(); + } else { + // Hide if not hovering over panel or button + panels.notifications.content.shouldShow = false; + } + } + // Show popouts on hover if (x < bar.implicitWidth) { bar.checkPopout(y); diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml index 4ce1182..8b5a251 100644 --- a/modules/drawers/Panels.qml +++ b/modules/drawers/Panels.qml @@ -8,6 +8,10 @@ import qs.modules.bar.popouts as BarPopouts import qs.modules.utilities as Utilities import qs.modules.utilities.toasts as Toasts import qs.modules.sidebar as Sidebar +import qs.components +import qs.components.controls +import qs.components.effects +import qs.services import Quickshell import QtQuick @@ -27,6 +31,7 @@ Item { readonly property alias utilities: utilities readonly property alias toasts: toasts readonly property alias sidebar: sidebar + readonly property alias clearAllButton: clearAllButton anchors.fill: parent anchors.margins: Config.border.thickness @@ -54,6 +59,89 @@ Item { anchors.right: parent.right } + // Clear all notifications button - positioned to the left of the notification panel + Item { + id: clearAllButton + + readonly property bool hasNotifications: Notifs.notClosed.length > 0 + readonly property bool panelVisible: notifications.height > 0 || notifications.implicitHeight > 0 + readonly property bool shouldShow: hasNotifications && panelVisible + + anchors.top: notifications.top + anchors.right: notifications.left + anchors.rightMargin: Appearance.padding.normal + anchors.topMargin: Appearance.padding.large + + width: button.implicitWidth + height: button.implicitHeight + enabled: shouldShow + + IconButton { + id: button + + icon: "clear_all" + radius: Appearance.rounding.normal + padding: Appearance.padding.normal + font.pointSize: Math.round(Appearance.font.size.large * 1.2) + + onClicked: { + // Clear all notifications + for (const notif of Notifs.list.slice()) + notif.close(); + } + + Elevation { + anchors.fill: parent + radius: parent.radius + z: -1 + level: button.stateLayer.containsMouse ? 4 : 3 + } + } + + // Keep notification panel visible when hovering over the button + MouseArea { + anchors.fill: button + hoverEnabled: true + acceptedButtons: Qt.NoButton + onEntered: { + if (notifications.content && Notifs.notClosed.length > 0) { + notifications.content.show(); + } + } + onExited: { + // Panel will be hidden by Interactions.qml if mouse is not over panel or button + } + } + + Behavior on opacity { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + + Behavior on scale { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + + opacity: shouldShow ? 1 : 0 + scale: shouldShow ? 1 : 0.5 + } + + Notifications.NotificationToasts { + id: notificationToasts + + panels: root + + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: Config.border.thickness + anchors.rightMargin: Config.border.thickness + } + Session.Wrapper { id: session -- cgit v1.2.3-freya From ffe14748a2cf5bc5710fe24d0ccae80b8437f35d Mon Sep 17 00:00:00 2001 From: ATMDA Date: Wed, 12 Nov 2025 20:57:38 -0500 Subject: notifs/toasts: reverted all changes to notifications to c0ea060f --- modules/drawers/Panels.qml | 88 ------------- modules/lock/NotifDock.qml | 4 +- modules/lock/NotifGroup.qml | 2 +- modules/notifications/AppIconBadge.qml | 57 -------- modules/notifications/Content.qml | 58 +-------- modules/notifications/Notification.qml | 72 +++++++---- modules/notifications/NotificationToast.qml | 120 ----------------- modules/notifications/NotificationToasts.qml | 186 --------------------------- modules/notifications/Wrapper.qml | 2 - modules/utilities/toasts/ToastItem.qml | 9 +- services/Notifs.qml | 72 +---------- 11 files changed, 61 insertions(+), 609 deletions(-) delete mode 100644 modules/notifications/AppIconBadge.qml delete mode 100644 modules/notifications/NotificationToast.qml delete mode 100644 modules/notifications/NotificationToasts.qml (limited to 'modules/drawers') diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml index 8b5a251..4ce1182 100644 --- a/modules/drawers/Panels.qml +++ b/modules/drawers/Panels.qml @@ -8,10 +8,6 @@ import qs.modules.bar.popouts as BarPopouts import qs.modules.utilities as Utilities import qs.modules.utilities.toasts as Toasts import qs.modules.sidebar as Sidebar -import qs.components -import qs.components.controls -import qs.components.effects -import qs.services import Quickshell import QtQuick @@ -31,7 +27,6 @@ Item { readonly property alias utilities: utilities readonly property alias toasts: toasts readonly property alias sidebar: sidebar - readonly property alias clearAllButton: clearAllButton anchors.fill: parent anchors.margins: Config.border.thickness @@ -59,89 +54,6 @@ Item { anchors.right: parent.right } - // Clear all notifications button - positioned to the left of the notification panel - Item { - id: clearAllButton - - readonly property bool hasNotifications: Notifs.notClosed.length > 0 - readonly property bool panelVisible: notifications.height > 0 || notifications.implicitHeight > 0 - readonly property bool shouldShow: hasNotifications && panelVisible - - anchors.top: notifications.top - anchors.right: notifications.left - anchors.rightMargin: Appearance.padding.normal - anchors.topMargin: Appearance.padding.large - - width: button.implicitWidth - height: button.implicitHeight - enabled: shouldShow - - IconButton { - id: button - - icon: "clear_all" - radius: Appearance.rounding.normal - padding: Appearance.padding.normal - font.pointSize: Math.round(Appearance.font.size.large * 1.2) - - onClicked: { - // Clear all notifications - for (const notif of Notifs.list.slice()) - notif.close(); - } - - Elevation { - anchors.fill: parent - radius: parent.radius - z: -1 - level: button.stateLayer.containsMouse ? 4 : 3 - } - } - - // Keep notification panel visible when hovering over the button - MouseArea { - anchors.fill: button - hoverEnabled: true - acceptedButtons: Qt.NoButton - onEntered: { - if (notifications.content && Notifs.notClosed.length > 0) { - notifications.content.show(); - } - } - onExited: { - // Panel will be hidden by Interactions.qml if mouse is not over panel or button - } - } - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - Behavior on scale { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - opacity: shouldShow ? 1 : 0 - scale: shouldShow ? 1 : 0.5 - } - - Notifications.NotificationToasts { - id: notificationToasts - - panels: root - - anchors.top: parent.top - anchors.right: parent.right - anchors.topMargin: Config.border.thickness - anchors.rightMargin: Config.border.thickness - } - Session.Wrapper { id: session diff --git a/modules/lock/NotifDock.qml b/modules/lock/NotifDock.qml index db087bd..7551e68 100644 --- a/modules/lock/NotifDock.qml +++ b/modules/lock/NotifDock.qml @@ -22,7 +22,7 @@ ColumnLayout { StyledText { Layout.fillWidth: true - text: Notifs.notClosed.length > 0 ? qsTr("%1 notification%2").arg(Notifs.notClosed.length).arg(Notifs.notClosed.length === 1 ? "" : "s") : qsTr("Notifications") + text: Notifs.list.length > 0 ? qsTr("%1 notification%2").arg(Notifs.list.length).arg(Notifs.list.length === 1 ? "" : "s") : qsTr("Notifications") color: Colours.palette.m3outline font.family: Appearance.font.family.mono font.weight: 500 @@ -42,7 +42,7 @@ ColumnLayout { anchors.centerIn: parent asynchronous: true active: opacity > 0 - opacity: Notifs.notClosed.length > 0 ? 0 : 1 + opacity: Notifs.list.length > 0 ? 0 : 1 sourceComponent: ColumnLayout { spacing: Appearance.spacing.large diff --git a/modules/lock/NotifGroup.qml b/modules/lock/NotifGroup.qml index 50b14ae..2a08c26 100644 --- a/modules/lock/NotifGroup.qml +++ b/modules/lock/NotifGroup.qml @@ -16,7 +16,7 @@ StyledRect { required property string modelData - readonly property list notifs: Notifs.notClosed.filter(notif => notif.appName === modelData) + readonly property list notifs: Notifs.list.filter(notif => notif.appName === modelData) readonly property string image: notifs.find(n => n.image.length > 0)?.image ?? "" readonly property string appIcon: notifs.find(n => n.appIcon.length > 0)?.appIcon ?? "" readonly property string urgency: notifs.some(n => n.urgency === NotificationUrgency.Critical) ? "critical" : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? "normal" : "low" diff --git a/modules/notifications/AppIconBadge.qml b/modules/notifications/AppIconBadge.qml deleted file mode 100644 index 286522f..0000000 --- a/modules/notifications/AppIconBadge.qml +++ /dev/null @@ -1,57 +0,0 @@ -import qs.components -import qs.components.effects -import qs.services -import qs.config -import qs.utils -import Quickshell -import Quickshell.Services.Notifications -import QtQuick - -StyledRect { - id: root - - required property Notifs.Notif modelData - required property bool hasImage - required property bool hasAppIcon - - radius: Appearance.rounding.full - color: modelData.getBadgeBackgroundColor() - implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image - implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image - - Loader { - id: icon - - active: root.hasAppIcon - asynchronous: false - visible: active - - anchors.centerIn: parent - - width: Math.round(parent.width * 0.6) - height: Math.round(parent.width * 0.6) - - sourceComponent: ColouredIcon { - anchors.fill: parent - source: Quickshell.iconPath(root.modelData.appIcon) - colour: root.modelData.getIconColor() - layer.enabled: root.modelData.appIcon.endsWith("symbolic") - } - } - - Loader { - active: !root.hasAppIcon - asynchronous: false - visible: active - anchors.centerIn: parent - anchors.horizontalCenterOffset: -Appearance.font.size.large * 0.02 - anchors.verticalCenterOffset: Appearance.font.size.large * 0.02 - - sourceComponent: MaterialIcon { - text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency) - color: root.modelData.getIconColor() - font.pointSize: Appearance.font.size.large - } - } -} - diff --git a/modules/notifications/Content.qml b/modules/notifications/Content.qml index 035a228..2d4590e 100644 --- a/modules/notifications/Content.qml +++ b/modules/notifications/Content.qml @@ -13,8 +13,6 @@ Item { required property Item panels readonly property int padding: Appearance.padding.large - property bool shouldShow: false - anchors.top: parent.top anchors.bottom: parent.bottom anchors.right: parent.right @@ -22,16 +20,13 @@ Item { implicitWidth: Config.notifs.sizes.width + padding * 2 implicitHeight: { const count = list.count; - if (count === 0 || !shouldShow) + if (count === 0) return 0; let height = (count - 1) * Appearance.spacing.smaller; for (let i = 0; i < count; i++) height += list.itemAtIndex(i)?.nonAnimHeight ?? 0; - const screenHeight = QsWindow.window?.screen?.height ?? 0; - const maxHeight = Math.floor(screenHeight * 0.45); - if (visibilities && panels) { if (visibilities.osd) { const h = panels.osd.y - Config.border.rounding * 2 - padding * 2; @@ -46,8 +41,7 @@ Item { } } - const availableHeight = Math.min(maxHeight, screenHeight - Config.border.thickness * 2); - return Math.min(availableHeight, height + padding * 2); + return Math.min((QsWindow.window?.screen?.height ?? 0) - Config.border.thickness * 2, height + padding * 2); } ClippingWrapperRectangle { @@ -61,7 +55,7 @@ Item { id: list model: ScriptModel { - values: [...Notifs.notClosed] + values: Notifs.popups.filter(n => !n.closed) } anchors.fill: parent @@ -198,52 +192,6 @@ Item { } } - Timer { - id: hideTimer - - interval: 5000 - onTriggered: { - if (list.count > 0) - root.shouldShow = false; - } - } - - function show(): void { - if (list.count > 0) { - shouldShow = true; - hideTimer.restart(); - } - } - - Connections { - target: list - - function onCountChanged(): void { - if (list.count === 0) { - root.shouldShow = false; - hideTimer.stop(); - } - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.NoButton - onEntered: { - if (list.count > 0) { - root.shouldShow = true; - hideTimer.restart(); - } - } - onExited: { - if (list.count > 0) { - root.shouldShow = false; - hideTimer.stop(); - } - } - } - Behavior on implicitHeight { Anim {} } diff --git a/modules/notifications/Notification.qml b/modules/notifications/Notification.qml index bc5c086..95507fc 100644 --- a/modules/notifications/Notification.qml +++ b/modules/notifications/Notification.qml @@ -17,31 +17,22 @@ StyledRect { required property Notifs.Notif modelData readonly property bool hasImage: modelData.image.length > 0 readonly property bool hasAppIcon: modelData.appIcon.length > 0 - readonly property int nonAnimHeight: { - const baseHeight = summary.implicitHeight + inner.anchors.margins * 2; - return root.expanded - ? baseHeight + appName.height + body.height + actions.height + actions.anchors.topMargin - : baseHeight + bodyPreview.height; - } + readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2 property bool expanded - property bool disableSlideIn: false - color: modelData.getBackgroundColor() + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer radius: Appearance.rounding.normal implicitWidth: Config.notifs.sizes.width implicitHeight: inner.implicitHeight - x: disableSlideIn ? 0 : Config.notifs.sizes.width + x: Config.notifs.sizes.width Component.onCompleted: { - if (!root.disableSlideIn) { - x = 0; - } + x = 0; modelData.lock(this); } Component.onDestruction: modelData.unlock(this) Behavior on x { - enabled: !disableSlideIn Anim { easing.bezierCurve: Appearance.anim.curves.emphasizedDecel } @@ -143,18 +134,53 @@ StyledRect { Loader { id: appIcon - active: !root.hasImage || root.hasAppIcon - asynchronous: false + active: root.hasAppIcon || !root.hasImage + asynchronous: true anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter anchors.right: root.hasImage ? image.right : undefined anchors.bottom: root.hasImage ? image.bottom : undefined - sourceComponent: AppIconBadge { - modelData: root.modelData - hasImage: root.hasImage - hasAppIcon: root.hasAppIcon + sourceComponent: StyledRect { + radius: Appearance.rounding.full + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) : Colours.palette.m3secondaryContainer + implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image + implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image + + Loader { + id: icon + + active: root.hasAppIcon + asynchronous: true + + anchors.centerIn: parent + + width: Math.round(parent.width * 0.6) + height: Math.round(parent.width * 0.6) + + sourceComponent: ColouredIcon { + anchors.fill: parent + source: Quickshell.iconPath(root.modelData.appIcon) + colour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer + layer.enabled: root.modelData.appIcon.endsWith("symbolic") + } + } + + Loader { + active: !root.hasAppIcon + asynchronous: true + anchors.centerIn: parent + anchors.horizontalCenterOffset: -Appearance.font.size.large * 0.02 + anchors.verticalCenterOffset: Appearance.font.size.large * 0.02 + + sourceComponent: MaterialIcon { + text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency) + + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onSecondaryContainer + font.pointSize: Appearance.font.size.large + } + } } } @@ -296,7 +322,7 @@ StyledRect { StateLayer { radius: Appearance.rounding.full - color: root.modelData.getStateLayerColor() + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondaryContainer : Colours.palette.m3onSurface function onClicked() { root.expanded = !root.expanded; @@ -417,7 +443,7 @@ StyledRect { required property var modelData radius: Appearance.rounding.full - color: root.modelData.getActionBackgroundColor() + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2) Layout.preferredWidth: actionText.width + Appearance.padding.normal * 2 Layout.preferredHeight: actionText.height + Appearance.padding.small * 2 @@ -426,7 +452,7 @@ StyledRect { StateLayer { radius: Appearance.rounding.full - color: root.modelData.getStateLayerColor() + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurface function onClicked(): void { action.modelData.invoke(); @@ -438,7 +464,7 @@ StyledRect { anchors.centerIn: parent text: actionTextMetrics.elidedText - color: root.modelData.getActionTextColor() + color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant font.pointSize: Appearance.font.size.small } diff --git a/modules/notifications/NotificationToast.qml b/modules/notifications/NotificationToast.qml deleted file mode 100644 index 90414fe..0000000 --- a/modules/notifications/NotificationToast.qml +++ /dev/null @@ -1,120 +0,0 @@ -import qs.components -import qs.components.effects -import qs.services -import qs.config -import qs.utils -import Quickshell -import Quickshell.Widgets -import Quickshell.Services.Notifications -import QtQuick -import QtQuick.Layouts - -StyledRect { - id: root - - required property Notifs.Notif modelData - - readonly property bool hasImage: modelData.image.length > 0 - readonly property bool hasAppIcon: modelData.appIcon.length > 0 - - anchors.left: parent.left - anchors.right: parent.right - implicitHeight: layout.implicitHeight + Appearance.padding.smaller * 2 - - radius: Appearance.rounding.normal - color: Colours.palette.m3surface - - border.width: 1 - border.color: Colours.palette.m3outlineVariant - - Elevation { - anchors.fill: parent - radius: parent.radius - opacity: parent.opacity - z: -1 - level: 3 - } - - RowLayout { - id: layout - - anchors.fill: parent - anchors.margins: Appearance.padding.smaller - anchors.leftMargin: Appearance.padding.normal - anchors.rightMargin: Appearance.padding.normal - spacing: Appearance.spacing.normal - - Item { - Layout.preferredWidth: Config.notifs.sizes.image - Layout.preferredHeight: Config.notifs.sizes.image - - Loader { - id: imageLoader - - active: root.hasImage - asynchronous: true - anchors.fill: parent - - sourceComponent: ClippingRectangle { - radius: Appearance.rounding.full - implicitWidth: Config.notifs.sizes.image - implicitHeight: Config.notifs.sizes.image - - Image { - anchors.fill: parent - source: Qt.resolvedUrl(root.modelData.image) - fillMode: Image.PreserveAspectCrop - cache: false - asynchronous: true - } - } - } - - Loader { - id: appIconLoader - - active: root.hasAppIcon || !root.hasImage - asynchronous: true - - anchors.horizontalCenter: root.hasImage ? undefined : parent.horizontalCenter - anchors.verticalCenter: root.hasImage ? undefined : parent.verticalCenter - anchors.right: root.hasImage ? parent.right : undefined - anchors.bottom: root.hasImage ? parent.bottom : undefined - - sourceComponent: AppIconBadge { - modelData: root.modelData - hasImage: root.hasImage - hasAppIcon: root.hasAppIcon - } - } - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 0 - - StyledText { - id: title - - Layout.fillWidth: true - text: root.modelData.summary - color: Colours.palette.m3onSurface - font.pointSize: Appearance.font.size.normal - elide: Text.ElideRight - } - - StyledText { - Layout.fillWidth: true - textFormat: Text.StyledText - text: root.modelData.body - color: Colours.palette.m3onSurface - opacity: 0.8 - elide: Text.ElideRight - } - } - } - - Behavior on border.color { - CAnim {} - } -} diff --git a/modules/notifications/NotificationToasts.qml b/modules/notifications/NotificationToasts.qml deleted file mode 100644 index 96fe817..0000000 --- a/modules/notifications/NotificationToasts.qml +++ /dev/null @@ -1,186 +0,0 @@ -pragma ComponentBehavior: Bound - -import qs.components -import qs.config -import qs.services -import Quickshell -import Quickshell.Widgets -import QtQuick - -Item { - id: root - - required property Item panels - - readonly property int spacing: Appearance.spacing.small - readonly property int maxToasts: 5 - readonly property bool listVisible: panels.notifications.content.shouldShow - - property bool flag - property var activeToasts: new Set() - - anchors.top: parent.top - anchors.right: parent.right - anchors.margins: Appearance.padding.normal - - implicitWidth: Config.notifs.sizes.width - implicitHeight: { - if (listVisible) - return 0; - - let height = -spacing; - for (let i = 0; i < repeater.count; i++) { - const item = repeater.itemAt(i) as ToastWrapper; - if (item && !item.modelData.closed && !item.previewHidden) - height += item.implicitHeight + spacing; - } - return height; - } - - opacity: listVisible ? 0 : 1 - visible: opacity > 0 - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - } - } - - Repeater { - id: repeater - - model: ScriptModel { - values: { - const toasts = []; - let visibleCount = 0; - - for (const notif of Notifs.list) { - if (notif.showAsToast) { - root.activeToasts.add(notif); - } - if (notif.closed) { - root.activeToasts.delete(notif); - } - } - - for (const notif of Notifs.list) { - if (root.activeToasts.has(notif)) { - toasts.push(notif); - if (notif.showAsToast && !notif.closed) { - visibleCount++; - if (visibleCount > root.maxToasts) - break; - } - } - } - return toasts; - } - onValuesChanged: root.flagChanged() - } - - ToastWrapper {} - } - - component ToastWrapper: MouseArea { - id: toast - - required property int index - required property Notifs.Notif modelData - - readonly property bool previewHidden: { - let extraHidden = 0; - for (let i = 0; i < index; i++) { - const item = repeater.itemAt(i); - if (item && item.modelData.closed) - extraHidden++; - } - return index >= root.maxToasts + extraHidden; - } - - opacity: modelData.closed || previewHidden || !modelData.showAsToast ? 0 : 1 - scale: modelData.closed || previewHidden || !modelData.showAsToast ? 0.7 : 1 - - anchors.topMargin: { - root.flag; - let margin = 0; - for (let i = 0; i < index; i++) { - const item = repeater.itemAt(i) as ToastWrapper; - if (item && !item.modelData.closed && !item.previewHidden) - margin += item.implicitHeight + root.spacing; - } - return margin; - } - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - implicitHeight: toastInner.implicitHeight - - acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton - onClicked: { - modelData.showAsToast = false; - modelData.close(); - } - - Component.onCompleted: modelData.lock(this) - - onPreviewHiddenChanged: { - if (initAnim.running && previewHidden) - initAnim.stop(); - } - - Anim { - id: initAnim - - Component.onCompleted: running = !toast.previewHidden - - target: toast - properties: "opacity,scale" - from: 0 - to: 1 - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - - ParallelAnimation { - running: toast.modelData.closed || (!toast.modelData.showAsToast && !toast.modelData.closed) - onStarted: toast.anchors.topMargin = toast.anchors.topMargin - onFinished: { - if (toast.modelData.closed) - toast.modelData.unlock(toast); - } - - Anim { - target: toast - property: "opacity" - to: 0 - } - Anim { - target: toast - property: "scale" - to: 0.7 - } - } - - NotificationToast { - id: toastInner - - modelData: toast.modelData - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim {} - } - - Behavior on anchors.topMargin { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - } -} diff --git a/modules/notifications/Wrapper.qml b/modules/notifications/Wrapper.qml index 4b54883..61acc56 100644 --- a/modules/notifications/Wrapper.qml +++ b/modules/notifications/Wrapper.qml @@ -8,8 +8,6 @@ Item { required property var visibilities required property Item panels - readonly property alias content: content - visible: height > 0 implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth) implicitHeight: content.implicitHeight diff --git a/modules/utilities/toasts/ToastItem.qml b/modules/utilities/toasts/ToastItem.qml index 477a23c..f475500 100644 --- a/modules/utilities/toasts/ToastItem.qml +++ b/modules/utilities/toasts/ToastItem.qml @@ -28,13 +28,14 @@ StyledRect { border.width: 1 border.color: { + let colour = Colours.palette.m3outlineVariant; if (root.modelData.type === Toast.Success) - return Colours.palette.m3success; + colour = Colours.palette.m3success; if (root.modelData.type === Toast.Warning) - return Colours.palette.m3secondaryContainer; + colour = Colours.palette.m3secondaryContainer; if (root.modelData.type === Toast.Error) - return Colours.palette.m3error; - return Colours.palette.m3outlineVariant; + colour = Colours.palette.m3error; + return Qt.alpha(colour, 0.3); } Elevation { diff --git a/services/Notifs.qml b/services/Notifs.qml index 82ed8c4..4a89c7f 100644 --- a/services/Notifs.qml +++ b/services/Notifs.qml @@ -77,10 +77,8 @@ Singleton { onNotification: notif => { notif.tracked = true; - const shouldShowAsToast = !props.dnd && ![...Visibilities.screens.values()].some(v => v.sidebar); const comp = notifComp.createObject(root, { - popup: shouldShowAsToast, - showAsToast: shouldShowAsToast, + popup: !props.dnd && ![...Visibilities.screens.values()].some(v => v.sidebar), notification: notif }); root.list = [comp, ...root.list]; @@ -145,7 +143,6 @@ Singleton { property bool popup property bool closed - property bool showAsToast: false property var locks: new Set() property date time: new Date() @@ -179,74 +176,7 @@ Singleton { property bool hasActionIcons property list actions - readonly property bool isCritical: urgency === NotificationUrgency.Critical - readonly property bool isLow: urgency === NotificationUrgency.Low - - function getBackgroundColor(): color { - if (isCritical) return Colours.palette.m3secondaryContainer; - return Colours.tPalette.m3surfaceContainer; - } - - function getBadgeBackgroundColor(): color { - if (isCritical) return Colours.palette.m3error; - if (isLow) return Colours.layer(Colours.palette.m3surfaceContainerHighest, 2); - return Colours.palette.m3secondaryContainer; - } - - function getIconColor(): color { - if (isCritical) return Colours.palette.m3onError; - if (isLow) return Colours.palette.m3onSurface; - return Colours.palette.m3onSecondaryContainer; - } - - function getStateLayerColor(): color { - if (isCritical) return Colours.palette.m3onSecondaryContainer; - return Colours.palette.m3onSurface; - } - - function getActionBackgroundColor(): color { - if (isCritical) return Colours.palette.m3secondary; - return Colours.layer(Colours.palette.m3surfaceContainerHigh, 2); - } - - function getActionTextColor(): color { - if (isCritical) return Colours.palette.m3onSecondary; - return Colours.palette.m3onSurfaceVariant; - } - readonly property Timer timer: Timer { - id: toastTimer - - running: notif.showAsToast - interval: { - let timeout = notif.expireTimeout; - if (timeout <= 0) { - switch (notif.urgency) { - case NotificationUrgency.Critical: - timeout = 10000; - break; - case NotificationUrgency.Normal: - timeout = 5000; - break; - case NotificationUrgency.Low: - timeout = 5000; - break; - default: - timeout = 5000; - } - } - return timeout; - } - onTriggered: { - if (Config.notifs.expire) - notif.popup = false; - if (notif.showAsToast) { - notif.showAsToast = false; - } - } - } - - readonly property Timer popupTimer: Timer { running: true interval: notif.expireTimeout > 0 ? notif.expireTimeout : Config.notifs.defaultExpireTimeout onTriggered: { -- cgit v1.2.3-freya From 1da9c68be8f336a671f9514cf5feaaf5998da981 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:41:14 -0500 Subject: cleanup: trailing whitespace removeal (entire project) --- components/controls/CollapsibleSection.qml | 2 +- .../controlcenter/appearance/AppearancePane.qml | 50 +++---- modules/controlcenter/launcher/LauncherPane.qml | 10 +- modules/controlcenter/network/WirelessDetails.qml | 6 +- modules/controlcenter/network/WirelessList.qml | 2 +- .../network/WirelessPasswordDialog.qml | 10 +- modules/controlcenter/taskbar/TaskbarPane.qml | 8 +- modules/drawers/Interactions.qml | 22 +-- services/Network.qml | 154 ++++++++++----------- services/VPN.qml | 6 +- utils/Icons.qml | 4 +- 11 files changed, 137 insertions(+), 137 deletions(-) (limited to 'modules/drawers') diff --git a/components/controls/CollapsibleSection.qml b/components/controls/CollapsibleSection.qml index 945386c..cb6e62a 100644 --- a/components/controls/CollapsibleSection.qml +++ b/components/controls/CollapsibleSection.qml @@ -12,7 +12,7 @@ ColumnLayout { required property string title property string description: "" property bool expanded: false - + signal toggleRequested spacing: Appearance.spacing.small / 2 diff --git a/modules/controlcenter/appearance/AppearancePane.qml b/modules/controlcenter/appearance/AppearancePane.qml index 68e2e2d..fc338f9 100644 --- a/modules/controlcenter/appearance/AppearancePane.qml +++ b/modules/controlcenter/appearance/AppearancePane.qml @@ -310,20 +310,20 @@ RowLayout { StateLayer { function onClicked(): void { const variant = modelData.variant; - + // Optimistic update - set immediately Schemes.currentVariant = variant; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-v", variant]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -410,20 +410,20 @@ RowLayout { const name = modelData.name; const flavour = modelData.flavour; const schemeKey = `${name} ${flavour}`; - + // Optimistic update - set immediately Schemes.currentScheme = schemeKey; - + // Execute the command Quickshell.execDetached(["caelestia", "scheme", "set", "-n", name, "-f", flavour]); - + // Reload after a delay to confirm Qt.callLater(() => { reloadTimer.restart(); }); } } - + Timer { id: reloadTimer interval: 300 @@ -1053,7 +1053,7 @@ RowLayout { columns: Math.max(1, Math.floor(parent.width / 200)) rowSpacing: Appearance.spacing.normal columnSpacing: Appearance.spacing.normal - + // Center the grid content Layout.maximumWidth: { const cols = columns; @@ -1100,16 +1100,16 @@ RowLayout { path: modelData.path anchors.fill: parent - + // Ensure sourceSize is always set to valid dimensions sourceSize: Qt.size( Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height)) ) - + // Show when ready, hide if fallback is showing opacity: status === Image.Ready && !fallbackImage.visible ? 1 : 0 - + Behavior on opacity { NumberAnimation { duration: 200 @@ -1129,11 +1129,11 @@ RowLayout { Math.max(1, Math.floor(parent.width)), Math.max(1, Math.floor(parent.height)) ) - + // Show if caching image hasn't loaded after a delay visible: opacity > 0 opacity: 0 - + Timer { id: fallbackTimer interval: 500 @@ -1144,7 +1144,7 @@ RowLayout { } } } - + // Also check status changes onStatusChanged: { if (status === Image.Ready && cachingImage.status !== Image.Ready) { @@ -1155,7 +1155,7 @@ RowLayout { }); } } - + Behavior on opacity { NumberAnimation { duration: 200 @@ -1182,26 +1182,26 @@ RowLayout { anchors.right: parent.right anchors.bottom: parent.bottom height: filenameText.implicitHeight + Appearance.padding.normal * 2 - + // Match the parent's rounded corners at the bottom radius: Appearance.rounding.normal - + gradient: Gradient { GradientStop { position: 0.0; color: Qt.rgba(0, 0, 0, 0) } GradientStop { position: 0.3; color: Qt.rgba(0, 0, 0, 0.3) } GradientStop { position: 0.7; color: Qt.rgba(0, 0, 0, 0.75) } GradientStop { position: 1.0; color: Qt.rgba(0, 0, 0, 0.85) } } - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } @@ -1228,20 +1228,20 @@ RowLayout { color: isCurrent ? Colours.palette.m3primary : "#FFFFFF" elide: Text.ElideMiddle maximumLineCount: 1 - + // Text shadow for better readability style: Text.Outline styleColor: Qt.rgba(0, 0, 0, 0.6) - + opacity: 0 - + Behavior on opacity { NumberAnimation { duration: 200 easing.type: Easing.OutCubic } } - + Component.onCompleted: { opacity = 1; } diff --git a/modules/controlcenter/launcher/LauncherPane.qml b/modules/controlcenter/launcher/LauncherPane.qml index 9b2570a..dd00877 100644 --- a/modules/controlcenter/launcher/LauncherPane.qml +++ b/modules/controlcenter/launcher/LauncherPane.qml @@ -53,7 +53,7 @@ RowLayout { try { const config = JSON.parse(configFile.text()); const appId = root.selectedApp.id || root.selectedApp.entry?.id; - + if (config.launcher && config.launcher.hiddenApps) { root.hideFromLauncherChecked = config.launcher.hiddenApps.includes(appId); } else { @@ -72,12 +72,12 @@ RowLayout { try { const config = JSON.parse(configFile.text()); const appId = root.selectedApp.id || root.selectedApp.entry?.id; - + if (!config.launcher) config.launcher = {}; if (!config.launcher.hiddenApps) config.launcher.hiddenApps = []; - + const hiddenApps = config.launcher.hiddenApps; - + if (isHidden) { // Add to hiddenApps if not already there if (!hiddenApps.includes(appId)) { @@ -90,7 +90,7 @@ RowLayout { hiddenApps.splice(index, 1); } } - + const jsonString = JSON.stringify(config, null, 4); configFile.setText(jsonString); } catch (e) { diff --git a/modules/controlcenter/network/WirelessDetails.qml b/modules/controlcenter/network/WirelessDetails.qml index 7039720..d5abc9d 100644 --- a/modules/controlcenter/network/WirelessDetails.qml +++ b/modules/controlcenter/network/WirelessDetails.qml @@ -26,7 +26,7 @@ Item { updateDeviceDetails(); checkSavedProfile(); } - + function checkSavedProfile(): void { // Refresh saved connections list to ensure it's up to date // This ensures the "Forget Network" button visibility is accurate @@ -102,7 +102,7 @@ Item { color: Colours.palette.m3errorContainer onColor: Colours.palette.m3onErrorContainer text: qsTr("Forget Network") - + onClicked: { if (root.network && root.network.ssid) { // Disconnect first if connected @@ -184,7 +184,7 @@ Item { if (root.network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(root.network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessList.qml b/modules/controlcenter/network/WirelessList.qml index f861db4..ca6947a 100644 --- a/modules/controlcenter/network/WirelessList.qml +++ b/modules/controlcenter/network/WirelessList.qml @@ -230,7 +230,7 @@ ColumnLayout { if (network.isSecure) { // Check if we have a saved connection profile for this network (by SSID) const hasSavedProfile = Network.hasSavedProfile(network.ssid); - + if (hasSavedProfile) { // Try connecting with saved password - don't show dialog if it fails // The saved password should work, but if connection fails for other reasons, diff --git a/modules/controlcenter/network/WirelessPasswordDialog.qml b/modules/controlcenter/network/WirelessPasswordDialog.qml index 2b33b43..8a71fa8 100644 --- a/modules/controlcenter/network/WirelessPasswordDialog.qml +++ b/modules/controlcenter/network/WirelessPasswordDialog.qml @@ -15,7 +15,7 @@ Item { id: root required property Session session - + readonly property var network: { // Prefer pendingNetwork, then active network if (session.network.pendingNetwork) { @@ -105,7 +105,7 @@ Item { StyledText { id: statusText - + Layout.alignment: Qt.AlignHCenter Layout.topMargin: Appearance.spacing.small visible: Network.connectionStatus.length > 0 || connectButton.connecting @@ -251,15 +251,15 @@ Item { // Check connection status message for success indicators const status = Network.connectionStatus; const statusLower = status.toLowerCase(); - + // Check for success indicators in status message - const hasSuccessIndicator = statusLower.includes("connection activated") || + const hasSuccessIndicator = statusLower.includes("connection activated") || statusLower.includes("successfully") || statusLower.includes("connected successfully") || (statusLower.includes("connected") && !statusLower.includes("error") && !statusLower.includes("failed")); // Check if we're connected to the target network (case-insensitive SSID comparison) - const isConnected = root.network && Network.active && Network.active.ssid && + const isConnected = root.network && Network.active && Network.active.ssid && Network.active.ssid.toLowerCase().trim() === root.network.ssid.toLowerCase().trim(); if (isConnected || hasSuccessIndicator) { diff --git a/modules/controlcenter/taskbar/TaskbarPane.qml b/modules/controlcenter/taskbar/TaskbarPane.qml index 2bb50d8..cf52fd3 100644 --- a/modules/controlcenter/taskbar/TaskbarPane.qml +++ b/modules/controlcenter/taskbar/TaskbarPane.qml @@ -120,13 +120,13 @@ RowLayout { if (!configFile.loaded) { return; } - + try { const config = JSON.parse(configFile.text()); - + // Ensure bar object exists if (!config.bar) config.bar = {}; - + // Update clock setting if (!config.bar.clock) config.bar.clock = {}; config.bar.clock.showIcon = clockShowIconSwitch.checked; @@ -163,7 +163,7 @@ RowLayout { // Update entries from the model (same approach as clock - use provided value if available) if (!config.bar.entries) config.bar.entries = []; config.bar.entries = []; - + for (let i = 0; i < entriesModel.count; i++) { const entry = entriesModel.get(i); // If this is the entry being updated, use the provided value (same as clock toggle reads from switch) diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 10190a4..2d0c115 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -204,18 +204,18 @@ CustomMouseArea { const panelWidth = panels.notifications.width || panels.notifications.implicitWidth || Config.notifs.sizes.width; const panelX = bar.implicitWidth + panels.notifications.x; const isPanelCollapsed = panelHeight < 10; // Consider collapsed if height is very small - + let showNotifications = inTopPanel(panels.notifications, x, y); - + // Only use fallback corner detection when panel is collapsed if (!showNotifications && isPanelCollapsed) { // Use panel's actual width and position for fallback, with some padding const cornerPadding = Config.border.rounding || 20; - showNotifications = x >= panelX - cornerPadding && - x <= panelX + panelWidth + cornerPadding && + showNotifications = x >= panelX - cornerPadding && + x <= panelX + panelWidth + cornerPadding && y < Config.border.thickness + cornerPadding; } - + // Check if mouse is over the clear all button area // Button is positioned to the left of the notification panel if (!showNotifications && panels.notifications.height > 0 && panels.clearAllButton && panels.clearAllButton.visible) { @@ -223,17 +223,17 @@ CustomMouseArea { const buttonY = Config.border.thickness + panels.clearAllButton.y; const buttonWidth = panels.clearAllButton.width; const buttonHeight = panels.clearAllButton.height; - - const inButtonArea = x >= buttonX && - x <= buttonX + buttonWidth && - y >= buttonY && + + const inButtonArea = x >= buttonX && + x <= buttonX + buttonWidth && + y >= buttonY && y <= buttonY + buttonHeight; - + if (inButtonArea) { showNotifications = true; } } - + // Show or hide notification panel based on hover if (panels.notifications.content) { if (showNotifications) { diff --git a/services/Network.qml b/services/Network.qml index 0b936b8..7732a1c 100644 --- a/services/Network.qml +++ b/services/Network.qml @@ -30,17 +30,17 @@ Singleton { property var wirelessDeviceDetails: null property string connectionStatus: "" property string connectionDebug: "" - + function clearConnectionStatus(): void { connectionStatus = ""; // Don't clear debug - keep it for reference // connectionDebug = ""; } - + function setConnectionStatus(status: string): void { connectionStatus = status; } - + function addDebugInfo(info: string): void { const timestamp = new Date().toLocaleTimeString(); const newInfo = "[" + timestamp + "] " + info; @@ -79,23 +79,23 @@ Singleton { // When no password, use SSID (will use saved password if available) const hasBssid = bssid !== undefined && bssid !== null && bssid.length > 0; let cmd = []; - + // Set up pending connection tracking if callback provided if (callback) { root.pendingConnection = { ssid: ssid, bssid: hasBssid ? bssid : "", callback: callback }; } - + if (password && password.length > 0) { // When password is provided, try BSSID first if available, otherwise use SSID if (hasBssid) { // Use BSSID when password is provided - ensure BSSID is uppercase const bssidUpper = bssid.toUpperCase(); - + // Check if a connection with this SSID already exists - const existingConnection = root.savedConnections.find(conn => + const existingConnection = root.savedConnections.find(conn => conn && conn.toLowerCase().trim() === ssid.toLowerCase().trim() ); - + if (existingConnection) { // Connection already exists - delete it first, then create new one with updated password root.addDebugInfo(qsTr("Connection '%1' already exists, deleting it first...").arg(existingConnection)); @@ -122,7 +122,7 @@ Singleton { root.setConnectionStatus(qsTr("Connecting to %1 (using saved password)...").arg(ssid)); root.addDebugInfo(qsTr("Using saved password for: %1").arg(ssid)); } - + // Show the exact command being executed const cmdStr = cmd.join(" "); root.addDebugInfo(qsTr("=== COMMAND TO EXECUTE ===")); @@ -130,17 +130,17 @@ Singleton { root.addDebugInfo(qsTr("Command array: [%1]").arg(cmd.map((arg, i) => `"${arg}"`).join(", "))); root.addDebugInfo(qsTr("Command array length: %1").arg(cmd.length)); root.addDebugInfo(qsTr("===========================")); - + // Set command and start process root.addDebugInfo(qsTr("Setting command property...")); connectProc.command = cmd; const setCmdStr = connectProc.command ? connectProc.command.join(" ") : "null"; root.addDebugInfo(qsTr("Command property set, value: %1").arg(setCmdStr)); root.addDebugInfo(qsTr("Command property verified: %1").arg(setCmdStr === cmdStr ? "Match" : "MISMATCH")); - + // If we're creating a connection profile, we need to activate it after creation const isConnectionAdd = cmd.length > 0 && cmd[0] === "nmcli" && cmd[1] === "connection" && cmd[2] === "add"; - + // Wait a moment before starting to ensure command is set Qt.callLater(() => { root.addDebugInfo(qsTr("=== STARTING PROCESS ===")); @@ -150,7 +150,7 @@ Singleton { connectProc.running = true; root.addDebugInfo(qsTr("Process running set to: %1").arg(connectProc.running)); root.addDebugInfo(qsTr("========================")); - + // Check if process actually started after a short delay Qt.callLater(() => { root.addDebugInfo(qsTr("Process status check (100ms later):")); @@ -162,7 +162,7 @@ Singleton { } }, 100); }); - + // Start connection check timer if we have a callback if (callback) { root.addDebugInfo(qsTr("Starting connection check timer (4 second interval)")); @@ -171,25 +171,25 @@ Singleton { root.addDebugInfo(qsTr("No callback provided - not starting connection check timer")); } } - + function createConnectionWithPassword(ssid: string, bssidUpper: string, password: string): void { // Create connection profile with all required properties for BSSID + password - const cmd = ["nmcli", "connection", "add", - "type", "wifi", + const cmd = ["nmcli", "connection", "add", + "type", "wifi", "con-name", ssid, "ifname", "*", "ssid", ssid, "802-11-wireless.bssid", bssidUpper, "802-11-wireless-security.key-mgmt", "wpa-psk", "802-11-wireless-security.psk", password]; - + root.setConnectionStatus(qsTr("Connecting to %1 (BSSID: %2)...").arg(ssid).arg(bssidUpper)); root.addDebugInfo(qsTr("Using BSSID: %1 for SSID: %2").arg(bssidUpper).arg(ssid)); root.addDebugInfo(qsTr("Creating connection profile with password and key-mgmt")); - + // Set command and start process connectProc.command = cmd; - + Qt.callLater(() => { connectProc.running = true; }); @@ -198,7 +198,7 @@ Singleton { function connectToNetworkWithPasswordCheck(ssid: string, isSecure: bool, callback: var, bssid: string): void { root.addDebugInfo(qsTr("=== connectToNetworkWithPasswordCheck ===")); root.addDebugInfo(qsTr("SSID: %1, isSecure: %2").arg(ssid).arg(isSecure)); - + // For secure networks, try connecting without password first // If connection succeeds (saved password exists), we're done // If it fails with password error, callback will be called to show password dialog @@ -228,7 +228,7 @@ Singleton { disconnectProc.exec(["nmcli", "device", "disconnect", "wifi"]); } } - + function forgetNetwork(ssid: string): void { // Delete the connection profile for this network // This will remove the saved password and connection settings @@ -240,7 +240,7 @@ Singleton { }, 500); } } - + function hasConnectionProfile(ssid: string): bool { // Check if a connection profile exists for this SSID // This is synchronous check - returns true if connection exists @@ -252,12 +252,12 @@ Singleton { // The actual check will be done asynchronously return false; } - + property list savedConnections: [] property list savedConnectionSsids: [] property var wifiConnectionQueue: [] property int currentSsidQueryIndex: 0 - + Process { id: listConnectionsProc command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] @@ -276,12 +276,12 @@ Singleton { } } } - + function parseConnectionList(output: string): void { const lines = output.trim().split("\n").filter(line => line.length > 0); const wifiConnections = []; const connections = []; - + // First pass: identify WiFi connections for (const line of lines) { const parts = line.split(":"); @@ -289,15 +289,15 @@ Singleton { const name = parts[0]; const type = parts[1]; connections.push(name); - + if (type === "802-11-wireless") { wifiConnections.push(name); } } } - + root.savedConnections = connections; - + // Second pass: get SSIDs for WiFi connections if (wifiConnections.length > 0) { root.wifiConnectionQueue = wifiConnections; @@ -310,10 +310,10 @@ Singleton { root.wifiConnectionQueue = []; } } - + Process { id: getSsidProc - + environment: ({ LANG: "C.UTF-8", LC_ALL: "C.UTF-8" @@ -332,7 +332,7 @@ Singleton { } } } - + function processSsidOutput(output: string): void { // Parse "802-11-wireless.ssid:SSID_NAME" format const lines = output.trim().split("\n"); @@ -351,11 +351,11 @@ Singleton { } } } - + // Query next connection queryNextSsid(); } - + function queryNextSsid(): void { if (root.currentSsidQueryIndex < root.wifiConnectionQueue.length) { const connectionName = root.wifiConnectionQueue[root.currentSsidQueryIndex]; @@ -368,13 +368,13 @@ Singleton { root.currentSsidQueryIndex = 0; } } - + function hasSavedProfile(ssid: string): bool { if (!ssid || ssid.length === 0) { return false; } const ssidLower = ssid.toLowerCase().trim(); - + // If currently connected to this network, it definitely has a saved profile if (root.active && root.active.ssid) { const activeSsidLower = root.active.ssid.toLowerCase().trim(); @@ -382,21 +382,21 @@ Singleton { return true; } } - + // Check if SSID is in saved connections (case-insensitive comparison) - const hasSsid = root.savedConnectionSsids.some(savedSsid => + const hasSsid = root.savedConnectionSsids.some(savedSsid => savedSsid && savedSsid.toLowerCase().trim() === ssidLower ); - + if (hasSsid) { return true; } - + // Fallback: also check if connection name matches SSID (some connections use SSID as name) - const hasConnectionName = root.savedConnections.some(connName => + const hasConnectionName = root.savedConnections.some(connName => connName && connName.toLowerCase().trim() === ssidLower ); - + return hasConnectionName; } @@ -442,7 +442,7 @@ Singleton { if (isNaN(cidrNum) || cidrNum < 0 || cidrNum > 32) { return ""; } - + const mask = (0xffffffff << (32 - cidrNum)) >>> 0; const octets = [ (mask >>> 24) & 0xff, @@ -450,7 +450,7 @@ Singleton { (mask >>> 8) & 0xff, mask & 0xff ]; - + return octets.join("."); } @@ -510,7 +510,7 @@ Singleton { root.addDebugInfo(qsTr(" Pending SSID: %1").arg(root.pendingConnection.ssid)); root.addDebugInfo(qsTr(" Active SSID: %1").arg(root.active ? root.active.ssid : "None")); root.addDebugInfo(qsTr(" Connected: %1").arg(connected)); - + if (!connected && root.pendingConnection.callback) { // Connection didn't succeed after multiple checks, show password dialog root.addDebugInfo(qsTr("Connection failed - calling password dialog callback")); @@ -543,19 +543,19 @@ Singleton { repeat: true triggeredOnStart: false property int checkCount: 0 - + onRunningChanged: { if (running) { root.addDebugInfo(qsTr("Immediate check timer started (checks every 500ms)")); } } - + onTriggered: { if (root.pendingConnection) { checkCount++; const connected = root.active && root.active.ssid === root.pendingConnection.ssid; root.addDebugInfo(qsTr("Immediate check #%1: Connected=%2").arg(checkCount).arg(connected)); - + if (connected) { // Connection succeeded, stop timers and clear pending root.addDebugInfo(qsTr("Connection succeeded on check #%1!").arg(checkCount)); @@ -586,32 +586,32 @@ Singleton { onRunningChanged: { root.addDebugInfo(qsTr("Process running changed to: %1").arg(running)); } - + onStarted: { root.addDebugInfo(qsTr("Process started successfully")); } - + onExited: { root.addDebugInfo(qsTr("=== PROCESS EXITED ===")); root.addDebugInfo(qsTr("Exit code: %1").arg(exitCode)); root.addDebugInfo(qsTr("(Exit code 0 = success, non-zero = error)")); - + // Check if this was a "connection add" command - if so, we need to activate it - const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 - && connectProc.command[0] === "nmcli" - && connectProc.command[1] === "connection" + const wasConnectionAdd = connectProc.command && connectProc.command.length > 0 + && connectProc.command[0] === "nmcli" + && connectProc.command[1] === "connection" && connectProc.command[2] === "add"; - + if (wasConnectionAdd && root.pendingConnection) { const ssid = root.pendingConnection.ssid; - + // Check for duplicate connection warning in stderr text const stderrText = connectProc.stderr ? connectProc.stderr.text : ""; const hasDuplicateWarning = stderrText && ( stderrText.includes("another connection with the name") || stderrText.includes("Reference the connection by its uuid") ); - + // Even with duplicate warning (or if connection already exists), we should try to activate it // Also try if exit code is non-zero but small (might be a warning, not a real error) if (exitCode === 0 || hasDuplicateWarning || (exitCode > 0 && exitCode < 10)) { @@ -622,10 +622,10 @@ Singleton { root.addDebugInfo(qsTr("Connection profile created successfully, now activating: %1").arg(ssid)); root.setConnectionStatus(qsTr("Activating connection...")); } - + // Update saved connections list listConnectionsProc.running = true; - + // Try to activate the connection by SSID (connection name) connectProc.command = ["nmcli", "connection", "up", ssid]; Qt.callLater(() => { @@ -644,7 +644,7 @@ Singleton { password = connectProc.command[pskIndex + 1]; } } - + if (password && password.length > 0) { root.addDebugInfo(qsTr("Using device wifi connect with password as fallback")); connectProc.command = ["nmcli", "device", "wifi", "connect", ssid, "password", password]; @@ -655,10 +655,10 @@ Singleton { } } } - + // Refresh network list after connection attempt getNetworks.running = true; - + // Check if connection succeeded after a short delay (network list needs to update) if (root.pendingConnection) { if (exitCode === 0) { @@ -704,10 +704,10 @@ Singleton { root.addDebugInfo(qsTr("STDERR: %1").arg(line)); } } - + // Check for specific errors that indicate password is needed // Be careful not to match success messages - const needsPassword = (error.includes("Secrets were required") || + const needsPassword = (error.includes("Secrets were required") || error.includes("No secrets provided") || error.includes("802-11-wireless-security.psk") || (error.includes("password") && !error.includes("Connection activated")) || @@ -715,7 +715,7 @@ Singleton { (error.includes("802.11") && !error.includes("Connection activated"))) && !error.includes("Connection activated") && !error.includes("successfully"); - + if (needsPassword && root.pendingConnection && root.pendingConnection.callback) { // Connection failed because password is needed - show dialog immediately connectionCheckTimer.stop(); @@ -784,7 +784,7 @@ Singleton { Process { id: deleteConnectionProc - + // Delete connection profile - refresh network list and saved connections after deletion onExited: { // Refresh network list and saved connections after deletion @@ -924,12 +924,12 @@ Singleton { onStreamFinished: { const output = text.trim(); root.ethernetDebugInfo = "Output received in onStreamFinished! Length: " + output.length + ", First 100 chars: " + output.substring(0, 100); - + if (!output || output.length === 0) { root.ethernetDebugInfo = "No output received (empty)"; return; } - + root.processEthernetOutput(output); } } @@ -942,7 +942,7 @@ Singleton { const lines = output.split("\n"); root.ethernetDebugInfo = "Processing " + lines.length + " lines"; - + const allDevices = lines.map(d => { const dev = d.replace(rep, PLACEHOLDER).split(":"); return { @@ -952,9 +952,9 @@ Singleton { connection: dev[3]?.replace(rep2, ":") ?? "" }; }); - + root.ethernetDebugInfo = "All devices: " + allDevices.length + ", Types: " + allDevices.map(d => d.type).join(", "); - + const ethernetOnly = allDevices.filter(d => d.type === "ethernet"); root.ethernetDebugInfo = "Ethernet devices found: " + ethernetOnly.length; @@ -975,7 +975,7 @@ Singleton { speed: "" }; }); - + root.ethernetDebugInfo = "Ethernet devices processed: " + ethernetDevices.length + ", First device: " + (ethernetDevices[0]?.interface || "none"); // Update the list - replace the entire array to ensure QML detects the change @@ -984,13 +984,13 @@ Singleton { for (let i = 0; i < ethernetDevices.length; i++) { newDevices.push(ethernetDevices[i]); } - + // Replace the entire list root.ethernetDevices = newDevices; - + // Force QML to detect the change by updating a property root.ethernetDeviceCount = ethernetDevices.length; - + // Force QML to re-evaluate the list by accessing it Qt.callLater(() => { const count = root.ethernetDevices.length; @@ -1130,7 +1130,7 @@ const line = lines[i]; // Find the connected wifi interface from device status const lines = output.split("\n"); let wifiInterface = ""; - + for (let i = 0; i < lines.length; i++) { const line = lines[i]; const parts = line.split(/\s+/); diff --git a/services/VPN.qml b/services/VPN.qml index 10e5e7e..412bda4 100644 --- a/services/VPN.qml +++ b/services/VPN.qml @@ -21,7 +21,7 @@ Singleton { const name = providerName; const iface = interfaceName; const defaults = getBuiltinDefaults(name, iface); - + if (isCustomProvider) { const custom = providerInput; return { @@ -31,7 +31,7 @@ Singleton { displayName: custom.displayName || defaults.displayName }; } - + return defaults; } @@ -62,7 +62,7 @@ Singleton { displayName: "Tailscale" } }; - + return builtins[name] || { connectCmd: [name, "up"], disconnectCmd: [name, "down"], diff --git a/utils/Icons.qml b/utils/Icons.qml index e946c4f..389eca3 100644 --- a/utils/Icons.qml +++ b/utils/Icons.qml @@ -194,13 +194,13 @@ Singleton { function getSpecialWsIcon(name: string): string { name = name.toLowerCase().slice("special:".length); - + for (const iconConfig of Config.bar.workspaces.specialWorkspaceIcons) { if (iconConfig.name === name) { return iconConfig.icon; } } - + if (name === "special") return "star"; if (name === "communication") -- cgit v1.2.3-freya From a13f69531ff67bbf4f1e7d9f4e5d391090459fd8 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:50:11 -0500 Subject: drawers: reverted changes to c0ea060f --- modules/drawers/Interactions.qml | 46 ---------------------------------------- 1 file changed, 46 deletions(-) (limited to 'modules/drawers') diff --git a/modules/drawers/Interactions.qml b/modules/drawers/Interactions.qml index 2d0c115..9579b15 100644 --- a/modules/drawers/Interactions.qml +++ b/modules/drawers/Interactions.qml @@ -198,52 +198,6 @@ CustomMouseArea { utilitiesShortcutActive = false; } - // Show notifications panel on hover - // Try using inTopPanel first (works when panel is visible), fallback to corner detection only when panel is collapsed - const panelHeight = panels.notifications.height || panels.notifications.implicitHeight || 0; - const panelWidth = panels.notifications.width || panels.notifications.implicitWidth || Config.notifs.sizes.width; - const panelX = bar.implicitWidth + panels.notifications.x; - const isPanelCollapsed = panelHeight < 10; // Consider collapsed if height is very small - - let showNotifications = inTopPanel(panels.notifications, x, y); - - // Only use fallback corner detection when panel is collapsed - if (!showNotifications && isPanelCollapsed) { - // Use panel's actual width and position for fallback, with some padding - const cornerPadding = Config.border.rounding || 20; - showNotifications = x >= panelX - cornerPadding && - x <= panelX + panelWidth + cornerPadding && - y < Config.border.thickness + cornerPadding; - } - - // Check if mouse is over the clear all button area - // Button is positioned to the left of the notification panel - if (!showNotifications && panels.notifications.height > 0 && panels.clearAllButton && panels.clearAllButton.visible) { - const buttonX = bar.implicitWidth + panels.clearAllButton.x; - const buttonY = Config.border.thickness + panels.clearAllButton.y; - const buttonWidth = panels.clearAllButton.width; - const buttonHeight = panels.clearAllButton.height; - - const inButtonArea = x >= buttonX && - x <= buttonX + buttonWidth && - y >= buttonY && - y <= buttonY + buttonHeight; - - if (inButtonArea) { - showNotifications = true; - } - } - - // Show or hide notification panel based on hover - if (panels.notifications.content) { - if (showNotifications) { - panels.notifications.content.show(); - } else { - // Hide if not hovering over panel or button - panels.notifications.content.shouldShow = false; - } - } - // Show popouts on hover if (x < bar.implicitWidth) { bar.checkPopout(y); -- cgit v1.2.3-freya From 02d4feb3ab2b8cce239d5bd2c22593b5cf972ea1 Mon Sep 17 00:00:00 2001 From: ATMDA Date: Thu, 13 Nov 2025 14:59:03 -0500 Subject: drawers: reverted changes to c0ea060 --- modules/drawers/Drawers.qml | 1 - 1 file changed, 1 deletion(-) (limited to 'modules/drawers') diff --git a/modules/drawers/Drawers.qml b/modules/drawers/Drawers.qml index 5337917..2ba79a4 100644 --- a/modules/drawers/Drawers.qml +++ b/modules/drawers/Drawers.qml @@ -140,7 +140,6 @@ Variants { property bool dashboard property bool utilities property bool sidebar - property bool notifications Component.onCompleted: Visibilities.load(scope.modelData, this) } -- cgit v1.2.3-freya From 21e715468e9800a73c731921c27177731033d2ec Mon Sep 17 00:00:00 2001 From: ATMDA Date: Sun, 16 Nov 2025 09:45:05 -0500 Subject: controlcenter: changed to popout vs floating to match the other settings buttons (fixed issues with resizing) --- modules/bar/components/Settings.qml | 4 +- modules/bar/components/SettingsIcon.qml | 4 +- modules/controlcenter/NavRail.qml | 119 +------------------------------- modules/drawers/Panels.qml | 1 + modules/utilities/Content.qml | 2 + modules/utilities/Wrapper.qml | 2 + modules/utilities/cards/Toggles.qml | 5 +- 7 files changed, 14 insertions(+), 123 deletions(-) (limited to 'modules/drawers') diff --git a/modules/bar/components/Settings.qml b/modules/bar/components/Settings.qml index e92fc46..0eceea7 100644 --- a/modules/bar/components/Settings.qml +++ b/modules/bar/components/Settings.qml @@ -21,7 +21,9 @@ Item { radius: Appearance.rounding.full function onClicked(): void { - WindowFactory.create(); + WindowFactory.create(null, { + active: "appearance" + }); } } diff --git a/modules/bar/components/SettingsIcon.qml b/modules/bar/components/SettingsIcon.qml index e92fc46..0eceea7 100644 --- a/modules/bar/components/SettingsIcon.qml +++ b/modules/bar/components/SettingsIcon.qml @@ -21,7 +21,9 @@ Item { radius: Appearance.rounding.full function onClicked(): void { - WindowFactory.create(); + WindowFactory.create(null, { + active: "appearance" + }); } } diff --git a/modules/controlcenter/NavRail.qml b/modules/controlcenter/NavRail.qml index 234f447..8432f17 100644 --- a/modules/controlcenter/NavRail.qml +++ b/modules/controlcenter/NavRail.qml @@ -30,129 +30,12 @@ Item { PropertyChanges { layout.spacing: Appearance.spacing.small - menuIcon.opacity: 0 - menuIconExpanded.opacity: 1 - menuIcon.rotation: 180 - menuIconExpanded.rotation: 0 } } transitions: Transition { Anim { - properties: "spacing,opacity,rotation" - } - } - - Item { - id: menuBtn - - Layout.topMargin: Appearance.spacing.large - implicitWidth: menuIcon.implicitWidth + menuIcon.anchors.leftMargin * 2 - implicitHeight: menuIcon.implicitHeight + Appearance.padding.normal * 2 - - StateLayer { - radius: Appearance.rounding.small - - function onClicked(): void { - root.session.navExpanded = !root.session.navExpanded; - } - } - - MaterialIcon { - id: menuIcon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.padding.large - - text: "menu" - font.pointSize: Appearance.font.size.large - } - - MaterialIcon { - id: menuIconExpanded - - anchors.fill: menuIcon - text: "menu_open" - font.pointSize: menuIcon.font.pointSize - opacity: 0 - rotation: -180 - } - } - - Loader { - asynchronous: true - active: !root.session.floating - visible: active - - sourceComponent: StyledRect { - readonly property int nonAnimWidth: normalWinIcon.implicitWidth + (root.session.navExpanded ? normalWinLabel.anchors.leftMargin + normalWinLabel.implicitWidth : 0) + normalWinIcon.anchors.leftMargin * 2 - - implicitWidth: nonAnimWidth - implicitHeight: root.session.navExpanded ? normalWinIcon.implicitHeight + Appearance.padding.normal * 2 : nonAnimWidth - - color: Colours.palette.m3primaryContainer - radius: Appearance.rounding.small - - StateLayer { - id: normalWinState - - color: Colours.palette.m3onPrimaryContainer - - function onClicked(): void { - root.session.root.close(); - WindowFactory.create(null, { - screen: root.screen, - active: root.session.active, - navExpanded: root.session.navExpanded - }); - } - } - - MaterialIcon { - id: normalWinIcon - - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.padding.large - - text: "select_window" - color: Colours.palette.m3onPrimaryContainer - font.pointSize: Appearance.font.size.large - fill: 1 - } - - StyledText { - id: normalWinLabel - - anchors.left: normalWinIcon.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: Appearance.spacing.normal - - text: qsTr("Float window") - color: Colours.palette.m3onPrimaryContainer - opacity: root.session.navExpanded ? 1 : 0 - - Behavior on opacity { - Anim { - duration: Appearance.anim.durations.small - } - } - } - - Behavior on implicitWidth { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - Behavior on implicitHeight { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } + properties: "spacing" } } diff --git a/modules/drawers/Panels.qml b/modules/drawers/Panels.qml index 4ce1182..7705732 100644 --- a/modules/drawers/Panels.qml +++ b/modules/drawers/Panels.qml @@ -109,6 +109,7 @@ Item { visibilities: root.visibilities sidebar: sidebar + popouts: popouts anchors.bottom: parent.bottom anchors.right: parent.right diff --git a/modules/utilities/Content.qml b/modules/utilities/Content.qml index d5be824..902656d 100644 --- a/modules/utilities/Content.qml +++ b/modules/utilities/Content.qml @@ -8,6 +8,7 @@ Item { required property var props required property var visibilities + required property Item popouts implicitWidth: layout.implicitWidth implicitHeight: layout.implicitHeight @@ -28,6 +29,7 @@ Item { Toggles { visibilities: root.visibilities + popouts: root.popouts } } diff --git a/modules/utilities/Wrapper.qml b/modules/utilities/Wrapper.qml index dd784bc..77178e3 100644 --- a/modules/utilities/Wrapper.qml +++ b/modules/utilities/Wrapper.qml @@ -10,6 +10,7 @@ Item { required property var visibilities required property Item sidebar + required property Item popouts readonly property PersistentProperties props: PersistentProperties { property bool recordingListExpanded: false @@ -89,6 +90,7 @@ Item { implicitWidth: root.implicitWidth - Appearance.padding.large * 2 props: root.props visibilities: root.visibilities + popouts: root.popouts } } } diff --git a/modules/utilities/cards/Toggles.qml b/modules/utilities/cards/Toggles.qml index d3ad51e..71f4d1d 100644 --- a/modules/utilities/cards/Toggles.qml +++ b/modules/utilities/cards/Toggles.qml @@ -12,6 +12,7 @@ StyledRect { id: root required property var visibilities + required property Item popouts Layout.fillWidth: true implicitHeight: layout.implicitHeight + Appearance.padding.large * 2 @@ -67,9 +68,7 @@ StyledRect { toggle: false onClicked: { root.visibilities.utilities = false; - WindowFactory.create(null, { - screen: QsWindow.window?.screen ?? null - }); + root.popouts.detach("appearance"); } } -- cgit v1.2.3-freya