fix: notif action size + more spacing

Also shape morph for notif actions + icon close/copy
This commit is contained in:
2 * r + 2 * t 2026-05-05 22:07:04 +10:00
parent 4eca117ade
commit 53a0a7d977
2 changed files with 78 additions and 71 deletions

View file

@ -46,18 +46,22 @@ StyledRect {
property real shapeMorphExpansion: shapeMorph && pressed ? 24 : 0 // Apparently it's always 24px no matter the width of the button 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 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 signal clicked
onCheckedChanged: internalChecked = checked onCheckedChanged: internalChecked = checked
radius: { radius: {
if (radiusMorph && pressed) if (radiusMorph && pressed)
return Tokens.rounding.small; return pressedRadius;
if (internalChecked) if (internalChecked)
return Tokens.rounding.medium; return checkedRadius;
if (isRound) if (isRound)
return implicitHeight / 2 * Math.min(1, Tokens.rounding.scale); 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 color: type === ButtonBase.Text ? "transparent" : disabled ? disabledColour : internalChecked ? activeColour : inactiveColour

View file

@ -1,12 +1,13 @@
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import QtQuick.Layouts
import QtQuick.Shapes import QtQuick.Shapes
import Quickshell import Quickshell
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
import Caelestia.Components
import Caelestia.Config import Caelestia.Config
import qs.components import qs.components
import qs.components.controls
import qs.components.effects import qs.components.effects
import qs.services import qs.services
import qs.utils import qs.utils
@ -18,7 +19,7 @@ StyledRect {
readonly property bool hasImage: modelData.image.length > 0 readonly property bool hasImage: modelData.image.length > 0
readonly property bool hasAppIcon: modelData.appIcon.length > 0 readonly property bool hasAppIcon: modelData.appIcon.length > 0
readonly property int bodyTextFormat: /[<*_`#\[\]]/.test(modelData.body) ? Text.MarkdownText : Text.PlainText 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 property bool expanded: Config.notifs.openExpanded
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer
@ -234,7 +235,7 @@ StyledRect {
text: appNameMetrics.elidedText text: appNameMetrics.elidedText
maximumLineCount: 1 maximumLineCount: 1
color: Colours.palette.m3onSurfaceVariant color: Colours.palette.m3onSurfaceVariant
font: Tokens.font.label.small font: Tokens.font.label.medium
opacity: root.expanded ? 1 : 0 opacity: root.expanded ? 1 : 0
@ -272,6 +273,9 @@ StyledRect {
PropertyChanges { PropertyChanges {
summary.maximumLineCount: undefined 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 { AnchorChanges {
@ -285,9 +289,10 @@ StyledRect {
target: summary target: summary
property: "maximumLineCount" property: "maximumLineCount"
} }
AnchorAnim { Anim {
type: AnchorAnim.Standard property: "topMargin"
} }
AnchorAnim {}
} }
Behavior on height { Behavior on height {
@ -326,9 +331,7 @@ StyledRect {
} }
transitions: Transition { transitions: Transition {
AnchorAnim { AnchorAnim {}
type: AnchorAnim.Standard
}
} }
} }
@ -352,8 +355,8 @@ StyledRect {
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
implicitWidth: expandIcon.height implicitWidth: expandIcon.implicitHeight
implicitHeight: expandIcon.height implicitHeight: expandIcon.implicitHeight
StateLayer { StateLayer {
radius: Tokens.rounding.full radius: Tokens.rounding.full
@ -365,10 +368,13 @@ StyledRect {
id: expandIcon id: expandIcon
anchors.centerIn: parent anchors.centerIn: parent
text: "expand_more"
animate: true
text: root.expanded ? "expand_less" : "expand_more"
fontStyle: Tokens.font.icon.medium fontStyle: Tokens.font.icon.medium
rotation: root.expanded ? 180 : 0
Behavior on rotation {
Anim {}
}
} }
} }
@ -437,15 +443,15 @@ StyledRect {
} }
} }
RowLayout { ButtonRow {
id: actions id: actions
anchors.horizontalCenter: parent.horizontalCenter anchors.left: body.left
anchors.right: body.right
anchors.top: body.bottom anchors.top: body.bottom
anchors.topMargin: Tokens.spacing.small anchors.topMargin: Tokens.spacing.small
spacing: Tokens.spacing.medium spacing: Tokens.spacing.extraSmall
opacity: root.expanded ? 1 : 0 opacity: root.expanded ? 1 : 0
Behavior on opacity { Behavior on opacity {
@ -454,64 +460,61 @@ StyledRect {
} }
} }
Action { IconButton {
modelData: QtObject { isRound: true
readonly property string text: qsTr("Close") shapeMorph: true
fillWidth: root.modelData.actions.length === 0
function invoke(): void { inactiveColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHighest, 2)
root.modelData.close(); inactiveOnColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant
} icon: "close"
} padding: Tokens.padding.extraSmall
onClicked: root.modelData.close()
} }
Repeater { Repeater {
model: root.modelData.actions model: root.modelData.actions
delegate: Component { TextButton {
Action {}
}
}
}
}
}
component Action: StyledRect {
id: action
required property var modelData required property var modelData
radius: Tokens.rounding.full isRound: true
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2) 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()
Layout.preferredWidth: actionText.width + Tokens.padding.medium * 2 label.horizontalAlignment: Text.AlignHCenter
Layout.preferredHeight: actionText.height + Tokens.padding.small label.anchors.left: left
implicitWidth: actionText.width + Tokens.padding.medium * 2 label.anchors.right: right
implicitHeight: actionText.height + Tokens.padding.small label.anchors.verticalCenter: verticalCenter
label.anchors.centerIn: undefined
StateLayer { label.anchors.margins: Tokens.padding.medium
radius: Tokens.rounding.full label.elide: Text.ElideRight
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurface }
onClicked: action.modelData.invoke()
} }
StyledText { IconButton {
id: actionText isRound: true
shapeMorph: true
anchors.centerIn: parent fillWidth: root.modelData.actions.length === 0
text: actionTextMetrics.elidedText inactiveColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondary : Colours.layer(Colours.palette.m3surfaceContainerHighest, 2)
color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant inactiveOnColour: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onSecondary : Colours.palette.m3onSurfaceVariant
font: Tokens.font.label.small icon: copyTimer.running ? "inventory" : "content_copy"
padding: Tokens.padding.extraSmall
onClicked: {
Quickshell.clipboardText = root.modelData.body;
copyTimer.restart();
} }
label.animate: true
TextMetrics { Timer {
id: actionTextMetrics id: copyTimer
text: action.modelData.text interval: 3000
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;
} }
} }
} }