diff --git a/Modules/Notifications/Center/NotificationCard.qml b/Modules/Notifications/Center/NotificationCard.qml index 80447f79..59bc4d75 100644 --- a/Modules/Notifications/Center/NotificationCard.qml +++ b/Modules/Notifications/Center/NotificationCard.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import Quickshell import Quickshell.Widgets import Quickshell.Services.Notifications @@ -115,41 +116,35 @@ Rectangle { height: 92 visible: !expanded - Rectangle { + DankCircularImage { id: iconContainer readonly property bool hasNotificationImage: notificationGroup?.latestNotification?.image && notificationGroup.latestNotification.image !== "" - width: 55 - height: 55 - radius: 27.5 - color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) - border.color: "transparent" - border.width: 0 + width: 63 + height: 63 anchors.left: parent.left anchors.top: parent.top - anchors.topMargin: 18 + anchors.topMargin: 14 - IconImage { - anchors.fill: parent - anchors.margins: 2 - source: { - if (parent.hasNotificationImage) - return notificationGroup.latestNotification.cleanImage - if (notificationGroup?.latestNotification?.appIcon) { - const appIcon = notificationGroup.latestNotification.appIcon - if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://")) - return appIcon - return Quickshell.iconPath(appIcon, true) - } + imageSource: { + if (hasNotificationImage) + return notificationGroup.latestNotification.cleanImage + if (notificationGroup?.latestNotification?.appIcon) { + const appIcon = notificationGroup.latestNotification.appIcon + if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://")) + return appIcon + return Quickshell.iconPath(appIcon, true) + } + return "" + } + + hasImage: hasNotificationImage + fallbackIcon: notificationGroup?.latestNotification?.appIcon || "notifications" + fallbackText: { + if (hasNotificationImage || (notificationGroup?.latestNotification?.appIcon && notificationGroup.latestNotification.appIcon !== "")) return "" - } - asynchronous: true - visible: status === Image.Ready - - Component.onCompleted: { - backer.sourceSize.width = 128 - backer.sourceSize.height = 128 - } + const appName = notificationGroup?.appName || "?" + return appName.charAt(0).toUpperCase() } Rectangle { @@ -159,22 +154,10 @@ Rectangle { color: "transparent" border.color: root.color border.width: 5 - visible: iconContainer.hasNotificationImage + visible: parent.hasImage antialiasing: true } - StyledText { - anchors.centerIn: parent - visible: !parent.hasNotificationImage && (!notificationGroup?.latestNotification?.appIcon || notificationGroup.latestNotification.appIcon === "") - text: { - const appName = notificationGroup?.appName || "?" - return appName.charAt(0).toUpperCase() - } - font.pixelSize: 20 - font.weight: Font.Bold - color: Theme.primaryText - } - Rectangle { width: 18 height: 18 diff --git a/Modules/Notifications/Popup/NotificationPopup.qml b/Modules/Notifications/Popup/NotificationPopup.qml index 106704db..cebf925a 100644 --- a/Modules/Notifications/Popup/NotificationPopup.qml +++ b/Modules/Notifications/Popup/NotificationPopup.qml @@ -1,5 +1,6 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import Quickshell import Quickshell.Wayland import Quickshell.Widgets @@ -208,71 +209,40 @@ PanelWindow { anchors.rightMargin: 56 height: 98 - Rectangle { + DankCircularImage { id: iconContainer readonly property bool hasNotificationImage: notificationData && notificationData.image && notificationData.image !== "" - width: 55 - height: 55 - radius: 27.5 - color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) - border.color: "transparent" - border.width: 0 + width: 63 + height: 63 anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - IconImage { - id: iconImage - - anchors.fill: parent - anchors.margins: 2 - asynchronous: true - source: { - if (!notificationData) - return "" - - if (parent.hasNotificationImage) - return notificationData.cleanImage || "" - - if (notificationData.appIcon) { - const appIcon = notificationData.appIcon - if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://")) - return appIcon - - return Quickshell.iconPath(appIcon, true) - } + imageSource: { + if (!notificationData) return "" - } - visible: status === Image.Ready - Component.onCompleted: { - backer.sourceSize.width = 128 - backer.sourceSize.height = 128 + if (hasNotificationImage) + return notificationData.cleanImage || "" + + if (notificationData.appIcon) { + const appIcon = notificationData.appIcon + if (appIcon.startsWith("file://") || appIcon.startsWith("http://") || appIcon.startsWith("https://")) + return appIcon + + return Quickshell.iconPath(appIcon, true) } + return "" } - Rectangle { - anchors.fill: parent - anchors.margins: 0 - radius: width / 2 - color: "transparent" - border.color: Theme.popupBackground() - border.width: 3 - visible: iconContainer.hasNotificationImage - antialiasing: true - } - - StyledText { - anchors.centerIn: parent - visible: !parent.hasNotificationImage && (!notificationData || !notificationData.appIcon || notificationData.appIcon === "") - text: { - const appName = notificationData && notificationData.appName ? notificationData.appName : "?" - return appName.charAt(0).toUpperCase() - } - font.pixelSize: 20 - font.weight: Font.Bold - color: Theme.primaryText + hasImage: hasNotificationImage + fallbackIcon: notificationData?.appIcon || "notifications" + fallbackText: { + if (hasNotificationImage || (notificationData?.appIcon && notificationData.appIcon !== "")) + return "" + const appName = notificationData?.appName || "?" + return appName.charAt(0).toUpperCase() } } diff --git a/Widgets/DankCircularImage.qml b/Widgets/DankCircularImage.qml new file mode 100644 index 00000000..b879521c --- /dev/null +++ b/Widgets/DankCircularImage.qml @@ -0,0 +1,94 @@ +import QtQuick +import QtQuick.Effects +import Quickshell +import qs.Common +import qs.Widgets + +Rectangle { + id: root + + property string imageSource: "" + property string fallbackIcon: "notifications" + property string fallbackText: "" + property bool hasImage: imageSource !== "" + property alias imageStatus: internalImage.status + + radius: width / 2 + color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) + border.color: "transparent" + border.width: 0 + + Image { + id: internalImage + anchors.fill: parent + anchors.margins: 2 + asynchronous: true + fillMode: Image.PreserveAspectCrop + smooth: true + mipmap: true + cache: true + visible: false + source: root.imageSource + + Component.onCompleted: { + sourceSize.width = 128 + sourceSize.height = 128 + } + } + + MultiEffect { + anchors.fill: parent + anchors.margins: 2 + source: internalImage + maskEnabled: true + maskSource: circularMask + visible: internalImage.status === Image.Ready && root.imageSource !== "" + maskThresholdMin: 0.5 + maskSpreadAtMin: 1 + } + + Item { + id: circularMask + width: parent.width - 4 + height: parent.height - 4 + anchors.centerIn: parent + layer.enabled: true + layer.smooth: true + visible: false + + Rectangle { + anchors.fill: parent + radius: width / 2 + color: "black" + antialiasing: true + } + } + + DankIcon { + anchors.centerIn: parent + name: root.fallbackIcon + size: parent.width * 0.5 + color: Theme.surfaceVariantText + visible: internalImage.status !== Image.Ready && root.imageSource === "" && root.fallbackIcon !== "" + } + + Rectangle { + anchors.fill: parent + anchors.margins: 0 + radius: width / 2 + color: "transparent" + border.color: Theme.popupBackground() + border.width: 3 + visible: root.hasImage && internalImage.status === Image.Ready + antialiasing: true + } + + StyledText { + anchors.centerIn: parent + visible: root.imageSource === "" && root.fallbackIcon === "" && root.fallbackText !== "" + text: root.fallbackText + font.pixelSize: Math.max(12, parent.width * 0.36) + font.weight: Font.Bold + color: Theme.primaryText + } +} \ No newline at end of file