mirror of
https://github.com/end-4/dots-hyprland.git
synced 2026-06-05 14:59:27 -05:00
waffles: notifications, kind of
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="M18.4923 2.33034L21.671 5.50911C22.5497 6.38779 22.5497 7.81241 21.671 8.69109L19.0866 11.275C20.1696 11.4375 21 12.3718 21 13.5V18.75C21 19.9926 19.9926 21 18.75 21H5.25C4.00736 21 3 19.9926 3 18.75V5.25001C3 4.00736 4.00736 3.00001 5.25 3.00001H10.5C11.6289 3.00001 12.5637 3.83146 12.7253 4.91541L15.3103 2.33034C16.189 1.45166 17.6136 1.45166 18.4923 2.33034ZM4.5 18.75C4.5 19.1642 4.83579 19.5 5.25 19.5L11.249 19.4993L11.25 12.75L4.5 12.7493V18.75ZM12.749 19.4993L18.75 19.5C19.1642 19.5 19.5 19.1642 19.5 18.75V13.5C19.5 13.0858 19.1642 12.75 18.75 12.75L12.749 12.7493V19.4993ZM10.5 4.50001H5.25C4.83579 4.50001 4.5 4.83579 4.5 5.25001V11.2493H11.25V5.25001C11.25 4.83579 10.9142 4.50001 10.5 4.50001ZM12.75 9.30933V11.25L14.69 11.2493L12.75 9.30933Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 930 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="M18.4923 2.33088L21.671 5.50966C22.5497 6.38834 22.5497 7.81296 21.671 8.69164L19.0866 11.2756C20.1696 11.438 21 12.3723 21 13.5006V18.7506C21 19.9932 19.9926 21.0006 18.75 21.0006H5.25C4.00736 21.0006 3 19.9932 3 18.7506V5.25055C3 4.00791 4.00736 3.00055 5.25 3.00055H10.5C11.6289 3.00055 12.5637 3.83201 12.7253 4.91596L15.3103 2.33088C16.189 1.45221 17.6136 1.45221 18.4923 2.33088ZM4.5 18.7506C4.5 19.1648 4.83579 19.5006 5.25 19.5006L11.249 19.4999L11.25 12.7506L4.5 12.7499V18.7506ZM12.749 19.4999L18.75 19.5006C19.1642 19.5006 19.5 19.1648 19.5 18.7506V13.5006C19.5 13.0863 19.1642 12.7506 18.75 12.7506L12.749 12.7499V19.4999ZM10.5 4.50055H5.25C4.83579 4.50055 4.5 4.83634 4.5 5.25055V11.2499H11.25V5.25055C11.25 4.83634 10.9142 4.50055 10.5 4.50055ZM12.75 9.30988V11.2506L14.69 11.2499L12.75 9.30988ZM16.3709 3.39154L13.1922 6.57032C12.8993 6.86321 12.8993 7.33808 13.1922 7.63098L16.3709 10.8097C16.6638 11.1026 17.1387 11.1026 17.4316 10.8097L20.6104 7.63098C20.9033 7.33808 20.9033 6.86321 20.6104 6.57032L17.4316 3.39154C17.1387 3.09865 16.6638 3.09865 16.3709 3.39154Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="M8 12C8 13.1046 7.10457 14 6 14C4.89543 14 4 13.1046 4 12C4 10.8954 4.89543 10 6 10C7.10457 10 8 10.8954 8 12ZM14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 10C13.1046 10 14 10.8954 14 12ZM18 14C19.1046 14 20 13.1046 20 12C20 10.8954 19.1046 10 18 10C16.8954 10 16 10.8954 16 12C16 13.1046 16.8954 14 18 14Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 520 B |
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
|
||||
<path d="M7.75 12C7.75 12.9665 6.9665 13.75 6 13.75C5.0335 13.75 4.25 12.9665 4.25 12C4.25 11.0335 5.0335 10.25 6 10.25C6.9665 10.25 7.75 11.0335 7.75 12ZM13.75 12C13.75 12.9665 12.9665 13.75 12 13.75C11.0335 13.75 10.25 12.9665 10.25 12C10.25 11.0335 11.0335 10.25 12 10.25C12.9665 10.25 13.75 11.0335 13.75 12ZM18 13.75C18.9665 13.75 19.75 12.9665 19.75 12C19.75 11.0335 18.9665 10.25 18 10.25C17.0335 10.25 16.25 11.0335 16.25 12C16.25 12.9665 17.0335 13.75 18 13.75Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 633 B |
@@ -0,0 +1,87 @@
|
||||
pragma Singleton
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
/**
|
||||
* @param { string } summary
|
||||
* @returns { string }
|
||||
*/
|
||||
function findSuitableMaterialSymbol(summary = "") {
|
||||
const defaultType = 'chat';
|
||||
if (summary.length === 0) return defaultType;
|
||||
|
||||
const keywordsToTypes = {
|
||||
'reboot': 'restart_alt',
|
||||
'record': 'screen_record',
|
||||
'battery': 'power',
|
||||
'power': 'power',
|
||||
'screenshot': 'screenshot_monitor',
|
||||
'welcome': 'waving_hand',
|
||||
'time': 'scheduleb',
|
||||
'installed': 'download',
|
||||
'configuration reloaded': 'reset_wrench',
|
||||
'unable': 'question_mark',
|
||||
"couldn't": 'question_mark',
|
||||
'config': 'reset_wrench',
|
||||
'update': 'update',
|
||||
'ai response': 'neurology',
|
||||
'control': 'settings',
|
||||
'upsca': 'compare',
|
||||
'music': 'queue_music',
|
||||
'install': 'deployed_code_update',
|
||||
'input': 'keyboard_alt',
|
||||
'preedit': 'keyboard_alt',
|
||||
'startswith:file': 'folder_copy', // Declarative startsWith check
|
||||
};
|
||||
|
||||
const lowerSummary = summary.toLowerCase();
|
||||
|
||||
for (const [keyword, type] of Object.entries(keywordsToTypes)) {
|
||||
if (keyword.startsWith('startswith:')) {
|
||||
const startsWithKeyword = keyword.replace('startswith:', '');
|
||||
if (lowerSummary.startsWith(startsWithKeyword)) {
|
||||
return type;
|
||||
}
|
||||
} else if (lowerSummary.includes(keyword)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number | string | Date } timestamp
|
||||
* @returns { string }
|
||||
*/
|
||||
function getFriendlyNotifTimeString(timestamp) {
|
||||
if (!timestamp) return '';
|
||||
const messageTime = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - messageTime.getTime();
|
||||
|
||||
// Less than 1 minute
|
||||
if (diffMs < 60000)
|
||||
return 'Now';
|
||||
|
||||
// Same day - show relative time
|
||||
if (messageTime.toDateString() === now.toDateString()) {
|
||||
const diffMinutes = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
|
||||
if (diffHours > 0) {
|
||||
return `${diffHours}h`;
|
||||
} else {
|
||||
return `${diffMinutes}m`;
|
||||
}
|
||||
}
|
||||
|
||||
// Yesterday
|
||||
if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString())
|
||||
return 'Yesterday';
|
||||
|
||||
// Older dates
|
||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import qs.modules.common
|
||||
import "notification_utils.js" as NotificationUtils
|
||||
import qs.modules.common.functions
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import "notification_utils.js" as NotificationUtils
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
@@ -136,7 +135,7 @@ MouseArea { // Notification group area
|
||||
}
|
||||
|
||||
clip: true
|
||||
implicitHeight: expanded ?
|
||||
implicitHeight: root.expanded ?
|
||||
row.implicitHeight + padding * 2 :
|
||||
Math.min(80, row.implicitHeight + padding * 2)
|
||||
|
||||
@@ -157,8 +156,8 @@ MouseArea { // Notification group area
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: false
|
||||
image: root?.multipleNotifications ? "" : notificationGroup?.notifications[0]?.image ?? ""
|
||||
appIcon: notificationGroup?.appIcon
|
||||
summary: notificationGroup?.notifications[root.notificationCount - 1]?.summary
|
||||
appIcon: root.notificationGroup?.appIcon
|
||||
summary: root.notificationGroup?.notifications[root.notificationCount - 1]?.summary
|
||||
urgency: root.notifications.some(n => n.urgency === NotificationUrgency.Critical.toString()) ?
|
||||
NotificationUrgency.Critical : NotificationUrgency.Normal
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
|
||||
/**
|
||||
* @param { string } summary
|
||||
* @returns { string }
|
||||
*/
|
||||
function findSuitableMaterialSymbol(summary = "") {
|
||||
const defaultType = 'chat';
|
||||
if(summary.length === 0) return defaultType;
|
||||
|
||||
const keywordsToTypes = {
|
||||
'reboot': 'restart_alt',
|
||||
'record': 'screen_record',
|
||||
'battery': 'power',
|
||||
'power': 'power',
|
||||
'screenshot': 'screenshot_monitor',
|
||||
'welcome': 'waving_hand',
|
||||
'time': 'scheduleb',
|
||||
'installed': 'download',
|
||||
'configuration reloaded': 'reset_wrench',
|
||||
'unable': 'question_mark',
|
||||
"couldn't": 'question_mark',
|
||||
'config': 'reset_wrench',
|
||||
'update': 'update',
|
||||
'ai response': 'neurology',
|
||||
'control': 'settings',
|
||||
'upsca': 'compare',
|
||||
'music': 'queue_music',
|
||||
'install': 'deployed_code_update',
|
||||
'input': 'keyboard_alt',
|
||||
'preedit': 'keyboard_alt',
|
||||
'startswith:file': 'folder_copy', // Declarative startsWith check
|
||||
};
|
||||
|
||||
const lowerSummary = summary.toLowerCase();
|
||||
|
||||
for (const [keyword, type] of Object.entries(keywordsToTypes)) {
|
||||
if (keyword.startsWith('startswith:')) {
|
||||
const startsWithKeyword = keyword.replace('startswith:', '');
|
||||
if (lowerSummary.startsWith(startsWithKeyword)) {
|
||||
return type;
|
||||
}
|
||||
} else if (lowerSummary.includes(keyword)) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return defaultType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param { number | string | Date } timestamp
|
||||
* @returns { string }
|
||||
*/
|
||||
const getFriendlyNotifTimeString = (timestamp) => {
|
||||
if (!timestamp) return '';
|
||||
const messageTime = new Date(timestamp);
|
||||
const now = new Date();
|
||||
const diffMs = now.getTime() - messageTime.getTime();
|
||||
|
||||
// Less than 1 minute
|
||||
if (diffMs < 60000)
|
||||
return 'Now';
|
||||
|
||||
// Same day - show relative time
|
||||
if (messageTime.toDateString() === now.toDateString()) {
|
||||
const diffMinutes = Math.floor(diffMs / 60000);
|
||||
const diffHours = Math.floor(diffMs / 3600000);
|
||||
|
||||
if (diffHours > 0) {
|
||||
return `${diffHours}h`;
|
||||
} else {
|
||||
return `${diffMinutes}m`;
|
||||
}
|
||||
}
|
||||
|
||||
// Yesterday
|
||||
if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString())
|
||||
return 'Yesterday';
|
||||
|
||||
// Older dates
|
||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||
};
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
-4
@@ -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
|
||||
|
||||
+25
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -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")
|
||||
}
|
||||
}
|
||||
+34
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user