Slint Kit Documentation

Dialogs

Dialog Surface for Slint

KitDialogSurface · Slint Kit 0.3.0

Modal host managing the scrim, close requests and focus traversal.

Modal host managing the scrim, close requests and focus traversal.
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.

Open project details, archive the project, or dismiss with Escape. Closing restores the opener’s focus.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitDialogSurface, KitDialogPanel, KitButton, DialogTabPolicy, CloseReason, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
    title: "KitDialogSurface example";
    preferred-width: 520px;
    preferred-height: 400px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in property <bool> showing;
    in property <string> result: "Project active";
    callback open();
    callback close(CloseReason);
    callback archive();
    public function focus-opener() { opener.take-focus(); }
    public function focus-dialog() { archive-button.take-focus(); }

    VerticalLayout {
        padding: 24px;
        spacing: 16px;
        alignment: start;
        opener := KitButton { theme: root.theme; text: "Open project details"; disabled: root.showing; activated => { root.open(); } }
        Text { text: root.result; color: root.theme.text-primary; }

    }
    modal := KitDialogSurface {
        theme: root.theme; width: root.width; height: root.height;
        state: { open: root.showing, topmost: root.showing, busy: false };
        tab-policy: DialogTabPolicy.native;
        dismiss(reason) => { root.close(reason); }
        KitDialogPanel {
            theme: root.theme; accessible-name: "Archive project";
            width: min(420px, parent.width - 40px); height: 180px;
            x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2;
            VerticalLayout { padding: 20px; spacing: 16px; alignment: start;
            Text { text: "Archive this project?"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; wrap: word-wrap; }
            archive-button := KitButton { theme: root.theme; text: "Archive project"; activated => { root.archive(); } }
            }
        }
    }
}

src/main.rs

// SPDX-License-Identifier: GPL-3.0-or-later
use slint::ComponentHandle;
use slint_kit::{OverlayStack, OverlaySpec, OverlayId, DismissReason, CloseResult};
mod ui { slint::include_modules!(); }
fn main() -> Result<(), slint::PlatformError> {
    let app = ui::App::new()?;
    slint_kit::init_form_validation!(ui, app);
    let overlays = std::rc::Rc::new(std::cell::RefCell::new(OverlayStack::new()));
    let weak = app.as_weak();
    let stack = overlays.clone();
    app.on_open(move || {
        if let Some(app) = weak.upgrade() {
            if stack.borrow().is_empty() {
                stack.borrow_mut().push(OverlaySpec::new(OverlayId(1), None, true)).expect("single modal");
                app.set_showing(true);
                app.invoke_focus_dialog();
            }
        }
    });
    let weak = app.as_weak();
    let stack = overlays.clone();
    app.on_close(move |reason| {
        let reason = match reason {
            ui::CloseReason::Escape => DismissReason::Escape,
            ui::CloseReason::OutsidePress => DismissReason::OutsidePress,
            ui::CloseReason::SystemBack => DismissReason::SystemBack,
            ui::CloseReason::TriggerToggle => DismissReason::TriggerToggle,
            ui::CloseReason::Programmatic => DismissReason::Programmatic,
            ui::CloseReason::HostClosed => DismissReason::HostClosed,
        };
        let closed = matches!(stack.borrow_mut().request_close(reason), CloseResult::Closed(_));
        if closed { if let Some(app) = weak.upgrade() { app.set_showing(false); app.invoke_focus_opener(); } }
    });
    let weak = app.as_weak();
    app.on_archive(move || { if let Some(app) = weak.upgrade() { app.set_result("Project archived".into()); app.invoke_close(ui::CloseReason::Programmatic); } });
    app.run()
}

Cargo.toml and build.rs

Cargo.toml

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

[dependencies]
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

Render it last in its window and supply DialogState from OverlayStack. With tab-policy: DialogTabPolicy.host (the default), connect focus-next(bool) to the registry of actual targets. With .native, let Slint traverse mounted controls and make every background target ineligible while the modal is open, using FieldAccess.disabled, disabled, or interactive: false as appropriate.

Only the topmost modal handles Escape and system Back (Key.Back), requesting CloseReason.escape and .system-back respectively. busy consumes these keys without requesting closure. In host mode it captures Tab/Shift+Tab; in native mode it leaves them to Slint traversal. Native policy does not automatically disable the background. A backdrop click requests CloseReason.outside-press when dismiss-on-backdrop is enabled.

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
Modal host managing the scrim, close requests and focus traversal.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
import { DialogTabPolicy, KitButton, KitDialogPanel, KitDialogSurface, KitTheme, KitThemes } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitDialogSurface example";
    preferred-width: 560px;
    preferred-height: 360px;
    max-width: 100000px;
    max-height: 100000px;
    background: root.theme.canvas;
    in-out property <KitTheme> theme: KitThemes.light;
    in-out property <string> status;
    KitDialogSurface { tab-policy: DialogTabPolicy.native; theme: root.theme; width: parent.width; height: parent.height; state: { open: true, topmost: true, busy: false }; KitDialogPanel { theme: root.theme; width: 420px; x: (parent.width - self.width) / 2; y: (parent.height - self.height) / 2; height: dialog-content.preferred-height; horizontal-stretch: 0; vertical-stretch: 0; accessible-name: "Archive project"; dialog-content := VerticalLayout { padding: 24px; spacing: 16px; alignment: start; Text { text: "Archive this project?"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; wrap: word-wrap; vertical-stretch: 0; }Text { text: "You can restore it later from the archive."; color: root.theme.text-primary; font-size: 14px * root.theme.text-scale; wrap: word-wrap; vertical-stretch: 0; }KitButton { theme: root.theme; text: "Archive project"; activated => { root.status = "Action requested"; } }if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; vertical-stretch: 0; } } } }
}

Download this example

Public API

Inherits Rectangle.

Properties

Name Type Direction
theme KitTheme in
state DialogState in
dismiss-on-backdrop bool in
tab-policy DialogTabPolicy in

Types and variants

Type Declaration
enum CloseReason escape, outside-press, system-back, trigger-toggle, programmatic, host-closed
struct DialogState open: bool, topmost: bool, busy: bool
enum DialogTabPolicy host, native

Callbacks

See also