NotificationHistory Updates
Add read/unread bell icon Edit style of the Panel Small fixes
This commit is contained in:
parent
cf26fe52d9
commit
dbb5a9160c
6 changed files with 416 additions and 409 deletions
76
Bar/Bar.qml
76
Bar/Bar.qml
|
|
@ -9,11 +9,11 @@ import qs.Settings
|
||||||
import qs.Services
|
import qs.Services
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Widgets
|
import qs.Widgets
|
||||||
import qs.Widgets.Notification
|
|
||||||
import qs.Widgets.Sidebar
|
import qs.Widgets.Sidebar
|
||||||
import qs.Widgets.Sidebar.Panel
|
import qs.Widgets.Sidebar.Panel
|
||||||
import qs.Helpers
|
import qs.Helpers
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls
|
||||||
|
import qs.Widgets.Notification
|
||||||
|
|
||||||
Scope {
|
Scope {
|
||||||
id: rootScope
|
id: rootScope
|
||||||
|
|
@ -83,40 +83,9 @@ Scope {
|
||||||
anchors.rightMargin: 18
|
anchors.rightMargin: 18
|
||||||
spacing: 12
|
spacing: 12
|
||||||
|
|
||||||
Item {
|
|
||||||
id: notificationBellButton
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
z: 1
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: bellBg
|
|
||||||
width: 22
|
|
||||||
height: 22
|
|
||||||
radius: 11
|
|
||||||
color: mouseAreaBell.containsMouse ? Theme.accentPrimary : "transparent"
|
|
||||||
visible: mouseAreaBell.containsMouse
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
Text {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
text: "notifications"
|
|
||||||
font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
|
|
||||||
font.pixelSize: 16
|
|
||||||
color: mouseAreaBell.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: mouseAreaBell
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NotificationHistory {
|
NotificationHistory {
|
||||||
id: notificationHistoryWin
|
id: notificationHistoryWin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
Brightness {
|
Brightness {
|
||||||
|
|
@ -158,28 +127,25 @@ Scope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Background {}
|
Background {}
|
||||||
Overview {}
|
Overview {}
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: topLeftPanel
|
id: topCornerPanel
|
||||||
anchors.top: true
|
anchors.top: true
|
||||||
anchors.left: true
|
anchors.left: true
|
||||||
|
anchors.right: true
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
screen: modelData
|
screen: modelData
|
||||||
margins.top: 36
|
margins.top: 36
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
visible: true
|
visible: true
|
||||||
WlrLayershell.layer: WlrLayer.Background
|
|
||||||
aboveWindows: false
|
|
||||||
WlrLayershell.namespace: "swww-daemon"
|
|
||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
|
|
||||||
Corners {
|
Corners {
|
||||||
id: topLeftCorner
|
id: topleftCorner
|
||||||
position: "bottomleft"
|
position: "bottomleft"
|
||||||
size: 1.3
|
size: 1.3
|
||||||
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
|
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
|
||||||
|
|
@ -187,25 +153,9 @@ Scope {
|
||||||
offsetY: 0
|
offsetY: 0
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
PanelWindow {
|
|
||||||
id: topRightPanel
|
|
||||||
anchors.top: true
|
|
||||||
anchors.right: true
|
|
||||||
color: "transparent"
|
|
||||||
screen: modelData
|
|
||||||
margins.top: 36
|
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
|
||||||
visible: true
|
|
||||||
WlrLayershell.layer: WlrLayer.Background
|
|
||||||
aboveWindows: false
|
|
||||||
WlrLayershell.namespace: "swww-daemon"
|
|
||||||
|
|
||||||
implicitHeight: 24
|
|
||||||
|
|
||||||
Corners {
|
Corners {
|
||||||
id: topRightCorner
|
id: toprightCorner
|
||||||
position: "bottomright"
|
position: "bottomright"
|
||||||
size: 1.3
|
size: 1.3
|
||||||
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
|
fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222"
|
||||||
|
|
@ -223,9 +173,6 @@ Scope {
|
||||||
screen: modelData
|
screen: modelData
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
visible: true
|
visible: true
|
||||||
WlrLayershell.layer: WlrLayer.Background
|
|
||||||
aboveWindows: false
|
|
||||||
WlrLayershell.namespace: "swww-daemon"
|
|
||||||
|
|
||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
|
|
||||||
|
|
@ -241,16 +188,13 @@ Scope {
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelWindow {
|
PanelWindow {
|
||||||
id: bottomRightPanel
|
id: bottomRightCornerPanel
|
||||||
anchors.bottom: true
|
anchors.bottom: true
|
||||||
anchors.right: true
|
anchors.right: true
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
screen: modelData
|
screen: modelData
|
||||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||||
visible: true
|
visible: true
|
||||||
WlrLayershell.layer: WlrLayer.Background
|
|
||||||
aboveWindows: false
|
|
||||||
WlrLayershell.namespace: "swww-daemon"
|
|
||||||
|
|
||||||
implicitHeight: 24
|
implicitHeight: 24
|
||||||
|
|
||||||
|
|
@ -264,6 +208,10 @@ Scope {
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: tabViewerLoader
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import Quickshell.Io
|
||||||
IpcHandler {
|
IpcHandler {
|
||||||
property var appLauncherPanel
|
property var appLauncherPanel
|
||||||
property var lockScreen
|
property var lockScreen
|
||||||
|
property var tabViewer
|
||||||
|
|
||||||
target: "globalIPC"
|
target: "globalIPC"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ Singleton {
|
||||||
id: i,
|
id: i,
|
||||||
idx: ws.id,
|
idx: ws.id,
|
||||||
name: ws.name || "",
|
name: ws.name || "",
|
||||||
output: ws.monitor?.name || "",
|
output: ws.monitor.name || "",
|
||||||
isActive: ws.active === true,
|
isActive: ws.active === true,
|
||||||
isFocused: ws.focused === true,
|
isFocused: ws.focused === true,
|
||||||
isUrgent: ws.urgent === true
|
isUrgent: ws.urgent === true
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,28 @@
|
||||||
{
|
{
|
||||||
"backgroundPrimary": "#111211",
|
"backgroundPrimary": "#0E0F10",
|
||||||
"backgroundSecondary": "#1D1E1D",
|
"backgroundSecondary": "#1A1B1C",
|
||||||
"backgroundTertiary": "#292A29",
|
"backgroundTertiary": "#262728",
|
||||||
|
|
||||||
"surface": "#242524",
|
"surface": "#212223",
|
||||||
"surfaceVariant": "#353635",
|
"surfaceVariant": "#323334",
|
||||||
|
|
||||||
"textPrimary": "#F4E9E3",
|
"textPrimary": "#F0F1E0",
|
||||||
"textSecondary": "#DCD2CC",
|
"textSecondary": "#D8D9CA",
|
||||||
"textDisabled": "#928C88",
|
"textDisabled": "#909186",
|
||||||
|
|
||||||
"accentPrimary": "#7D8079",
|
"accentPrimary": "#A3A485",
|
||||||
"accentSecondary": "#979994",
|
"accentSecondary": "#B5B69D",
|
||||||
"accentTertiary": "#646661",
|
"accentTertiary": "#82836A",
|
||||||
|
|
||||||
"error": "#939849",
|
"error": "#A5A9ED",
|
||||||
"warning": "#ABAF72",
|
"warning": "#B9BCF1",
|
||||||
|
|
||||||
"highlight": "#B1B3AF",
|
"highlight": "#C8C8B6",
|
||||||
"rippleEffect": "#8A8D86",
|
"rippleEffect": "#ACAD91",
|
||||||
|
|
||||||
"onAccent": "#111211",
|
"onAccent": "#0E0F10",
|
||||||
"outline": "#585958",
|
"outline": "#565758",
|
||||||
|
|
||||||
"shadow": "#111211",
|
"shadow": "#0E0F10",
|
||||||
"overlay": "#111211"
|
"overlay": "#0E0F10"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,48 @@
|
||||||
import QtQuick
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls
|
import QtQuick.Controls 2.15
|
||||||
import Quickshell
|
import Quickshell
|
||||||
import Quickshell.Io
|
import Quickshell.Io
|
||||||
import qs.Components
|
import qs.Components
|
||||||
import qs.Settings
|
import qs.Settings
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
PanelWindow {
|
Item {
|
||||||
|
id: root
|
||||||
|
property string configDir: Quickshell.configDir
|
||||||
|
property string historyFilePath: configDir + "/notification_history.json"
|
||||||
|
property bool hasUnread: notificationHistoryWin.hasUnread && !notificationHistoryWin.visible
|
||||||
|
function addToHistory(notification) { notificationHistoryWin.addToHistory(notification) }
|
||||||
|
width: 22; height: 22
|
||||||
|
|
||||||
|
// Bell icon/button
|
||||||
|
Item {
|
||||||
|
id: bell
|
||||||
|
width: 22; height: 22
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.hasUnread ? "notifications_unread" : "notifications"
|
||||||
|
font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
|
||||||
|
font.pixelSize: 16
|
||||||
|
font.weight: root.hasUnread ? Font.Bold : Font.Normal
|
||||||
|
color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (root.hasUnread ? Theme.accentPrimary : Theme.textDisabled)
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
id: mouseAreaBell
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The popup window
|
||||||
|
PanelWindow {
|
||||||
id: notificationHistoryWin
|
id: notificationHistoryWin
|
||||||
width: 400
|
width: 400
|
||||||
height: 500
|
property int maxPopupHeight: 500
|
||||||
|
property int minPopupHeight: 230
|
||||||
|
property int contentHeight: headerRow.height + historyList.contentHeight + 56
|
||||||
|
height: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight)
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
visible: false
|
visible: false
|
||||||
screen: Quickshell.primaryScreen
|
screen: Quickshell.primaryScreen
|
||||||
|
|
@ -20,48 +53,47 @@ PanelWindow {
|
||||||
margins.right: 4
|
margins.right: 4
|
||||||
|
|
||||||
property int maxHistory: 100
|
property int maxHistory: 100
|
||||||
property string configDir: Quickshell.configDir
|
property bool hasUnread: false
|
||||||
property string historyFilePath: configDir + "/notification_history.json"
|
signal unreadChanged(bool hasUnread)
|
||||||
|
|
||||||
ListModel {
|
ListModel {
|
||||||
id: historyModel // Holds notification objects
|
id: historyModel
|
||||||
}
|
}
|
||||||
|
|
||||||
FileView {
|
FileView {
|
||||||
id: historyFileView
|
id: historyFileView
|
||||||
path: historyFilePath
|
path: root.historyFilePath
|
||||||
blockLoading: true
|
blockLoading: true
|
||||||
printErrors: true
|
printErrors: true
|
||||||
watchChanges: true
|
watchChanges: true
|
||||||
|
|
||||||
JsonAdapter {
|
JsonAdapter {
|
||||||
id: historyAdapter
|
id: historyAdapter
|
||||||
property var notifications: [] // Array of notification objects
|
property var notifications: []
|
||||||
}
|
|
||||||
|
|
||||||
onFileChanged: {
|
|
||||||
reload() // Reload if file changes on disk
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoaded: {
|
|
||||||
loadHistory() // Populate model after loading
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onFileChanged: historyFileView.reload()
|
||||||
|
onLoaded: notificationHistoryWin.loadHistory()
|
||||||
onLoadFailed: function(error) {
|
onLoadFailed: function(error) {
|
||||||
console.error("Failed to load history file:", error)
|
|
||||||
if (error.includes("No such file")) {
|
if (error.includes("No such file")) {
|
||||||
historyAdapter.notifications = [] // Create new file if missing
|
historyAdapter.notifications = []
|
||||||
writeAdapter()
|
writeAdapter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Component.onCompleted: if (path) reload()
|
||||||
onSaved: {}
|
|
||||||
onSaveFailed: function(error) {
|
|
||||||
console.error("Failed to save history:", error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Component.onCompleted: {
|
function updateHasUnread() {
|
||||||
if (path) reload()
|
var unread = false;
|
||||||
|
for (let i = 0; i < historyModel.count; ++i) {
|
||||||
|
if (historyModel.get(i).read === false) {
|
||||||
|
unread = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasUnread !== unread) {
|
||||||
|
hasUnread = unread;
|
||||||
|
unreadChanged(hasUnread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,10 +103,15 @@ PanelWindow {
|
||||||
const notifications = historyAdapter.notifications
|
const notifications = historyAdapter.notifications
|
||||||
const count = Math.min(notifications.length, maxHistory)
|
const count = Math.min(notifications.length, maxHistory)
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
if (typeof notifications[i] === 'object' && notifications[i] !== null) {
|
let n = notifications[i]
|
||||||
historyModel.append(notifications[i])
|
if (typeof n === 'object' && n !== null) {
|
||||||
|
if (n.read === undefined) n.read = false;
|
||||||
|
// Mark as read if window is open
|
||||||
|
if (visible) n.read = true;
|
||||||
|
historyModel.append(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updateHasUnread();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +126,8 @@ PanelWindow {
|
||||||
appName: obj.appName,
|
appName: obj.appName,
|
||||||
summary: obj.summary,
|
summary: obj.summary,
|
||||||
body: obj.body,
|
body: obj.body,
|
||||||
timestamp: obj.timestamp
|
timestamp: obj.timestamp,
|
||||||
|
read: obj.read === undefined ? false : obj.read
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -97,18 +135,26 @@ PanelWindow {
|
||||||
Qt.callLater(function() {
|
Qt.callLater(function() {
|
||||||
historyFileView.writeAdapter()
|
historyFileView.writeAdapter()
|
||||||
})
|
})
|
||||||
|
updateHasUnread();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToHistory(notification) {
|
function addToHistory(notification) {
|
||||||
if (!notification.id) notification.id = Date.now()
|
if (!notification.id) notification.id = Date.now()
|
||||||
if (!notification.timestamp) notification.timestamp = new Date().toISOString()
|
if (!notification.timestamp) notification.timestamp = new Date().toISOString()
|
||||||
|
|
||||||
|
// Mark as read if window is open
|
||||||
|
notification.read = visible
|
||||||
|
|
||||||
|
// Remove duplicate by id
|
||||||
for (let i = 0; i < historyModel.count; ++i) {
|
for (let i = 0; i < historyModel.count; ++i) {
|
||||||
if (historyModel.get(i).id === notification.id) {
|
if (historyModel.get(i).id === notification.id) {
|
||||||
historyModel.remove(i)
|
historyModel.remove(i)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
historyModel.insert(0, notification)
|
historyModel.insert(0, notification)
|
||||||
|
|
||||||
if (historyModel.count > maxHistory) historyModel.remove(maxHistory)
|
if (historyModel.count > maxHistory) historyModel.remove(maxHistory)
|
||||||
saveHistory()
|
saveHistory()
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +176,20 @@ PanelWindow {
|
||||||
return `${y}-${m}-${d} ${h}:${min}`;
|
return `${y}-${m}-${d} ${h}:${min}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onVisibleChanged: {
|
||||||
|
if (visible) {
|
||||||
|
// Mark all as read when popup is opened
|
||||||
|
let changed = false;
|
||||||
|
for (let i = 0; i < historyModel.count; ++i) {
|
||||||
|
if (historyModel.get(i).read === false) {
|
||||||
|
historyModel.setProperty(i, 'read', true);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) saveHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: notificationHistoryWin.width
|
width: notificationHistoryWin.width
|
||||||
height: notificationHistoryWin.height
|
height: notificationHistoryWin.height
|
||||||
|
|
@ -143,9 +203,10 @@ PanelWindow {
|
||||||
spacing: 8
|
spacing: 8
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: headerRow
|
||||||
spacing: 4
|
spacing: 4
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.topMargin: 16
|
anchors.topMargin: 4
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.leftMargin: 16
|
anchors.leftMargin: 16
|
||||||
|
|
@ -162,7 +223,7 @@ PanelWindow {
|
||||||
id: clearAllButton
|
id: clearAllButton
|
||||||
width: 90
|
width: 90
|
||||||
height: 32
|
height: 32
|
||||||
radius: 20
|
radius: 16
|
||||||
color: clearAllMouseArea.containsMouse ? Theme.accentPrimary : Theme.surfaceVariant
|
color: clearAllMouseArea.containsMouse ? Theme.accentPrimary : Theme.surfaceVariant
|
||||||
border.color: Theme.accentPrimary
|
border.color: Theme.accentPrimary
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
|
@ -213,10 +274,8 @@ PanelWindow {
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
color: Theme.backgroundPrimary
|
color: Theme.surface
|
||||||
radius: 20
|
radius: 20
|
||||||
border.width: 1
|
|
||||||
border.color: Theme.surfaceVariant
|
|
||||||
z: 0
|
z: 0
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
|
@ -226,12 +285,19 @@ PanelWindow {
|
||||||
anchors.bottomMargin: 12
|
anchors.bottomMargin: 12
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
clip: true
|
clip: true
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
Item { id: topSpacer; height: (parent.height - historyList.height) / 2 }
|
||||||
ListView {
|
ListView {
|
||||||
id: historyList
|
id: historyList
|
||||||
anchors.fill: parent
|
width: parent.width
|
||||||
|
height: Math.min(contentHeight, parent.height)
|
||||||
spacing: 12
|
spacing: 12
|
||||||
model: historyModel
|
model: historyModel.count > 0 ? historyModel : placeholderModel
|
||||||
|
clip: true
|
||||||
delegate: Item {
|
delegate: Item {
|
||||||
|
width: parent.width
|
||||||
height: notificationCard.implicitHeight + 12
|
height: notificationCard.implicitHeight + 12
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: notificationCard
|
id: notificationCard
|
||||||
|
|
@ -239,8 +305,6 @@ PanelWindow {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: Theme.backgroundPrimary
|
color: Theme.backgroundPrimary
|
||||||
radius: 16
|
radius: 16
|
||||||
border.color: Theme.outline
|
|
||||||
border.width: 1
|
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.margins: 0
|
anchors.margins: 0
|
||||||
|
|
@ -253,9 +317,6 @@ PanelWindow {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: headerRow
|
id: headerRow
|
||||||
spacing: 8
|
spacing: 8
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 0
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: iconBackground
|
id: iconBackground
|
||||||
width: 28
|
width: 28
|
||||||
|
|
@ -279,7 +340,7 @@ PanelWindow {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
Layout.alignment: Qt.AlignVCenter
|
Layout.alignment: Qt.AlignVCenter
|
||||||
Text {
|
Text {
|
||||||
text: model.appName || "Unknown App"
|
text: model.appName || "No Notifications"
|
||||||
font.bold: true
|
font.bold: true
|
||||||
color: Theme.textPrimary
|
color: Theme.textPrimary
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
|
|
@ -287,49 +348,18 @@ PanelWindow {
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: formatTimestamp(model.timestamp)
|
visible: !model.isPlaceholder
|
||||||
color: Theme.textSecondary
|
text: model.timestamp ? notificationHistoryWin.formatTimestamp(model.timestamp) : ""
|
||||||
|
color: Theme.textDisabled
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
font.pixelSize: Theme.fontSizeCaption
|
font.pixelSize: Theme.fontSizeCaption
|
||||||
verticalAlignment: Text.AlignVCenter
|
verticalAlignment: Text.AlignVCenter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Item { Layout.fillWidth: true }
|
Item { Layout.fillWidth: true }
|
||||||
Rectangle {
|
|
||||||
id: deleteButton
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
radius: 12
|
|
||||||
color: deleteMouseArea.containsMouse ? Theme.accentPrimary : Theme.surfaceVariant
|
|
||||||
border.color: Theme.accentPrimary
|
|
||||||
border.width: 1
|
|
||||||
Layout.alignment: Qt.AlignVCenter
|
|
||||||
z: 2
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: 0
|
|
||||||
Text {
|
|
||||||
text: "close"
|
|
||||||
font.family: "Material Symbols Outlined"
|
|
||||||
font.pixelSize: 16
|
|
||||||
color: deleteMouseArea.containsMouse ? Theme.onAccent : Theme.error
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MouseArea {
|
|
||||||
id: deleteMouseArea
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
historyModel.remove(index)
|
|
||||||
saveHistory()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: model.summary || ""
|
text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "")
|
||||||
color: Theme.textSecondary
|
color: Theme.textSecondary
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
font.pixelSize: Theme.fontSizeBody
|
font.pixelSize: Theme.fontSizeBody
|
||||||
|
|
@ -337,7 +367,7 @@ PanelWindow {
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
text: model.body || ""
|
text: model.body || (model.isPlaceholder ? "No notifications to show." : "")
|
||||||
color: Theme.textDisabled
|
color: Theme.textDisabled
|
||||||
font.family: Theme.fontFamily
|
font.family: Theme.fontFamily
|
||||||
font.pixelSize: Theme.fontSizeBody
|
font.pixelSize: Theme.fontSizeBody
|
||||||
|
|
@ -350,8 +380,20 @@ PanelWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle { width: 1; height: 24; color: "transparent" }
|
Rectangle { width: 1; height: 24; color: "transparent" }
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: placeholderModel
|
||||||
|
ListElement {
|
||||||
|
appName: ""
|
||||||
|
summary: ""
|
||||||
|
body: ""
|
||||||
|
isPlaceholder: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
shell.qml
16
shell.qml
|
|
@ -15,6 +15,7 @@ Scope {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
property alias appLauncherPanel: appLauncherPanel
|
property alias appLauncherPanel: appLauncherPanel
|
||||||
|
property var notificationHistoryWin: notificationHistoryWin
|
||||||
|
|
||||||
function updateVolume(vol) {
|
function updateVolume(vol) {
|
||||||
volume = vol;
|
volume = vol;
|
||||||
|
|
@ -30,6 +31,7 @@ Scope {
|
||||||
Bar {
|
Bar {
|
||||||
id: bar
|
id: bar
|
||||||
shell: root
|
shell: root
|
||||||
|
property var notificationHistoryWin: notificationHistoryWin
|
||||||
}
|
}
|
||||||
|
|
||||||
Applauncher {
|
Applauncher {
|
||||||
|
|
@ -47,6 +49,15 @@ Scope {
|
||||||
console.log("Notification received:", notification.appName);
|
console.log("Notification received:", notification.appName);
|
||||||
notification.tracked = true;
|
notification.tracked = true;
|
||||||
notificationPopup.addNotification(notification);
|
notificationPopup.addNotification(notification);
|
||||||
|
if (notificationHistoryWin) {
|
||||||
|
notificationHistoryWin.addToHistory({
|
||||||
|
id: notification.id,
|
||||||
|
appName: notification.appName || "Notification",
|
||||||
|
summary: notification.summary || "",
|
||||||
|
body: notification.body || "",
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +66,11 @@ Scope {
|
||||||
barVisible: bar.visible
|
barVisible: bar.visible
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notification History Window
|
||||||
|
NotificationHistory {
|
||||||
|
id: notificationHistoryWin
|
||||||
|
}
|
||||||
|
|
||||||
property var defaultAudioSink: Pipewire.defaultAudioSink
|
property var defaultAudioSink: Pipewire.defaultAudioSink
|
||||||
property int volume: defaultAudioSink && defaultAudioSink.audio && defaultAudioSink.audio.volume ? Math.round(defaultAudioSink.audio.volume * 100) : 0
|
property int volume: defaultAudioSink && defaultAudioSink.audio && defaultAudioSink.audio.volume ? Math.round(defaultAudioSink.audio.volume * 100) : 0
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue