PBToolboxAI v1 ← 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_toasternon-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.

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.

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
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
il_max_visiblelong5Maximum 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_ownerpowerobjectThe visual object the toast is attached to (its window corner serves as the reference point)
ipo_receiverpowerobjectThe visual object that receives the events of the toast. Leave it empty for a notification without feedback

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)

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), or a negative value on error. Does not block
of_close ( long al_id ) → longCloses 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 }) → longAdds 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_receiver must 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:

EventRaised 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 #


← Component reference · Guide contents