Slint Kit Documentation

Feedback and progress

Toast Host for Slint

KitToastHost · Slint Kit 0.3.0

Transient notification stack supplied by a Rust-owned queue.

Transient notification stack supplied by a Rust-owned queue.
Native app built from the example below.

Try the component

Download the complete project, extract it and run cargo run --locked in its folder. Rust 1.92+; Slint Kit 0.3.0 from crates.io. You can also copy the four files below.

Show an archive notice, Undo its action, or dismiss it. This example keeps notices until an explicit action.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitToastHost, KitButton, ToastEntry, ToastInteraction, AlertSeverity, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
    title: "KitToastHost example";
    preferred-width: 520px;
    preferred-height: 420px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in property <[ToastEntry]> entries;
    in property <string> result: "Project archived";
    callback undo(int);
    callback dismiss-toast(int);
    callback interaction(int, ToastInteraction, bool);
    callback add();
    VerticalLayout {
        padding: 24px;
        spacing: 16px;
        alignment: start;
        KitButton { theme: root.theme; text: "Show archive notice"; activated => { root.add(); } }
        control := KitToastHost { theme: root.theme; entries: root.entries; action(token) => { root.undo(token); } dismiss(token) => { root.dismiss-toast(token); } interaction(token, source, engaged) => { root.interaction(token, source, engaged); } }
        result := Text { text: root.result; color: root.theme.text-primary; }
    }

}

src/main.rs

// SPDX-License-Identifier: GPL-3.0-or-later
use web_time::Instant;
use slint::{ComponentHandle, ModelRc, VecModel};
use slint_kit::{ToastQueue, ToastLifetime, ToastPause};
use std::{cell::RefCell, rc::Rc, num::NonZeroUsize};
mod ui { slint::include_modules!(); }
fn publish(app: &ui::App, queue: &ToastQueue<()>) {
    let entries = queue.entries().map(|entry| ui::ToastEntry { token: i32::try_from(entry.id.get()).expect("view token capacity"), title: "Project archived".into(), message: "Your project can be restored.".into(), action_label: "Undo".into(), severity: ui::AlertSeverity::Success, dismissible: true }).collect::<Vec<_>>();
    app.set_entries(ModelRc::new(VecModel::from(entries)));
}
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let epoch = Instant::now();
    let queue = Rc::new(RefCell::new(ToastQueue::new(NonZeroUsize::new(3).unwrap())));
    queue.borrow_mut().push((), ToastLifetime::Persistent, epoch.elapsed()).expect("toast identity");
    publish(&app, &queue.borrow());
    let state = queue.clone(); let weak = app.as_weak();
    app.on_add(move || { state.borrow_mut().push((), ToastLifetime::Persistent, epoch.elapsed()).expect("toast identity"); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Project archived".into()); } });
    let state = queue.clone(); let weak = app.as_weak();
    app.on_undo(move |token| {
        let id = state.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { state.borrow_mut().remove(id); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Archive undone".into()); } }
    });
    let state = queue.clone(); let weak = app.as_weak();
    app.on_dismiss_toast(move |token| {
        let id = state.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { state.borrow_mut().remove(id); if let Some(app) = weak.upgrade() { publish(&app, &state.borrow()); app.set_result("Toast dismissed".into()); } }
    });
    app.on_interaction(move |token, source, engaged| {
        let id = queue.borrow().entries().find(|entry| i32::try_from(entry.id.get()).ok() == Some(token)).map(|entry| entry.id);
        if let Some(id) = id { queue.borrow_mut().set_paused(id, match source { ui::ToastInteraction::Pointer => ToastPause::Pointer, ui::ToastInteraction::Focus => ToastPause::Focus }, engaged, epoch.elapsed()); }
    });
    app.run()
}

Cargo.toml and build.rs

Cargo.toml

[package]
name = "slint-kit-toast-host-example"
version = "0.1.0"
edition = "2024"
rust-version = "1.92"
license = "GPL-3.0-or-later"
publish = false

[dependencies]
web-time = "=1.1.0"
slint-kit = "=0.3.0"
slint = { version = "=1.18.1", default-features = false, features = ["std", "compat-1-18", "backend-winit", "renderer-skia", "accessibility"] }

[build-dependencies]
slint-kit-build = "=0.3.0"

[workspace]

build.rs

fn main() { slint_kit_build::compile("ui/app.slint").expect("compile component example"); }

Files: Cargo.toml · build.rs · ui/app.slint · src/main.rs

Project ToastQueue entries through entries and forward action, dismissal and interaction callbacks. Place the stack in a window region appropriate for your application.

Each entry is rendered by KitToast without a per-delegate timer. The owner decides lifetime, ordering and removal while preserving remaining time after a pause.

Visual composition

The file below produces the screenshot. For controller-driven components, it shows the displayed state without supplying the Rust wiring. Create a project.

Show the screenshot source
Transient notification stack supplied by a Rust-owned queue.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
import { AlertSeverity, KitTheme, KitThemes, KitToastHost } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitToastHost example";
    preferred-width: 480px;
    preferred-height: 390px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in-out property <string> status;
    VerticalLayout {
        padding: 24px; spacing: 16px; alignment: start;
        Text { text: "Toast Host"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitToastHost { theme: root.theme; entries: [{ token: 11, title: "Project archived", message: "Your notes are safely stored.", action-label: "Undo", severity: AlertSeverity.success, dismissible: true }, { token: 23, title: "Invite sent", message: "Jamie can now join the workspace.", action-label: "", severity: AlertSeverity.information, dismissible: true }]; }
        if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
    }
}

Download this example

Public API

Inherits VerticalLayout.

Properties

Name Type Direction
theme KitTheme in
entries [ToastEntry] in

Types and variants

Type Declaration
enum AlertSeverity information, success, warning, error
struct ToastEntry token: int, title: string, message: string, action-label: string, severity: AlertSeverity, dismissible: bool
enum ToastInteraction pointer, focus

Callbacks

See also