Skip to content

Guide Notification

MeowLynxSea edited this page Jun 18, 2026 · 4 revisions

NotificationCenter

The global toast / notification manager. Owns the queue, schedules auto-dismiss timers, and exposes items() for the renderer to paint. The headless state machine lives in yororen_ui_core::notification. The visual host is not shipped — implement it in your app.

When to use it

  • Show toasts from anywhere in your app.
  • Auto-dismiss (default 4 s) or sticky (manual dismiss, persists across restarts).
  • Click / dismiss callbacks attached to a specific notification.

If you only need a single in-page message (e.g. inline error under a form field), use a Label with text_color(content.danger) instead.

Install

Once at boot, after the renderer is installed:

use yororen_ui::notification::NotificationCenter;
cx.set_global(NotificationCenter::new());

Push

use yororen_ui::notification::{Notification, ToastKind};

let center = cx.global::<NotificationCenter>().clone();
let id = center.notify(
    Notification::new("Saved!")
        .title("Done")
        .kind(ToastKind::Success),
    cx,
);

center.dismiss(id, cx);   // remove it

ToastKind is Neutral | Success | Warning | Error | Info. The renderer maps each kind to status.<kind>.bg / .fg in your theme.

Notification builder

Method What it does
Notification::new(message) Constructor.
.title("Done") Optional bold title.
.kind(ToastKind::Success) Neutral (default) / Success / Warning / Error / Info.
.dismiss(DismissStrategy::After { duration_ms: 4000 }) Override the default 4-second timer.
.dismiss(DismissStrategy::Manual) Never auto-dismiss.
.sticky(true) Upgrade dismiss to Manual and mark for cross-restart persistence.
.action_label("View") Label for a click action.
.payload(json!({…})) Arbitrary JSON for the click callback.

DismissStrategy is Manual or After { duration_ms: u64 }. Default is After { duration_ms: 4000 }.

The toast host

Implement it in your app. Wrap in gpui::deferred(...).with_priority(3) so it paints above modals (priority 2) and popovers (priority 1). Call center.register_host_window(window.window_handle()) on every paint — without it, non-sticky toasts never auto-dismiss.

The shape (paraphrasing gallery_demo/src/notifications_host.rs):

use gpui::{Hsla, hsla};
use yororen_ui::notification::{Notification, NotificationCenter, ToastKind};
use yororen_ui::theme::ActiveTheme;

fn toast_card(cx: &mut Context<MyApp>, n: Notification) -> impl IntoElement {
    let kind_path = match n.kind {
        ToastKind::Success => "status.success",
        ToastKind::Warning => "status.warning",
        ToastKind::Error   => "status.error",
        ToastKind::Info    => "status.info",
        ToastKind::Neutral => "status.neutral",
    };
    let bg = cx.theme().get_color(&format!("{kind_path}.bg"))
        .unwrap_or_else(|| cx.theme().get_color("surface.raised").unwrap_or_else(default_surface));
    let fg = cx.theme().get_color(&format!("{kind_path}.fg"))
        .unwrap_or_else(|| cx.theme().get_color("content.primary").unwrap_or_else(default_text));

    div().w(px(320.)).p(px(12.)).rounded(px(6.)).bg(bg).child(n.message)
}

Paint order:

root
├── page content
├── modal scrim + panel        (deferred priority 2)
├── popover / dropdown / menu  (deferred priority 1, owned by renderer)
└── notification host          (deferred priority 3)

Sticky semantics

.sticky(true) upgrades dismiss to Manual (both in the builder and in the scheduler — dual-guarded). Use it for:

  • Error alerts that need acknowledgment.
  • Long-running-task notifications that should stay until dismissed.
  • Notifications you want to persist across process restarts — sticky items are written to the persisted snapshot and reloaded on next launch.

The scheduler is defensive: a hand-built Notification with sticky: true and dismiss: After { … } will still not auto-dismiss — sticky always wins.

Callbacks

For click / dismiss callbacks, use notify_with_callbacks:

center.notify_with_callbacks(
    Notification::new("Click to view details")
        .kind(ToastKind::Info)
        .action_label("View")
        .payload(serde_json::json!({ "id": 42 })),
    Some(Arc::new(|n, _ev, _w, cx| { /* n.payload */ })),
    Some(Arc::new(|_n, _w, cx| { /* fired on explicit dismiss */ })),
    cx,
);

Callbacks are not persisted across restarts. Re-attach them each launch by re-emitting notify_with_callbacks for each loaded sticky notification.

Configuration

center.set_max_queue_len(10);                      // default 5; oldest dropped when over
center.set_persistence(false, "my-app-key");       // disable, or change the persistence key

API summary

Method What it does
NotificationCenter::new() Constructor.
.notify(notification, cx) Push a notification; returns NotificationId.
.notify_with_callbacks(n, on_click, on_dismiss, cx) Push with callbacks.
.dismiss(id, cx) Dismiss a specific notification.
.clear(cx) Dismiss all.
.items() Snapshot the queue.
.set_max_queue_len(n) Set the queue cap.
.set_persistence(enabled, key) Toggle persistence.
.register_host_window(window) Required for auto-dismiss to fire.

NotificationId is a stable, opaque, monotonically-increasing u64 used to disambiguate stacked toasts in the gpui element tree.

See also

  • Quick start §5 — installing the global at boot.
  • Demosgallery_demo/src/notifications_host.rs is the canonical host implementation.

Clone this wiki locally