toaster — n_pbt_toaster #
← Component reference · Guide contents
"Toast" notifications in a screen corner: a message that appears, informs and disappears without blocking the user or interrupting their typing.
▶ See it live — Demo application, Toaster tile: the preview, the code behind it and this page, side by side.
At a glance #
| Object | n_pbt_toaster — non-visual: nothing to place on the window |
| Used for | Confirming a successful action, reporting a warning or an error, without stopping the work in progress |
| Return | Non-blocking: of_show() returns immediately; user reactions come back through events |
The toast is a detached window: it floats above your application (or above the whole screen) and closes on its own.
n_pbt_toaster lnv_toast
lnv_toast = create n_pbt_toaster
// ... configuration ...
destroy lnv_toast
Quick start #
n_pbt_toaster lnv_toast
lnv_toast = create n_pbt_toaster
lnv_toast.ipo_owner = this // window the toast attaches to
lnv_toast.is_kind = lnv_toast.KIND_SUCCESS // green icon + success stripe
lnv_toast.is_text = "Your changes have been [b]saved[/b]."
lnv_toast.of_show()
destroy lnv_toast
Everything is configured through properties, then of_show() — which takes no argument — makes the notification appear.
Properties #
To be set before of_show.
| Property | Type | Default | Purpose |
|---|---|---|---|
is_text | string | "" | Body of the message. Accepts rich text markup |
is_kind | string | KIND_INFO | Level: drives the icon and the color of the stripe. KIND_* constants |
is_position | string | POSITION_BOTTOM_RIGHT | Anchor corner. POSITION_* constants |
ib_screen | boolean | false | false = anchored to the corner of the window; true = anchored to the corner of the screen, floating above everything |
il_timeout | long | TIMEOUT_AUTO | Display time in milliseconds before the toast closes on its own. TIMEOUT_AUTO (-1, the default) means 4 s for an information but until it is clicked for an error — an error that vanishes after four seconds is an error lost. TIMEOUT_UNTIL_CLICKED (0) makes any toast persistent; an explicit duration is honoured as-is. While the pointer rests on a toast its countdown is suspended, and a bar shows the time left |
is_title | string | "" | Bold title line above the message (rich toast) |
is_image | string | "" | Illustration image on the left, in place of the level icon (accepted forms: path, mono:, DLL resource) |
is_key | string | "" | Key of the toast: showing the same key again updates the toast already on screen instead of opening a second one. That is what a progress notification needs ("Export 3/10" then "4/10"): closing and reopening would restart the animation and shuffle the stack. Leave it empty for an ordinary toast |
il_max_visible | long | 5 | Maximum number of toasts on screen at the same corner (1 to 20). Past that, the next ones wait and appear as room frees up: a batch loop firing one toast per row used to stack windows straight off the screen |
ipo_owner | powerobject | — | The visual object the toast is attached to (its window corner serves as the reference point) |
ipo_receiver | powerobject | — | The visual object that receives the events of the toast. Leave it empty for a notification without feedback |
Constants #
| Constant | Value | Use |
|---|---|---|
KIND_INFO | "info" | Neutral information |
KIND_SUCCESS | "success" | Successful operation |
KIND_WARNING | "warning" | Warning |
KIND_ERROR | "error" | Failure |
POSITION_TOP_LEFT | "top-left" | Top left corner |
POSITION_TOP_CENTER | "top-center" | Top, centered |
POSITION_TOP_RIGHT | "top-right" | Top right corner |
POSITION_BOTTOM_LEFT | "bottom-left" | Bottom left corner |
POSITION_BOTTOM_CENTER | "bottom-center" | Bottom, centered |
POSITION_BOTTOM_RIGHT | "bottom-right" | Bottom right corner (default) |
Why
LEFT/RIGHThere, andSTART/ENDelsewhere? The toast is a system window placed in screen pixels, not content that follows a reading direction: a screen corner has no "beginning". These constants therefore deliberately remain physical, and do not switch sides when the application moves to right-to-left writing. See Language and RTL.
Methods #
| Method | Purpose |
|---|---|
of_show ( ) → long | Displays the notification built from the properties. Returns the identifier of the toast (> 0), or a negative value on error. Does not block |
of_close ( long al_id ) → long | Closes a toast still on screen, designated by the identifier returned by of_show. Returns 0, or -5 if the id is empty or names a toast that has already disappeared |
of_reset ( ) | Resets every content property to its default and clears the buttons (ipo_owner and ipo_receiver are kept: they are wiring, not content) |
of_process_events ( ) | Empties the queue of toast feedback and raises the matching ue_toast_* events — see below |
of_add_button (string as_key, string as_label {, string as_image }) → long | Adds an action button (up to 3) and returns how many there are. A click on it raises ue_toast_action(id, key); of_reset clears the buttons. A label may contain any character — a comma, an equals sign — without being cut |
Several toasts displayed in the same corner stack automatically. Each one carries a close cross; the identifier returned by of_show lets you tell them apart in the events and close them from your code.
The countdown pauses while the pointer is over the toast: a notification must not vanish under the eyes of whoever is reading it. A thin bar at the bottom of the toast shows the time left — and therefore explains its disappearance. A click on the body answers and closes, in both hosting modes.
A notification set with il_timeout = 0 stays on screen as long as nobody closes it: keep its identifier so you can remove it once the task it announces is finished.
long ll_toast
// Persistent notification : it will stay on screen until of_close.
inv_toaster.is_text = "Export in progress..."
inv_toaster.il_timeout = /*ms, 0 = no auto close*/ 0
ll_toast = inv_toaster.of_show()
// ... long processing ...
inv_toaster.of_close(/*id*/ ll_toast)
Events — the toast talks back #
A notification is not a plain "show and forget": it can tell you that it was clicked, that an action button was chosen, or that it closed.
Because the toast lives in a detached window, the wiring takes three steps.
1. Designate the target visual object:
inv_toaster.ipo_owner = this
inv_toaster.ipo_receiver = this // this userobject / this window will receive the feedback
ipo_receivermust be a visual object (window or userobject). A non-visual object cannot receive a system notification.
2. Declare on that visual object an event mapped to pbm_custom02, which empties the queue:
// ue_toast_notified event, mapped to pbm_custom02
if IsValid(inv_toaster) then inv_toaster.of_process_events()
3. Handle the events raised on the toaster:
| Event | Raised when |
|---|---|
ue_toast_clicked (string as_id) | The body of the toast is clicked (not a button) |
ue_toast_action (string as_id, string as_action) | An action button is clicked; as_action holds the key passed to of_add_button |
ue_toast_dismissed (string as_id) | The toast closes: timeout elapsed, close cross, or after an action |
Without ipo_receiver, the notification appears and disappears without ever sending anything back — that is the simplest mode, perfect for a plain confirmation.
Examples #
The four levels #
inv_toaster.of_reset()
inv_toaster.is_kind = inv_toaster.KIND_INFO
inv_toaster.is_text = "Operation complete."
inv_toaster.of_show()
inv_toaster.of_reset()
inv_toaster.is_kind = inv_toaster.KIND_SUCCESS
inv_toaster.is_text = "Your changes have been [b]saved[/b]."
inv_toaster.of_show()
inv_toaster.of_reset()
inv_toaster.is_kind = inv_toaster.KIND_WARNING
inv_toaster.il_timeout = 5000 // a little longer
inv_toaster.is_text = "Low disk space on drive C:."
inv_toaster.of_show()
inv_toaster.of_reset()
inv_toaster.is_kind = inv_toaster.KIND_ERROR
inv_toaster.il_timeout = 6000
inv_toaster.is_text = "Unable to reach the server."
inv_toaster.of_show()
Choosing the corner, in the window or on the screen #
// Anchored to the top right corner of the WINDOW (default : follows the application)
inv_toaster.is_position = inv_toaster.POSITION_TOP_RIGHT
inv_toaster.ib_screen = false
inv_toaster.is_text = "Anchored to the window corner."
inv_toaster.of_show()
// Detached : anchored to the SCREEN corner, visible even if the window is minimized
inv_toaster.is_position = inv_toaster.POSITION_TOP_RIGHT
inv_toaster.ib_screen = true
inv_toaster.is_text = "Overnight processing complete."
inv_toaster.of_show()
Rich toast: title, image and duration #
inv_toaster.of_reset()
inv_toaster.is_title = "Backup complete"
inv_toaster.is_text = "1,240 files copied to [b]\\server\backup[/b]."
inv_toaster.is_image = "mono:img\backup.svg"
inv_toaster.il_timeout = 8000 // stays 8 seconds
inv_toaster.of_show()
Notification with action buttons #
// open event : wire the feedback once and for all
inv_toaster.ipo_owner = this
inv_toaster.ipo_receiver = this
// Offer two actions ; timeout 0 = the toast waits for the user decision
inv_toaster.of_reset()
inv_toaster.is_title = "Update available"
inv_toaster.is_text = "Version 2.0 is ready to install."
inv_toaster.il_timeout = 0
inv_toaster.of_add_button(/*key*/ "installer", /*label*/ "Install")
inv_toaster.of_add_button(/*key*/ "plus_tard", /*label*/ "Later")
inv_toaster.of_show()
// ue_toast_notified event of the window, mapped to pbm_custom02
if IsValid(inv_toaster) then inv_toaster.of_process_events()
// ue_toast_action event of inv_toaster : (string as_id, string as_action)
choose case as_action
case "installer" ; of_lancer_mise_a_jour()
case "plus_tard" ; of_reporter(1)
end choose
Reacting to a click on the message #
// ue_toast_clicked event of inv_toaster : (string as_id)
// The user clicked the body of the toast : open the relevant screen
Open(w_journal_import)
Notifying from a long process #
// End of an import : inform without blocking the data entry screen
inv_toaster.of_reset()
if ll_erreurs = 0 then
inv_toaster.is_kind = inv_toaster.KIND_SUCCESS
inv_toaster.is_text = "Import complete : [b]" + String(ll_lignes) + " rows[/b] loaded."
else
inv_toaster.is_kind = inv_toaster.KIND_ERROR
inv_toaster.il_timeout = 0 // an error must be read
inv_toaster.is_text = "Import interrupted : " + String(ll_erreurs) + " errors."
end if
inv_toaster.of_show()
Best practices #
- One persistent instance per window (an instance variable created on opening) rather than creating and destroying one for each message: the
ipo_receiverwiring stays in place and the feedback arrives. - Call
of_reset()before each notification: without it, the title, the image or the buttons of the previous one stay in place. - Reserve
il_timeout = 0for messages that demand a decision (blocking error, offered action): a toast that never leaves on its own ends up being annoying. - Use
ib_screen = trueonly for what must stay visible when the application is in the background (end of a long process, overnight job). - A toast is a transient message: if you absolutely need an answer before continuing, use messagebox, which blocks and returns the choice.
- For a permanent status rather than a notification, prefer statusbar.