diff --git a/modules/sidebar/Content.qml b/modules/sidebar/Content.qml index 1b7feed6..e09dd6a9 100644 --- a/modules/sidebar/Content.qml +++ b/modules/sidebar/Content.qml @@ -8,7 +8,7 @@ Item { id: root required property Props props - required property var visibilities + required property DrawerVisibilities visibilities ColumnLayout { id: layout diff --git a/modules/sidebar/Notif.qml b/modules/sidebar/Notif.qml index bfe7dd55..01593f2b 100644 --- a/modules/sidebar/Notif.qml +++ b/modules/sidebar/Notif.qml @@ -13,9 +13,9 @@ StyledRect { required property NotifData modelData required property Props props required property bool expanded - required property var visibilities + required property DrawerVisibilities visibilities - readonly property StyledText body: expandedContent.item?.body ?? null + readonly property StyledText body: (expandedContent.item as ExpandedBody)?.body ?? null readonly property real nonAnimHeight: expanded ? summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + Appearance.padding.normal * 2 : summaryHeightMetrics.height implicitHeight: nonAnimHeight @@ -118,30 +118,7 @@ StyledRect { anchors.right: parent.right anchors.topMargin: Appearance.spacing.small / 2 - sourceComponent: ColumnLayout { - readonly property alias body: body - - spacing: Appearance.spacing.smaller - - StyledText { - id: body - - Layout.fillWidth: true - textFormat: Text.MarkdownText - text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body here! :/") - color: root.modelData.urgency === "critical" ? Colours.palette.m3secondary : Colours.palette.m3outline - wrapMode: Text.WordWrap - - onLinkActivated: link => { - Quickshell.execDetached(["app2unit", "-O", "--", link]); - root.visibilities.sidebar = false; - } - } - - NotifActionList { - notif: root.modelData - } - } + sourceComponent: ExpandedBody {} } Behavior on implicitHeight { @@ -151,6 +128,31 @@ StyledRect { } } + component ExpandedBody: ColumnLayout { + readonly property alias body: bodyText + + spacing: Appearance.spacing.smaller + + StyledText { + id: bodyText + + Layout.fillWidth: true + textFormat: Text.MarkdownText + text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body here! :/") + color: root.modelData.urgency === "critical" ? Colours.palette.m3secondary : Colours.palette.m3outline + wrapMode: Text.WordWrap + + onLinkActivated: link => { + Quickshell.execDetached(["app2unit", "-O", "--", link]); + root.visibilities.sidebar = false; + } + } + + NotifActionList { + notif: root.modelData + } + } + component WrappedLoader: Loader { required property bool shouldBeActive diff --git a/modules/sidebar/NotifDock.qml b/modules/sidebar/NotifDock.qml index c38bddce..daa1dfda 100644 --- a/modules/sidebar/NotifDock.qml +++ b/modules/sidebar/NotifDock.qml @@ -15,7 +15,7 @@ Item { id: root required property Props props - required property var visibilities + required property DrawerVisibilities visibilities readonly property int notifCount: Notifs.list.reduce((acc, n) => n.closed ? acc : acc + 1, 0) anchors.fill: parent @@ -156,13 +156,14 @@ Item { let next = null; for (let i = 0; i < notifList.repeater.count; i++) { next = notifList.repeater.itemAt(i); - if (!next?.closed) + if (!next?.closed) // qmllint disable missing-property break; } - if (next) - next.closeAll(); - else + if (next) { + next.closeAll(); // qmllint disable missing-property + } else { stop(); + } } } diff --git a/modules/sidebar/NotifDockList.qml b/modules/sidebar/NotifDockList.qml index b927e91a..8ab76a57 100644 --- a/modules/sidebar/NotifDockList.qml +++ b/modules/sidebar/NotifDockList.qml @@ -11,7 +11,7 @@ Item { required property Props props required property Flickable container - required property var visibilities + required property DrawerVisibilities visibilities readonly property alias repeater: repeater readonly property int spacing: Appearance.spacing.small @@ -39,128 +39,130 @@ Item { onValuesChanged: root.flagChanged() } - MouseArea { - id: notif + delegate: NotifGroupDelegate {} + } - required property int index - required property string modelData + component NotifGroupDelegate: MouseArea { + id: notif - readonly property bool closed: notifInner.notifCount === 0 - readonly property alias nonAnimHeight: notifInner.nonAnimHeight - property int startY + required property int index + required property string modelData - function closeAll(): void { - for (const n of Notifs.notClosed.filter(n => n.appName === modelData)) - n.close(); + readonly property bool closed: notifInner.notifCount === 0 + readonly property alias nonAnimHeight: notifInner.nonAnimHeight + property int startY + + function closeAll(): void { + for (const n of Notifs.notClosed.filter(n => n.appName === modelData)) + n.close(); + } + + y: { + root.flag; // Force update + let y = 0; + for (let i = 0; i < index; i++) { + const item = repeater.itemAt(i) as NotifGroupDelegate; + if (item && !item.closed) + y += item.nonAnimHeight + root.spacing; } + return y; + } - y: { - root.flag; // Force update - let y = 0; - for (let i = 0; i < index; i++) { - const item = repeater.itemAt(i); - if (!item.closed) - y += item.nonAnimHeight + root.spacing; - } - return y; + containmentMask: QtObject { + function contains(p: point): bool { + if (!root.container.contains(notif.mapToItem(root.container, p))) + return false; + return notifInner.contains(p); } + } - containmentMask: QtObject { - function contains(p: point): bool { - if (!root.container.contains(notif.mapToItem(root.container, p))) - return false; - return notifInner.contains(p); - } + implicitWidth: root.width + implicitHeight: notifInner.implicitHeight + + hoverEnabled: true + cursorShape: pressed ? Qt.ClosedHandCursor : undefined + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + preventStealing: true + enabled: !closed + + drag.target: this + drag.axis: Drag.XAxis + + onPressed: event => { + startY = event.y; + if (event.button === Qt.RightButton) + notifInner.toggleExpand(!notifInner.expanded); + else if (event.button === Qt.MiddleButton) + closeAll(); + } + onPositionChanged: event => { + if (pressed) { + const diffY = event.y - startY; + if (Math.abs(diffY) > Config.notifs.expandThreshold) + notifInner.toggleExpand(diffY > 0); } + } + onReleased: event => { + if (Math.abs(x) < width * Config.notifs.clearThreshold) + x = 0; + else + closeAll(); + } - implicitWidth: root.width - implicitHeight: notifInner.implicitHeight + ParallelAnimation { + running: true - hoverEnabled: true - cursorShape: pressed ? Qt.ClosedHandCursor : undefined - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - preventStealing: true - enabled: !closed - - drag.target: this - drag.axis: Drag.XAxis - - onPressed: event => { - startY = event.y; - if (event.button === Qt.RightButton) - notifInner.toggleExpand(!notifInner.expanded); - else if (event.button === Qt.MiddleButton) - closeAll(); + Anim { + target: notif + property: "opacity" + from: 0 + to: 1 } - onPositionChanged: event => { - if (pressed) { - const diffY = event.y - startY; - if (Math.abs(diffY) > Config.notifs.expandThreshold) - notifInner.toggleExpand(diffY > 0); - } + Anim { + target: notif + property: "scale" + from: 0 + to: 1 + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } - onReleased: event => { - if (Math.abs(x) < width * Config.notifs.clearThreshold) - x = 0; - else - closeAll(); + } + + ParallelAnimation { + running: notif.closed + + Anim { + target: notif + property: "opacity" + to: 0 } - - ParallelAnimation { - running: true - - Anim { - target: notif - property: "opacity" - from: 0 - to: 1 - } - Anim { - target: notif - property: "scale" - from: 0 - to: 1 - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } + Anim { + target: notif + property: "scale" + to: 0.6 } + } - ParallelAnimation { - running: notif.closed + NotifGroup { + id: notifInner - Anim { - target: notif - property: "opacity" - to: 0 - } - Anim { - target: notif - property: "scale" - to: 0.6 - } + modelData: notif.modelData + props: root.props + container: root.container + visibilities: root.visibilities + } + + Behavior on x { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } + } - NotifGroup { - id: notifInner - - modelData: notif.modelData - props: root.props - container: root.container - visibilities: root.visibilities - } - - Behavior on x { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - Behavior on y { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } + Behavior on y { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } } } diff --git a/modules/sidebar/NotifGroup.qml b/modules/sidebar/NotifGroup.qml index 6a92ce05..da3f2b80 100644 --- a/modules/sidebar/NotifGroup.qml +++ b/modules/sidebar/NotifGroup.qml @@ -16,7 +16,7 @@ StyledRect { required property string modelData required property Props props required property Flickable container - required property var visibilities + required property DrawerVisibilities visibilities readonly property list notifs: Notifs.list.filter(n => n.appName === modelData) readonly property var groupProps: { diff --git a/modules/sidebar/NotifGroupList.qml b/modules/sidebar/NotifGroupList.qml index 2d9ba38b..a01cfb0a 100644 --- a/modules/sidebar/NotifGroupList.qml +++ b/modules/sidebar/NotifGroupList.qml @@ -14,13 +14,13 @@ Item { required property list notifs required property bool expanded required property Flickable container - required property var visibilities + required property DrawerVisibilities visibilities readonly property real nonAnimHeight: { let h = -root.spacing; for (let i = 0; i < repeater.count; i++) { - const item = repeater.itemAt(i); - if (!item.modelData.closed && !item.previewHidden) + const item = repeater.itemAt(i) as NotifDelegate; + if (item && !item.modelData.closed && !item.previewHidden) h += item.nonAnimHeight + root.spacing; } return h; @@ -59,149 +59,7 @@ Item { onValuesChanged: root.flagChanged() } - MouseArea { - id: notif - - required property int index - required property NotifData modelData - - readonly property alias nonAnimHeight: notifInner.nonAnimHeight - readonly property bool previewHidden: { - if (root.expanded) - return false; - - let extraHidden = 0; - for (let i = 0; i < index; i++) - if (root.notifs[i].closed) - extraHidden++; - - return index >= Config.notifs.groupPreviewNum + extraHidden; - } - property int startY - - y: { - root.flag; // Force update - let y = 0; - for (let i = 0; i < index; i++) { - const item = repeater.itemAt(i); - if (!item.modelData.closed && !item.previewHidden) - y += item.nonAnimHeight + root.spacing; - } - return y; - } - - containmentMask: QtObject { - function contains(p: point): bool { - if (!root.container.contains(notif.mapToItem(root.container, p))) - return false; - return notifInner.contains(p); - } - } - - opacity: previewHidden ? 0 : 1 - scale: previewHidden ? 0.7 : 1 - - implicitWidth: root.width - implicitHeight: notifInner.implicitHeight - - hoverEnabled: true - cursorShape: notifInner.body?.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined - acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - preventStealing: !root.expanded - enabled: !modelData.closed - - drag.target: this - drag.axis: Drag.XAxis - - onPressed: event => { - startY = event.y; - if (event.button === Qt.RightButton) - root.requestToggleExpand(!root.expanded); - else if (event.button === Qt.MiddleButton) - modelData.close(); - } - onPositionChanged: event => { - if (pressed && !root.expanded) { - const diffY = event.y - startY; - if (Math.abs(diffY) > Config.notifs.expandThreshold) - root.requestToggleExpand(diffY > 0); - } - } - onReleased: event => { - if (Math.abs(x) < width * Config.notifs.clearThreshold) - x = 0; - else - modelData.close(); - } - - Component.onCompleted: modelData.lock(this) - Component.onDestruction: modelData.unlock(this) - - ParallelAnimation { - Component.onCompleted: running = !notif.previewHidden - - Anim { - target: notif - property: "opacity" - from: 0 - to: 1 - } - Anim { - target: notif - property: "scale" - from: 0.7 - to: 1 - } - } - - ParallelAnimation { - running: notif.modelData.closed - onFinished: notif.modelData.unlock(notif) - - Anim { - target: notif - property: "opacity" - to: 0 - } - Anim { - target: notif - property: "x" - to: notif.x >= 0 ? notif.width : -notif.width - } - } - - Notif { - id: notifInner - - anchors.fill: parent - modelData: notif.modelData - props: root.props - expanded: root.expanded - visibilities: root.visibilities - } - - Behavior on opacity { - Anim {} - } - - Behavior on scale { - Anim {} - } - - Behavior on x { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - - Behavior on y { - Anim { - duration: Appearance.anim.durations.expressiveDefaultSpatial - easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial - } - } - } + delegate: NotifDelegate {} } Behavior on implicitHeight { @@ -210,4 +68,148 @@ Item { easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial } } + + component NotifDelegate: MouseArea { + id: notif + + required property int index + required property NotifData modelData + + readonly property alias nonAnimHeight: notifInner.nonAnimHeight + readonly property bool previewHidden: { + if (root.expanded) + return false; + + let extraHidden = 0; + for (let i = 0; i < index; i++) + if (root.notifs[i].closed) + extraHidden++; + + return index >= Config.notifs.groupPreviewNum + extraHidden; + } + property int startY + + y: { + root.flag; // Force update + let y = 0; + for (let i = 0; i < index; i++) { + const item = repeater.itemAt(i) as NotifDelegate; + if (item && !item.modelData.closed && !item.previewHidden) + y += item.nonAnimHeight + root.spacing; + } + return y; + } + + containmentMask: QtObject { + function contains(p: point): bool { + if (!root.container.contains(notif.mapToItem(root.container, p))) + return false; + return notifInner.contains(p); + } + } + + opacity: previewHidden ? 0 : 1 + scale: previewHidden ? 0.7 : 1 + + implicitWidth: root.width + implicitHeight: notifInner.implicitHeight + + hoverEnabled: true + cursorShape: notifInner.body?.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + preventStealing: !root.expanded + enabled: !modelData.closed + + drag.target: this + drag.axis: Drag.XAxis + + onPressed: event => { + startY = event.y; + if (event.button === Qt.RightButton) + root.requestToggleExpand(!root.expanded); + else if (event.button === Qt.MiddleButton) + modelData.close(); + } + onPositionChanged: event => { + if (pressed && !root.expanded) { + const diffY = event.y - startY; + if (Math.abs(diffY) > Config.notifs.expandThreshold) + root.requestToggleExpand(diffY > 0); + } + } + onReleased: event => { + if (Math.abs(x) < width * Config.notifs.clearThreshold) + x = 0; + else + modelData.close(); + } + + Component.onCompleted: modelData.lock(this) + Component.onDestruction: modelData.unlock(this) + + ParallelAnimation { + Component.onCompleted: running = !notif.previewHidden + + Anim { + target: notif + property: "opacity" + from: 0 + to: 1 + } + Anim { + target: notif + property: "scale" + from: 0.7 + to: 1 + } + } + + ParallelAnimation { + running: notif.modelData.closed + onFinished: notif.modelData.unlock(notif) + + Anim { + target: notif + property: "opacity" + to: 0 + } + Anim { + target: notif + property: "x" + to: notif.x >= 0 ? notif.width : -notif.width + } + } + + Notif { + id: notifInner + + anchors.fill: parent + modelData: notif.modelData + props: root.props + expanded: root.expanded + visibilities: root.visibilities + } + + Behavior on opacity { + Anim {} + } + + Behavior on scale { + Anim {} + } + + Behavior on x { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + + Behavior on y { + Anim { + duration: Appearance.anim.durations.expressiveDefaultSpatial + easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial + } + } + } } diff --git a/modules/sidebar/Wrapper.qml b/modules/sidebar/Wrapper.qml index 9303c6b9..d38de3e6 100644 --- a/modules/sidebar/Wrapper.qml +++ b/modules/sidebar/Wrapper.qml @@ -7,7 +7,7 @@ import QtQuick Item { id: root - required property var visibilities + required property DrawerVisibilities visibilities required property var panels readonly property Props props: Props {}