Files
noctalia-shell/Modules/Toast/SimpleToast.qml
T
2025-11-18 00:05:52 +01:00

196 lines
4.2 KiB
QML

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
Item {
id: root
property string message: ""
property string description: ""
property string icon: ""
property string type: "notice"
property int duration: 3000
readonly property real initialScale: 0.7
signal hidden
width: Math.round(420 * Style.uiScaleRatio + Style.marginM * 1.5 * 2)
height: Math.round(contentLayout.implicitHeight + Style.marginM * 3 * 2)
visible: true
opacity: 0
scale: initialScale
// Background rectangle (apply shadows here)
Rectangle {
id: background
anchors.fill: parent
anchors.margins: Style.marginM * 1.5
radius: Style.radiusL
color: Qt.alpha(Color.mSurface, Settings.data.osd.backgroundOpacity || 1.0)
// Colored border based on type
border.width: Math.max(2, Style.borderM)
border.color: {
var baseColor;
switch (root.type) {
case "warning":
baseColor = Color.mPrimary;
break;
case "error":
baseColor = Color.mError;
break;
default:
baseColor = Color.mOutline;
break;
}
return Qt.alpha(baseColor, Settings.data.osd.backgroundOpacity || 1.0)
}
}
NDropShadows {
anchors.fill: background
source: background
autoPaddingEnabled: true
}
Behavior on opacity {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Timer {
id: hideTimer
interval: root.duration
onTriggered: root.hide()
}
Timer {
id: hideAnimation
interval: Style.animationFast
onTriggered: {
root.visible = false;
root.hidden();
}
}
// Cleanup on destruction
Component.onDestruction: {
hideTimer.stop();
hideAnimation.stop();
}
RowLayout {
id: contentLayout
anchors.fill: parent
anchors.topMargin: Style.marginL
anchors.bottomMargin: Style.marginL
anchors.leftMargin: Style.marginL * 2
anchors.rightMargin: Style.marginL * 2
spacing: Style.marginL
// Icon
NIcon {
icon: if (root.icon !== "") {
return root.icon;
} else if (type === "warning") {
return "toast-warning";
} else if (type === "error") {
return "toast-error";
} else {
return "toast-notice";
}
color: {
switch (type) {
case "warning":
return Color.mPrimary;
case "error":
return Color.mError;
default:
return Color.mOnSurface;
}
}
pointSize: Style.fontSizeXXL * 1.5
Layout.alignment: Qt.AlignVCenter
}
// Label and description
ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
NText {
Layout.fillWidth: true
text: root.message
color: Color.mOnSurface
pointSize: Style.fontSizeL
font.weight: Style.fontWeightBold
wrapMode: Text.WordWrap
visible: text.length > 0
}
NText {
Layout.fillWidth: true
text: root.description
color: Color.mOnSurface
pointSize: Style.fontSizeM
wrapMode: Text.WordWrap
visible: text.length > 0
}
}
}
// Click anywhere dismiss the toast
MouseArea {
anchors.fill: background
acceptedButtons: Qt.LeftButton
onClicked: root.hide()
cursorShape: Qt.PointingHandCursor
}
function show(msg, desc, msgIcon, msgType, msgDuration) {
// Stop all timers first
hideTimer.stop();
hideAnimation.stop();
message = msg;
description = desc || "";
icon = msgIcon || "";
type = msgType || "notice";
duration = msgDuration || 3000;
visible = true;
opacity = 1;
scale = 1.0;
hideTimer.restart();
}
function hide() {
hideTimer.stop();
opacity = 0;
scale = initialScale;
hideAnimation.restart();
}
function hideImmediately() {
hideTimer.stop();
hideAnimation.stop();
opacity = 0;
scale = initialScale;
root.visible = false;
root.hidden();
}
}