formatter

This commit is contained in:
Zacharias-Brohn
2026-02-24 23:20:11 +01:00
parent 40cd984b6d
commit d56a0260fb
202 changed files with 15037 additions and 15352 deletions
+48 -42
View File
@@ -4,50 +4,56 @@ import QtQuick
import QtQuick.Shapes
ShapePath {
id: root
id: root
required property Wrapper wrapper
required property var sidebar
readonly property real rounding: 8
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real rounding: 8
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
required property var sidebar
required property Wrapper wrapper
strokeWidth: -1
fillColor: DynamicColors.palette.m3surface
fillColor: DynamicColors.palette.m3surface
strokeWidth: -1
PathLine {
relativeX: -(root.wrapper.width + root.rounding)
relativeY: 0
}
PathArc {
relativeX: root.rounding
relativeY: root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.roundingY * 2
}
PathArc {
relativeX: root.sidebar.notifsRoundingX
relativeY: root.roundingY
radiusX: root.sidebar.notifsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
relativeX: root.rounding
relativeY: root.rounding
radiusX: root.rounding
radiusY: root.rounding
}
Behavior on fillColor {
CAnim {
}
}
Behavior on fillColor {
CAnim {}
}
PathLine {
relativeX: -(root.wrapper.width + root.rounding)
relativeY: 0
}
PathArc {
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: root.roundingY
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.roundingY * 2
}
PathArc {
direction: PathArc.Counterclockwise
radiusX: root.sidebar.notifsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.sidebar.notifsRoundingX
relativeY: root.roundingY
}
PathLine {
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
radiusX: root.rounding
radiusY: root.rounding
relativeX: root.rounding
relativeY: root.rounding
}
}
+153 -155
View File
@@ -6,198 +6,196 @@ import Quickshell.Widgets
import QtQuick
Item {
id: root
id: root
required property PersistentProperties visibilities
required property Item panels
readonly property int padding: 6
readonly property int padding: 6
required property Item panels
required property PersistentProperties visibilities
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: {
const count = list.count;
if (count === 0)
return 0;
implicitWidth: Config.notifs.sizes.width + padding * 2
implicitHeight: {
const count = list.count;
if (count === 0)
return 0;
let height = (count - 1) * 8;
for (let i = 0; i < count; i++)
height += list.itemAtIndex(i)?.nonAnimHeight ?? 0;
let height = (count - 1) * 8;
for (let i = 0; i < count; i++)
height += list.itemAtIndex(i)?.nonAnimHeight ?? 0;
if (visibilities && panels) {
if (visibilities.osd) {
const h = panels.osd.y - 8 * 2 - padding * 2;
if (height > h)
height = h;
}
if (visibilities && panels) {
if (visibilities.osd) {
const h = panels.osd.y - 8 * 2 - padding * 2;
if (height > h)
height = h;
}
if (visibilities.session) {
const h = panels.session.y - 8 * 2 - padding * 2;
if (height > h)
height = h;
}
}
if (visibilities.session) {
const h = panels.session.y - 8 * 2 - padding * 2;
if (height > h)
height = h;
}
}
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
}
implicitWidth: Config.notifs.sizes.width + padding * 2
return Math.min((QsWindow.window?.screen?.height ?? 0) - 1 * 2, height + padding * 2);
}
Behavior on implicitHeight {
Anim {
}
}
ClippingWrapperRectangle {
anchors.fill: parent
anchors.margins: root.padding
ClippingWrapperRectangle {
anchors.fill: parent
anchors.margins: root.padding
color: "transparent"
radius: 4
color: "transparent"
radius: 4
CustomListView {
id: list
CustomListView {
id: list
anchors.fill: parent
cacheBuffer: QsWindow.window?.screen.height ?? 0
orientation: Qt.Vertical
spacing: 0
model: ScriptModel {
values: NotifServer.popups.filter(n => !n.closed)
}
delegate: Item {
id: wrapper
anchors.fill: parent
property int idx
required property int index
required property NotifServer.Notif modelData
readonly property alias nonAnimHeight: notif.nonAnimHeight
orientation: Qt.Vertical
spacing: 0
cacheBuffer: QsWindow.window?.screen.height ?? 0
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
implicitWidth: notif.implicitWidth
delegate: Item {
id: wrapper
ListView.onRemove: removeAnim.start()
onIndexChanged: {
if (index !== -1)
idx = index;
}
required property NotifServer.Notif modelData
required property int index
readonly property alias nonAnimHeight: notif.nonAnimHeight
property int idx
SequentialAnimation {
id: removeAnim
onIndexChanged: {
if (index !== -1)
idx = index;
}
PropertyAction {
property: "ListView.delayRemove"
target: wrapper
value: true
}
implicitWidth: notif.implicitWidth
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
PropertyAction {
property: "enabled"
target: wrapper
value: false
}
ListView.onRemove: removeAnim.start()
PropertyAction {
property: "implicitHeight"
target: wrapper
value: 0
}
SequentialAnimation {
id: removeAnim
PropertyAction {
property: "z"
target: wrapper
value: 1
}
PropertyAction {
target: wrapper
property: "ListView.delayRemove"
value: true
}
PropertyAction {
target: wrapper
property: "enabled"
value: false
}
PropertyAction {
target: wrapper
property: "implicitHeight"
value: 0
}
PropertyAction {
target: wrapper
property: "z"
value: 1
}
Anim {
target: notif
property: "x"
to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
PropertyAction {
target: wrapper
property: "ListView.delayRemove"
value: false
}
}
property: "x"
target: notif
to: (notif.x >= 0 ? Config.notifs.sizes.width : -Config.notifs.sizes.width) * 2
}
ClippingRectangle {
anchors.top: parent.top
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
PropertyAction {
property: "ListView.delayRemove"
target: wrapper
value: false
}
}
color: "transparent"
radius: 4
implicitWidth: notif.implicitWidth
implicitHeight: notif.implicitHeight
ClippingRectangle {
anchors.top: parent.top
anchors.topMargin: wrapper.idx === 0 ? 0 : 8
color: "transparent"
implicitHeight: notif.implicitHeight
implicitWidth: notif.implicitWidth
radius: 4
Notification {
id: notif
Notification {
id: notif
modelData: wrapper.modelData
}
}
}
modelData: wrapper.modelData
}
}
}
displaced: Transition {
Anim {
property: "y"
}
}
model: ScriptModel {
values: NotifServer.popups.filter(n => !n.closed)
}
move: Transition {
Anim {
property: "y"
}
}
move: Transition {
Anim {
property: "y"
}
}
ExtraIndicator {
anchors.top: parent.top
extra: {
const count = list.count;
if (count === 0)
return 0;
displaced: Transition {
Anim {
property: "y"
}
}
const scrollY = list.contentY;
ExtraIndicator {
anchors.top: parent.top
extra: {
const count = list.count;
if (count === 0)
return 0;
let height = 0;
for (let i = 0; i < count; i++) {
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
const scrollY = list.contentY;
if (height - 8 >= scrollY)
return i;
}
let height = 0;
for (let i = 0; i < count; i++) {
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
return count;
}
}
if (height - 8 >= scrollY)
return i;
}
ExtraIndicator {
anchors.bottom: parent.bottom
extra: {
const count = list.count;
if (count === 0)
return 0;
return count;
}
}
const scrollY = list.contentHeight - (list.contentY + list.height);
ExtraIndicator {
anchors.bottom: parent.bottom
extra: {
const count = list.count;
if (count === 0)
return 0;
let height = 0;
for (let i = count - 1; i >= 0; i--) {
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
const scrollY = list.contentHeight - (list.contentY + list.height);
if (height - 8 >= scrollY)
return count - i - 1;
}
let height = 0;
for (let i = count - 1; i >= 0; i--) {
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
return 0;
}
}
}
}
if (height - 8 >= scrollY)
return count - i - 1;
}
return 0;
}
}
}
}
Behavior on implicitHeight {
Anim {}
}
component Anim: NumberAnimation {
easing.type: Easing.BezierSpline
component Anim: NumberAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
easing.type: Easing.BezierSpline
}
}
+372 -389
View File
@@ -12,473 +12,456 @@ import QtQuick
import QtQuick.Layouts
CustomRect {
id: root
id: root
required property NotifServer.Notif modelData
readonly property bool hasImage: modelData.image.length > 0
readonly property bool hasAppIcon: modelData.appIcon.length > 0
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
property bool expanded: Config.notifs.openExpanded
property bool expanded: Config.notifs.openExpanded
readonly property bool hasAppIcon: modelData.appIcon.length > 0
readonly property bool hasImage: modelData.image.length > 0
required property NotifServer.Notif modelData
readonly property int nonAnimHeight: summary.implicitHeight + (root.expanded ? appName.height + body.height + actions.height + actions.anchors.topMargin : bodyPreview.height) + inner.anchors.margins * 2
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
radius: 6
implicitWidth: Config.notifs.sizes.width
implicitHeight: inner.implicitHeight
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondaryContainer : DynamicColors.tPalette.m3surfaceContainer
implicitHeight: inner.implicitHeight
implicitWidth: Config.notifs.sizes.width
radius: 6
x: Config.notifs.sizes.width
x: Config.notifs.sizes.width
Component.onCompleted: {
x = 0;
modelData.lock(this);
}
Component.onDestruction: modelData.unlock(this)
Behavior on x {
Anim {
Behavior on x {
Anim {
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
MouseArea {
property int startY
Component.onCompleted: {
x = 0;
modelData.lock(this);
}
Component.onDestruction: modelData.unlock(this)
anchors.fill: parent
hoverEnabled: true
cursorShape: root.expanded && body.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
preventStealing: true
MouseArea {
property int startY
onEntered: root.modelData.timer.stop()
onExited: {
if (!pressed)
root.modelData.timer.start();
}
acceptedButtons: Qt.LeftButton | Qt.MiddleButton
anchors.fill: parent
cursorShape: root.expanded && body.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
drag.axis: Drag.XAxis
drag.target: parent
hoverEnabled: true
preventStealing: true
drag.target: parent
drag.axis: Drag.XAxis
onClicked: event => {
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton)
return;
onPressed: event => {
root.modelData.timer.stop();
startY = event.y;
if (event.button === Qt.MiddleButton)
root.modelData.close();
}
onReleased: event => {
if (!containsMouse)
root.modelData.timer.start();
const actions = root.modelData.actions;
if (actions?.length === 1)
actions[0].invoke();
}
onEntered: root.modelData.timer.stop()
onExited: {
if (!pressed)
root.modelData.timer.start();
}
onPositionChanged: event => {
if (pressed) {
const diffY = event.y - startY;
if (Math.abs(diffY) > Config.notifs.expandThreshold)
root.expanded = diffY > 0;
}
}
onPressed: event => {
root.modelData.timer.stop();
startY = event.y;
if (event.button === Qt.MiddleButton)
root.modelData.close();
}
onReleased: event => {
if (!containsMouse)
root.modelData.timer.start();
if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
root.x = 0;
else
root.modelData.popup = false;
}
onPositionChanged: event => {
if (pressed) {
const diffY = event.y - startY;
if (Math.abs(diffY) > Config.notifs.expandThreshold)
root.expanded = diffY > 0;
}
}
onClicked: event => {
if (!Config.notifs.actionOnClick || event.button !== Qt.LeftButton)
return;
if (Math.abs(root.x) < Config.notifs.sizes.width * Config.notifs.clearThreshold)
root.x = 0;
else
root.modelData.popup = false;
}
const actions = root.modelData.actions;
if (actions?.length === 1)
actions[0].invoke();
}
Item {
id: inner
Item {
id: inner
anchors.left: parent.left
anchors.margins: 8
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: root.nonAnimHeight
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 8
implicitHeight: root.nonAnimHeight
Behavior on implicitHeight {
Anim {
Behavior on implicitHeight {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
Loader {
id: image
Loader {
id: image
active: root.hasImage
anchors.left: parent.left
anchors.top: parent.top
width: Config.notifs.sizes.image
height: Config.notifs.sizes.image
visible: root.hasImage || root.hasAppIcon
active: root.hasImage
anchors.left: parent.left
anchors.top: parent.top
asynchronous: true
height: Config.notifs.sizes.image
visible: root.hasImage || root.hasAppIcon
width: Config.notifs.sizes.image
sourceComponent: ClippingRectangle {
radius: 1000
implicitWidth: Config.notifs.sizes.image
implicitHeight: Config.notifs.sizes.image
sourceComponent: ClippingRectangle {
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
radius: 1000
Image {
anchors.fill: parent
source: Qt.resolvedUrl(root.modelData.image)
fillMode: Image.PreserveAspectCrop
Image {
anchors.fill: parent
asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop
mipmap: true
cache: false
asynchronous: true
}
}
}
source: Qt.resolvedUrl(root.modelData.image)
}
}
}
Loader {
id: appIcon
Loader {
id: appIcon
active: root.hasAppIcon || !root.hasImage
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
anchors.right: root.hasImage ? image.right : undefined
anchors.bottom: root.hasImage ? image.bottom : undefined
active: root.hasAppIcon || !root.hasImage
anchors.bottom: root.hasImage ? image.bottom : undefined
anchors.horizontalCenter: root.hasImage ? undefined : image.horizontalCenter
anchors.right: root.hasImage ? image.right : undefined
anchors.verticalCenter: root.hasImage ? undefined : image.verticalCenter
asynchronous: true
sourceComponent: CustomRect {
radius: 1000
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2) : DynamicColors.palette.m3secondaryContainer
implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
sourceComponent: CustomRect {
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2) : DynamicColors.palette.m3secondaryContainer
implicitHeight: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
implicitWidth: root.hasImage ? Config.notifs.sizes.badge : Config.notifs.sizes.image
radius: 1000
Loader {
id: icon
Loader {
id: icon
active: root.hasAppIcon
active: root.hasAppIcon
anchors.centerIn: parent
asynchronous: true
height: Math.round(parent.width * 0.6)
width: Math.round(parent.width * 0.6)
anchors.centerIn: parent
sourceComponent: CustomIcon {
anchors.fill: parent
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
source: Quickshell.iconPath(root.modelData.appIcon)
}
}
Loader {
active: !root.hasAppIcon
anchors.centerIn: parent
anchors.horizontalCenterOffset: -18 * 0.02
anchors.verticalCenterOffset: 18 * 0.02
asynchronous: true
width: Math.round(parent.width * 0.6)
height: Math.round(parent.width * 0.6)
sourceComponent: MaterialIcon {
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
}
}
}
}
sourceComponent: CustomIcon {
anchors.fill: parent
source: Quickshell.iconPath(root.modelData.appIcon)
layer.enabled: root.modelData.appIcon.endsWith("symbolic")
}
}
CustomText {
id: appName
Loader {
active: !root.hasAppIcon
anchors.centerIn: parent
anchors.horizontalCenterOffset: -18 * 0.02
anchors.verticalCenterOffset: 18 * 0.02
asynchronous: true
anchors.left: image.right
anchors.leftMargin: 10
anchors.top: parent.top
animate: true
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
maximumLineCount: 1
opacity: root.expanded ? 1 : 0
text: appNameMetrics.elidedText
sourceComponent: MaterialIcon {
text: Icons.getNotifIcon(root.modelData.summary, root.modelData.urgency)
Behavior on opacity {
Anim {
}
}
}
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.modelData.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
}
}
}
}
TextMetrics {
id: appNameMetrics
CustomText {
id: appName
elide: Text.ElideRight
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
font.family: appName.font.family
font.pointSize: appName.font.pointSize
text: root.modelData.appName
}
anchors.top: parent.top
anchors.left: image.right
anchors.leftMargin: 10
CustomText {
id: summary
animate: true
text: appNameMetrics.elidedText
maximumLineCount: 1
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
anchors.left: image.right
anchors.leftMargin: 10
anchors.top: parent.top
animate: true
height: implicitHeight
maximumLineCount: 1
text: summaryMetrics.elidedText
opacity: root.expanded ? 1 : 0
Behavior on height {
Anim {
}
}
states: State {
name: "expanded"
when: root.expanded
Behavior on opacity {
Anim {}
}
}
PropertyChanges {
summary.maximumLineCount: undefined
}
TextMetrics {
id: appNameMetrics
AnchorChanges {
anchors.top: appName.bottom
target: summary
}
}
transitions: Transition {
PropertyAction {
property: "maximumLineCount"
target: summary
}
text: root.modelData.appName
font.family: appName.font.family
font.pointSize: appName.font.pointSize
elide: Text.ElideRight
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
}
CustomText {
id: summary
anchors.top: parent.top
anchors.left: image.right
anchors.leftMargin: 10
animate: true
text: summaryMetrics.elidedText
maximumLineCount: 1
height: implicitHeight
states: State {
name: "expanded"
when: root.expanded
PropertyChanges {
summary.maximumLineCount: undefined
}
AnchorChanges {
target: summary
anchors.top: appName.bottom
}
}
transitions: Transition {
PropertyAction {
target: summary
property: "maximumLineCount"
}
AnchorAnimation {
easing.type: Easing.BezierSpline
AnchorAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on height {
Anim {}
}
}
TextMetrics {
id: summaryMetrics
text: root.modelData.summary
font.family: summary.font.family
font.pointSize: summary.font.pointSize
elide: Text.ElideRight
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
}
CustomText {
id: timeSep
anchors.top: parent.top
anchors.left: summary.right
anchors.leftMargin: 7
text: "•"
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
target: timeSep
anchors.left: appName.right
}
}
transitions: Transition {
AnchorAnimation {
easing.type: Easing.BezierSpline
}
}
}
TextMetrics {
id: summaryMetrics
elide: Text.ElideRight
elideWidth: expandBtn.x - time.width - timeSep.width - summary.x - 7 * 3
font.family: summary.font.family
font.pointSize: summary.font.pointSize
text: root.modelData.summary
}
CustomText {
id: timeSep
anchors.left: summary.right
anchors.leftMargin: 7
anchors.top: parent.top
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
text: "•"
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
anchors.left: appName.right
target: timeSep
}
}
transitions: Transition {
AnchorAnimation {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
easing.type: Easing.BezierSpline
}
}
}
CustomText {
id: time
CustomText {
id: time
anchors.top: parent.top
anchors.left: timeSep.right
anchors.leftMargin: 7
anchors.left: timeSep.right
anchors.leftMargin: 7
anchors.top: parent.top
animate: true
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
horizontalAlignment: Text.AlignLeft
text: root.modelData.timeStr
}
animate: true
horizontalAlignment: Text.AlignLeft
text: root.modelData.timeStr
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
}
Item {
id: expandBtn
Item {
id: expandBtn
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: expandIcon.height
implicitWidth: expandIcon.height
anchors.right: parent.right
anchors.top: parent.top
StateLayer {
function onClicked() {
root.expanded = !root.expanded;
}
implicitWidth: expandIcon.height
implicitHeight: expandIcon.height
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
radius: 1000
}
StateLayer {
radius: 1000
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
MaterialIcon {
id: expandIcon
function onClicked() {
root.expanded = !root.expanded;
}
}
anchors.centerIn: parent
animate: true
font.pointSize: 13
text: root.expanded ? "expand_less" : "expand_more"
}
}
MaterialIcon {
id: expandIcon
CustomText {
id: bodyPreview
anchors.centerIn: parent
anchors.left: summary.left
anchors.right: expandBtn.left
anchors.rightMargin: 7
anchors.top: summary.bottom
animate: true
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
opacity: root.expanded ? 0 : 1
text: bodyPreviewMetrics.elidedText
textFormat: Text.MarkdownText
animate: true
text: root.expanded ? "expand_less" : "expand_more"
font.pointSize: 13
}
}
Behavior on opacity {
Anim {
}
}
}
CustomText {
id: bodyPreview
TextMetrics {
id: bodyPreviewMetrics
anchors.left: summary.left
anchors.right: expandBtn.left
anchors.top: summary.bottom
anchors.rightMargin: 7
elide: Text.ElideRight
elideWidth: bodyPreview.width
font.family: bodyPreview.font.family
font.pointSize: bodyPreview.font.pointSize
text: root.modelData.body
}
animate: true
textFormat: Text.MarkdownText
text: bodyPreviewMetrics.elidedText
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
CustomText {
id: body
opacity: root.expanded ? 0 : 1
anchors.left: summary.left
anchors.right: expandBtn.left
anchors.rightMargin: 7
anchors.top: summary.bottom
animate: true
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
height: text ? implicitHeight : 0
opacity: root.expanded ? 1 : 0
text: root.modelData.body
textFormat: Text.MarkdownText
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
Behavior on opacity {
Anim {}
}
}
Behavior on opacity {
Anim {
}
}
TextMetrics {
id: bodyPreviewMetrics
onLinkActivated: link => {
if (!root.expanded)
return;
text: root.modelData.body
font.family: bodyPreview.font.family
font.pointSize: bodyPreview.font.pointSize
elide: Text.ElideRight
elideWidth: bodyPreview.width
}
Quickshell.execDetached(["app2unit", "-O", "--", link]);
root.modelData.popup = false;
}
}
CustomText {
id: body
RowLayout {
id: actions
anchors.left: summary.left
anchors.right: expandBtn.left
anchors.top: summary.bottom
anchors.rightMargin: 7
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: body.bottom
anchors.topMargin: 7
opacity: root.expanded ? 1 : 0
spacing: 10
animate: true
textFormat: Text.MarkdownText
text: root.modelData.body
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
height: text ? implicitHeight : 0
Behavior on opacity {
Anim {
}
}
onLinkActivated: link => {
if (!root.expanded)
return;
Action {
modelData: QtObject {
readonly property string text: qsTr("Close")
Quickshell.execDetached(["app2unit", "-O", "--", link]);
root.modelData.popup = false;
}
function invoke(): void {
root.modelData.close();
}
}
}
opacity: root.expanded ? 1 : 0
Repeater {
model: root.modelData.actions
Behavior on opacity {
Anim {}
}
}
delegate: Component {
Action {
}
}
}
}
}
}
RowLayout {
id: actions
component Action: CustomRect {
id: action
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: body.bottom
anchors.topMargin: 7
required property var modelData
spacing: 10
Layout.preferredHeight: actionText.height + 4 * 2
Layout.preferredWidth: actionText.width + 8 * 2
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
implicitHeight: actionText.height + 4 * 2
implicitWidth: actionText.width + 8 * 2
radius: 1000
opacity: root.expanded ? 1 : 0
StateLayer {
function onClicked(): void {
action.modelData.invoke();
}
Behavior on opacity {
Anim {}
}
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurface
radius: 1000
}
Action {
modelData: QtObject {
readonly property string text: qsTr("Close")
function invoke(): void {
root.modelData.close();
}
}
}
CustomText {
id: actionText
Repeater {
model: root.modelData.actions
anchors.centerIn: parent
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
text: actionTextMetrics.elidedText
}
delegate: Component {
Action {}
}
}
}
}
}
TextMetrics {
id: actionTextMetrics
component Action: CustomRect {
id: action
required property var modelData
radius: 1000
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3secondary : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2)
Layout.preferredWidth: actionText.width + 8 * 2
Layout.preferredHeight: actionText.height + 4 * 2
implicitWidth: actionText.width + 8 * 2
implicitHeight: actionText.height + 4 * 2
StateLayer {
radius: 1000
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurface
function onClicked(): void {
action.modelData.invoke();
}
}
CustomText {
id: actionText
anchors.centerIn: parent
text: actionTextMetrics.elidedText
color: root.modelData.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 10
}
TextMetrics {
id: actionTextMetrics
text: action.modelData.text
font.family: actionText.font.family
font.pointSize: actionText.font.pointSize
elide: Text.ElideRight
elideWidth: {
const numActions = root.modelData.actions.length + 1;
return (inner.width - actions.spacing * (numActions - 1)) / numActions - 8 * 2;
}
}
}
elide: Text.ElideRight
elideWidth: {
const numActions = root.modelData.actions.length + 1;
return (inner.width - actions.spacing * (numActions - 1)) / numActions - 8 * 2;
}
font.family: actionText.font.family
font.pointSize: actionText.font.pointSize
text: action.modelData.text
}
}
}
+41 -40
View File
@@ -4,50 +4,51 @@ import QtQuick
import QtQuick.Shapes
ShapePath {
id: root
required property Wrapper wrapper
required property var panels
readonly property real rounding: 8
readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
readonly property real utilsRoundingX: utilsWidthDiff < rounding * 2 ? utilsWidthDiff / 2 : rounding
id: root
readonly property bool flatten: wrapper.width < rounding * 2
readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
required property var panels
readonly property real rounding: 8
readonly property real utilsRoundingX: utilsWidthDiff < rounding * 2 ? utilsWidthDiff / 2 : rounding
readonly property real utilsWidthDiff: panels.utilities.width - wrapper.width
required property Wrapper wrapper
strokeWidth: -1
fillColor: flatten ? "transparent" : DynamicColors.palette.m3surface
strokeWidth: -1
PathLine {
relativeX: -root.wrapper.width - root.notifsRoundingX
relativeY: 0
}
PathArc {
relativeX: root.notifsRoundingX
relativeY: root.rounding
radiusX: root.notifsRoundingX
radiusY: root.rounding
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.rounding * 2
}
PathArc {
relativeX: -root.utilsRoundingX
relativeY: root.rounding
radiusX: root.utilsRoundingX
radiusY: root.rounding
}
PathLine {
relativeX: root.wrapper.width + root.utilsRoundingX
relativeY: 0
}
Behavior on fillColor {
CAnim {
}
}
Behavior on fillColor {
CAnim {}
}
PathLine {
relativeX: -root.wrapper.width - root.notifsRoundingX
relativeY: 0
}
PathArc {
radiusX: root.notifsRoundingX
radiusY: root.rounding
relativeX: root.notifsRoundingX
relativeY: root.rounding
}
PathLine {
relativeX: 0
relativeY: root.wrapper.height - root.rounding * 2
}
PathArc {
radiusX: root.utilsRoundingX
radiusY: root.rounding
relativeX: -root.utilsRoundingX
relativeY: root.rounding
}
PathLine {
relativeX: root.wrapper.width + root.utilsRoundingX
relativeY: 0
}
}
+24 -26
View File
@@ -4,36 +4,34 @@ import QtQuick
import QtQuick.Layouts
Item {
id: root
id: root
required property Props props
required property var visibilities
required property Props props
required property var visibilities
ColumnLayout {
id: layout
ColumnLayout {
id: layout
anchors.fill: parent
spacing: 8
anchors.fill: parent
spacing: 8
CustomRect {
Layout.fillWidth: true
Layout.fillHeight: true
CustomRect {
Layout.fillHeight: true
Layout.fillWidth: true
color: DynamicColors.tPalette.m3surfaceContainerLow
radius: 8
radius: 8
color: DynamicColors.tPalette.m3surfaceContainerLow
NotifDock {
props: root.props
visibilities: root.visibilities
}
}
NotifDock {
props: root.props
visibilities: root.visibilities
}
}
CustomRect {
Layout.topMargin: 8 - layout.spacing
Layout.fillWidth: true
implicitHeight: 1
color: DynamicColors.tPalette.m3outlineVariant
}
}
CustomRect {
Layout.fillWidth: true
Layout.topMargin: 8 - layout.spacing
color: DynamicColors.tPalette.m3outlineVariant
implicitHeight: 1
}
}
}
+141 -146
View File
@@ -9,158 +9,153 @@ import QtQuick
import QtQuick.Layouts
CustomRect {
id: root
id: root
required property NotifServer.Notif modelData
required property Props props
required property bool expanded
required property var visibilities
readonly property CustomText body: expandedContent.item?.body ?? null
required property bool expanded
required property NotifServer.Notif modelData
readonly property real nonAnimHeight: expanded ? summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + 10 * 2 : summaryHeightMetrics.height
required property Props props
required property var visibilities
readonly property CustomText body: expandedContent.item?.body ?? null
readonly property real nonAnimHeight: expanded ? summary.implicitHeight + expandedContent.implicitHeight + expandedContent.anchors.topMargin + 10 * 2 : summaryHeightMetrics.height
color: {
const c = root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
return expanded ? c : Qt.alpha(c, 0);
}
implicitHeight: nonAnimHeight
radius: 6
implicitHeight: nonAnimHeight
radius: 6
color: {
const c = root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondaryContainer : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 2);
return expanded ? c : Qt.alpha(c, 0);
}
states: State {
name: "expanded"
when: root.expanded
PropertyChanges {
summary.anchors.margins: 10
dummySummary.anchors.margins: 10
compactBody.anchors.margins: 10
timeStr.anchors.margins: 10
expandedContent.anchors.margins: 10
summary.width: root.width - 10 * 2 - timeStr.implicitWidth - 7
summary.maximumLineCount: Number.MAX_SAFE_INTEGER
}
}
transitions: Transition {
Anim {
properties: "margins,width,maximumLineCount"
}
}
TextMetrics {
id: summaryHeightMetrics
font: summary.font
text: " " // Use this height to prevent weird characters from changing the line height
}
CustomText {
id: summary
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
text: root.modelData.summary
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
elide: Text.ElideRight
wrapMode: Text.WordWrap
maximumLineCount: 1
}
CustomText {
id: dummySummary
anchors.top: parent.top
anchors.left: parent.left
visible: false
text: root.modelData.summary
}
WrappedLoader {
id: compactBody
shouldBeActive: !root.expanded
anchors.top: parent.top
anchors.left: dummySummary.right
anchors.right: parent.right
anchors.leftMargin: 7
sourceComponent: CustomText {
textFormat: Text.StyledText
text: root.modelData.body.replace(/\n/g, " ")
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
elide: Text.ElideRight
}
}
WrappedLoader {
id: timeStr
shouldBeActive: root.expanded
anchors.top: parent.top
anchors.right: parent.right
sourceComponent: CustomText {
animate: true
text: root.modelData.timeStr
color: DynamicColors.palette.m3outline
font.pointSize: 11
}
}
WrappedLoader {
id: expandedContent
shouldBeActive: root.expanded
anchors.top: summary.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.topMargin: 7 / 2
sourceComponent: ColumnLayout {
readonly property alias body: body
spacing: 10
CustomText {
id: body
Layout.fillWidth: true
textFormat: Text.MarkdownText
text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body given")
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurface
wrapMode: Text.WordWrap
onLinkActivated: link => {
Quickshell.execDetached(["app2unit", "-O", "--", link]);
root.visibilities.sidebar = false;
}
}
NotifActionList {
notif: root.modelData
}
}
}
Behavior on implicitHeight {
Anim {
Behavior on implicitHeight {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
states: State {
name: "expanded"
when: root.expanded
component WrappedLoader: Loader {
required property bool shouldBeActive
PropertyChanges {
compactBody.anchors.margins: 10
dummySummary.anchors.margins: 10
expandedContent.anchors.margins: 10
summary.anchors.margins: 10
summary.maximumLineCount: Number.MAX_SAFE_INTEGER
summary.width: root.width - 10 * 2 - timeStr.implicitWidth - 7
timeStr.anchors.margins: 10
}
}
transitions: Transition {
Anim {
properties: "margins,width,maximumLineCount"
}
}
opacity: shouldBeActive ? 1 : 0
active: opacity > 0
TextMetrics {
id: summaryHeightMetrics
Behavior on opacity {
Anim {}
}
}
font: summary.font
text: " " // Use this height to prevent weird characters from changing the line height
}
CustomText {
id: summary
anchors.left: parent.left
anchors.top: parent.top
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurface
elide: Text.ElideRight
maximumLineCount: 1
text: root.modelData.summary
width: parent.width
wrapMode: Text.WordWrap
}
CustomText {
id: dummySummary
anchors.left: parent.left
anchors.top: parent.top
text: root.modelData.summary
visible: false
}
WrappedLoader {
id: compactBody
anchors.left: dummySummary.right
anchors.leftMargin: 7
anchors.right: parent.right
anchors.top: parent.top
shouldBeActive: !root.expanded
sourceComponent: CustomText {
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3outline
elide: Text.ElideRight
text: root.modelData.body.replace(/\n/g, " ")
textFormat: Text.StyledText
}
}
WrappedLoader {
id: timeStr
anchors.right: parent.right
anchors.top: parent.top
shouldBeActive: root.expanded
sourceComponent: CustomText {
animate: true
color: DynamicColors.palette.m3outline
font.pointSize: 11
text: root.modelData.timeStr
}
}
WrappedLoader {
id: expandedContent
anchors.left: parent.left
anchors.right: parent.right
anchors.top: summary.bottom
anchors.topMargin: 7 / 2
shouldBeActive: root.expanded
sourceComponent: ColumnLayout {
readonly property alias body: body
spacing: 10
CustomText {
id: body
Layout.fillWidth: true
color: root.modelData.urgency === "critical" ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3onSurface
text: root.modelData.body.replace(/(.)\n(?!\n)/g, "$1\n\n") || qsTr("No body given")
textFormat: Text.MarkdownText
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
active: opacity > 0
opacity: shouldBeActive ? 1 : 0
Behavior on opacity {
Anim {
}
}
}
}
+105 -106
View File
@@ -10,125 +10,124 @@ import QtQuick
import QtQuick.Layouts
Item {
id: root
id: root
required property NotifServer.Notif notif
required property NotifServer.Notif notif
Layout.fillWidth: true
implicitHeight: flickable.contentHeight
Layout.fillWidth: true
implicitHeight: flickable.contentHeight
CustomFlickable {
id: flickable
CustomFlickable {
id: flickable
anchors.fill: parent
contentWidth: Math.max(width, actionList.implicitWidth)
contentHeight: actionList.implicitHeight
anchors.fill: parent
contentHeight: actionList.implicitHeight
contentWidth: Math.max(width, actionList.implicitWidth)
RowLayout {
id: actionList
RowLayout {
id: actionList
anchors.fill: parent
spacing: 7
anchors.fill: parent
spacing: 7
Repeater {
model: [
{
isClose: true
},
...root.notif.actions,
{
isCopy: true
}
]
Repeater {
model: [
{
isClose: true
},
...root.notif.actions,
{
isCopy: true
}
]
CustomRect {
id: action
CustomRect {
id: action
required property var modelData
required property var modelData
Layout.fillWidth: true
Layout.fillHeight: true
implicitWidth: actionInner.implicitWidth + 5 * 2
implicitHeight: actionInner.implicitHeight + 5 * 2
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: implicitWidth + (actionStateLayer.pressed ? 18 : 0)
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 4)
implicitHeight: actionInner.implicitHeight + 5 * 2
implicitWidth: actionInner.implicitWidth + 5 * 2
radius: actionStateLayer.pressed ? 6 / 2 : 6
Layout.preferredWidth: implicitWidth + (actionStateLayer.pressed ? 18 : 0)
radius: actionStateLayer.pressed ? 6 / 2 : 6
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 4)
Timer {
id: copyTimer
interval: 1000
onTriggered: actionInner.item.text = "content_copy"
}
StateLayer {
id: actionStateLayer
function onClicked(): void {
if (action.modelData.isClose) {
root.notif.close();
} else if (action.modelData.isCopy) {
Quickshell.clipboardText = root.notif.body;
actionInner.item.text = "inventory";
copyTimer.start();
} else if (action.modelData.invoke) {
action.modelData.invoke();
} else if (!root.notif.resident) {
root.notif.close();
}
}
}
Loader {
id: actionInner
anchors.centerIn: parent
sourceComponent: action.modelData.isClose || action.modelData.isCopy ? iconBtn : root.notif.hasActionIcons ? iconComp : textComp
}
Component {
id: iconBtn
MaterialIcon {
animate: action.modelData.isCopy ?? false
text: action.modelData.isCopy ? "content_copy" : "close"
color: DynamicColors.palette.m3onSurfaceVariant
}
}
Component {
id: iconComp
IconImage {
source: Quickshell.iconPath(action.modelData.identifier)
}
}
Component {
id: textComp
CustomText {
text: action.modelData.text
color: DynamicColors.palette.m3onSurfaceVariant
}
}
Behavior on Layout.preferredWidth {
Anim {
Behavior on Layout.preferredWidth {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on radius {
Anim {
}
}
Behavior on radius {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
}
}
}
}
Timer {
id: copyTimer
interval: 1000
onTriggered: actionInner.item.text = "content_copy"
}
StateLayer {
id: actionStateLayer
function onClicked(): void {
if (action.modelData.isClose) {
root.notif.close();
} else if (action.modelData.isCopy) {
Quickshell.clipboardText = root.notif.body;
actionInner.item.text = "inventory";
copyTimer.start();
} else if (action.modelData.invoke) {
action.modelData.invoke();
} else if (!root.notif.resident) {
root.notif.close();
}
}
}
Loader {
id: actionInner
anchors.centerIn: parent
sourceComponent: action.modelData.isClose || action.modelData.isCopy ? iconBtn : root.notif.hasActionIcons ? iconComp : textComp
}
Component {
id: iconBtn
MaterialIcon {
animate: action.modelData.isCopy ?? false
color: DynamicColors.palette.m3onSurfaceVariant
text: action.modelData.isCopy ? "content_copy" : "close"
}
}
Component {
id: iconComp
IconImage {
source: Quickshell.iconPath(action.modelData.identifier)
}
}
Component {
id: textComp
CustomText {
color: DynamicColors.palette.m3onSurfaceVariant
text: action.modelData.text
}
}
}
}
}
}
}
+144 -150
View File
@@ -10,183 +10,177 @@ import QtQuick
import QtQuick.Layouts
Item {
id: root
id: root
required property Props props
required property var visibilities
readonly property int notifCount: NotifServer.list.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
readonly property int notifCount: NotifServer.list.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
required property Props props
required property var visibilities
anchors.fill: parent
anchors.margins: 8
anchors.fill: parent
anchors.margins: 8
Component.onCompleted: NotifServer.list.forEach(n => n.popup = false)
Component.onCompleted: NotifServer.list.forEach(n => n.popup = false)
Item {
id: title
Item {
id: title
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 4
anchors.left: parent.left
anchors.margins: 4
anchors.right: parent.right
anchors.top: parent.top
implicitHeight: Math.max(count.implicitHeight, titleText.implicitHeight)
implicitHeight: Math.max(count.implicitHeight, titleText.implicitHeight)
CustomText {
id: count
CustomText {
id: count
anchors.left: parent.left
anchors.leftMargin: root.notifCount > 0 ? 0 : -width - titleText.anchors.leftMargin
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3outline
font.family: "CaskaydiaCove NF"
font.pointSize: 13
font.weight: 500
opacity: root.notifCount > 0 ? 1 : 0
text: root.notifCount
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.notifCount > 0 ? 0 : -width - titleText.anchors.leftMargin
opacity: root.notifCount > 0 ? 1 : 0
Behavior on anchors.leftMargin {
Anim {
}
}
Behavior on opacity {
Anim {
}
}
}
text: root.notifCount
color: DynamicColors.palette.m3outline
font.pointSize: 13
font.family: "CaskaydiaCove NF"
font.weight: 500
CustomText {
id: titleText
Behavior on anchors.leftMargin {
Anim {}
}
anchors.left: count.right
anchors.leftMargin: 7
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
color: DynamicColors.palette.m3outline
elide: Text.ElideRight
font.family: "CaskaydiaCove NF"
font.pointSize: 13
font.weight: 500
text: root.notifCount > 0 ? qsTr("notification%1").arg(root.notifCount === 1 ? "" : "s") : qsTr("Notifications")
}
}
Behavior on opacity {
Anim {}
}
}
ClippingRectangle {
id: clipRect
CustomText {
id: titleText
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.top: title.bottom
anchors.topMargin: 10
color: "transparent"
radius: 6
anchors.verticalCenter: parent.verticalCenter
anchors.left: count.right
anchors.right: parent.right
anchors.leftMargin: 7
Loader {
active: opacity > 0
anchors.centerIn: parent
opacity: root.notifCount > 0 ? 0 : 1
text: root.notifCount > 0 ? qsTr("notification%1").arg(root.notifCount === 1 ? "" : "s") : qsTr("Notifications")
color: DynamicColors.palette.m3outline
font.pointSize: 13
font.family: "CaskaydiaCove NF"
font.weight: 500
elide: Text.ElideRight
}
}
ClippingRectangle {
id: clipRect
anchors.left: parent.left
anchors.right: parent.right
anchors.top: title.bottom
anchors.bottom: parent.bottom
anchors.topMargin: 10
radius: 6
color: "transparent"
Loader {
anchors.centerIn: parent
active: opacity > 0
opacity: root.notifCount > 0 ? 0 : 1
sourceComponent: ColumnLayout {
spacing: 20
CustomText {
Layout.alignment: Qt.AlignHCenter
text: qsTr("No Notifications")
color: DynamicColors.palette.m3outlineVariant
font.pointSize: 18
font.family: "CaskaydiaCove NF"
font.weight: 500
}
}
Behavior on opacity {
Anim {
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
}
}
}
sourceComponent: ColumnLayout {
spacing: 20
CustomFlickable {
id: view
CustomText {
Layout.alignment: Qt.AlignHCenter
color: DynamicColors.palette.m3outlineVariant
font.family: "CaskaydiaCove NF"
font.pointSize: 18
font.weight: 500
text: qsTr("No Notifications")
}
}
}
anchors.fill: parent
CustomFlickable {
id: view
flickableDirection: Flickable.VerticalFlick
contentWidth: width
contentHeight: notifList.implicitHeight
anchors.fill: parent
contentHeight: notifList.implicitHeight
contentWidth: width
flickableDirection: Flickable.VerticalFlick
CustomScrollBar.vertical: CustomScrollBar {
flickable: view
}
CustomScrollBar.vertical: CustomScrollBar {
flickable: view
}
NotifDockList {
id: notifList
NotifDockList {
id: notifList
props: root.props
visibilities: root.visibilities
container: view
}
}
}
container: view
props: root.props
visibilities: root.visibilities
}
}
}
Timer {
id: clearTimer
Timer {
id: clearTimer
repeat: true
interval: 50
onTriggered: {
let next = null;
for (let i = 0; i < notifList.repeater.count; i++) {
next = notifList.repeater.itemAt(i);
if (!next?.closed)
break;
}
if (next)
next.closeAll();
else
stop();
}
}
interval: 50
repeat: true
Loader {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 8
onTriggered: {
let next = null;
for (let i = 0; i < notifList.repeater.count; i++) {
next = notifList.repeater.itemAt(i);
if (!next?.closed)
break;
}
if (next)
next.closeAll();
else
stop();
}
}
scale: root.notifCount > 0 ? 1 : 0.5
opacity: root.notifCount > 0 ? 1 : 0
active: opacity > 0
Loader {
active: opacity > 0
anchors.bottom: parent.bottom
anchors.margins: 8
anchors.right: parent.right
opacity: root.notifCount > 0 ? 1 : 0
scale: root.notifCount > 0 ? 1 : 0.5
sourceComponent: IconButton {
id: clearBtn
icon: "clear_all"
radius: 8
padding: 8
font.pointSize: Math.round(18 * 1.2)
onClicked: clearTimer.start()
Elevation {
anchors.fill: parent
radius: parent.radius
z: -1
level: clearBtn.stateLayer.containsMouse ? 4 : 3
}
}
Behavior on scale {
Anim {
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
Behavior on scale {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
sourceComponent: IconButton {
id: clearBtn
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
}
font.pointSize: Math.round(18 * 1.2)
icon: "clear_all"
padding: 8
radius: 8
onClicked: clearTimer.start()
Elevation {
anchors.fill: parent
level: clearBtn.stateLayer.containsMouse ? 4 : 3
radius: parent.radius
z: -1
}
}
}
}
+136 -140
View File
@@ -8,162 +8,158 @@ import Quickshell
import QtQuick
Item {
id: root
id: root
required property Props props
required property Flickable container
required property var visibilities
required property Flickable container
property bool flag
required property Props props
readonly property alias repeater: repeater
readonly property int spacing: 8
required property var visibilities
readonly property alias repeater: repeater
readonly property int spacing: 8
property bool flag
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: {
const item = repeater.itemAt(repeater.count - 1);
return item ? item.y + item.implicitHeight : 0;
}
anchors.left: parent.left
anchors.right: parent.right
implicitHeight: {
const item = repeater.itemAt(repeater.count - 1);
return item ? item.y + item.implicitHeight : 0;
}
Repeater {
id: repeater
Repeater {
id: repeater
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()];
}
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()
}
onValuesChanged: root.flagChanged()
}
MouseArea {
id: notif
MouseArea {
id: notif
required property int index
required property string modelData
readonly property bool closed: notifInner.notifCount === 0
required property int index
required property string modelData
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
property int startY
readonly property bool closed: notifInner.notifCount === 0
readonly property alias nonAnimHeight: notifInner.nonAnimHeight
property int startY
function closeAll(): void {
for (const n of NotifServer.notClosed.filter(n => n.appName === modelData)) {
n.close();
function closeAll(): void {
for (const n of NotifServer.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);
if (!item.closed)
y += item.nonAnimHeight + root.spacing;
}
return y;
}
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
cursorShape: pressed ? Qt.ClosedHandCursor : undefined
drag.axis: Drag.XAxis
drag.target: this
enabled: !closed
hoverEnabled: true
implicitHeight: notifInner.implicitHeight
implicitWidth: root.width
preventStealing: true
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);
}
}
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();
}
ParallelAnimation {
running: true
Anim {
target: notif
property: "opacity"
from: 0
to: 1
}
Anim {
target: notif
property: "scale"
from: 0
to: 1
containmentMask: QtObject {
function contains(p: point): bool {
if (!root.container.contains(notif.mapToItem(root.container, p)))
return false;
return notifInner.contains(p);
}
}
Behavior on x {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
ParallelAnimation {
running: notif.closed
Anim {
target: notif
property: "opacity"
to: 0
}
Anim {
target: notif
property: "scale"
to: 0.6
}
}
NotifGroup {
id: notifInner
modelData: notif.modelData
props: root.props
container: root.container
visibilities: root.visibilities
}
Behavior on x {
Anim {
}
}
Behavior on y {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
Behavior on y {
Anim {
onPositionChanged: event => {
if (pressed) {
const diffY = event.y - startY;
if (Math.abs(diffY) > Config.notifs.expandThreshold)
notifInner.toggleExpand(diffY > 0);
}
}
onPressed: event => {
startY = event.y;
if (event.button === Qt.RightButton)
notifInner.toggleExpand(!notifInner.expanded);
else if (event.button === Qt.MiddleButton)
closeAll();
}
onReleased: event => {
if (Math.abs(x) < width * Config.notifs.clearThreshold)
x = 0;
else
closeAll();
}
ParallelAnimation {
running: true
Anim {
from: 0
property: "opacity"
target: notif
to: 1
}
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
from: 0
property: "scale"
target: notif
to: 1
}
}
ParallelAnimation {
running: notif.closed
Anim {
property: "opacity"
target: notif
to: 0
}
Anim {
property: "scale"
target: notif
to: 0.6
}
}
NotifGroup {
id: notifInner
container: root.container
modelData: notif.modelData
props: root.props
visibilities: root.visibilities
}
}
}
}
+179 -184
View File
@@ -11,229 +11,224 @@ import QtQuick
import QtQuick.Layouts
CustomRect {
id: root
id: root
required property string modelData
required property Props props
required property Flickable container
required property var visibilities
readonly property string appIcon: notifs.find(n => !n.closed && n.appIcon.length > 0)?.appIcon ?? ""
required property Flickable container
readonly property bool expanded: props.expandedNotifs.includes(modelData)
readonly property string image: notifs.find(n => !n.closed && n.image.length > 0)?.image ?? ""
required property string modelData
readonly property int nonAnimHeight: {
const headerHeight = header.implicitHeight + (root.expanded ? Math.round(7 / 2) : 0);
const columnHeight = headerHeight + notifList.nonAnimHeight + column.Layout.topMargin + column.Layout.bottomMargin;
return Math.round(Math.max(Config.notifs.sizes.image, columnHeight) + 10 * 2);
}
readonly property int notifCount: notifs.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
readonly property list<var> notifs: NotifServer.list.filter(n => n.appName === modelData)
required property Props props
readonly property int urgency: notifs.some(n => !n.closed && n.urgency === NotificationUrgency.Critical) ? NotificationUrgency.Critical : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? NotificationUrgency.Normal : NotificationUrgency.Low
required property var visibilities
readonly property list<var> notifs: NotifServer.list.filter(n => n.appName === modelData)
readonly property int notifCount: notifs.reduce((acc, n) => n.closed ? acc : acc + 1, 0)
readonly property string image: notifs.find(n => !n.closed && n.image.length > 0)?.image ?? ""
readonly property string appIcon: notifs.find(n => !n.closed && n.appIcon.length > 0)?.appIcon ?? ""
readonly property int urgency: notifs.some(n => !n.closed && n.urgency === NotificationUrgency.Critical) ? NotificationUrgency.Critical : notifs.some(n => n.urgency === NotificationUrgency.Normal) ? NotificationUrgency.Normal : NotificationUrgency.Low
function toggleExpand(expand: bool): void {
if (expand) {
if (!expanded)
props.expandedNotifs.push(modelData);
} else if (expanded) {
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
}
}
readonly property int nonAnimHeight: {
const headerHeight = header.implicitHeight + (root.expanded ? Math.round(7 / 2) : 0);
const columnHeight = headerHeight + notifList.nonAnimHeight + column.Layout.topMargin + column.Layout.bottomMargin;
return Math.round(Math.max(Config.notifs.sizes.image, columnHeight) + 10 * 2);
}
readonly property bool expanded: props.expandedNotifs.includes(modelData)
anchors.left: parent?.left
anchors.right: parent?.right
clip: true
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
implicitHeight: content.implicitHeight + 10 * 2
radius: 8
function toggleExpand(expand: bool): void {
if (expand) {
if (!expanded)
props.expandedNotifs.push(modelData);
} else if (expanded) {
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
}
}
Component.onDestruction: {
if (notifCount === 0 && expanded)
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
}
Component.onDestruction: {
if (notifCount === 0 && expanded)
props.expandedNotifs.splice(props.expandedNotifs.indexOf(modelData), 1);
}
RowLayout {
id: content
anchors.left: parent?.left
anchors.right: parent?.right
implicitHeight: content.implicitHeight + 10 * 2
anchors.left: parent.left
anchors.margins: 10
anchors.right: parent.right
anchors.top: parent.top
spacing: 10
clip: true
radius: 8
color: DynamicColors.layer(DynamicColors.palette.m3surfaceContainer, 2)
Item {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
implicitHeight: Config.notifs.sizes.image
implicitWidth: Config.notifs.sizes.image
RowLayout {
id: content
Component {
id: imageComp
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: 10
Image {
asynchronous: true
cache: false
fillMode: Image.PreserveAspectCrop
height: Config.notifs.sizes.image
source: Qt.resolvedUrl(root.image)
width: Config.notifs.sizes.image
}
}
spacing: 10
Component {
id: appIconComp
Item {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
implicitWidth: Config.notifs.sizes.image
implicitHeight: Config.notifs.sizes.image
CustomIcon {
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
layer.enabled: root.appIcon.endsWith("symbolic")
source: Quickshell.iconPath(root.appIcon)
}
}
Component {
id: imageComp
Component {
id: materialIconComp
Image {
source: Qt.resolvedUrl(root.image)
fillMode: Image.PreserveAspectCrop
cache: false
asynchronous: true
width: Config.notifs.sizes.image
height: Config.notifs.sizes.image
}
}
MaterialIcon {
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
}
}
Component {
id: appIconComp
CustomClippingRect {
anchors.fill: parent
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3) : DynamicColors.palette.m3secondaryContainer
radius: 1000
CustomIcon {
implicitSize: Math.round(Config.notifs.sizes.image * 0.6)
source: Quickshell.iconPath(root.appIcon)
layer.enabled: root.appIcon.endsWith("symbolic")
}
}
Loader {
anchors.centerIn: parent
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
}
}
Component {
id: materialIconComp
Loader {
active: root.appIcon && root.image
anchors.bottom: parent.bottom
anchors.right: parent.right
MaterialIcon {
text: Icons.getNotifIcon(root.notifs[0]?.summary, root.urgency)
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3onSurface : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
}
}
sourceComponent: CustomRect {
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3secondaryContainer
implicitHeight: Config.notifs.sizes.badge
implicitWidth: Config.notifs.sizes.badge
radius: 1000
CustomClippingRect {
anchors.fill: parent
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3) : DynamicColors.palette.m3secondaryContainer
radius: 1000
CustomIcon {
anchors.centerIn: parent
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
layer.enabled: root.appIcon.endsWith("symbolic")
source: Quickshell.iconPath(root.appIcon)
}
}
}
}
Loader {
anchors.centerIn: parent
sourceComponent: root.image ? imageComp : root.appIcon ? appIconComp : materialIconComp
}
}
ColumnLayout {
id: column
Loader {
anchors.right: parent.right
anchors.bottom: parent.bottom
active: root.appIcon && root.image
Layout.bottomMargin: -10 / 2
Layout.fillWidth: true
Layout.topMargin: -10
spacing: 0
sourceComponent: CustomRect {
implicitWidth: Config.notifs.sizes.badge
implicitHeight: Config.notifs.sizes.badge
RowLayout {
id: header
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : root.urgency === NotificationUrgency.Low ? DynamicColors.palette.m3surfaceContainerHigh : DynamicColors.palette.m3secondaryContainer
radius: 1000
Layout.bottomMargin: root.expanded ? Math.round(7 / 2) : 0
Layout.fillWidth: true
spacing: 5
CustomIcon {
anchors.centerIn: parent
implicitSize: Math.round(Config.notifs.sizes.badge * 0.6)
source: Quickshell.iconPath(root.appIcon)
layer.enabled: root.appIcon.endsWith("symbolic")
}
}
}
}
Behavior on Layout.bottomMargin {
Anim {
}
}
ColumnLayout {
id: column
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: 11
text: root.modelData
}
Layout.topMargin: -10
Layout.bottomMargin: -10 / 2
Layout.fillWidth: true
spacing: 0
CustomText {
animate: true
color: DynamicColors.palette.m3outline
font.pointSize: 11
text: root.notifs.find(n => !n.closed)?.timeStr ?? ""
}
RowLayout {
id: header
CustomRect {
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3)
implicitHeight: groupCount.implicitHeight + 10
implicitWidth: expandBtn.implicitWidth + 7 * 2
radius: 1000
Layout.bottomMargin: root.expanded ? Math.round(7 / 2) : 0
Layout.fillWidth: true
spacing: 5
StateLayer {
function onClicked(): void {
root.toggleExpand(!root.expanded);
}
CustomText {
Layout.fillWidth: true
text: root.modelData
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 11
elide: Text.ElideRight
}
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
}
CustomText {
animate: true
text: root.notifs.find(n => !n.closed)?.timeStr ?? ""
color: DynamicColors.palette.m3outline
font.pointSize: 11
}
RowLayout {
id: expandBtn
CustomRect {
implicitWidth: expandBtn.implicitWidth + 7 * 2
implicitHeight: groupCount.implicitHeight + 10
anchors.centerIn: parent
spacing: 7 / 2
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3error : DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHigh, 3)
radius: 1000
CustomText {
id: groupCount
StateLayer {
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
Layout.leftMargin: 10 / 2
animate: true
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
font.pointSize: 11
text: root.notifCount
}
function onClicked(): void {
root.toggleExpand(!root.expanded);
}
}
MaterialIcon {
Layout.rightMargin: -10 / 2
Layout.topMargin: root.expanded ? -Math.floor(7 / 2) : 0
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
rotation: root.expanded ? 180 : 0
text: "expand_more"
RowLayout {
id: expandBtn
anchors.centerIn: parent
spacing: 7 / 2
CustomText {
id: groupCount
Layout.leftMargin: 10 / 2
animate: true
text: root.notifCount
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
font.pointSize: 11
}
MaterialIcon {
Layout.rightMargin: -10 / 2
text: "expand_more"
color: root.urgency === NotificationUrgency.Critical ? DynamicColors.palette.m3onError : DynamicColors.palette.m3onSurface
rotation: root.expanded ? 180 : 0
Layout.topMargin: root.expanded ? -Math.floor(7 / 2) : 0
Behavior on rotation {
Anim {
Behavior on Layout.topMargin {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on Layout.topMargin {
Anim {
}
}
Behavior on rotation {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
}
}
}
}
}
}
}
Behavior on Layout.bottomMargin {
Anim {}
}
}
NotifGroupList {
id: notifList
NotifGroupList {
id: notifList
container: root.container
expanded: root.expanded
notifs: root.notifs
props: root.props
visibilities: root.visibilities
props: root.props
notifs: root.notifs
expanded: root.expanded
container: root.container
visibilities: root.visibilities
onRequestToggleExpand: expand => root.toggleExpand(expand)
}
}
}
onRequestToggleExpand: expand => root.toggleExpand(expand)
}
}
}
}
+191 -196
View File
@@ -9,206 +9,201 @@ import QtQuick
import QtQuick.Layouts
Item {
id: root
id: root
required property Props props
required property list<var> notifs
required property bool expanded
required property Flickable container
required property var visibilities
required property Flickable container
required property bool expanded
property bool flag
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)
h += item.nonAnimHeight + root.spacing;
}
return h;
}
required property list<var> notifs
required property Props props
property bool showAllNotifs
readonly property int spacing: Math.round(7 / 2)
required property var 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)
h += item.nonAnimHeight + root.spacing;
}
return h;
}
signal requestToggleExpand(expand: bool)
readonly property int spacing: Math.round(7 / 2)
property bool showAllNotifs
property bool flag
Layout.fillWidth: true
implicitHeight: nonAnimHeight
signal requestToggleExpand(expand: bool)
onExpandedChanged: {
if (expanded) {
clearTimer.stop();
showAllNotifs = true;
} else {
clearTimer.start();
}
}
Layout.fillWidth: true
implicitHeight: nonAnimHeight
Timer {
id: clearTimer
interval: MaterialEasing.standardTime
onTriggered: root.showAllNotifs = false
}
Repeater {
id: repeater
model: ScriptModel {
values: root.showAllNotifs ? root.notifs : root.notifs.slice(0, Config.notifs.groupPreviewNum + 1)
onValuesChanged: root.flagChanged()
}
MouseArea {
id: notif
required property int index
required property NotifServer.Notif 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: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on y {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
Behavior on implicitHeight {
Anim {
Behavior on implicitHeight {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
onExpandedChanged: {
if (expanded) {
clearTimer.stop();
showAllNotifs = true;
} else {
clearTimer.start();
}
}
Timer {
id: clearTimer
interval: MaterialEasing.standardTime
onTriggered: root.showAllNotifs = false
}
Repeater {
id: repeater
model: ScriptModel {
values: root.showAllNotifs ? root.notifs : root.notifs.slice(0, Config.notifs.groupPreviewNum + 1)
onValuesChanged: root.flagChanged()
}
MouseArea {
id: notif
required property int index
required property NotifServer.Notif 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
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
cursorShape: notifInner.body?.hoveredLink ? Qt.PointingHandCursor : pressed ? Qt.ClosedHandCursor : undefined
drag.axis: Drag.XAxis
drag.target: this
enabled: !modelData.closed
hoverEnabled: true
implicitHeight: notifInner.implicitHeight
implicitWidth: root.width
opacity: previewHidden ? 0 : 1
preventStealing: !root.expanded
scale: previewHidden ? 0.7 : 1
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);
}
}
Behavior on opacity {
Anim {
}
}
Behavior on scale {
Anim {
}
}
Behavior on x {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on y {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Component.onCompleted: modelData.lock(this)
Component.onDestruction: modelData.unlock(this)
onPositionChanged: event => {
if (pressed && !root.expanded) {
const diffY = event.y - startY;
if (Math.abs(diffY) > Config.notifs.expandThreshold)
root.requestToggleExpand(diffY > 0);
}
}
onPressed: event => {
startY = event.y;
if (event.button === Qt.RightButton)
root.requestToggleExpand(!root.expanded);
else if (event.button === Qt.MiddleButton)
modelData.close();
}
onReleased: event => {
if (Math.abs(x) < width * Config.notifs.clearThreshold)
x = 0;
else
modelData.close();
}
ParallelAnimation {
Component.onCompleted: running = !notif.previewHidden
Anim {
from: 0
property: "opacity"
target: notif
to: 1
}
Anim {
from: 0.7
property: "scale"
target: notif
to: 1
}
}
ParallelAnimation {
running: notif.modelData.closed
onFinished: notif.modelData.unlock(notif)
Anim {
property: "opacity"
target: notif
to: 0
}
Anim {
property: "x"
target: notif
to: notif.x >= 0 ? notif.width : -notif.width
}
}
Notif {
id: notifInner
anchors.fill: parent
expanded: root.expanded
modelData: notif.modelData
props: root.props
visibilities: root.visibilities
}
}
}
}
+2 -2
View File
@@ -1,7 +1,7 @@
import Quickshell
PersistentProperties {
property list<string> expandedNotifs: []
property list<string> expandedNotifs: []
reloadableId: "sidebar"
reloadableId: "sidebar"
}
@@ -4,51 +4,57 @@ import QtQuick
import QtQuick.Shapes
ShapePath {
id: root
id: root
required property Wrapper wrapper
required property var sidebar
readonly property real rounding: 8
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
readonly property bool flatten: wrapper.height < rounding * 2
readonly property real rounding: 8
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
required property var sidebar
required property Wrapper wrapper
strokeWidth: -1
fillColor: DynamicColors.palette.m3surface
fillColor: DynamicColors.palette.m3surface
strokeWidth: -1
PathLine {
relativeX: -(root.wrapper.width + root.rounding)
relativeY: 0
}
PathArc {
relativeX: root.rounding
relativeY: -root.roundingY
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
direction: PathArc.Counterclockwise
}
PathLine {
relativeX: 0
relativeY: -(root.wrapper.height - root.roundingY * 2)
}
PathArc {
relativeX: root.sidebar.utilsRoundingX
relativeY: -root.roundingY
radiusX: root.sidebar.utilsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
}
PathLine {
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
relativeX: root.rounding
relativeY: -root.rounding
radiusX: root.rounding
radiusY: root.rounding
direction: PathArc.Counterclockwise
}
Behavior on fillColor {
CAnim {
}
}
Behavior on fillColor {
CAnim {}
}
PathLine {
relativeX: -(root.wrapper.width + root.rounding)
relativeY: 0
}
PathArc {
direction: PathArc.Counterclockwise
radiusX: root.rounding
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.rounding
relativeY: -root.roundingY
}
PathLine {
relativeX: 0
relativeY: -(root.wrapper.height - root.roundingY * 2)
}
PathArc {
radiusX: root.sidebar.utilsRoundingX
radiusY: Math.min(root.rounding, root.wrapper.height)
relativeX: root.sidebar.utilsRoundingX
relativeY: -root.roundingY
}
PathLine {
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.utilsRoundingX : root.wrapper.width
relativeY: 0
}
PathArc {
direction: PathArc.Counterclockwise
radiusX: root.rounding
radiusY: root.rounding
relativeX: root.rounding
relativeY: -root.rounding
}
}
@@ -8,75 +8,80 @@ import qs.Modules
import qs.Daemons
CustomRect {
id: root
id: root
required property var visibilities
required property Item popouts
required property Item popouts
required property var visibilities
Layout.fillWidth: true
implicitHeight: layout.implicitHeight + 18 * 2
Layout.fillWidth: true
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + 18 * 2
radius: 8
radius: 8
color: DynamicColors.tPalette.m3surfaceContainer
ColumnLayout {
id: layout
ColumnLayout {
id: layout
anchors.fill: parent
anchors.margins: 18
spacing: 10
anchors.fill: parent
anchors.margins: 18
spacing: 10
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 7
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 7
Toggle {
visible: QSNetwork.Networking.devices.values.length > 0
icon: Network.wifiEnabled ? "wifi" : "wifi_off"
checked: Network.wifiEnabled
icon: Network.wifiEnabled ? "wifi" : "wifi_off"
visible: QSNetwork.Networking.devices.values.length > 0
onClicked: Network.toggleWifi()
}
Toggle {
Toggle {
id: toggle
icon: NotifServer.dnd ? "notifications_off" : "notifications"
checked: !NotifServer.dnd
onClicked: NotifServer.dnd = !NotifServer.dnd
}
checked: !NotifServer.dnd
icon: NotifServer.dnd ? "notifications_off" : "notifications"
onClicked: NotifServer.dnd = !NotifServer.dnd
}
Toggle {
icon: Audio.sourceMuted ? "mic_off" : "mic"
checked: !Audio.sourceMuted
icon: Audio.sourceMuted ? "mic_off" : "mic"
onClicked: {
const audio = Audio.source?.audio;
if ( audio )
if (audio)
audio.muted = !audio.muted;
}
}
Toggle {
icon: Audio.muted ? "volume_off" : "volume_up"
checked: !Audio.muted
icon: Audio.muted ? "volume_off" : "volume_up"
onClicked: {
const audio = Audio.sink?.audio;
if ( audio )
if (audio)
audio.muted = !audio.muted;
}
}
Toggle {
visible: Bluetooth.defaultAdapter ?? false
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
checked: Bluetooth.defaultAdapter?.enabled ?? false
icon: Bluetooth.defaultAdapter?.enabled ? "bluetooth" : "bluetooth_disabled"
visible: Bluetooth.defaultAdapter ?? false
onClicked: {
// console.log(Bluetooth.defaultAdapter)
const adapter = Bluetooth.defaultAdapter
if ( adapter )
const adapter = Bluetooth.defaultAdapter;
if (adapter)
adapter.enabled = !adapter.enabled;
}
}
}
}
}
}
CustomShortcut {
name: "toggle-dnd"
@@ -86,20 +91,20 @@ CustomRect {
}
}
component Toggle: IconButton {
Layout.fillWidth: true
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
toggle: true
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
component Toggle: IconButton {
Layout.fillWidth: true
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
toggle: true
Behavior on Layout.preferredWidth {
Anim {
Behavior on Layout.preferredWidth {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
}
}
}
+17 -16
View File
@@ -4,26 +4,27 @@ import QtQuick
import QtQuick.Layouts
Item {
id: root
id: root
required property var props
required property var visibilities
required property Item popouts
required property Item popouts
required property var props
required property var visibilities
implicitWidth: layout.implicitWidth
implicitHeight: layout.implicitHeight
implicitHeight: layout.implicitHeight
implicitWidth: layout.implicitWidth
ColumnLayout {
id: layout
ColumnLayout {
id: layout
anchors.fill: parent
spacing: 8
anchors.fill: parent
spacing: 8
IdleInhibit {}
IdleInhibit {
}
Toggles {
visibilities: root.visibilities
popouts: root.popouts
}
}
Toggles {
popouts: root.popouts
visibilities: root.visibilities
}
}
}
@@ -5,120 +5,115 @@ import QtQuick
import QtQuick.Layouts
CustomRect {
id: root
id: root
Layout.fillWidth: true
implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + 18 * 2
Layout.fillWidth: true
clip: true
color: DynamicColors.tPalette.m3surfaceContainer
implicitHeight: layout.implicitHeight + (IdleInhibitor.enabled ? activeChip.implicitHeight + activeChip.anchors.topMargin : 0) + 18 * 2
radius: 8
radius: 8
color: DynamicColors.tPalette.m3surfaceContainer
clip: true
RowLayout {
id: layout
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 18
spacing: 10
CustomRect {
implicitWidth: implicitHeight
implicitHeight: icon.implicitHeight + 7 * 2
radius: 1000
color: IdleInhibitor.enabled ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3secondaryContainer
MaterialIcon {
id: icon
anchors.centerIn: parent
text: "coffee"
color: IdleInhibitor.enabled ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
CustomText {
Layout.fillWidth: true
text: qsTr("Keep Awake")
font.pointSize: 13
elide: Text.ElideRight
}
CustomText {
Layout.fillWidth: true
text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
color: DynamicColors.palette.m3onSurfaceVariant
font.pointSize: 11
elide: Text.ElideRight
}
}
CustomSwitch {
checked: IdleInhibitor.enabled
onToggled: IdleInhibitor.enabled = checked
}
}
Loader {
id: activeChip
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.topMargin: 20
anchors.bottomMargin: IdleInhibitor.enabled ? 18 : -implicitHeight
anchors.leftMargin: 18
opacity: IdleInhibitor.enabled ? 1 : 0
scale: IdleInhibitor.enabled ? 1 : 0.5
Component.onCompleted: active = Qt.binding(() => opacity > 0)
sourceComponent: CustomRect {
implicitWidth: activeText.implicitWidth + 10 * 2
implicitHeight: activeText.implicitHeight + 10 * 2
radius: 1000
color: DynamicColors.palette.m3primary
CustomText {
id: activeText
anchors.centerIn: parent
text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
color: DynamicColors.palette.m3onPrimary
font.pointSize: Math.round(11 * 0.9)
}
}
Behavior on anchors.bottomMargin {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
Behavior on scale {
Anim {}
}
}
Behavior on implicitHeight {
Anim {
Behavior on implicitHeight {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
}
}
RowLayout {
id: layout
anchors.left: parent.left
anchors.margins: 18
anchors.right: parent.right
anchors.top: parent.top
spacing: 10
CustomRect {
color: IdleInhibitor.enabled ? DynamicColors.palette.m3secondary : DynamicColors.palette.m3secondaryContainer
implicitHeight: icon.implicitHeight + 7 * 2
implicitWidth: implicitHeight
radius: 1000
MaterialIcon {
id: icon
anchors.centerIn: parent
color: IdleInhibitor.enabled ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3onSecondaryContainer
font.pointSize: 18
text: "coffee"
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 0
CustomText {
Layout.fillWidth: true
elide: Text.ElideRight
font.pointSize: 13
text: qsTr("Keep Awake")
}
CustomText {
Layout.fillWidth: true
color: DynamicColors.palette.m3onSurfaceVariant
elide: Text.ElideRight
font.pointSize: 11
text: IdleInhibitor.enabled ? qsTr("Preventing sleep mode") : qsTr("Normal power management")
}
}
CustomSwitch {
checked: IdleInhibitor.enabled
onToggled: IdleInhibitor.enabled = checked
}
}
Loader {
id: activeChip
anchors.bottom: parent.bottom
anchors.bottomMargin: IdleInhibitor.enabled ? 18 : -implicitHeight
anchors.left: parent.left
anchors.leftMargin: 18
anchors.topMargin: 20
opacity: IdleInhibitor.enabled ? 1 : 0
scale: IdleInhibitor.enabled ? 1 : 0.5
Behavior on anchors.bottomMargin {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Behavior on opacity {
Anim {
duration: MaterialEasing.expressiveEffectsTime
}
}
Behavior on scale {
Anim {
}
}
sourceComponent: CustomRect {
color: DynamicColors.palette.m3primary
implicitHeight: activeText.implicitHeight + 10 * 2
implicitWidth: activeText.implicitWidth + 10 * 2
radius: 1000
CustomText {
id: activeText
anchors.centerIn: parent
color: DynamicColors.palette.m3onPrimary
font.pointSize: Math.round(11 * 0.9)
text: qsTr("Active since %1").arg(Qt.formatTime(IdleInhibitor.enabledSince, Config.services.useTwelveHourClock ? "hh:mm a" : "hh:mm"))
}
}
Component.onCompleted: active = Qt.binding(() => opacity > 0)
}
}
+69 -71
View File
@@ -6,91 +6,89 @@ import Quickshell
import QtQuick
Item {
id: root
id: root
required property var visibilities
required property Item sidebar
required property Item popouts
required property Item popouts
readonly property PersistentProperties props: PersistentProperties {
property string recordingConfirmDelete
property bool recordingListExpanded: false
property string recordingMode
readonly property PersistentProperties props: PersistentProperties {
property bool recordingListExpanded: false
property string recordingConfirmDelete
property string recordingMode
reloadableId: "utilities"
}
readonly property bool shouldBeActive: visibilities.sidebar
required property Item sidebar
required property var visibilities
reloadableId: "utilities"
}
readonly property bool shouldBeActive: visibilities.sidebar
implicitHeight: 0
implicitWidth: sidebar.visible ? sidebar.width : Config.utilities.sizes.width
visible: height > 0
visible: height > 0
implicitHeight: 0
implicitWidth: sidebar.visible ? sidebar.width : Config.utilities.sizes.width
states: State {
name: "visible"
when: root.shouldBeActive
onStateChanged: {
if (state === "visible" && timer.running) {
timer.triggered();
timer.stop();
}
}
PropertyChanges {
root.implicitHeight: content.implicitHeight + 8 * 2
}
}
transitions: [
Transition {
from: ""
to: "visible"
states: State {
name: "visible"
when: root.shouldBeActive
PropertyChanges {
root.implicitHeight: content.implicitHeight + 8 * 2
}
}
transitions: [
Transition {
from: ""
to: "visible"
Anim {
target: root
property: "implicitHeight"
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
},
Transition {
from: "visible"
to: ""
property: "implicitHeight"
target: root
}
},
Transition {
from: "visible"
to: ""
Anim {
target: root
property: "implicitHeight"
Anim {
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
]
property: "implicitHeight"
target: root
}
}
]
Timer {
id: timer
onStateChanged: {
if (state === "visible" && timer.running) {
timer.triggered();
timer.stop();
}
}
running: true
interval: 1000
onTriggered: {
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
content.visible = true;
}
}
Timer {
id: timer
Loader {
id: content
interval: 1000
running: true
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 8
onTriggered: {
content.active = Qt.binding(() => root.shouldBeActive || root.visible);
content.visible = true;
}
}
visible: false
active: true
Loader {
id: content
sourceComponent: Content {
implicitWidth: root.implicitWidth - 8 * 2
props: root.props
visibilities: root.visibilities
popouts: root.popouts
}
}
active: true
anchors.left: parent.left
anchors.margins: 8
anchors.top: parent.top
visible: false
sourceComponent: Content {
implicitWidth: root.implicitWidth - 8 * 2
popouts: root.popouts
props: root.props
visibilities: root.visibilities
}
}
}
+47 -47
View File
@@ -5,64 +5,64 @@ import qs.Config
import QtQuick
Item {
id: root
id: root
required property var visibilities
required property var panels
readonly property Props props: Props {}
required property var panels
readonly property Props props: Props {
}
required property var visibilities
visible: width > 0
implicitWidth: 0
implicitWidth: 0
visible: width > 0
states: State {
name: "visible"
when: root.visibilities.sidebar
states: State {
name: "visible"
when: root.visibilities.sidebar
PropertyChanges {
root.implicitWidth: Config.sidebar.sizes.width
}
}
PropertyChanges {
root.implicitWidth: Config.sidebar.sizes.width
}
}
transitions: [
Transition {
from: ""
to: "visible"
transitions: [
Transition {
from: ""
to: "visible"
Anim {
target: root
property: "implicitWidth"
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
},
Transition {
from: "visible"
to: ""
property: "implicitWidth"
target: root
}
},
Transition {
from: "visible"
to: ""
Anim {
target: root
property: "implicitWidth"
Anim {
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
]
property: "implicitWidth"
target: root
}
}
]
Loader {
id: content
Loader {
id: content
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 8
anchors.bottomMargin: 0
active: true
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.left: parent.left
anchors.margins: 8
anchors.top: parent.top
active: true
Component.onCompleted: active = Qt.binding(() => (root.visibilities.sidebar && Config.sidebar.enabled) || root.visible)
sourceComponent: Content {
implicitWidth: Config.sidebar.sizes.width - 8 * 2
props: root.props
visibilities: root.visibilities
}
sourceComponent: Content {
implicitWidth: Config.sidebar.sizes.width - 8 * 2
props: root.props
visibilities: root.visibilities
}
}
Component.onCompleted: active = Qt.binding(() => (root.visibilities.sidebar && Config.sidebar.enabled) || root.visible)
}
}
+26 -27
View File
@@ -3,37 +3,36 @@ import qs.Components
import qs.Config
Item {
id: root
id: root
required property var visibilities
required property Item panels
required property Item panels
required property var visibilities
visible: height > 0
implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth)
implicitHeight: content.implicitHeight
implicitHeight: content.implicitHeight
implicitWidth: Math.max(panels.sidebar.width, content.implicitWidth)
visible: height > 0
states: State {
name: "hidden"
when: root.visibilities.sidebar
states: State {
name: "hidden"
when: root.visibilities.sidebar
PropertyChanges {
root.implicitHeight: 0
}
}
PropertyChanges {
root.implicitHeight: 0
}
}
transitions: Transition {
Anim {
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
property: "implicitHeight"
target: root
}
}
transitions: Transition {
Anim {
target: root
property: "implicitHeight"
duration: MaterialEasing.expressiveEffectsTime
easing.bezierCurve: MaterialEasing.expressiveEffects
}
}
Content {
id: content
Content {
id: content
visibilities: root.visibilities
panels: root.panels
}
panels: root.panels
visibilities: root.visibilities
}
}