**animations** #5

Merged
Zacharias-Brohn merged 2 commits from ListView-NC-testing into main 2025-11-17 15:03:39 +01:00
6 changed files with 275 additions and 219 deletions
Showing only changes of commit d8199f792a - Show all commits
+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`]
-7
View File
@@ -237,13 +237,6 @@ Singleton {
} }
} }
Component {
id: notificationPopup
TrackedNotification {
centerX: NotificationCenter.posX
}
}
Component { Component {
id: notifComp id: notifComp
+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
}
}
+92 -86
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
@@ -20,9 +21,11 @@ PanelWindow {
left: true left: true
bottom: true bottom: true
} }
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 }
@@ -61,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;
} }
@@ -74,10 +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
} }
HyprlandFocusGrab { HyprlandFocusGrab {
@@ -89,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
@@ -114,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
@@ -176,7 +188,7 @@ PanelWindow {
move: Transition { move: Transition {
NumberAnimation { NumberAnimation {
properties: "y,x"; properties: "x";
duration: 200; duration: 200;
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
@@ -215,31 +227,45 @@ PanelWindow {
Anim {} Anim {}
} }
// add: Transition { Behavior on y {
// id: addTrans Anim {
// SequentialAnimation { duration: MaterialEasing.expressiveEffectsTime
// PauseAnimation { easing.bezierCurve: MaterialEasing.expressiveEffects
// duration: ( addTrans.ViewTransition.index - addTrans.ViewTransition.targetIndexes[ 0 ]) * 50 }
// } }
// ParallelAnimation {
// NumberAnimation { add: Transition {
// properties: "y"; id: addTrans
// from: addTrans.ViewTransition.destination.y - (height / 2); SequentialAnimation {
// to: addTrans.ViewTransition.destination.y; PauseAnimation {
// duration: 100; duration: ( addTrans.ViewTransition.index - addTrans.ViewTransition.targetIndexes[ 0 ]) * 50
// easing.type: Easing.OutCubic }
// } ParallelAnimation {
// NumberAnimation { NumberAnimation {
// properties: "opacity"; properties: "y";
// from: 0; from: addTrans.ViewTransition.destination.y - (height / 2);
// to: 1; to: addTrans.ViewTransition.destination.y;
// duration: 100; duration: 100;
// easing.type: Easing.OutCubic easing.type: Easing.OutCubic
// } }
// } NumberAnimation {
// } properties: "opacity";
// } from: 0;
// to: 1;
duration: 100;
easing.type: Easing.OutCubic
}
NumberAnimation {
properties: "scale";
from: 0.7;
to: 1.0;
duration: 100
easing.type: Easing.InOutQuad
}
}
}
}
move: Transition { move: Transition {
id: moveTrans id: moveTrans
NumberAnimation { NumberAnimation {
@@ -281,79 +307,35 @@ PanelWindow {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
onClicked: { onClicked: {
groupColumn.isExpanded = false; groupColumn.shouldShow = false;
} }
} }
} }
} }
ListView { Repeater {
id: groupListView id: groupListView
model: ScriptModel { model: ScriptModel {
id: groupModel id: groupModel
values: groupColumn.isExpanded ? groupColumn.notifications : groupColumn.notifications.slice( 0, 1 ) values: groupColumn.isExpanded ? groupColumn.notifications : groupColumn.notifications.slice( 0, 1 )
} }
width: parent.width Rectangle {
spacing: 10
height: contentHeight
contentHeight: childrenRect.height
clip: false
pixelAligned: true
boundsBehavior: Flickable.StopAtBounds
displayMarginBeginning: 0
displayMarginEnd: 5000
Behavior on height {
Anim {
duration: 20;
}
}
add: Transition {
id: add
NumberAnimation {
properties: "y,opacity";
duration: 100 * ( add.ViewTransition.targetIndexes.length / ( add.ViewTransition.targetIndexes.length < 3 ? 1 : 3 ));
easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial
}
}
remove: Transition {
NumberAnimation {
properties: "opacity";
from: 1;
to: 0;
duration: 300;
easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial
}
}
displaced: Transition {
NumberAnimation {
properties: "y";
duration: 200;
easing.bezierCurve: MaterialEasing.expressiveDefaultSpatial
}
}
delegate: Rectangle {
id: groupHeader id: groupHeader
required property int index required property int index
required property NotifServer.Notif modelData required property NotifServer.Notif modelData
property alias notifHeight: groupHeader.height property alias notifHeight: groupHeader.height
property bool previewHidden: !groupColumn.isExpanded && index > 0 property bool previewHidden: groupColumn.shouldShow && index > 0
width: groupListView.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
opacity: 1 opacity: previewHidden ? 0 : 1.0
scale: 1.0 scale: previewHidden ? 0.7 : 1.0
Component.onCompleted: modelData.lock(this); Component.onCompleted: modelData.lock(this);
Component.onDestruction: modelData.unlock(this); Component.onDestruction: modelData.unlock(this);
@@ -366,11 +348,35 @@ 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 { ParallelAnimation {
running: groupHeader.modelData.closed running: groupHeader.modelData.closed
onFinished: groupHeader.modelData.unlock(groupHeader) onFinished: groupHeader.modelData.unlock(groupHeader)
+80 -43
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.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
property int index: notifIndex.indexOf(notif.id) visible: Hyprland.monitorFor(screen).focused
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 {}
NotificationCenter {} // NotificationCenter {}
Launcher {} Launcher {}
} }