Files
noctalia-shell/Modules/Toast/SimpleToast.qml
T
2025-11-09 15:02:33 -05:00

191 lines
4.0 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: Color.mSurface
// Colored border based on type
border.width: Math.max(2, Style.borderM)
border.color: {
switch (root.type) {
case "warning":
return Color.mPrimary
case "error":
return Color.mError
default:
return Color.mOutline
}
}
}
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()
}
}