waffles: expandable notifs

This commit is contained in:
end-4
2025-11-29 00:23:19 +01:00
parent b4038dafa9
commit 6b90e37b0f
9 changed files with 185 additions and 50 deletions
@@ -5,17 +5,12 @@ import qs.modules.common
import qs.modules.common.functions import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
WButton { AcrylicButton {
id: root id: root
property var altAction: () => {} property var altAction: () => {}
property var middleClickAction: () => {} property var middleClickAction: () => {}
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
colBackgroundHover: Looks.colors.bg1Hover
colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
Layout.fillHeight: true Layout.fillHeight: true
topInset: 4 topInset: 4
bottomInset: 4 bottomInset: 4
@@ -23,16 +18,7 @@ WButton {
rightInset: 0 rightInset: 0
horizontalPadding: 8 horizontalPadding: 8
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1) colBackground: ColorUtils.transparentize(Looks.colors.bg1)
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
@@ -50,15 +36,4 @@ WButton {
} }
} }
background: AcrylicRectangle {
shiny: ((root.hovered && !root.down) || root.checked)
color: root.color
radius: Looks.radius.medium
border.width: 1
border.color: root.colBackgroundBorder
Behavior on border.color {
animation: Looks.transition.color.createObject(this)
}
}
} }
@@ -0,0 +1,42 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WButton {
id: root
colBackground: Looks.colors.bg1
colBackgroundHover: Looks.colors.bg1Hover
colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
property alias border: background.border
property alias shinyColor: background.borderColor
colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0)
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
background: AcrylicRectangle {
id: background
shiny: ((root.hovered && !root.down) || root.checked)
color: root.color
radius: Looks.radius.medium
border.width: 1
border.color: root.colBackgroundBorder
Behavior on border.color {
animation: Looks.transition.color.createObject(this)
}
}
}
@@ -9,16 +9,17 @@ Rectangle {
id: root id: root
property bool shiny: true // Top border property bool shiny: true // Top border
property color borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, shiny ? 0.5 : 1) property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7)
property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1)
color: Looks.colors.bg1Hover color: Looks.colors.bg1Hover
radius: Looks.radius.medium radius: Looks.radius.medium
Behavior on color { Behavior on color {
animation: Looks.transition.color.createObject(this) animation: Looks.transition.color.createObject(this)
} }
Behavior on borderColor { Behavior on internalBorderColor {
animation: Looks.transition.color.createObject(this) animation: Looks.transition.color.createObject(this)
} }
onBorderColorChanged: { onInternalBorderColorChanged: {
borderCanvas.requestPaint(); borderCanvas.requestPaint();
} }
@@ -32,7 +33,7 @@ Rectangle {
var ctx = getContext("2d"); var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height); ctx.clearRect(0, 0, width, height);
var borderColor = root.borderColor; var borderColor = root.internalBorderColor;
var r = root.radius; var r = root.radius;
var fadeLength = Math.max(1, r); var fadeLength = Math.max(1, r);
@@ -53,6 +53,7 @@ Singleton {
property color controlBgHover: '#57575B' property color controlBgHover: '#57575B'
property color controlFg: "#FFFFFF" property color controlFg: "#FFFFFF"
property color accentUnfocused: "#848484" property color accentUnfocused: "#848484"
property color link: "#235CCF"
} }
darkColors: QtObject { darkColors: QtObject {
id: darkColors id: darkColors
@@ -80,6 +81,7 @@ Singleton {
property color controlBgHover: "#CFCED1" property color controlBgHover: "#CFCED1"
property color controlFg: "#454545" property color controlFg: "#454545"
property color accentUnfocused: "#989898" property color accentUnfocused: "#989898"
property color link: "#A7C9FC"
} }
colors: QtObject { colors: QtObject {
id: colors id: colors
@@ -110,6 +112,7 @@ Singleton {
property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg
property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover
property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg
property color link: root.dark ? root.darkColors.link : root.lightColors.link
property color danger: "#C42B1C" property color danger: "#C42B1C"
property color dangerActive: "#B62D1F" property color dangerActive: "#B62D1F"
property color warning: "#FF9900" property color warning: "#FF9900"
@@ -8,8 +8,11 @@ Text {
color: Looks.colors.fg color: Looks.colors.fg
font { font {
hintingPreference: Font.PreferFullHinting
family: Looks.font.family.ui family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.normal pixelSize: Looks.font.pixelSize.normal
weight: Looks.font.weight.regular weight: Looks.font.weight.regular
} }
linkColor: Looks.colors.link
} }
@@ -8,18 +8,24 @@ import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
WBorderlessButton { WBorderlessButton {
id: headerButton id: root
Layout.fillWidth: false Layout.fillWidth: false
implicitWidth: 16 property real implicitSize: 16
implicitHeight: 16 implicitWidth: implicitSize
implicitHeight: implicitSize
color: "transparent" color: "transparent"
colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1
Behavior on colForeground {
animation: Looks.transition.color.createObject(this)
}
contentItem: Item { contentItem: Item {
FluentIcon { FluentIcon {
anchors.centerIn: parent anchors.centerIn: parent
implicitSize: 16 implicitSize: root.implicitSize
icon: headerButton.icon.name icon: root.icon.name
color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1 color: root.colForeground
} }
} }
} }
@@ -2,18 +2,27 @@ import QtQuick
import qs import qs
import qs.services import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
SmallBorderedIconButton { AcrylicButton {
id: root id: root
property bool iconVisible: true property bool iconVisible: true
property string iconName: "" property string iconName: ""
property bool iconFilled: true property bool iconFilled: true
colBackground: Looks.colors.bg2
colBackgroundHover: Looks.colors.bg2Hover
colBackgroundActive: Looks.colors.bg2Active
property color colBorder: Looks.colors.bg2Border
property color colBorderToggled: Looks.colors.accent
border.color: checked ? colBorderToggled : colBorder
leftPadding: 12 leftPadding: 12
rightPadding: 12 rightPadding: 12
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
implicitHeight: 24
contentItem: Row { contentItem: Row {
id: focusButtonContent id: focusButtonContent
@@ -12,6 +12,7 @@ MouseArea {
required property var notificationGroup required property var notificationGroup
readonly property var notifications: notificationGroup?.notifications ?? [] readonly property var notifications: notificationGroup?.notifications ?? []
property bool expanded: false
implicitWidth: contentLayout.implicitWidth implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight implicitHeight: contentLayout.implicitHeight
@@ -34,12 +35,23 @@ MouseArea {
interactive: false interactive: false
spacing: 4 spacing: 4
model: ScriptModel { model: ScriptModel {
values: root.notifications.slice().reverse() values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1)
objectProp: "notificationId"
} }
delegate: WSingleNotification { delegate: WSingleNotification {
required property int index
required property var modelData required property var modelData
width: ListView.view.width width: ListView.view.width
notification: modelData notification: modelData
groupExpandControlMessage: {
if (root.notifications.length === 0) return "";
if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1);
if (index === root.notifications.length - 1) return Translation.tr("See fewer");
return "";
}
onGroupExpandToggle: {
root.expanded = !root.expanded;
}
} }
} }
} }
@@ -13,10 +13,17 @@ MouseArea {
required property var notification required property var notification
property bool expanded: false property bool expanded: false
property string groupExpandControlMessage: ""
signal groupExpandToggle
hoverEnabled: true
implicitHeight: contentItem.implicitHeight implicitHeight: contentItem.implicitHeight
implicitWidth: contentItem.implicitWidth implicitWidth: contentItem.implicitWidth
Behavior on implicitHeight {
animation: Looks.transition.enter.createObject(this)
}
Rectangle { Rectangle {
id: contentItem id: contentItem
anchors.fill: parent anchors.fill: parent
@@ -26,32 +33,109 @@ MouseArea {
implicitHeight: notificationContent.implicitHeight + padding * 2 implicitHeight: notificationContent.implicitHeight + padding * 2
implicitWidth: notificationContent.implicitWidth + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2
border.width: 1 border.width: 1
border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow) border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
ColumnLayout { ColumnLayout {
id: notificationContent id: notificationContent
anchors.fill: parent anchors.fill: parent
anchors.margins: contentItem.padding anchors.margins: contentItem.padding
spacing: 19
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
WText {
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) ExpandButton {
Layout.topMargin: -2
}
Item {
Layout.fillWidth: true
}
NotificationHeaderButton {
Layout.rightMargin: 4
opacity: root.containsMouse ? 1 : 0
icon.name: "dismiss"
implicitSize: 12
onClicked: {
Qt.callLater(() => {
Notifications.discardNotification(root.notification?.notificationId);
});
}
} }
} }
ColumnLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
WText { spacing: 3
Layout.fillWidth: true
elide: Text.ElideRight SummaryText {}
text: root.notification.summary BodyText {}
}
AcrylicButton {
id: groupExpandButton
visible: root.groupExpandControlMessage !== ""
Layout.bottomMargin: 2
horizontalPadding: 10
implicitHeight: 24
implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2
onClicked: root.groupExpandToggle()
contentItem: Item {
WText {
id: expandButtonText
anchors.centerIn: parent
text: root.groupExpandControlMessage
}
} }
}
}
}
component SummaryText: WText {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.notification?.summary
font.pixelSize: Looks.font.pixelSize.large
}
component BodyText: WText {
Layout.fillWidth: true
Layout.fillHeight: true
elide: Text.ElideRight
verticalAlignment: Text.AlignTop
wrapMode: Text.Wrap
maximumLineCount: root.expanded ? 100 : 1
text: root.notification?.body
color: Looks.colors.subfg
}
component ExpandButton: NotificationHeaderButton {
id: expandButton
implicitWidth: expandButtonContent.implicitWidth
onClicked: root.expanded = !root.expanded
contentItem: Item {
id: expandButtonContent
implicitWidth: expandButtonRow.implicitWidth
implicitHeight: expandButtonRow.implicitHeight
RowLayout {
id: expandButtonRow
anchors.centerIn: parent
spacing: 8
WText { WText {
Layout.fillWidth: true color: expandButton.colForeground
elide: Text.ElideRight text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
wrapMode: Text.Wrap }
maximumLineCount: root.expanded ? 100 : 1 FluentIcon {
Layout.rightMargin: 12
icon: "chevron-down"
implicitSize: 18
rotation: root.expanded ? -180 : 0
color: expandButton.colForeground
Behavior on rotation {
animation: Looks.transition.rotate.createObject(this)
}
} }
} }
} }