Files
z-bar-qt/Modules/Notifications/Content.qml
T

289 lines
6.6 KiB
QML

pragma ComponentBehavior: Bound
import Quickshell
import QtQuick
import ZShell
import ZShell.Components
import ZShell.Config
import ZShell.Services
import qs.Components
import qs.Components.Notifs
import qs.Daemons
import qs.Helpers
import qs.Services
Item {
id: root
readonly property int padding: Tokens.padding.small
readonly property int clamped: ZUtils.clamp(padding - Config.bar.border, 0, padding)
required property int topPadding
required property Item panels
required property ScreenState screenState
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: {
if (list.count === 0)
return 0;
let height = list.layoutHeight;
if (panels.popouts.hasCurrent && (panels.popouts.currentCenter + (panels.popouts.current?.width / 2)) > panels.notifications.x)
return 0;
if (screenState.dashboard && (panels.dashboard.x + panels.dashboard.width >= panels.notifications.x))
return 0;
if (screenState.osd) {
const h = panels.osd.y - Tokens.spacing.small * 2 - padding * 2;
if (height > h)
height = h;
}
return Math.min(((QsWindow.window as QsWindow)?.screen?.height ?? 0) + padding - clamped * 2 - topPadding, height + padding * 2);
}
implicitWidth: Config.notifs.sizes.width + padding * 2
Behavior on implicitHeight {
Anim {}
}
Item {
anchors.fill: parent
anchors.margins: root.padding
clip: true
KineticFlickable {
id: view
anchors.fill: parent
contentHeight: list.implicitHeight
contentWidth: width
flickableDirection: Flickable.VerticalFlick
LazyListView {
id: list
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: contentHeight
spacing: Tokens.spacing.medium
readyDelay: 1
cacheBuffer: (QsWindow.window as QsWindow)?.screen.height ?? 0
removeDuration: Tokens.anim.durations.normal
useCustomViewport: true
viewport: Qt.rect(0, view.contentY, width, view.height)
model: ScriptModel {
values: {
if (!Config.notifs.showInFullscreen) {
if (Hypr.monitorFor(root.panels.screen).activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false)
return [];
}
return [...NotifServer.popupGroups];
}
}
delegate: NotifWrapper {}
onViewportAdjustNeeded: d => {
if (contentYAnim.running)
contentYAnim.complete();
contentYAnim.to = Math.max(0, view.contentY + d);
contentYAnim.start();
}
Anim {
id: contentYAnim
target: view
property: "contentY"
}
}
}
}
component NotifWrapper: Item {
id: wrapper
required property NotificationGroup modelData
property bool expanded: Config.notifs.openExpanded
readonly property bool holding: area.containsMouse || area.pressed || card.rowPressed
function forEachPopup(fn: var): void {
for (const n of [...card.notifs])
fn(n);
}
function holdGroupExpiry(): void {
wrapper.modelData.holdExpiry();
}
LazyListView.trackViewport: !expanded && card.nonAnimHeight < card.implicitHeight
LazyListView.preferredHeight: LazyListView.removing ? 0 : card.nonAnimHeight
LazyListView.visibleHeight: LazyListView.removing ? 0 : card.implicitHeight
LazyListView.onRemovingChanged: {
if (!wrapper.LazyListView.removing)
return;
slideOut.to = (area.x >= 0 ? root.implicitWidth : -root.implicitWidth) * 2;
slideOut.start();
}
implicitWidth: area.implicitWidth
implicitHeight: card.implicitHeight
enabled: !LazyListView.removing
z: LazyListView.removing ? 1 : 0
onHoldingChanged: {
if (holding)
holdGroupExpiry();
else
wrapper.modelData.releaseExpiry();
}
Connections {
function onNotifsChanged(): void {
if (wrapper.holding)
wrapper.holdGroupExpiry();
}
function onExpireTimerRestarted(): void {
if (expireProgress.running)
expireProgress.stop();
expireProgressReset.start();
}
function onExpirePausedChanged(): void {
if (wrapper.modelData.expirePaused) {
if (expireProgress.running)
expireProgress.pause();
} else {
if (expireProgress.running)
expireProgress.resume();
else
expireProgress.start();
}
}
target: wrapper.modelData
}
Anim {
id: slideOut
target: area
property: "x"
duration: Tokens.anim.durations.normal
easing: Tokens.anim.emphasized
}
Anim {
id: expireProgressReset
readonly property bool nPaused: wrapper.modelData.expirePaused
target: progress
property: "implicitWidth"
type: Anim.FastEffects
to: nPaused ? area.width : area.width * (1 - duration / expireProgress.duration)
onFinished: {
if (nPaused)
expireProgress.duration = wrapper.modelData.expireTimeout;
else
expireProgress.duration = wrapper.modelData.expireTimeout - expireProgressReset.duration;
expireProgress.from = to;
if (!nPaused)
expireProgress.start();
}
}
Anim {
id: expireProgress
running: wrapper.modelData.expireActive
target: progress
property: "implicitWidth"
duration: wrapper.modelData.expireTimeout
easing.type: Easing.Linear
from: area.width
to: 0
}
Item {
anchors.top: parent.top
implicitWidth: area.implicitWidth
implicitHeight: card.implicitHeight
clip: true
NotifSwipeArea {
id: area
implicitWidth: root.implicitWidth - root.padding * 2
implicitHeight: card.implicitHeight
x: wrapper.LazyListView.adding ? implicitWidth : 0
expanded: wrapper.expanded
onRequestExpand: expand => wrapper.expanded = expand
onSwiped: wrapper.forEachPopup(n => n.dismissPopup())
onDismissed: wrapper.forEachPopup(n => n.dismiss())
onClicked: event => {
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton || card.notifs.length !== 1)
return;
const actions = card.notifs[0].actions;
if (actions.length === 1)
actions[0].invoke();
}
NotifGroupCard {
id: card
group: wrapper.modelData
container: view
expanded: wrapper.expanded
onRequestExpand: expand => wrapper.expanded = expand
onSwiped: notif => notif.dismissPopup()
onLinkActivated: notif => notif.dismissPopup()
bottomPadding: Config.notifs.expire ? progress.implicitHeight : 0
}
CustomClippingRect {
anchors.fill: parent
radius: Tokens.rounding.large
visible: Config.notifs.expire
CustomRect {
id: progress
anchors.bottom: parent.bottom
anchors.right: parent.right
implicitWidth: parent.width
implicitHeight: 6
color: Colors.palette.m3outlineVariant
}
}
}
}
Behavior on y {
enabled: wrapper.LazyListView.ready
Anim {}
}
}
}