From 53a0a7d9773aedf89e86a3e61f8d938a07e12b3d Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 5 May 2026 22:07:04 +1000 Subject: [PATCH] fix: notif action size + more spacing Also shape morph for notif actions + icon close/copy --- components/controls/ButtonBase.qml | 10 +- modules/notifications/Notification.qml | 139 +++++++++++++------------ 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/components/controls/ButtonBase.qml b/components/controls/ButtonBase.qml index 44ed2c09..4c85bc9e 100644 --- a/components/controls/ButtonBase.qml +++ b/components/controls/ButtonBase.qml @@ -46,18 +46,22 @@ StyledRect { property real shapeMorphExpansion: shapeMorph && pressed ? 24 : 0 // Apparently it's always 24px no matter the width of the button readonly property color onColour: disabled ? disabledOnColour : internalChecked ? activeOnColour : inactiveOnColour + property real pressedRadius: Tokens.rounding.small + property real checkedRadius: Tokens.rounding.medium + property real defaultRadius: Tokens.rounding.large + signal clicked onCheckedChanged: internalChecked = checked radius: { if (radiusMorph && pressed) - return Tokens.rounding.small; + return pressedRadius; if (internalChecked) - return Tokens.rounding.medium; + return checkedRadius; if (isRound) return implicitHeight / 2 * Math.min(1, Tokens.rounding.scale); - return Tokens.rounding.large; + return defaultRadius; } color: type === ButtonBase.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour diff --git a/modules/notifications/Notification.qml b/modules/notifications/Notification.qml index 00c50ec1..a6d70c72 100644 --- a/modules/notifications/Notification.qml +++ b/modules/notifications/Notification.qml @@ -1,12 +1,13 @@ pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Layouts import QtQuick.Shapes import Quickshell import Quickshell.Services.Notifications +import Caelestia.Components import Caelestia.Config import qs.components +import qs.components.controls import qs.components.effects import qs.services import qs.utils @@ -18,7 +19,7 @@ StyledRect { readonly property bool hasImage: modelData.image.length > 0 readonly property bool hasAppIcon: modelData.appIcon.length > 0 readonly property int bodyTextFormat: /[<*_`#\[\]]/.test(modelData.body) ? Text.MarkdownText : Text.PlainText - readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2 + readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? Tokens.spacing.extraSmall * 2 + appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2 property bool expanded: Config.notifs.openExpanded color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer @@ -234,7 +235,7 @@ StyledRect { text: appNameMetrics.elidedText maximumLineCount: 1 color: Colours.palette.m3onSurfaceVariant - font: Tokens.font.label.small + font: Tokens.font.label.medium opacity: root.expanded ? 1 : 0 @@ -272,6 +273,9 @@ StyledRect { PropertyChanges { summary.maximumLineCount: undefined + summary.anchors.topMargin: root.Tokens.spacing.extraSmall + bodyPreview.anchors.topMargin: root.Tokens.spacing.extraSmall + body.anchors.topMargin: root.Tokens.spacing.extraSmall } AnchorChanges { @@ -285,9 +289,10 @@ StyledRect { target: summary property: "maximumLineCount" } - AnchorAnim { - type: AnchorAnim.Standard + Anim { + property: "topMargin" } + AnchorAnim {} } Behavior on height { @@ -326,9 +331,7 @@ StyledRect { } transitions: Transition { - AnchorAnim { - type: AnchorAnim.Standard - } + AnchorAnim {} } } @@ -352,8 +355,8 @@ StyledRect { anchors.right: parent.right anchors.top: parent.top - implicitWidth: expandIcon.height - implicitHeight: expandIcon.height + implicitWidth: expandIcon.implicitHeight + implicitHeight: expandIcon.implicitHeight StateLayer { radius: Tokens.rounding.full @@ -365,10 +368,13 @@ StyledRect { id: expandIcon anchors.centerIn: parent - - animate: true - text: root.expanded ? "expand_less" : "expand_more" + text: "expand_more" fontStyle: Tokens.font.icon.medium + rotation: root.expanded ? 180 : 0 + + Behavior on rotation { + Anim {} + } } } @@ -437,15 +443,15 @@ StyledRect { } } - RowLayout { + ButtonRow { id: actions - anchors.horizontalCenter: parent.horizontalCenter + anchors.left: body.left + anchors.right: body.right anchors.top: body.bottom anchors.topMargin: Tokens.spacing.small - spacing: Tokens.spacing.medium - + spacing: Tokens.spacing.extraSmall opacity: root.expanded ? 1 : 0 Behavior on opacity { @@ -454,65 +460,62 @@ StyledRect { } } - Action { - modelData: QtObject { - readonly property string text: qsTr("Close") - - function invoke(): void { - root.modelData.close(); - } - } + IconButton { + isRound: true + shapeMorph: true + fillWidth: root.modelData.actions.length === 0 + inactiveColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) + inactiveOnColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant + icon: "close" + padding: Tokens.padding.extraSmall + onClicked: root.modelData.close() } Repeater { model: root.modelData.actions - delegate: Component { - Action {} + TextButton { + required property var modelData + + isRound: true + shapeMorph: true + fillWidth: true + inactiveColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) + inactiveOnColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant + text: modelData.text + onClicked: modelData.invoke() + + label.horizontalAlignment: Text.AlignHCenter + label.anchors.left: left + label.anchors.right: right + label.anchors.verticalCenter: verticalCenter + label.anchors.centerIn: undefined + label.anchors.margins: Tokens.padding.medium + label.elide: Text.ElideRight + } + } + + IconButton { + isRound: true + shapeMorph: true + fillWidth: root.modelData.actions.length === 0 + inactiveColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHighest, 2) + inactiveOnColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant + icon: copyTimer.running ? "inventory" : "content_copy" + padding: Tokens.padding.extraSmall + onClicked: { + Quickshell.clipboardText = root.modelData.body; + copyTimer.restart(); + } + label.animate: true + + Timer { + id: copyTimer + + interval: 3000 } } } } } - - component Action: StyledRect { - id: action - - required property var modelData - - radius: Tokens.rounding.full - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2) - - Layout.preferredWidth: actionText.width + Tokens.padding.medium * 2 - Layout.preferredHeight: actionText.height + Tokens.padding.small - implicitWidth: actionText.width + Tokens.padding.medium * 2 - implicitHeight: actionText.height + Tokens.padding.small - - StateLayer { - radius: Tokens.rounding.full - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurface - onClicked: action.modelData.invoke() - } - - StyledText { - id: actionText - - anchors.centerIn: parent - text: actionTextMetrics.elidedText - color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant - font: Tokens.font.label.small - } - - TextMetrics { - id: actionTextMetrics - - text: action.modelData.text - font: actionText.font - elide: Text.ElideRight - elideWidth: { - const numActions = root.modelData.actions.length + 1; - return (inner.width - actions.spacing * (numActions - 1)) / numActions - root.Tokens.padding.medium * 2; - } - } - } }