summaryrefslogtreecommitdiff
path: root/modules/lock/Notification.qml
blob: c9bf05e9e64b31624acbe965def947a5cfa9ec08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
pragma ComponentBehavior: Bound

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
    readonly property int nonAnimHeight: Math.max(image.height, details.implicitHeight) + Appearance.padding.normal * 2

    color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3secondaryContainer : Colours.tPalette.m3surfaceContainer
    radius: Appearance.rounding.normal
    implicitWidth: Config.notifs.sizes.width

    Component.onCompleted: implicitHeight = Qt.binding(() => nonAnimHeight)

    Behavior on implicitHeight {
        NumberAnimation {
            duration: Appearance.anim.durations.normal
            easing.type: Easing.BezierSpline
            easing.bezierCurve: Appearance.anim.curves.standard
        }
    }

    Behavior on x {
        NumberAnimation {
            duration: Appearance.anim.durations.normal
            easing.type: Easing.BezierSpline
            easing.bezierCurve: Appearance.anim.curves.emphasizedDecel
        }
    }

    RetainableLock {
        object: root.modelData.notification
        locked: true
    }

    MouseArea {
        anchors.fill: parent
        hoverEnabled: true
        cursorShape: pressed ? Qt.ClosedHandCursor : undefined
        acceptedButtons: Qt.LeftButton | Qt.MiddleButton
        preventStealing: true

        onEntered: root.modelData.timer.stop()
        onExited: root.modelData.timer.start()

        drag.target: parent
        drag.axis: Drag.XAxis

        onPressed: event => {
            if (event.button === Qt.MiddleButton)
                root.modelData.notification.dismiss();
        }
        onReleased: event => {
            if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
                root.x = 0;
            else
                root.modelData.notification.dismiss(); // TODO: change back to popup when notif dock impled
        }
    }

    Loader {
        id: image

        active: root.hasImage
        asynchronous: true

        anchors.left: parent.left
        anchors.verticalCenter: parent.verticalCenter
        anchors.leftMargin: Appearance.padding.normal

        width: Config.notifs.sizes.image
        height: Config.notifs.sizes.image
        visible: root.hasImage || root.hasAppIcon

        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: appIcon

        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: StyledRect {
            radius: Appearance.rounding.full
            color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? Colours.tPalette.m3surfaceContainerHighest : Colours.palette.m3tertiaryContainer
            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: IconImage {
                    anchors.fill: parent
                    source: Quickshell.iconPath(root.modelData.appIcon)
                    asynchronous: true

                    layer.enabled: root.modelData.appIcon.endsWith("symbolic")
                    layer.effect: Colouriser {
                        colorizationColor: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
                    }
                }
            }

            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.toLowerCase(), root.modelData.urgency)

                    color: root.modelData.urgency === NotificationUrgency.Critical ? Colours.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? Colours.palette.m3onSurface : Colours.palette.m3onTertiaryContainer
                    font.pointSize: Appearance.font.size.large
                }
            }
        }
    }

    ColumnLayout {
        id: details

        anchors.verticalCenter: parent.verticalCenter
        anchors.left: image.right
        anchors.right: parent.right
        anchors.leftMargin: Appearance.spacing.smaller
        anchors.rightMargin: Appearance.padding.larger

        spacing: 0

        RowLayout {
            Layout.fillWidth: true

            spacing: Appearance.spacing.small

            StyledText {
                Layout.fillWidth: true
                Layout.maximumWidth: implicitWidth

                animate: true
                text: root.modelData.summary
                elide: Text.ElideRight
                maximumLineCount: 1
            }

            StyledText {
                text: "•"
                color: Colours.palette.m3onSurfaceVariant
                font.pointSize: Appearance.font.size.small
            }

            StyledText {
                animate: true
                text: root.modelData.timeStr
                color: Colours.palette.m3onSurfaceVariant
                font.pointSize: Appearance.font.size.small
            }
        }

        StyledText {
            Layout.fillWidth: true

            animate: true
            text: root.modelData.body
            color: Colours.palette.m3onSurfaceVariant
            font.pointSize: Appearance.font.size.small
            elide: Text.ElideRight
            maximumLineCount: 1
        }
    }
}