Select Surface for Slint
KitSelectSurface · Slint Kit 0.3.0
Popup surface combining native search, keyboard handling and active-option visibility.

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.
Choose Activity with the pointer, or navigate with the keyboard and press Enter. Rust accepts its key.
ui/app.slint
export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
// SPDX-License-Identifier: GPL-3.0-or-later
import { KitSelectSurface, KitOption, OptionRow, SelectState, NavigationStep, CloseReason, SelectionAvailability, KitTheme, KitThemes } from "@kit/kit.slint";
export component App inherits Window {
title: "KitSelectSurface example";
preferred-width: 520px;
preferred-height: 360px;
max-width: 100000px;
max-height: 100000px;
background: root.theme.canvas;
in-out property <KitTheme> theme: KitThemes.light;
in property <[OptionRow]> rows;
in property <SelectState> select-state;
in property <string> selected-label: "Overview";
in-out property <string> query;
callback select-open();
callback select-navigate(NavigationStep);
callback select-activate(int, int);
callback select-dismiss(CloseReason);
callback select-query(string);
callback select-typeahead(string);
VerticalLayout {
padding: 24px;
spacing: 16px;
alignment: start;
control := KitSelectSurface {
state: root.select-state;
theme: root.theme; height: 240px; searchable: true; query <=> root.query;
search-label: "Find destination"; focus-search-on-open: true;
query-edited(text) => { root.select-query(text); }
navigate(step) => { root.select-navigate(step); }
activate(token, generation) => { root.select-activate(token, generation); }
dismiss(reason) => { root.select-dismiss(reason); }
typeahead(text) => { root.select-typeahead(text); }
tab-navigation(reverse) => { return reject; }
VerticalLayout {
alignment: start;
for row in root.rows: KitOption {
theme: root.theme; option-data: row; height: 48px;
active: root.select-state.active.present && root.select-state.active.token == row.token;
selected: root.select-state.selected.present && root.select-state.selected.token == row.token;
activate(token, generation) => { root.select-activate(token, generation); }
Text { x: 0px; y: 0px; width: parent.width; height: parent.height; horizontal-alignment: left; font-size: 14px * root.theme.text-scale; overflow: elide; text: row.label; color: root.theme.text-primary; vertical-alignment: center; }
}
}
}
result := Text { text: "Selected: " + root.selected-label; color: root.theme.text-primary; }
}
}
src/main.rs
// SPDX-License-Identifier: GPL-3.0-or-later
use slint::ComponentHandle;
use slint_kit::{Ownership, SelectOption, SelectionIntent};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
enum Destination { Overview, Activity, Settings }
impl Destination {
fn label(self) -> &'static str {
match self { Self::Overview => "Overview", Self::Activity => "Activity", Self::Settings => "Settings" }
}
}
mod ui { slint::include_modules!(); }
fn main() -> Result<(), slint::PlatformError> {
let app = ui::App::new()?;
slint_kit::init_form_validation!(ui, app);
let selection = slint_kit::bind_select!(ui, app, vec![
SelectOption::new(Destination::Overview, "Overview"),
SelectOption::new(Destination::Activity, "Activity"),
SelectOption::new(Destination::Settings, "Settings"),
], Some(Destination::Overview), Ownership::Local);
let weak = app.as_weak();
selection.on_intent(move |intent| {
if let Some(app) = weak.upgrade() {
app.set_selected_label(match intent {
SelectionIntent::Commit(key) => key.label(),
SelectionIntent::Clear => "No selection",
}.into());
}
});
selection.open();
let persistent = selection.clone();
app.on_select_activate(move |token, generation| {
persistent.activate(token, generation);
persistent.open();
});
app.run()
}
Cargo.toml and build.rs
Cargo.toml
[package]
name = "slint-kit-select-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
Place a virtualized list of KitOption children and bind scroll-y to that list’s scroll position. Supply its visible height in viewport-height and a matching row-height.
searchable and focus-search-on-open choose presentation and initial focus. ensure-active-visible() follows state.active-index, while query-edited passes the search draft without deciding selection.
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

// SPDX-License-Identifier: GPL-3.0-or-later
import { KitOption, KitSelectSurface, KitTheme, KitThemes, SelectionAvailability } from "@kit/kit.slint";
export component Example inherits Window {
title: "KitSelectSurface example";
preferred-width: 420px;
preferred-height: 320px;
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: "Select Surface"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
KitSelectSurface { theme: root.theme; height: 220px; state: { open: true, generation: 1, active-index: 0, active: { present: true, token: 11 }, selected: { present: true, token: 11 }, selection-availability: SelectionAvailability.available }; VerticalLayout { for row in [{ token: 11, generation: 1, label: "Overview", description: "Project summary", disabled: false }, { token: 23, generation: 1, label: "Activity", description: "Recent changes", disabled: false }, { token: 38, generation: 1, label: "Settings", description: "Project preferences", disabled: false }]: KitOption { theme: root.theme; option-data: row; height: 48px; Text { text: row.label; color: root.theme.text-primary; vertical-alignment: center; font-size: 14px * root.theme.text-scale; } } } }
if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
}
}
Public API
Inherits Rectangle.
Properties
| Name | Type | Direction |
|---|---|---|
theme |
KitTheme |
in |
state |
SelectState |
in |
row-height |
length |
in |
viewport-height |
length |
in |
searchable |
bool |
in |
focus-search-on-open |
bool |
in |
empty |
bool |
in |
empty-label |
string |
in |
search-label |
string |
in |
query |
string |
in-out |
scroll-y |
length |
in-out |
active-scroll-y |
length |
out |
Types and variants
| Type | Declaration |
|---|---|
enum CloseReason |
escape, outside-press, system-back, trigger-toggle, programmatic, host-closed |
enum NavigationStep |
next, previous, first, last, page-next, page-previous |
enum SelectionAvailability |
absent, available, unavailable |
struct SelectionRef |
present: bool, token: int |
struct SelectState |
selection-availability: SelectionAvailability, selected: SelectionRef, active: SelectionRef, open: bool, active-index: int, generation: int |
Callbacks
navigate(NavigationStep)activate(int, int)dismiss(CloseReason)typeahead(string)tab-navigation(bool) → EventResultquery-edited(string)
Functions
take-focus()ensure-active-visible()