PBToolboxAI v4 ← Site

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 #

Objectn_pbt_toaster — non-visual: nothing to place on the window
Used forConfirming a successful action, reporting a warning or an error, without stopping the work in progress
ReturnNon-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.

// Local variables
n_pbt_toaster lnv_toast

// Create the notifier
lnv_toast = create n_pbt_toaster

// ... configuration ...

// Destroy it when done
destroy lnv_toast

Quick start #

// Local variables
n_pbt_toaster lnv_toast

// Create the notifier
lnv_toast = create n_pbt_toaster

// Configure, then show
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 it when done
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.

PropertyTypeDefaultPurpose
is_textstring""Body of the message. Accepts rich text markup
is_kindstringKIND_INFOLevel: drives the icon and the color of the stripe. KIND_* constants
is_positionstringPOSITION_BOTTOM_RIGHTAnchor corner. POSITION_* constants
ib_screenbooleanfalsefalse = anchored to the corner of the window; true = anchored to the corner of the screen, floating above everything. Toasts anchored to the screen stack by monitor: two windows showing one each no longer cover each other
il_timeoutlongTIMEOUT_AUTODisplay 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_titlestring""Bold title line above the message (rich toast)
is_imagestring""Illustration image on the left, in place of the level icon (accepted forms: path, mono:, DLL resource)
is_keystring""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. The key belongs to the toaster: another toaster showing the same key opens its own toast. An update also takes the new corner, screen anchoring and anchor window. The key comes back as as_key in the ue_toast_* events
is_soundstringSOUND_AUTOSystem sound of the toast, played when it is asked for. SOUND_AUTO (the default): an error or a warning sounds, an information or a success stays quiet; SOUND_ALWAYS: every kind; SOUND_NEVER: silent
il_max_visiblelong0Maximum 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. This cap is shared by every toaster of the application: the last value set wins; 0 (the default) leaves it as it is — 5 until someone sets it. A stack is the same corner of the same window — or, for a toast anchored to the screen or without ipo_owner, the same corner of the same monitor, whatever window showed it
ipo_ownerpowerobject—The visual object the toast is attached to (its window corner serves as the reference point). It is the only wiring to do: the events of the toast are raised on the toaster itself. While that window is minimized, the toast waits: it is not shown and its countdown does not run until the window comes back
ipo_receiverpowerobject—Optional, legacy: when set, its window gets its pbm_custom02 rung at each toast event (for older code that had mapped it). Leave it empty — the toaster delivers its events on its own, on itself

Constants #

ConstantValueUse
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)
SOUND_AUTO"auto"Sound for an error or a warning only (default)
SOUND_ALWAYS"always"Sound for every kind
SOUND_NEVER"never"Never a sound

Why LEFT/RIGHT here, and START/END elsewhere? 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 #

MethodPurpose
of_show ( ) → longDisplays the notification built from the properties. Returns the identifier of the toast (> 0) — a toast that waits for room (il_max_visible) gets its own at once too — or a negative value on error. Does not block. -5 (and nothing is shown) for a setting out of its range: an is_kind, is_position or is_sound that is none of its constants, il_max_visible outside 0 to 20, il_timeout below TIMEOUT_AUTO
of_close ( long al_id ) → longCloses a toast still on screen — or still waiting for room —, designated by the identifier returned by of_show. A toast ON SCREEN closed this way raises ue_toast_dismissed, like its cross; one still waiting was never seen and raises nothing. 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. The component calls it itself while a toast is on screen: you normally do not have to — see below
of_add_button (string as_key, string as_label {, string as_image }) → longAdds an action button (up to 3). A click on it raises ue_toast_action(id, key, action); of_reset clears the buttons. A label may contain any character — a comma, an equals sign — without being cut. Returns 0, or -5 (nothing added) for an empty key, a key already taken, a key holding / or a vertical bar, or a fourth button
of_count ( ) → longReturns how many action buttons the notification holds
of_keys_at ( long al_index ) → stringThe key of the button at rank al_index (1 based), or "" past either end
of_has ( string as_key ) → booleanWas a button added under this key? of_add_button refuses (-5) a key already taken: asking first tells why

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.

// Local variables
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 ...

// Close the toast once the work is done
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.

You have nothing to wire for it: set ipo_owner (the window the toast is anchored to) and handle the events. The component drains them itself while a toast is on screen and raises them on the object — no receiver, no timer.

1. Set ipo_owner to the window the toast is anchored to:

// Anchor the toasts to this window
inv_toaster.ipo_owner = this      // the window the toast is anchored to

2. Handle the events raised on the toaster:

EventRaised when
ue_toast_clicked (long al_id, string as_key)The body of the toast is clicked (not a button). al_id is the identifier of_show returned, as_key the is_key of the toast (empty without one)
ue_toast_action (long al_id, string as_key, string as_action)An action button is clicked; as_action holds the key passed to of_add_button. al_id is the identifier of_show returned, as_key the is_key of the toast (empty without one)
ue_toast_dismissed (long al_id, string as_key)The toast closes: timeout elapsed, close cross, or of_close while it is on screen (a toast still waiting raises nothing). A click on the body raises ue_toast_clicked, a button ue_toast_action. al_id is the identifier of_show returned, as_key the is_key of the toast (empty without one)

If you expect nothing back — no action button, no click on the body — you have none of these events to handle: the notification appears and disappears on its own. That is the simplest mode, perfect for a plain confirmation.


Examples #

The four levels #

// Info : a neutral message
inv_toaster.of_reset()
inv_toaster.is_kind = inv_toaster.KIND_INFO
inv_toaster.is_text = "Operation complete."
inv_toaster.of_show()

// Success : it went through
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()

// Warning : worth a look
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()

// Error : it failed
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 #

// Start again from the default settings
inv_toaster.of_reset()

// A title, a text and an image
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 : anchor the toaster once and for all ; the feedback arrives on its own
inv_toaster.ipo_owner = this
// Offer two actions ; timeout 0 = the toast waits for the user decision
inv_toaster.of_reset()

// Title, text and two buttons, then show
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*/ "later", /*label*/ "Later")
inv_toaster.of_show()
// ue_toast_action event of inv_toaster : (long al_id, string as_key, string as_action)
choose case as_action
    case "installer" ; of_start_update()
    case "later" ; of_reporter(1)
end choose

Reacting to a click on the message #

// ue_toast_clicked event of inv_toaster : (long al_id, string as_key)
// 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()

// The kind and the text follow the outcome
if ll_errors = 0 then
    inv_toaster.is_kind = inv_toaster.KIND_SUCCESS
    inv_toaster.is_text = "Import complete : [b]" + String(ll_rows) + " 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_errors) + " errors."
end if

// Show the toast
inv_toaster.of_show()

Best practices #


← Component reference · Guide contents