Bugfix: PanelWithOverlay would close when clicking in the background of the panel.
This commit is contained in:
parent
0b5f1cd9e5
commit
8c7f6e491d
6 changed files with 543 additions and 314 deletions
|
|
@ -1,61 +1,31 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Settings
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
|
||||
import qs.Settings
|
||||
|
||||
PanelWithOverlay {
|
||||
id: notificationHistoryWin
|
||||
|
||||
property string historyFilePath: Settings.settingsDir + "notification_history.json"
|
||||
property bool hasUnread: notificationHistoryWinRect.hasUnread && !notificationHistoryWinRect.visible
|
||||
function addToHistory(notification) { notificationHistoryWinRect.addToHistory(notification) }
|
||||
|
||||
function addToHistory(notification) {
|
||||
notificationHistoryWinRect.addToHistory(notification);
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: notificationHistoryWinRect
|
||||
implicitWidth: 400
|
||||
|
||||
property int maxPopupHeight: 800
|
||||
property int minPopupHeight: 210
|
||||
property int contentHeight: headerRow.height + historyList.contentHeight + 56
|
||||
implicitHeight: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight)
|
||||
visible: parent.visible
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 4
|
||||
anchors.rightMargin: 4
|
||||
color: Theme.backgroundPrimary
|
||||
radius: 20
|
||||
|
||||
property int maxHistory: 100
|
||||
property bool hasUnread: false
|
||||
|
||||
signal unreadChanged(bool hasUnread)
|
||||
|
||||
ListModel {
|
||||
id: historyModel
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: historyFileView
|
||||
path: notificationHistoryWin.historyFilePath
|
||||
blockLoading: true
|
||||
printErrors: true
|
||||
watchChanges: true
|
||||
|
||||
JsonAdapter {
|
||||
id: historyAdapter
|
||||
property var notifications: []
|
||||
}
|
||||
|
||||
onFileChanged: historyFileView.reload()
|
||||
onLoaded: notificationHistoryWinRect.loadHistory()
|
||||
onLoadFailed: function (error) {
|
||||
historyAdapter.notifications = [];
|
||||
historyFileView.writeAdapter();
|
||||
}
|
||||
Component.onCompleted: if (path)
|
||||
reload()
|
||||
}
|
||||
|
||||
function updateHasUnread() {
|
||||
var unread = false;
|
||||
for (let i = 0; i < historyModel.count; ++i) {
|
||||
|
|
@ -80,9 +50,11 @@ PanelWithOverlay {
|
|||
if (typeof n === 'object' && n !== null) {
|
||||
if (n.read === undefined)
|
||||
n.read = false;
|
||||
|
||||
// Mark as read if window is open
|
||||
if (notificationHistoryWinRect.visible)
|
||||
n.read = true;
|
||||
|
||||
historyModel.append(n);
|
||||
}
|
||||
}
|
||||
|
|
@ -95,19 +67,19 @@ PanelWithOverlay {
|
|||
const count = Math.min(historyModel.count, maxHistory);
|
||||
for (let i = 0; i < count; ++i) {
|
||||
let obj = historyModel.get(i);
|
||||
if (typeof obj === 'object' && obj !== null) {
|
||||
if (typeof obj === 'object' && obj !== null)
|
||||
historyArray.push({
|
||||
id: obj.id,
|
||||
appName: obj.appName,
|
||||
summary: obj.summary,
|
||||
body: obj.body,
|
||||
timestamp: obj.timestamp,
|
||||
read: obj.read === undefined ? false : obj.read
|
||||
"id": obj.id,
|
||||
"appName": obj.appName,
|
||||
"summary": obj.summary,
|
||||
"body": obj.body,
|
||||
"timestamp": obj.timestamp,
|
||||
"read": obj.read === undefined ? false : obj.read
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
historyAdapter.notifications = historyArray;
|
||||
Qt.callLater(function () {
|
||||
Qt.callLater(function() {
|
||||
historyFileView.writeAdapter();
|
||||
});
|
||||
updateHasUnread();
|
||||
|
|
@ -116,12 +88,12 @@ PanelWithOverlay {
|
|||
function addToHistory(notification) {
|
||||
if (!notification.id)
|
||||
notification.id = Date.now();
|
||||
|
||||
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) {
|
||||
if (historyModel.get(i).id === notification.id) {
|
||||
|
|
@ -129,11 +101,10 @@ PanelWithOverlay {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
historyModel.insert(0, notification);
|
||||
|
||||
if (historyModel.count > maxHistory)
|
||||
historyModel.remove(maxHistory);
|
||||
|
||||
saveHistory();
|
||||
}
|
||||
|
||||
|
|
@ -146,6 +117,7 @@ PanelWithOverlay {
|
|||
function formatTimestamp(ts) {
|
||||
if (!ts)
|
||||
return "";
|
||||
|
||||
var date = typeof ts === "number" ? new Date(ts) : new Date(Date.parse(ts));
|
||||
var y = date.getFullYear();
|
||||
var m = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
|
|
@ -155,6 +127,15 @@ PanelWithOverlay {
|
|||
return `${y}-${m}-${d} ${h}:${min}`;
|
||||
}
|
||||
|
||||
implicitWidth: 400
|
||||
implicitHeight: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight)
|
||||
visible: parent.visible
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 4
|
||||
anchors.rightMargin: 4
|
||||
color: Theme.backgroundPrimary
|
||||
radius: 20
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
// Mark all as read when popup is opened
|
||||
|
|
@ -167,9 +148,46 @@ PanelWithOverlay {
|
|||
}
|
||||
if (changed)
|
||||
saveHistory();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent closing when clicking in the panel bg
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: historyModel
|
||||
}
|
||||
|
||||
FileView {
|
||||
id: historyFileView
|
||||
|
||||
path: notificationHistoryWin.historyFilePath
|
||||
blockLoading: true
|
||||
printErrors: true
|
||||
watchChanges: true
|
||||
onFileChanged: historyFileView.reload()
|
||||
onLoaded: notificationHistoryWinRect.loadHistory()
|
||||
onLoadFailed: function(error) {
|
||||
historyAdapter.notifications = [];
|
||||
historyFileView.writeAdapter();
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (path) {
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
JsonAdapter {
|
||||
id: historyAdapter
|
||||
|
||||
property var notifications: []
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: notificationHistoryWinRect.width
|
||||
height: notificationHistoryWinRect.height
|
||||
|
|
@ -184,6 +202,7 @@ PanelWithOverlay {
|
|||
|
||||
RowLayout {
|
||||
id: headerRow
|
||||
|
||||
spacing: 4
|
||||
anchors.topMargin: 4
|
||||
anchors.left: parent.left
|
||||
|
|
@ -193,6 +212,7 @@ PanelWithOverlay {
|
|||
Layout.preferredHeight: 52
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
Text {
|
||||
text: "Notification History"
|
||||
font.pixelSize: 18
|
||||
|
|
@ -200,11 +220,14 @@ PanelWithOverlay {
|
|||
color: Theme.textPrimary
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: clearAllButton
|
||||
|
||||
width: 90
|
||||
height: 32
|
||||
radius: 16
|
||||
|
|
@ -212,9 +235,11 @@ PanelWithOverlay {
|
|||
border.color: Theme.accentPrimary
|
||||
border.width: 1
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: "delete_sweep"
|
||||
font.family: "Material Symbols Outlined"
|
||||
|
|
@ -222,6 +247,7 @@ PanelWithOverlay {
|
|||
color: clearAllMouseArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Clear"
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
|
|
@ -229,15 +255,20 @@ PanelWithOverlay {
|
|||
font.bold: true
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: clearAllMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: notificationHistoryWinRect.clearHistory()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
@ -261,29 +292,36 @@ PanelWithOverlay {
|
|||
radius: 20
|
||||
z: 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: listContainer
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: 12
|
||||
anchors.bottomMargin: 12
|
||||
color: "transparent"
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ListView {
|
||||
id: historyList
|
||||
|
||||
width: parent.width
|
||||
height: Math.min(contentHeight, parent.height)
|
||||
spacing: 12
|
||||
model: historyModel.count > 0 ? historyModel : placeholderModel
|
||||
clip: true
|
||||
|
||||
delegate: Item {
|
||||
width: parent.width
|
||||
height: notificationCard.implicitHeight + 12
|
||||
|
||||
Rectangle {
|
||||
id: notificationCard
|
||||
|
||||
width: parent.width - 24
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Theme.backgroundPrimary
|
||||
|
|
@ -292,16 +330,22 @@ PanelWithOverlay {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.margins: 0
|
||||
implicitHeight: contentColumn.implicitHeight + 20
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: 14
|
||||
spacing: 6
|
||||
|
||||
RowLayout {
|
||||
id: headerRow2
|
||||
|
||||
spacing: 8
|
||||
|
||||
Rectangle {
|
||||
id: iconBackground
|
||||
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 20
|
||||
|
|
@ -309,6 +353,7 @@ PanelWithOverlay {
|
|||
border.color: Qt.darker(Theme.accentPrimary, 1.2)
|
||||
border.width: 1.2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: model.appName ? model.appName.charAt(0).toUpperCase() : "?"
|
||||
|
|
@ -317,11 +362,15 @@ PanelWithOverlay {
|
|||
font.bold: true
|
||||
color: Theme.backgroundPrimary
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
id: appInfoColumn
|
||||
|
||||
spacing: 0
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Text {
|
||||
text: model.appName || "No Notifications"
|
||||
font.bold: true
|
||||
|
|
@ -330,6 +379,7 @@ PanelWithOverlay {
|
|||
font.pixelSize: Theme.fontSizeSmall
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
visible: !model.isPlaceholder
|
||||
text: model.timestamp ? notificationHistoryWinRect.formatTimestamp(model.timestamp) : ""
|
||||
|
|
@ -338,11 +388,15 @@ PanelWithOverlay {
|
|||
font.pixelSize: Theme.fontSizeCaption
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "")
|
||||
color: Theme.textSecondary
|
||||
|
|
@ -351,6 +405,7 @@ PanelWithOverlay {
|
|||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Text {
|
||||
text: model.body || (model.isPlaceholder ? "No notifications to show." : "")
|
||||
color: Theme.textDisabled
|
||||
|
|
@ -359,12 +414,19 @@ PanelWithOverlay {
|
|||
width: parent.width
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
@ -375,14 +437,20 @@ PanelWithOverlay {
|
|||
|
||||
ListModel {
|
||||
id: placeholderModel
|
||||
|
||||
ListElement {
|
||||
appName: ""
|
||||
summary: ""
|
||||
body: ""
|
||||
isPlaceholder: true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue