180 lines
5.3 KiB
QML
180 lines
5.3 KiB
QML
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)
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|