Files
qemby/src/qEmbyApp/qml/components/ToastHost.qml
T

180 lines
5.3 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import QtQuick
import QtQuick.Layouts
import qEmby 1.0
Item {
id: root
property int defaultDuration: 4000
property int maximumVisible: 5
property int nextToastId: 1
function show(message, kind, duration) {
const normalizedKind = ["info", "success", "warning", "error"].indexOf(kind) >= 0 ? kind : "info";
const timeout = duration === undefined ? defaultDuration : Math.max(0, duration);
const toastId = nextToastId++;
toastModel.append({
"toastId": toastId,
"message": String(message),
"kind": normalizedKind,
"timeoutMs": timeout
});
while (toastModel.count > maximumVisible)toastModel.remove(0)
return toastId;
}
function info(message, duration) {
return show(message, "info", duration);
}
function success(message, duration) {
return show(message, "success", duration);
}
function warning(message, duration) {
return show(message, "warning", duration);
}
function error(message, duration) {
return show(message, "error", duration);
}
function dismiss(toastId) {
for (let i = 0; i < toastModel.count; ++i) {
if (toastModel.get(i).toastId === toastId) {
toastModel.remove(i);
return ;
}
}
}
function clear() {
toastModel.clear();
}
ListModel {
id: toastModel
}
ListView {
id: toastView
anchors.top: parent.top
anchors.right: parent.right
anchors.topMargin: Theme.spacingLg
anchors.rightMargin: Theme.spacingLg
width: Math.min(360, Math.max(240, root.width - Theme.spacingXl * 2))
height: Math.min(contentHeight, Math.max(0, root.height - Theme.spacingXl * 2))
spacing: Theme.spacingSm
interactive: contentHeight > height
clip: true
model: toastModel
z: 1000
add: Transition {
NumberAnimation {
properties: "opacity,x"
from: 0
duration: Theme.animationFast
easing.type: Easing.OutCubic
}
}
remove: Transition {
NumberAnimation {
property: "opacity"
to: 0
duration: Theme.animationFast
}
}
displaced: Transition {
NumberAnimation {
properties: "x,y"
duration: Theme.animationFast
easing.type: Easing.OutCubic
}
}
delegate: Rectangle {
id: toast
required property int toastId
required property string message
required property string kind
required property int timeoutMs
readonly property color statusColor: kind === "success" ? Theme.success : kind === "warning" ? Theme.warning : kind === "error" ? Theme.danger : Theme.accent
readonly property string statusIcon: kind === "success" ? "✓" : kind === "warning" ? "!" : kind === "error" ? "×" : "i"
readonly property string statusLabel: kind === "success" ? qsTr("Success") : kind === "warning" ? qsTr("Warning") : kind === "error" ? qsTr("Error") : qsTr("Information")
width: toastView.width
implicitHeight: toastLayout.implicitHeight + Theme.spacingMd * 2
radius: Theme.radiusLg
color: Theme.bgSurface
border.width: 1
border.color: Theme.alpha(statusColor, 0.72)
Accessible.role: Accessible.AlertMessage
Accessible.name: qsTr("%1: %2").arg(statusLabel).arg(message)
RowLayout {
id: toastLayout
anchors.fill: parent
anchors.margins: Theme.spacingMd
spacing: Theme.spacingMd
Rectangle {
color: toast.statusColor
radius: width / 2
Layout.preferredWidth: 24
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignTop
Text {
anchors.centerIn: parent
text: toast.statusIcon
color: "#FFFFFF"
font.pixelSize: Theme.fontBody
font.weight: Font.Bold
}
}
Text {
text: toast.message
color: Theme.textBody
font.pixelSize: Theme.fontBody
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
}
ThemedButton {
text: "×"
implicitWidth: 28
implicitHeight: 28
leftPadding: 0
rightPadding: 0
Accessible.name: qsTr("Dismiss notification")
Layout.alignment: Qt.AlignTop
onClicked: root.dismiss(toast.toastId)
}
}
Timer {
interval: toast.timeoutMs
running: toast.timeoutMs > 0
repeat: false
onTriggered: root.dismiss(toast.toastId)
}
}
}
}