**animations** #5

Merged
Zacharias-Brohn merged 2 commits from ListView-NC-testing into main 2025-11-17 15:03:39 +01:00
10 changed files with 662 additions and 259 deletions
+4
View File
@@ -17,6 +17,10 @@ Scope {
screen: modelData screen: modelData
property var root: Quickshell.shellDir property var root: Quickshell.shellDir
NotificationCenter {
bar: bar
}
Process { Process {
id: ncProcess id: ncProcess
command: ["sh", "-c", `qs -p ${bar.root}/shell.qml ipc call root showCenter`] command: ["sh", "-c", `qs -p ${bar.root}/shell.qml ipc call root showCenter`]
+245
View File
@@ -0,0 +1,245 @@
pragma Singleton
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Io
import Quickshell.Services.Notifications
import QtQuick
import qs.Modules
import qs.Helpers
Singleton {
id: root
property list<Notif> list: []
readonly property list<Notif> notClosed: list.filter( n => !n.closed )
readonly property list<Notif> popups: list.filter( n => n.popup )
property alias dnd: props.dnd
property alias server: server
property bool loaded
onListChanged: {
if ( loaded ) {
saveTimer.restart();
}
if ( root.list.length > 0 ) {
HasNotifications.hasNotifications = true;
} else {
HasNotifications.hasNotifications = false;
}
}
Timer {
id: saveTimer
interval: 1000
onTriggered: storage.setText( JSON.stringify( root.notClosed.map( n => ({
time: n.time,
id: n.id,
summary: n.summary,
body: n.body,
appIcon: n.appIcon,
appName: n.appName,
image: n.image,
expireTimeout: n.expireTimeout,
urgency: n.urgency,
resident: n.resident,
hasActionIcons: n.hasActionIcons,
actions: n.actions
}))));
}
PersistentProperties {
id: props
property bool dnd
reloadableId: "notifs"
}
NotificationServer {
id: server
keepOnReload: false
actionsSupported: true
bodyHyperlinksSupported: true
bodyImagesSupported: true
bodyMarkupSupported: true
imageSupported: true
persistenceSupported: true
onNotification: notif => {
notif.tracked = true;
const comp = notifComp.createObject(root, {
popup: !props.dnd,
notification: notif
});
root.list = [comp, ...root.list];
}
}
FileView {
id: storage
path: NotifPath.notifPath
onLoaded: {
const data = JSON.parse(text());
for (const notif of data)
root.list.push(notifComp.createObject(root, notif));
root.list.sort((a, b) => b.time - a.time);
root.loaded = true;
}
onLoadFailed: err => {
if (err === FileViewError.FileNotFound) {
root.loaded = true;
setText("[]");
}
}
}
component Notif: QtObject {
id: notif
property bool popup
property bool closed
property var locks: new Set()
property date time: new Date()
readonly property string timeStr: {
const diff = Time.date.getTime() - time.getTime();
const m = Math.floor(diff / 60000);
if (m < 1)
return qsTr("now");
const h = Math.floor(m / 60);
const d = Math.floor(h / 24);
if (d > 0)
return `${d}d`;
if (h > 0)
return `${h}h`;
return `${m}m`;
}
property Notification notification
property string id
property string summary
property string body
property string appIcon
property string appName
property string image
property real expireTimeout: 5
property int urgency: NotificationUrgency.Normal
property bool resident
property bool hasActionIcons
property list<var> actions
readonly property Timer timer: Timer {
running: true
interval: 5000
onTriggered: {
notif.popup = false;
}
}
readonly property Connections conn: Connections {
target: notif.notification
function onClosed(): void {
notif.close();
}
function onSummaryChanged(): void {
notif.summary = notif.notification.summary;
}
function onBodyChanged(): void {
notif.body = notif.notification.body;
}
function onAppIconChanged(): void {
notif.appIcon = notif.notification.appIcon;
}
function onAppNameChanged(): void {
notif.appName = notif.notification.appName;
}
function onImageChanged(): void {
notif.image = notif.notification.image;
}
function onExpireTimeoutChanged(): void {
notif.expireTimeout = notif.notification.expireTimeout;
}
function onUrgencyChanged(): void {
notif.urgency = notif.notification.urgency;
}
function onResidentChanged(): void {
notif.resident = notif.notification.resident;
}
function onHasActionIconsChanged(): void {
notif.hasActionIcons = notif.notification.hasActionIcons;
}
function onActionsChanged(): void {
notif.actions = notif.notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
}
function lock(item: Item): void {
locks.add(item);
}
function unlock(item: Item): void {
locks.delete(item);
if (closed)
close();
}
function close(): void {
closed = true;
if (locks.size === 0 && root.list.includes(this)) {
root.list = root.list.filter(n => n !== this);
notification?.dismiss();
destroy();
}
}
Component.onCompleted: {
if (!notification)
return;
id = notification.id;
summary = notification.summary;
body = notification.body;
appIcon = notification.appIcon;
appName = notification.appName;
image = notification.image;
expireTimeout = notification.expireTimeout;
urgency = notification.urgency;
resident = notification.resident;
hasActionIcons = notification.hasActionIcons;
actions = notification.actions.map(a => ({
identifier: a.identifier,
text: a.text,
invoke: () => a.invoke()
}));
}
}
Component {
id: notifComp
Notif {}
}
}
+16
View File
@@ -0,0 +1,16 @@
pragma Singleton
import Quickshell
import Quickshell.Io
Singleton {
id: root
property alias centerX: notifCenterSpacing.centerX
JsonAdapter {
id: notifCenterSpacing
property int centerX
}
}
+16
View File
@@ -0,0 +1,16 @@
pragma Singleton
import Quickshell.Io
import Quickshell
Singleton {
id: root
property alias notifPath: storage.notifPath
JsonAdapter {
id: storage
property string notifPath: Quickshell.statePath("notifications.json")
}
}
+20
View File
@@ -0,0 +1,20 @@
pragma Singleton
import Quickshell
Singleton {
property alias enabled: clock.enabled
readonly property date date: clock.date
readonly property int hours: clock.hours
readonly property int minutes: clock.minutes
readonly property int seconds: clock.seconds
function format(fmt: string): string {
return Qt.formatDateTime(clock.date, fmt);
}
SystemClock {
id: clock
precision: SystemClock.Seconds
}
}
-45
View File
@@ -1,45 +0,0 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
import qs.Modules
Scope {
id: root
property list<int> notifIds: []
property list<TrackedNotification> notifications;
NotificationServer {
id: notificationServer
imageSupported: true
actionsSupported: true
persistenceSupported: true
bodyImagesSupported: true
bodySupported: true
onNotification: notification => {
notification.tracked = true;
notification.receivedTime = Date.now();
root.notifIds.push(notification.id);
const notif = notificationComponent.createObject(root, { notif: notification, visible: !notificationCenter.doNotDisturb });
root.notifications.push(notif);
}
}
Component {
id: notificationComponent
TrackedNotification {
centerX: notificationCenter.posX
notifIndex: root.notifIds
notifList: root.notifications
onNotifDestroy: {
root.notifications.shift();
root.notifIds.shift();
}
}
}
NotificationCenter {
id: notificationCenter
notifications: notificationServer.trackedNotifications.values
}
}
+52
View File
@@ -0,0 +1,52 @@
pragma ComponentBehavior: Bound
import Quickshell
import Quickshell.Services.Notifications
import QtQuick
import qs.Modules
Scope {
id: root
property list<int> notifIds: []
property list<TrackedNotification> notifications;
// NotificationServer {
// id: notificationServer
// imageSupported: true
// actionsSupported: true
// persistenceSupported: true
// bodyImagesSupported: true
// bodySupported: true
// onNotification: notification => {
// notification.tracked = true;
// notification.receivedTime = Date.now();
// root.notifIds.push(notification.id);
// const notif = notificationComponent.createObject(root, { notif: notification, visible: !notificationCenter.doNotDisturb });
// root.notifications.push(notif);
// }
// }
Connections {
target: NotifServer.server
function onNotification() {
notificationComponent.createObject( root, { notif: NotifServer.list[0] });
}
}
Component {
id: notificationComponent
TrackedNotification {
centerX: notificationCenter.posX
notifIndex: root.notifIds
notifList: root.notifications
onNotifDestroy: {
root.notifications.shift();
root.notifIds.shift();
}
}
}
NotificationCenter {
id: notificationCenter
notifications: notificationServer.trackedNotifications.values
}
}
+145 -87
View File
@@ -2,6 +2,7 @@ import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick.Controls.FluentWinUI3 import QtQuick.Controls.FluentWinUI3
import QtQuick.Effects import QtQuick.Effects
@@ -9,6 +10,7 @@ import QtQuick
import Quickshell.Services.Notifications import Quickshell.Services.Notifications
import qs.Config import qs.Config
import qs.Helpers import qs.Helpers
import qs.Daemons
PanelWindow { PanelWindow {
id: root id: root
@@ -19,22 +21,15 @@ PanelWindow {
left: true left: true
bottom: true bottom: true
} }
required property list<Notification> notifications
WlrLayershell.layer: WlrLayer.Overlay
required property PanelWindow bar
property bool centerShown: false property bool centerShown: false
property alias posX: backgroundRect.x property alias posX: backgroundRect.x
property alias doNotDisturb: dndSwitch.checked
visible: false visible: false
mask: Region { item: backgroundRect } mask: Region { item: backgroundRect }
onNotificationsChanged: {
if ( root.notifications.length > 0 ) {
HasNotifications.hasNotifications = true;
} else {
HasNotifications.hasNotifications = false;
}
}
IpcHandler { IpcHandler {
id: ipcHandler id: ipcHandler
target: "root" target: "root"
@@ -69,10 +64,10 @@ PanelWindow {
id: showAnimation id: showAnimation
target: backgroundRect target: backgroundRect
property: "x" property: "x"
from: Screen.width to: root.bar.screen.width - backgroundRect.implicitWidth - 10
to: Screen.width - backgroundRect.implicitWidth - 10 from: root.bar.screen.width
duration: 300 duration: MaterialEasing.expressiveEffectsTime
easing.type: Easing.OutCubic easing.bezierCurve: MaterialEasing.expressiveEffects
onStopped: { onStopped: {
focusGrab.active = true; focusGrab.active = true;
} }
@@ -82,42 +77,10 @@ PanelWindow {
id: closeAnimation id: closeAnimation
target: backgroundRect target: backgroundRect
property: "x" property: "x"
from: Screen.width - backgroundRect.implicitWidth - 10 from: root.bar.screen.width - backgroundRect.implicitWidth - 10
to: Screen.width to: root.bar.screen.width
duration: 300 duration: MaterialEasing.expressiveEffectsTime
easing.type: Easing.OutCubic easing.bezierCurve: MaterialEasing.expressiveEffects
}
QtObject {
id: groupedData
property var groups: ({})
function updateGroups() {
var newGroups = {};
for ( var i = 0; i < root.notifications.length; i++ ) {
var notif = root.notifications[ i ];
var appName = notif.appName || "Unknown";
if ( !newGroups[ appName ]) {
newGroups[ appName ] = [];
}
newGroups[ appName ].push( notif );
}
// Sort notifications within each group (latest first)
for ( var app in newGroups ) {
newGroups[ app ].sort(( a, b ) => b.receivedTime - a.receivedTime );
}
groups = newGroups;
groupsChanged();
}
Component.onCompleted: updateGroups()
}
Connections {
target: root
function onNotificationsChanged() {
groupedData.updateGroups();
}
} }
HyprlandFocusGrab { HyprlandFocusGrab {
@@ -129,10 +92,16 @@ PanelWindow {
} }
} }
TrackedNotification {
centerShown: root.centerShown
bar: root.bar
}
Rectangle { Rectangle {
id: backgroundRect id: backgroundRect
y: 10 y: 10
x: Screen.width x: Screen.width
z: 1
implicitWidth: 400 implicitWidth: 400
implicitHeight: root.height - 20 implicitHeight: root.height - 20
color: Config.baseBgColor color: Config.baseBgColor
@@ -154,6 +123,9 @@ PanelWindow {
focus: false focus: false
activeFocusOnTab: false activeFocusOnTab: false
focusPolicy: Qt.NoFocus focusPolicy: Qt.NoFocus
onCheckedChanged: {
NotifServer.dnd = dndSwitch.checked;
}
} }
RowLayout { RowLayout {
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
@@ -180,9 +152,8 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
for ( var app in groupedData.groups ) { for ( const n of NotifServer.notClosed )
groupedData.groups[ app ].forEach( function( n ) { n.dismiss(); }); n.close();
}
} }
} }
} }
@@ -198,6 +169,7 @@ PanelWindow {
Flickable { Flickable {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
pixelAligned: true
contentHeight: notificationColumn.implicitHeight contentHeight: notificationColumn.implicitHeight
clip: true clip: true
@@ -216,28 +188,52 @@ PanelWindow {
move: Transition { move: Transition {
NumberAnimation { NumberAnimation {
properties: "y,x"; properties: "x";
duration: 200; duration: 200;
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
Repeater { Repeater {
model: Object.keys( groupedData.groups ) model: ScriptModel {
values: {
const map = new Map();
for ( const n of NotifServer.notClosed )
map.set( n.appName, null );
for ( const n of NotifServer.list )
map.set( n.appName, null );
return [ ...map.keys() ];
}
onValuesChanged: {
root.flagChanged();
}
}
Column { Column {
id: groupColumn id: groupColumn
required property string modelData required property string modelData
property var notifications: groupedData.groups[ modelData ] property list<var> notifications: NotifServer.list.filter( n => n.appName === modelData )
width: parent.width width: parent.width
spacing: 10 spacing: 10
property bool shouldShow: false property bool shouldShow: false
property bool isExpanded: false property bool isExpanded: false
function closeAll(): void {
for ( const n of NotifServer.notClosed.filter( n => n.appName === modelData ))
n.close();
}
Behavior on height { Behavior on height {
Anim {} Anim {}
} }
Behavior on y {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
add: Transition { add: Transition {
id: addTrans id: addTrans
SequentialAnimation { SequentialAnimation {
@@ -259,6 +255,13 @@ PanelWindow {
duration: 100; duration: 100;
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
NumberAnimation {
properties: "scale";
from: 0.7;
to: 1.0;
duration: 100
easing.type: Easing.InOutQuad
}
} }
} }
} }
@@ -266,9 +269,8 @@ PanelWindow {
move: Transition { move: Transition {
id: moveTrans id: moveTrans
NumberAnimation { NumberAnimation {
properties: "opacity"; properties: "y";
duration: 100; duration: 100;
to: 0;
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
@@ -305,46 +307,38 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
groupColumn.isExpanded = false; groupColumn.shouldShow = false;
} }
} }
} }
} }
Repeater { Repeater {
model: groupColumn.notifications id: groupListView
model: ScriptModel {
id: groupModel
values: groupColumn.isExpanded ? groupColumn.notifications : groupColumn.notifications.slice( 0, 1 )
}
Rectangle { Rectangle {
id: groupHeader id: groupHeader
required property var modelData required property int index
required property var index required property NotifServer.Notif modelData
property alias notifHeight: groupHeader.height
property bool previewHidden: groupColumn.shouldShow && index > 0
width: parent.width width: parent.width
height: contentColumn.height + 20 height: contentColumn.height + 20
color: Config.baseBgColor color: Config.baseBgColor
border.color: "#555555" border.color: "#555555"
border.width: 1 border.width: 1
radius: 8 radius: 8
visible: groupColumn.notifications[0].id === modelData.id || groupColumn.isExpanded opacity: previewHidden ? 0 : 1.0
opacity: groupColumn.notifications[0].id === modelData.id ? 1 : 0 scale: previewHidden ? 0.7 : 1.0
Connections { Component.onCompleted: modelData.lock(this);
target: groupColumn Component.onDestruction: modelData.unlock(this);
function onShouldShowChanged() {
if ( !shouldShow ) {
// collapseAnim.start();
}
}
}
onVisibleChanged: {
if ( visible ) {
// expandAnim.start();
} else {
if ( groupColumn.notifications[0].id !== modelData.id ) {
groupHeader.opacity = 0;
}
groupColumn.isExpanded = false;
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -354,11 +348,73 @@ PanelWindow {
groupHeader.modelData.actions[0].invoke(); groupHeader.modelData.actions[0].invoke();
} }
} else { } else {
groupColumn.shouldShow = true;
groupColumn.isExpanded = true; groupColumn.isExpanded = true;
} }
} }
} }
ParallelAnimation {
id: collapseAnim
running: !groupColumn.shouldShow && index > 0
Anim {
target: groupHeader
property: "opacity"
duration: 100
from: 1
to: index > 0 ? 0 : 1.0
}
Anim {
target: groupHeader
property: "scale"
duration: 100
from: 1
to: index > 0 ? 0.7 : 1.0
}
onFinished: {
groupColumn.isExpanded = false;
}
}
ParallelAnimation {
running: groupHeader.modelData.closed
onFinished: groupHeader.modelData.unlock(groupHeader)
Anim {
target: groupHeader
property: "opacity"
to: 0
}
Anim {
target: groupHeader
property: "x"
to: groupHeader.x >= 0 ? groupHeader.width : -groupHeader.width
}
}
Behavior on opacity {
Anim {}
}
Behavior on scale {
Anim {}
}
Behavior on x {
Anim {
duration: MaterialEasing.expressiveDefaultSpatialTime
easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial
}
}
Behavior on y {
Anim {
duration: MaterialEasing.expressiveDefaultSpatialTime
easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial
}
}
Column { Column {
id: contentColumn id: contentColumn
anchors.centerIn: parent anchors.centerIn: parent
@@ -420,13 +476,15 @@ PanelWindow {
RowLayout { RowLayout {
spacing: 2 spacing: 2
visible: groupColumn.isExpanded ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : ( groupColumn.notifications.length === 1 ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : false ) // visible: groupColumn.isExpanded ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : ( groupColumn.notifications.length === 1 ? ( groupHeader.modelData.actions.length > 1 ? true : false ) : false )
visible: true
height: 30 height: 30
width: parent.width width: parent.width
Repeater { Repeater {
model: groupHeader.modelData.actions model: groupHeader.modelData.actions
Rectangle { Rectangle {
id: actionButton
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 30 Layout.preferredHeight: 30
required property var modelData required property var modelData
@@ -434,7 +492,7 @@ PanelWindow {
radius: 4 radius: 4
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: modelData.text text: actionButton.modelData.text
color: "white" color: "white"
font.pointSize: 12 font.pointSize: 12
} }
@@ -443,7 +501,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
modelData.invoke(); actionButton.modelData.invoke();
} }
} }
} }
@@ -472,7 +530,7 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
groupColumn.isExpanded ? groupColumn.notifications[0].dismiss() : groupColumn.notifications.forEach( function( n ) { n.dismiss(); }); groupColumn.isExpanded ? groupHeader.modelData.close() : groupColumn.closeAll();
} }
} }
} }
+81 -44
View File
@@ -1,85 +1,122 @@
import Quickshell import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Hyprland
import QtQuick.Layouts import QtQuick.Layouts
import QtQuick import QtQuick
import Quickshell.Services.Notifications
import qs.Config import qs.Config
import qs.Daemons
import qs.Helpers
PanelWindow { PanelWindow {
id: root id: root
color: "transparent" color: "transparent"
screen: root.bar.screen
anchors { anchors {
top: true top: true
right: true right: true
left: true left: true
bottom: true bottom: true
} }
mask: Region { item: backgroundRect } mask: Region { regions: root.notifRegions }
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore
WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.layer: WlrLayer.Overlay
required property Notification notif property list<Region> notifRegions: []
required property int centerX required property bool centerShown
required property list<int> notifIndex required property PanelWindow bar
required property list<TrackedNotification> notifList visible: Hyprland.monitorFor(screen).focused
property int index: notifIndex.indexOf(notif.id)
property alias y: backgroundRect.y
property alias notifHeight: backgroundRect.implicitHeight
signal notifDestroy()
Component.onCompleted: { Component.onCompleted: {
openAnim.start(); console.log(NotifServer.list.filter( n => n.popup ).length + " notification popups loaded.");
console.log(root.index);
} }
Timer { ListView {
id: timeout id: notifListView
interval: 5000 model: ScriptModel {
onTriggered: { values: NotifServer.list.filter( n => n.popup )
closeAnim.start();
} }
anchors.top: parent.top
anchors.bottom: parent.bottom
x: root.centerShown ? root.bar.width - width - 420 : root.bar.width - width - 20
z: 0
anchors.topMargin: 54
width: 400
spacing: 10
Behavior on x {
NumberAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
displaced: Transition {
NumberAnimation {
property: "y"
duration: 100
easing.type: Easing.InOutQuad
}
}
remove: Transition {
id: hideTransition
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 1
to: 0
duration: 200
easing.type: Easing.InOutQuad
} }
NumberAnimation { NumberAnimation {
id: openAnim
target: backgroundRect
property: "x" property: "x"
from: root.centerX to: hideTransition.ViewTransition.destination.x + 200
to: root.centerX - backgroundRect.implicitWidth - 20 duration: 200
easing.type: Easing.InOutQuad
}
}
}
add: Transition {
id: showTransition
ParallelAnimation {
NumberAnimation {
property: "opacity"
from: 0
to: 1
duration: 200 duration: 200
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
onStopped: { timeout.start(); }
} }
NumberAnimation { NumberAnimation {
id: closeAnim
target: backgroundRect
property: "x" property: "x"
from: root.centerX - backgroundRect.implicitWidth - 20 from: showTransition.ViewTransition.destination.x + 200
to: root.centerX to: showTransition.ViewTransition.destination.x
duration: 200 duration: 200
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
onStopped: { }
root.destroy();
root.notifDestroy();
} }
} }
Rectangle { component NotifRegion: Region { }
Component {
id: notifRegion
NotifRegion {}
}
delegate: Rectangle {
id: backgroundRect id: backgroundRect
required property NotifServer.Notif modelData
implicitWidth: 400 implicitWidth: 400
implicitHeight: contentLayout.childrenRect.height + 16 implicitHeight: contentLayout.childrenRect.height + 16
x: root.centerX - implicitWidth - 20
y: !root.notifList[ root.index - 1 ] ? 34 + 20 : root.notifList[ root.index - 1 ].y + root.notifList[ root.index - 1 ].notifHeight + 10
color: Config.baseBgColor color: Config.baseBgColor
border.color: "#555555" border.color: "#555555"
radius: 8 radius: 8
Behavior on y { Component.onCompleted: {
NumberAnimation { root.notifRegions.push( notifRegion.createObject(root, { item: backgroundRect }));
duration: 200
easing.type: Easing.InOutQuad
}
} }
Column { Column {
@@ -92,11 +129,11 @@ PanelWindow {
RowLayout { RowLayout {
spacing: 12 spacing: 12
IconImage { IconImage {
source: root.notif.image source: backgroundRect.modelData.image
Layout.preferredWidth: 48 Layout.preferredWidth: 48
Layout.preferredHeight: 48 Layout.preferredHeight: 48
Layout.alignment: Qt.AlignHCenter | Qt.AlignLeft Layout.alignment: Qt.AlignHCenter | Qt.AlignLeft
visible: root.notif.image !== "" visible: backgroundRect.modelData.image !== ""
} }
ColumnLayout { ColumnLayout {
@@ -106,7 +143,7 @@ PanelWindow {
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
Text { Text {
text: root.notif.appName text: backgroundRect.modelData.appName
color: "white" color: "white"
font.bold: true font.bold: true
font.pointSize: 14 font.pointSize: 14
@@ -116,7 +153,7 @@ PanelWindow {
} }
Text { Text {
text: root.notif.summary text: backgroundRect.modelData.summary
color: "white" color: "white"
font.pointSize: 12 font.pointSize: 12
font.bold: true font.bold: true
@@ -128,7 +165,7 @@ PanelWindow {
} }
} }
Text { Text {
text: root.notif.body text: backgroundRect.modelData.body
color: "#dddddd" color: "#dddddd"
font.pointSize: 14 font.pointSize: 14
elide: Text.ElideRight elide: Text.ElideRight
@@ -160,8 +197,8 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
root.notif.dismiss(); backgroundRect.modelData.close();
root.visible = false; }
} }
} }
} }
+1 -1
View File
@@ -6,6 +6,6 @@ import qs.Modules
Scope { Scope {
Bar {} Bar {}
Wallpaper {} Wallpaper {}
NotifServer {} // NotificationCenter {}
Launcher {} Launcher {}
} }