waffles: notifications, kind of

This commit is contained in:
end-4
2025-11-27 23:25:59 +01:00
parent b7ad7361d6
commit 2fd25af353
19 changed files with 470 additions and 116 deletions
@@ -11,8 +11,8 @@ WButton {
colBackground: Looks.colors.bg2
colBackgroundHover: Looks.colors.bg2Hover
colBackgroundActive: Looks.colors.bg2Active
border.color: Looks.colors.bg2Border
property color colBorder: Looks.colors.bg2Border
property color colBorderToggled: Looks.colors.accent
border.color: checked ? colBorderToggled : colBorder
border.width: root.pressed ? 2 : 1
}
@@ -14,6 +14,7 @@ Item {
property real radius: Looks.radius.large
property alias border: borderRect
property alias borderColor: borderRect.border.color
property alias borderWidth: borderRect.border.width
implicitWidth: borderRect.implicitWidth
implicitHeight: borderRect.implicitHeight
@@ -54,35 +54,19 @@ FooterRectangle {
Layout.fillWidth: true
}
SmallBorderedIconButton {
leftPadding: 12
rightPadding: 12
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
SmallBorderedIconAndTextButton {
iconName: TimerService.pomodoroRunning ? "stop" : "play"
text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus")
onClicked: {
if (TimerService.pomodoroRunning) {
TimerService.togglePomodoro()
TimerService.resetPomodoro()
TimerService.togglePomodoro();
TimerService.resetPomodoro();
} else {
TimerService.togglePomodoro()
TimerService.togglePomodoro();
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarRight", "toggle"]);
}
}
contentItem: Row {
id: focusButtonContent
spacing: 4
FluentIcon {
icon: TimerService.pomodoroRunning ? "stop" : "play"
filled: true
implicitSize: 14
anchors.verticalCenter: parent.verticalCenter
}
WText {
anchors.verticalCenter: parent.verticalCenter
text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus")
}
}
}
}
}
@@ -19,16 +19,40 @@ WBarAttachedPanelContent {
property bool collapsed: false
contentItem: Column {
contentItem: ColumnLayout {
anchors {
horizontalCenter: parent.horizontalCenter
top: root.barAtBottom ? undefined : parent.top
bottom: root.barAtBottom ? parent.bottom : undefined
top: parent.top
bottom: parent.bottom
}
spacing: 12
Item {
id: notificationArea
Layout.fillHeight: true
implicitWidth: notificationPane.implicitWidth
WPane {
id: notificationPane
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
contentItem: NotificationPaneContent {
implicitWidth: calendarColumnLayout.implicitWidth
implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230
Behavior on implicitHeight {
animation: Looks.transition.enter.createObject(this)
}
}
}
}
WPane {
contentItem: ColumnLayout {
id: calendarColumnLayout
spacing: 0
DateHeader {
Layout.fillWidth: true
@@ -37,7 +61,9 @@ WBarAttachedPanelContent {
}
}
WPanelSeparator { visible: !root.collapsed }
WPanelSeparator {
visible: !root.collapsed
}
CalendarWidget {
Layout.fillWidth: true
@@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
WBorderlessButton {
id: headerButton
Layout.fillWidth: false
implicitWidth: 16
implicitHeight: 16
color: "transparent"
contentItem: Item {
FluentIcon {
anchors.centerIn: parent
implicitSize: 16
icon: headerButton.icon.name
color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1
}
}
}
@@ -0,0 +1,81 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
BodyRectangle {
id: root
anchors.fill: parent
implicitHeight: 230
ColumnLayout {
id: contentLayout
anchors.fill: parent
anchors.margins: 4
spacing: 12
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 12
Layout.rightMargin: 12
Layout.topMargin: 8
spacing: 8
WText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
text: Translation.tr("Notifications")
font.pixelSize: Looks.font.pixelSize.large
}
SmallBorderedIconButton {
icon.name: "alert-snooze"
checked: Notifications.silent
onClicked: {
Notifications.silent = !Notifications.silent;
}
}
SmallBorderedIconAndTextButton {
visible: Notifications.list.length > 0
iconVisible: false
text: Translation.tr("Clear all")
onClicked: {
Notifications.discardAllNotifications();
}
}
}
StyledListView {
Layout.fillWidth: true
Layout.fillHeight: true
animateAppearance: false
clip: true
model: Notifications.appNameList
delegate: WNotificationGroup {
required property int index
required property var modelData
width: ListView.view.width
notificationGroup: Notifications.groupsByAppName[modelData]
}
EmptyPlaceholder {
visible: Notifications.list.length === 0
anchors.centerIn: parent
}
}
}
component EmptyPlaceholder: WText {
horizontalAlignment: Text.AlignHCenter
text: Translation.tr("No new notifications")
}
}
@@ -0,0 +1,34 @@
import QtQuick
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
SmallBorderedIconButton {
id: root
property bool iconVisible: true
property string iconName: ""
property bool iconFilled: true
leftPadding: 12
rightPadding: 12
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
contentItem: Row {
id: focusButtonContent
spacing: 4
FluentIcon {
visible: root.iconVisible
icon: root.iconName
filled: root.iconFilled
implicitSize: 14
anchors.verticalCenter: parent.verticalCenter
}
WText {
anchors.verticalCenter: parent.verticalCenter
text: root.text
}
}
}
@@ -13,6 +13,7 @@ WBorderedButton {
anchors.centerIn: parent
implicitSize: 12
icon: root.icon.name
color: root.fgColor
}
}
}
@@ -0,0 +1,30 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import org.kde.kirigami as Kirigami
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
Item {
id: root
property string icon: ""
property real implicitSize: 16
implicitWidth: implicitSize
implicitHeight: implicitSize
Kirigami.Icon {
anchors.fill: parent
implicitWidth: root.implicitSize
implicitHeight: root.implicitSize
source: root.icon || fallback
fallback: `${Looks.iconsPath}/apps.svg`
roundToIconSize: false
isMask: !root.icon
color: Looks.colors.fg
}
}
@@ -0,0 +1,93 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
MouseArea {
id: root
required property var notificationGroup
readonly property var notifications: notificationGroup?.notifications ?? []
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
ColumnLayout {
id: contentLayout
anchors.fill: parent
spacing: 4
GroupHeader {
id: notifHeader
Layout.fillWidth: true
Layout.margins: 11
}
ListView {
Layout.fillWidth: true
implicitWidth: notifHeader.implicitWidth
implicitHeight: contentHeight
interactive: false
spacing: 4
model: ScriptModel {
values: root.notifications.slice().reverse()
}
delegate: WSingleNotification {
required property var modelData
width: ListView.view.width
notification: modelData
}
}
}
component GroupHeader: MouseArea {
id: headerMouseArea
hoverEnabled: true
acceptedButtons: Qt.NoButton
implicitWidth: appHeader.implicitWidth
implicitHeight: appHeader.implicitHeight
RowLayout {
id: appHeader
anchors.fill: parent
spacing: 7
WNotificationAppIcon {
Layout.alignment: Qt.AlignVCenter
icon: root.notificationGroup?.appIcon ?? ""
}
WText {
Layout.fillWidth: true
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
text: root.notificationGroup?.appName ?? ""
}
// NotificationHeaderButton { // TODO: More notification functionality needed so we can have this button
// visible: headerMouseArea.containsMouse
// Layout.leftMargin: 25
// Layout.rightMargin: 25
// icon.name: "more-horizontal"
// }
NotificationHeaderButton {
visible: headerMouseArea.containsMouse
Layout.rightMargin: 3
icon.name: "dismiss"
onClicked: {
root.notifications.forEach(notif => {
Qt.callLater(() => {
Notifications.discardNotification(notif.notificationId);
});
});
}
}
}
}
}
@@ -0,0 +1,59 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.Notifications
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
MouseArea {
id: root
required property var notification
property bool expanded: false
implicitHeight: contentItem.implicitHeight
implicitWidth: contentItem.implicitWidth
Rectangle {
id: contentItem
anchors.fill: parent
color: Looks.colors.bgPanelBody
radius: Looks.radius.medium
property real padding: 12
implicitHeight: notificationContent.implicitHeight + padding * 2
implicitWidth: notificationContent.implicitWidth + padding * 2
border.width: 1
border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow)
ColumnLayout {
id: notificationContent
anchors.fill: parent
anchors.margins: contentItem.padding
RowLayout {
Layout.fillWidth: true
WText {
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
}
}
ColumnLayout {
Layout.fillWidth: true
WText {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.notification.summary
}
WText {
Layout.fillWidth: true
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: root.expanded ? 100 : 1
}
}
}
}
}