forked from Shinonome/dots-hyprland
waffles: notifications: image support
This commit is contained in:
@@ -84,4 +84,28 @@ Singleton {
|
|||||||
// Older dates
|
// Older dates
|
||||||
return Qt.formatDateTime(messageTime, "MMMM dd");
|
return Qt.formatDateTime(messageTime, "MMMM dd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processNotificationBody(body, appName) {
|
||||||
|
let processedBody = body
|
||||||
|
|
||||||
|
// Clean Chromium-based browsers notifications - remove first line
|
||||||
|
if (appName) {
|
||||||
|
const lowerApp = appName.toLowerCase()
|
||||||
|
const chromiumBrowsers = [
|
||||||
|
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
|
||||||
|
]
|
||||||
|
|
||||||
|
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
|
||||||
|
const lines = body.split('\n\n')
|
||||||
|
|
||||||
|
if (lines.length > 1 && lines[0].startsWith('<a')) {
|
||||||
|
processedBody = lines.slice(1).join('\n\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processedBody = processedBody.replace(/<img/gi, '\n\n<img');
|
||||||
|
|
||||||
|
return processedBody
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,28 +31,6 @@ Item { // Notification item area
|
|||||||
|
|
||||||
implicitHeight: background.implicitHeight
|
implicitHeight: background.implicitHeight
|
||||||
|
|
||||||
function processNotificationBody(body, appName) {
|
|
||||||
let processedBody = body
|
|
||||||
|
|
||||||
// Clean Chromium-based browsers notifications - remove first line
|
|
||||||
if (appName) {
|
|
||||||
const lowerApp = appName.toLowerCase()
|
|
||||||
const chromiumBrowsers = [
|
|
||||||
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
|
|
||||||
]
|
|
||||||
|
|
||||||
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
|
|
||||||
const lines = body.split('\n\n')
|
|
||||||
|
|
||||||
if (lines.length > 1 && lines[0].startsWith('<a')) {
|
|
||||||
processedBody = lines.slice(1).join('\n\n')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return processedBody
|
|
||||||
}
|
|
||||||
|
|
||||||
function destroyWithAnimation(left = false) {
|
function destroyWithAnimation(left = false) {
|
||||||
root.qmlParent.resetDrag()
|
root.qmlParent.resetDrag()
|
||||||
background.anchors.leftMargin = background.anchors.leftMargin; // Break binding
|
background.anchors.leftMargin = background.anchors.leftMargin; // Break binding
|
||||||
@@ -196,12 +174,13 @@ Item { // Notification item area
|
|||||||
maximumLineCount: 1
|
maximumLineCount: 1
|
||||||
textFormat: Text.StyledText
|
textFormat: Text.StyledText
|
||||||
text: {
|
text: {
|
||||||
return processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
|
return NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout { // Expanded content
|
ColumnLayout { // Expanded content
|
||||||
|
id: expandedContentColumn
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
opacity: root.expanded ? 1 : 0
|
opacity: root.expanded ? 1 : 0
|
||||||
visible: opacity > 0
|
visible: opacity > 0
|
||||||
@@ -218,8 +197,8 @@ Item { // Notification item area
|
|||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
text: {
|
text: {
|
||||||
return `<style>img{max-width:${300 /* binding to notificationBodyText.width would cause a binding loop */}px;}</style>` +
|
return `<style>img{max-width:${expandedContentColumn.width}px;}</style>` +
|
||||||
`${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
|
`${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: (link) => {
|
onLinkActivated: (link) => {
|
||||||
@@ -293,6 +272,8 @@ Item { // Notification item area
|
|||||||
id: actionRepeater
|
id: actionRepeater
|
||||||
model: notificationObject.actions
|
model: notificationObject.actions
|
||||||
NotificationActionButton {
|
NotificationActionButton {
|
||||||
|
id: notifAction
|
||||||
|
required property var modelData
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
buttonText: modelData.text
|
buttonText: modelData.text
|
||||||
urgency: notificationObject.urgency
|
urgency: notificationObject.urgency
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import qs.services
|
|||||||
import qs.modules.common
|
import qs.modules.common
|
||||||
import qs.modules.waffle.looks
|
import qs.modules.waffle.looks
|
||||||
|
|
||||||
|
// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation
|
||||||
AppButton {
|
AppButton {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
|
|||||||
+18
-1
@@ -20,6 +20,7 @@ WBarAttachedPanelContent {
|
|||||||
property bool collapsed: false
|
property bool collapsed: false
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
|
id: contentLayout
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
top: parent.top
|
top: parent.top
|
||||||
@@ -41,9 +42,24 @@ WBarAttachedPanelContent {
|
|||||||
}
|
}
|
||||||
contentItem: NotificationPaneContent {
|
contentItem: NotificationPaneContent {
|
||||||
implicitWidth: calendarColumnLayout.implicitWidth
|
implicitWidth: calendarColumnLayout.implicitWidth
|
||||||
implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230
|
implicitHeight: {
|
||||||
|
if (Notifications.list.length > 0) {
|
||||||
|
return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2)
|
||||||
|
}
|
||||||
|
return 230;
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: enableTimer
|
||||||
|
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||||
|
onTriggered: heightBehavior.enabled = true;
|
||||||
|
}
|
||||||
Behavior on implicitHeight {
|
Behavior on implicitHeight {
|
||||||
|
id: heightBehavior
|
||||||
|
enabled: false
|
||||||
|
Component.onCompleted: {
|
||||||
|
enableTimer.restart();
|
||||||
|
}
|
||||||
animation: Looks.transition.enter.createObject(this)
|
animation: Looks.transition.enter.createObject(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,6 +67,7 @@ WBarAttachedPanelContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WPane {
|
WPane {
|
||||||
|
id: calendarPane
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
id: calendarColumnLayout
|
id: calendarColumnLayout
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|||||||
+123
-34
@@ -1,3 +1,4 @@
|
|||||||
|
pragma ComponentBehavior: Bound
|
||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Layouts
|
import QtQuick.Layouts
|
||||||
import Quickshell
|
import Quickshell
|
||||||
@@ -41,52 +42,115 @@ MouseArea {
|
|||||||
anchors.margins: contentItem.padding
|
anchors.margins: contentItem.padding
|
||||||
spacing: 19
|
spacing: 19
|
||||||
|
|
||||||
RowLayout {
|
// Header
|
||||||
|
SingleNotificationHeader {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
ExpandButton {
|
// Content
|
||||||
Layout.topMargin: -2
|
Item {
|
||||||
|
id: actualContent
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
property real spacing: 16
|
||||||
|
implicitHeight: Math.max(contentColumn.implicitHeight, imageLoader.height)
|
||||||
|
implicitWidth: contentColumn.implicitWidth
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: imageLoader
|
||||||
|
active: root.notification.image != ""
|
||||||
|
sourceComponent: StyledImage {
|
||||||
|
width: 48
|
||||||
|
height: 48
|
||||||
|
sourceSize.width: width
|
||||||
|
sourceSize.height: height
|
||||||
|
source: root.notification.image
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
id: contentColumn
|
||||||
}
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
}
|
||||||
|
spacing: 3
|
||||||
|
|
||||||
NotificationHeaderButton {
|
SummaryText {
|
||||||
Layout.rightMargin: 4
|
id: summaryText
|
||||||
opacity: root.containsMouse ? 1 : 0
|
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
|
||||||
icon.name: "dismiss"
|
}
|
||||||
implicitSize: 12
|
BodyText {
|
||||||
onClicked: {
|
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
|
||||||
Qt.callLater(() => {
|
// onLineLaidOut: (line) => {
|
||||||
Notifications.discardNotification(root.notification?.notificationId);
|
// if (!imageLoader.active) return;
|
||||||
});
|
// const dodgeDistance = imageLoader.width + actualContent.spacing;
|
||||||
|
// // print(line.y, dodgeDistance)
|
||||||
|
// if (summaryText.height + line.y > dodgeDistance) {
|
||||||
|
// line.x -= dodgeDistance;
|
||||||
|
// line.width += dodgeDistance;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
// Actions
|
||||||
|
ActionsRow {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
spacing: 3
|
|
||||||
|
|
||||||
SummaryText {}
|
|
||||||
BodyText {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AcrylicButton {
|
// "+1 notifications" button
|
||||||
id: groupExpandButton
|
GroupExpandButton {
|
||||||
visible: root.groupExpandControlMessage !== ""
|
|
||||||
Layout.bottomMargin: 2
|
Layout.bottomMargin: 2
|
||||||
horizontalPadding: 10
|
}
|
||||||
implicitHeight: 24
|
}
|
||||||
implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2
|
}
|
||||||
onClicked: root.groupExpandToggle()
|
|
||||||
contentItem: Item {
|
component SingleNotificationHeader: RowLayout {
|
||||||
WText {
|
ExpandButton {
|
||||||
id: expandButtonText
|
Layout.topMargin: -2
|
||||||
anchors.centerIn: parent
|
}
|
||||||
text: root.groupExpandControlMessage
|
|
||||||
}
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component ActionsRow: RowLayout {
|
||||||
|
visible: root.expanded && root.notification.actions.length > 0
|
||||||
|
uniformCellSizes: true
|
||||||
|
Repeater {
|
||||||
|
id: actionRepeater
|
||||||
|
model: root.notification.actions
|
||||||
|
delegate: WBorderedButton {
|
||||||
|
id: actionButton
|
||||||
|
Layout.fillHeight: true
|
||||||
|
required property var modelData
|
||||||
|
Layout.fillWidth: true
|
||||||
|
verticalPadding: 16
|
||||||
|
horizontalPadding: 12
|
||||||
|
text: modelData.text
|
||||||
|
implicitHeight: actionButtonText.implicitHeight + verticalPadding * 2
|
||||||
|
contentItem: WText {
|
||||||
|
id: actionButtonText
|
||||||
|
text: actionButton.text
|
||||||
|
font.pixelSize: Looks.font.pixelSize.large
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
wrapMode: Text.Wrap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,8 +170,17 @@ MouseArea {
|
|||||||
verticalAlignment: Text.AlignTop
|
verticalAlignment: Text.AlignTop
|
||||||
wrapMode: Text.Wrap
|
wrapMode: Text.Wrap
|
||||||
maximumLineCount: root.expanded ? 100 : 1
|
maximumLineCount: root.expanded ? 100 : 1
|
||||||
text: root.notification?.body
|
text: {
|
||||||
|
if (root.expanded)
|
||||||
|
return `<style>img{max-width:${summaryText.width}px; align: right}</style>` + `${NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>")}`;
|
||||||
|
return NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>");
|
||||||
|
}
|
||||||
color: Looks.colors.subfg
|
color: Looks.colors.subfg
|
||||||
|
textFormat: root.expanded ? Text.RichText : Text.StyledText
|
||||||
|
onLinkActivated: link => {
|
||||||
|
Qt.openUrlExternally(link);
|
||||||
|
GlobalStates.sidebarRightOpen = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component ExpandButton: NotificationHeaderButton {
|
component ExpandButton: NotificationHeaderButton {
|
||||||
@@ -140,4 +213,20 @@ MouseArea {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component GroupExpandButton: AcrylicButton {
|
||||||
|
id: groupExpandButton
|
||||||
|
visible: root.groupExpandControlMessage !== ""
|
||||||
|
horizontalPadding: 10
|
||||||
|
implicitHeight: 24
|
||||||
|
implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2
|
||||||
|
onClicked: root.groupExpandToggle()
|
||||||
|
contentItem: Item {
|
||||||
|
WText {
|
||||||
|
id: expandButtonText
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.groupExpandControlMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user