Slint Kit Documentation

Times and zones

Time Picker for Slint

KitTimePicker · Slint Kit 0.3.0

Local-time input with hour and minute adjustment controls.

Local-time input with hour and minute adjustment controls.
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.

Type a 24-hour local time, or use the hour/minute buttons. Rust validates the draft before adjusting and accepting it; no time zone is inferred.

ui/app.slint

export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
import { KitTimePicker, KitButton, KitThemes, ValidationPresentation, TimeSegment, TimeAdjustment } from "@kit/kit.slint";
export { TimeSegment, TimeAdjustment }
export component App inherits Window {
    title: "Local time"; preferred-width: 520px; preferred-height: 400px;
    max-width: 100000px; max-height: 100000px; background: KitThemes.light.canvas;
    in-out property <string> draft; in property <string> error; in property <string> accepted;
    callback edited(string); callback commit(); callback blurred(); callback cancel();
    callback adjust(TimeSegment,TimeAdjustment);
    VerticalLayout {
        padding: 24px; spacing: 16px; alignment: start;
        control := KitTimePicker {
            required: true;
            label: "Local time"; text <=> root.draft; error: root.error;
            
            
            edited(value) => { root.edited(value); }
            commit-requested(value) => { root.commit(); }
            cancelled => { root.cancel(); }
            
            adjust(segment,direction)=>{root.adjust(segment,direction);}
        }
        KitButton { text: "Accept"; activated => { root.commit(); } }
        Text { text: "Accepted: " + root.accepted; color: KitThemes.light.text-primary; wrap: word-wrap; }
    }
}

src/main.rs

use slint::ComponentHandle;
use slint_kit::*;
slint::include_modules!();
type Value = LocalTime;
fn parse(text: &str) -> DraftValue<Value> {
    LocalTime::parse(text, slint_kit::TimePrecision::Minute)
}
fn format(value: &Value) -> String {
    value.format(
        slint_kit::TimePrecision::Minute,
        slint_kit::ClockFormat::TwentyFourHour,
    )
}
fn main() -> Result<(), slint::PlatformError> {
    let app = App::new()?;
    slint_kit::init_form_validation!(self, app);
    let initial = LocalTime::new(9, 30, 0).unwrap();
    app.set_accepted(format(&initial).into());
    let weak = app.as_weak();
    let field = FieldBinding::<Value, ()>::new(
        Some(initial),
        Ownership::Local,
        CommitPolicy::OnBlur,
        AbsencePolicy::Required,
        parse,
        format,
        |_| String::new(),
        move |view| {
            if let Some(app) = weak.upgrade() {
                app.set_draft(view.draft.into());
                app.set_error(view.error.into());
            }
        },
    );
    field.set_html_constraints(slint_kit::HtmlConstraints {
        kind: slint_kit::HtmlInputKind::Time, required: true,
        step: Some("60".into()),
        ..Default::default()
    });
    let weak = app.as_weak();
    field.on_intent(move |intent| {
        if let Some(app) = weak.upgrade() {
            if let FieldIntent::Commit(value) = intent {
                app.set_accepted(format(&value).into());
            }
        }
    });
    let binding = field.clone();
    app.on_edited(move |text| {
        let _ = binding.edited(&text);
    });
    let binding = field.clone();
    app.on_commit(move || {
        let _ = binding.commit(CommitTrigger::Submit);
    });
    let binding = field.clone();
    app.on_blurred(move || {
        let _ = binding.commit(CommitTrigger::Blur);
    });
    let binding = field.clone();
    app.on_cancel(move || binding.cancel());
    let binding = field.clone();
    app.on_adjust(move |segment, direction| {
        let DraftValue::Parsed(value) = parse(&binding.snapshot().draft) else {
            return;
        };
        let delta = match segment {
            TimeSegment::Hour => 3600,
            TimeSegment::Minute => 60,
            TimeSegment::Second => 1,
        } * if direction == TimeAdjustment::Increment {
            1
        } else {
            -1
        };
        let seconds = (value.hour() as i32 * 3600
            + value.minute() as i32 * 60
            + value.second() as i32
            + delta)
            .rem_euclid(86400) as u32;
        let next = LocalTime::new(seconds / 3600, seconds / 60 % 60, seconds % 60).unwrap();
        let _ = binding.edited(&format(&next));
        let _ = binding.commit(CommitTrigger::Explicit);
    });
    app.run()
}

Cargo.toml and build.rs

Cargo.toml

[package]
name = "slint-kit-time-picker-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

Connect text commits to LocalTime and adjustment requests to hour/minute arithmetic. The example uses 24-hour HH:MM.

Adjustments wrap within one day. An invalid text draft must be corrected before a step can be accepted.

Choose between built-in options and custom composition

Shared options

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
Local-time input with hour and minute adjustment controls.
Native screenshot of the same example, rendered with Skia.
// SPDX-License-Identifier: GPL-3.0-or-later
// Display projections are fixtures. Validate accepted domain values in Rust.
import { KitTheme, KitThemes, KitTimePicker } from "@kit/kit.slint";

export component Example inherits Window {
    title: "KitTimePicker example";
    preferred-width: 620px;
    preferred-height: 300px;
    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: "Time Picker"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
        KitTimePicker { theme: root.theme; label: "Start time"; text: "09:30"; accepted-time: { hour: 9, minute: 30, second: 0 }; }
        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 Rectangle.

Properties

Name Type Direction
required bool in
min string in
max string in
step string in
custom-validity string in-out
validity KitValidityState out
will-validate bool out
validation-message string out
theme KitTheme in
control-size ControlSize in
access FieldAccess in
accepted-time LocalTimeView in
clock-format ClockFormat in
precision TimePrecision in
label string in
error string in
text string in-out

Types and variants

Type Declaration
enum ClockFormat twenty-four-hour, twelve-hour
enum ControlSize compact, medium, comfortable
enum FieldAccess editable, read-only, disabled
struct KitValidityState valid: bool, value-missing: bool, type-mismatch: bool, pattern-mismatch: bool, too-long: bool, too-short: bool, range-underflow: bool, range-overflow: bool, step-mismatch: bool, bad-input: bool, custom-error: bool,
struct LocalTimeView hour: int, minute: int, second: int
enum TimeAdjustment increment, decrement
enum TimePrecision minute, second
enum TimeSegment hour, minute, second

Callbacks

Functions

See also