Skip to content

Instantly share code, notes, and snippets.

@jonmcclung
Last active July 19, 2024 14:10
Show Gist options
  • Save jonmcclung/bae669101d17b103e94790341301c129 to your computer and use it in GitHub Desktop.
Save jonmcclung/bae669101d17b103e94790341301c129 to your computer and use it in GitHub Desktop.
Toast in QML. This implementation creates black toast with white text that appears at the bottom of the screen. It also supports adding multiple toast at a time using ToastManager with the newest toast at the bottom.
import QtQuick 2.0
import QtQuick.Controls 2.0
ApplicationWindow {
visible: true
height: 640
width: 480
id: root
ToastManager {
id: toast
}
Timer {
interval: 1000
repeat: true
running: true
property int i: 0
onTriggered: {
toast.show("This timer has triggered " + (++i) + " times!");
}
}
Timer {
interval: 3000
repeat: true
running: true
property int i: 0
onTriggered: {
toast.show("This important message has been shown " + (++i) + " times.", 5000);
}
}
}
import QtQuick 2.0
/**
* adapted from StackOverflow:
* http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
*/
/**
* @brief An Android-like timed message text in a box that self-destroys when finished if desired
*/
Rectangle {
/**
* Public
*/
/**
* @brief Shows this Toast
*
* @param {string} text Text to show
* @param {real} duration Duration to show in milliseconds, defaults to 3000
*/
function show(text, duration) {
message.text = text;
if (typeof duration !== "undefined") { // checks if parameter was passed
time = Math.max(duration, 2 * fadeTime);
}
else {
time = defaultTime;
}
animation.start();
}
property bool selfDestroying: false // whether this Toast will self-destroy when it is finished
/**
* Private
*/
id: root
readonly property real defaultTime: 3000
property real time: defaultTime
readonly property real fadeTime: 300
property real margin: 10
anchors {
left: parent.left
right: parent.right
margins: margin
}
height: message.height + margin
radius: margin
opacity: 0
color: "#222222"
Text {
id: message
color: "white"
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: margin / 2
}
}
SequentialAnimation on opacity {
id: animation
running: false
NumberAnimation {
to: .9
duration: fadeTime
}
PauseAnimation {
duration: time - 2 * fadeTime
}
NumberAnimation {
to: 0
duration: fadeTime
}
onRunningChanged: {
if (!running && selfDestroying) {
root.destroy();
}
}
}
}
import QtQuick 2.0
/**
* adapted from StackOverflow:
* http://stackoverflow.com/questions/26879266/make-toast-in-android-by-qml
* @brief Manager that creates Toasts dynamically
*/
ListView {
/**
* Public
*/
/**
* @brief Shows a Toast
*
* @param {string} text Text to show
* @param {real} duration Duration to show in milliseconds, defaults to 3000
*/
function show(text, duration) {
model.insert(0, {text: text, duration: duration});
}
/**
* Private
*/
id: root
z: Infinity
spacing: 5
anchors.fill: parent
anchors.bottomMargin: 10
verticalLayoutDirection: ListView.BottomToTop
interactive: false
displaced: Transition {
NumberAnimation {
properties: "y"
easing.type: Easing.InOutQuad
}
}
delegate: Toast {
Component.onCompleted: {
if (typeof duration === "undefined") {
show(text);
}
else {
show(text, duration);
}
}
}
model: ListModel {id: model}
}
@vadi2
Copy link

vadi2 commented Apr 16, 2020

@vadi2
Copy link

vadi2 commented May 20, 2020

This would be a great candidate to put up on https://cutes.io btw.

@gpollo
Copy link

gpollo commented Jul 28, 2021

There are a few problems with this implementation. For a start, toasts are never deleted (model.count never decreases). It is noticeable because if a newer toast expires before an older one, the older ones will not "slide" to fill the empty space.

In my cases, I found the selfDestroying to be useless. I removed this line

-property bool selfDestroying: false

and changed

         onRunningChanged: {
-            if (!running && selfDestroying) {
-                root.destroy();
+            if (!running) {
+                toastManager.model.remove(index);
             }
         }

since root.destroy(); gave errors and usually shouldn't be used this way. Finally, I also changed these lines:

     anchors {
-        left: parent.left
-        right: parent.right
+        left: (parent != null) ? parent.left : undefined
+        right: (parent != null) ? parent.right : undefined
         margins: margin
     }

because they gave errors when a toast was being destroyed.

With these changes, older toasts will always fill the empty space left by a newer toast expiring.

@cagnulein
Copy link

It's in use in https://github.com/cagnulein/qdomyos-zwift as well! Thanks!

@SpringDeveloperGuy
Copy link

@gpollo
Thanks. Please can I get help with this Reference error below?

onRunningChanged: {
if (!running) {
toastManager.model.remove(index); // ReferenceError: toastManager is not defined
}
}

ReferenceError: toastManager is not defined

@novval
Copy link

novval commented May 8, 2024

Hi, friends
I tested it and saw that the objects are not deleted from the list.
2024-05-08_17-54-53
2024-05-08_17-57-47
An error is returned when trying to delete it.
It can be seen that the objects that were not deleted have changed color.

I changed:
ToastManager.qml: model -> listModel
2024-05-08_18-02-18
Toast.qml: also model -> listModel
2024-05-08_18-03-36
and its worked:
2024-05-08_18-05-42

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment