263 lines
10 KiB
QML
263 lines
10 KiB
QML
|
|
import QtQuick
|
|||
|
|
import QtQuick.Controls
|
|||
|
|
import QtQuick.Layouts
|
|||
|
|
import Qt5Compat.GraphicalEffects
|
|||
|
|
import "root:/Data" as Data
|
|||
|
|
|
|||
|
|
// Notification history viewer
|
|||
|
|
Item {
|
|||
|
|
id: root
|
|||
|
|
implicitHeight: 400
|
|||
|
|
|
|||
|
|
required property var shell
|
|||
|
|
property bool hovered: false
|
|||
|
|
property real targetX: 0
|
|||
|
|
|
|||
|
|
ColumnLayout {
|
|||
|
|
anchors.fill: parent
|
|||
|
|
anchors.margins: 16
|
|||
|
|
spacing: 12
|
|||
|
|
|
|||
|
|
// Header with title, count, and clear all button
|
|||
|
|
RowLayout {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
Layout.preferredHeight: 40
|
|||
|
|
spacing: 8
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
text: "Notification History"
|
|||
|
|
color: Data.ThemeManager.accentColor
|
|||
|
|
font.pixelSize: 18
|
|||
|
|
font.bold: true
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
text: "(" + (shell.notificationHistory ? shell.notificationHistory.count : 0) + ")"
|
|||
|
|
color: Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 12
|
|||
|
|
opacity: 0.7
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Item { Layout.fillWidth: true }
|
|||
|
|
|
|||
|
|
Rectangle {
|
|||
|
|
visible: shell.notificationHistory && shell.notificationHistory.count > 0
|
|||
|
|
width: clearText.implicitWidth + 16
|
|||
|
|
height: 24
|
|||
|
|
radius: 12
|
|||
|
|
color: clearMouseArea.containsMouse ? Qt.rgba(Data.ThemeManager.accentColor.r, Data.ThemeManager.accentColor.g, Data.ThemeManager.accentColor.b, 0.2) : "transparent"
|
|||
|
|
border.color: Data.ThemeManager.accentColor
|
|||
|
|
border.width: 1
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
id: clearText
|
|||
|
|
anchors.centerIn: parent
|
|||
|
|
text: "Clear All"
|
|||
|
|
color: Data.ThemeManager.accentColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 11
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MouseArea {
|
|||
|
|
id: clearMouseArea
|
|||
|
|
anchors.fill: parent
|
|||
|
|
hoverEnabled: true
|
|||
|
|
onClicked: shell.notificationHistory.clear()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Scrollable notification list
|
|||
|
|
Item {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
Layout.fillHeight: true
|
|||
|
|
|
|||
|
|
ScrollView {
|
|||
|
|
id: scrollView
|
|||
|
|
anchors.fill: parent
|
|||
|
|
clip: true
|
|||
|
|
ScrollBar.vertical: ScrollBar {
|
|||
|
|
policy: ScrollBar.AsNeeded
|
|||
|
|
interactive: true
|
|||
|
|
visible: notificationListView.contentHeight > notificationListView.height
|
|||
|
|
contentItem: Rectangle {
|
|||
|
|
implicitWidth: 6
|
|||
|
|
radius: width / 2
|
|||
|
|
color: parent.pressed ? Data.ThemeManager.accentColor
|
|||
|
|
: parent.hovered ? Qt.lighter(Data.ThemeManager.accentColor, 1.2)
|
|||
|
|
: Qt.rgba(Data.ThemeManager.accentColor.r, Data.ThemeManager.accentColor.g, Data.ThemeManager.accentColor.b, 0.7)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
|||
|
|
|
|||
|
|
ListView {
|
|||
|
|
id: notificationListView
|
|||
|
|
model: shell.notificationHistory
|
|||
|
|
spacing: 12
|
|||
|
|
cacheBuffer: 50 // Memory optimization
|
|||
|
|
reuseItems: true
|
|||
|
|
boundsBehavior: Flickable.StopAtBounds
|
|||
|
|
maximumFlickVelocity: 2500
|
|||
|
|
flickDeceleration: 1500
|
|||
|
|
clip: true
|
|||
|
|
interactive: true
|
|||
|
|
|
|||
|
|
// Smooth scrolling behavior
|
|||
|
|
property real targetY: contentY
|
|||
|
|
Behavior on targetY {
|
|||
|
|
NumberAnimation {
|
|||
|
|
duration: 200
|
|||
|
|
easing.type: Easing.OutQuad
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
onTargetYChanged: {
|
|||
|
|
if (!moving && !dragging) {
|
|||
|
|
contentY = targetY
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
delegate: Rectangle {
|
|||
|
|
width: notificationListView.width
|
|||
|
|
height: Math.max(80, contentLayout.implicitHeight + 24)
|
|||
|
|
radius: 8
|
|||
|
|
color: mouseArea.containsMouse ? Qt.darker(Data.ThemeManager.bgColor, 1.15) : Qt.darker(Data.ThemeManager.bgColor, 1.1)
|
|||
|
|
border.color: Data.ThemeManager.accentColor
|
|||
|
|
border.width: 1
|
|||
|
|
|
|||
|
|
// View optimization - only render visible items
|
|||
|
|
visible: y + height > notificationListView.contentY - height &&
|
|||
|
|
y < notificationListView.contentY + notificationListView.height + height
|
|||
|
|
|
|||
|
|
MouseArea {
|
|||
|
|
id: mouseArea
|
|||
|
|
anchors.fill: parent
|
|||
|
|
hoverEnabled: true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Main notification content layout
|
|||
|
|
RowLayout {
|
|||
|
|
id: contentLayout
|
|||
|
|
anchors.fill: parent
|
|||
|
|
anchors.margins: 12
|
|||
|
|
spacing: 12
|
|||
|
|
|
|||
|
|
// App icon area
|
|||
|
|
Item {
|
|||
|
|
Layout.preferredWidth: 24
|
|||
|
|
Layout.preferredHeight: 24
|
|||
|
|
Layout.alignment: Qt.AlignTop
|
|||
|
|
|
|||
|
|
Image {
|
|||
|
|
width: 24
|
|||
|
|
height: 24
|
|||
|
|
source: model.icon || ""
|
|||
|
|
visible: source.toString() !== ""
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Notification text content
|
|||
|
|
ColumnLayout {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
Layout.fillHeight: true
|
|||
|
|
spacing: 6
|
|||
|
|
|
|||
|
|
// App name and timestamp row
|
|||
|
|
RowLayout {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
text: model.appName || "Unknown"
|
|||
|
|
color: Data.ThemeManager.accentColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 13
|
|||
|
|
font.bold: true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
text: Qt.formatDateTime(model.timestamp, "hh:mm")
|
|||
|
|
color: Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 10
|
|||
|
|
opacity: 0.7
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Notification summary
|
|||
|
|
Text {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
visible: model.summary && model.summary.length > 0
|
|||
|
|
text: model.summary || ""
|
|||
|
|
color: Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 13
|
|||
|
|
font.bold: true
|
|||
|
|
wrapMode: Text.WordWrap
|
|||
|
|
lineHeight: 1.2
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Notification body text
|
|||
|
|
Text {
|
|||
|
|
Layout.fillWidth: true
|
|||
|
|
visible: model.body && model.body.length > 0
|
|||
|
|
text: model.body || ""
|
|||
|
|
color: Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 12
|
|||
|
|
opacity: 0.9
|
|||
|
|
wrapMode: Text.WordWrap
|
|||
|
|
maximumLineCount: 4
|
|||
|
|
elide: Text.ElideRight
|
|||
|
|
lineHeight: 1.2
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Individual delete button
|
|||
|
|
Rectangle {
|
|||
|
|
width: 24
|
|||
|
|
height: 24
|
|||
|
|
radius: 12
|
|||
|
|
anchors.right: parent.right
|
|||
|
|
anchors.top: parent.top
|
|||
|
|
anchors.margins: 8
|
|||
|
|
color: deleteArea.containsMouse ? Qt.rgba(255, 0, 0, 0.2) : "transparent"
|
|||
|
|
border.color: deleteArea.containsMouse ? "#ff4444" : Data.ThemeManager.fgColor
|
|||
|
|
border.width: 1
|
|||
|
|
opacity: deleteArea.containsMouse ? 1 : 0.5
|
|||
|
|
|
|||
|
|
Text {
|
|||
|
|
anchors.centerIn: parent
|
|||
|
|
text: "×"
|
|||
|
|
color: deleteArea.containsMouse ? "#ff4444" : Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 16
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MouseArea {
|
|||
|
|
id: deleteArea
|
|||
|
|
anchors.fill: parent
|
|||
|
|
hoverEnabled: true
|
|||
|
|
onClicked: shell.notificationHistory.remove(model.index)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Empty state message
|
|||
|
|
Text {
|
|||
|
|
anchors.centerIn: parent
|
|||
|
|
visible: !notificationListView.count
|
|||
|
|
text: "No notifications"
|
|||
|
|
color: Data.ThemeManager.fgColor
|
|||
|
|
font.family: "Roboto"
|
|||
|
|
font.pixelSize: 14
|
|||
|
|
opacity: 0.7
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|