Sélecteur de date pour Slint
KitDatePicker · Slint Kit 0.3.1
Saisie de date avec calendrier contextuel.

Essayer le composant
Télécharger le projet complet, le décompresser et lancer cargo run --locked dans son dossier. Rust 1.92+ ; Slint Kit 0.3.0 depuis crates.io. Vous pouvez aussi copier les quatre fichiers ci-dessous.
Naviguez entre les mois ou choisissez un jour. Rust valide la date civile et régénère la grille du calendrier.
ui/app.slint
export { KitFormValidation, HtmlInputKind, NumberInputFormat, KitInputConstraints, KitValidationResult, KitValidityState } from "@kit/kit.slint";
import { KitDatePicker,KitThemes,CalendarDay,CalendarNavigation,CivilDateView,DateRangePhase } from "@kit/kit.slint";
export {CalendarDay,CalendarNavigation,CivilDateView,DateRangePhase}
export component App inherits Window {
title:"date-picker";preferred-width:480px;preferred-height:520px;max-width:100000px;max-height:100000px;background:KitThemes.light.canvas;
in property <[CalendarDay]> days;in property <int> active;in property <string> calendar-title;in property <string> accepted;
callback navigate(CalendarNavigation);callback choose(CalendarDay);callback cancel();in-out property <string> draft;in property <string> error;in property <CivilDateView> accepted-date;callback commit(string);
VerticalLayout {padding:24px;spacing:16px;alignment:start;
control:=KitDatePicker{days:root.days;weekdays:["M","T","W","T","F","S","S"];active-index:root.active;title:root.calendar-title;navigate(step)=>{root.navigate(step);}choose(day)=>{root.choose(day);}cancelled=>{root.cancel();}label:"Event date";text<=>root.draft;error:root.error;accepted-date:root.accepted-date;commit-requested(text)=>{root.commit(text);}}
Text{text:"Accepted: "+root.accepted;color:KitThemes.light.text-primary;wrap:word-wrap;}
}
}
src/main.rs
use slint::ComponentHandle;
use slint_kit::{CalendarController, CalendarMove, CivilDate, WeekStart};
use std::{cell::RefCell, rc::Rc};
mod ui {
slint::include_modules!();
}
struct State {
calendar: CalendarController,
accepted: CivilDate,
}
fn movement(step: ui::CalendarNavigation) -> CalendarMove {
match step {
ui::CalendarNavigation::PreviousDay => CalendarMove::PreviousDay,
ui::CalendarNavigation::NextDay => CalendarMove::NextDay,
ui::CalendarNavigation::PreviousWeek => CalendarMove::PreviousWeek,
ui::CalendarNavigation::NextWeek => CalendarMove::NextWeek,
ui::CalendarNavigation::WeekStart => CalendarMove::WeekStart,
ui::CalendarNavigation::WeekEnd => CalendarMove::WeekEnd,
ui::CalendarNavigation::PreviousMonth => CalendarMove::PreviousMonth,
ui::CalendarNavigation::NextMonth => CalendarMove::NextMonth,
ui::CalendarNavigation::PreviousYear => CalendarMove::PreviousYear,
ui::CalendarNavigation::NextYear => CalendarMove::NextYear,
}
}
fn publish(app: &ui::App, state: &State) {
let slots = state.calendar.grid_slots();
let days = slots
.iter()
.map(|slot| {
slot.map_or_else(ui::CalendarDay::default, |cell| ui::CalendarDay {
year: cell.date.year(),
month: cell.date.month() as i32,
day: cell.date.day() as i32,
label: cell.date.day().to_string().into(),
accessible_label: cell.date.iso_string().into(),
enabled: cell.enabled,
selected: cell.selected,
in_range: false,
today: false,
})
})
.collect::<Vec<_>>();
app.set_days(slint::ModelRc::new(slint::VecModel::from(days)));
app.set_active(
slots
.iter()
.position(|s| s.is_some_and(|cell| cell.focused))
.unwrap_or(0) as i32,
);
let month = state.calendar.displayed();
app.set_calendar_title(format!("{:04}-{:02}", month.year(), month.month()).into());
app.set_accepted(state.accepted.iso_string().into());
app.set_accepted_date(ui::CivilDateView {
year: state.accepted.year(),
month: state.accepted.month() as i32,
day: state.accepted.day() as i32,
});
}
fn main() -> Result<(), slint::PlatformError> {
let app = ui::App::new()?;
slint_kit::init_form_validation!(ui, app);
let date = CivilDate::new(2026, 10, 17).unwrap();
let state = Rc::new(RefCell::new(State {
calendar: CalendarController::new(Some(date), date.year_month(), None, WeekStart::Monday),
accepted: date,
}));
publish(&app, &state.borrow());
app.set_draft(date.iso_string().into());
let weak = app.as_weak();
let shared = state.clone();
app.on_navigate(move |step| {
if let Some(app) = weak.upgrade() {
let mut state = shared.borrow_mut();
if state.calendar.navigate(movement(step)).is_ok() {
publish(&app, &state);
}
}
});
let weak = app.as_weak();
let shared = state.clone();
app.on_choose(move |day| {
if !day.enabled {
return;
}
if let (Some(app), Ok(date)) = (
weak.upgrade(),
CivilDate::new(day.year, day.month as u32, day.day as u32),
) {
let mut state = shared.borrow_mut();
if state.calendar.choose(date).is_ok() {
state.accepted = date;
app.set_draft(date.iso_string().into());
app.set_error("".into());
publish(&app, &state);
}
}
});
let weak = app.as_weak();
let shared = state.clone();
app.on_cancel(move || {
if let Some(app) = weak.upgrade() {
let state = shared.borrow();
app.set_draft(state.accepted.iso_string().into());
app.set_error("".into());
}
});
let weak = app.as_weak();
let shared = state.clone();
app.on_commit(move |text| {
if let Some(app) = weak.upgrade() {
match slint_kit::CivilDate::parse_iso(&text) {
slint_kit::DraftValue::Parsed(date) => {
let mut state = shared.borrow_mut();
if state.calendar.choose(date).is_ok() {
state.accepted = date;
app.set_draft(date.iso_string().into());
app.set_error("".into());
publish(&app, &state);
}
}
_ => app.set_error("Use a valid YYYY-MM-DD date".into()),
}
}
});
app.run()
}
Cargo.toml et build.rs
Cargo.toml
[package]
name = "slint-kit-date-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");
}
Fichiers : Cargo.toml · build.rs · ui/app.slint · src/main.rs
Liez le texte de date, fournissez les jours de CalendarController et connectez validation texte et sélection du calendrier.
L’exemple analyse AAAA-MM-JJ. Choisir un jour accepte cette date ; Annuler restaure la date acceptée.
Choisir entre une option intégrée et une composition personnalisée
Options partagées
label— propriété intégrée ; exemple de composition personnalisédescription— propriété intégrée ; exemple de composition personnaliséerror— propriété intégrée ; exemple de composition personnalisé
Composition visuelle
Le fichier ci-dessous produit la capture. Pour les composants pilotés par un contrôleur, il montre l’état affiché sans fournir le branchement Rust. Créer un projet.
Afficher le fichier de la capture

// Public label overrides localize this fixture; toolkit/host defaults keep their language.
// SPDX-License-Identifier: GPL-3.0-or-later
// Display projections are fixtures. Validate accepted domain values in Rust.
import { KitDatePicker, KitTheme, KitThemes } from "@kit/kit.slint";
export component Example inherits Window {
title: "KitDatePicker — exemple";
preferred-width: 420px;
preferred-height: 180px;
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: "Sélecteur de date"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
KitDatePicker { placeholder: "AAAA-MM-JJ"; description: "Date civile, sans fuseau horaire"; calendar-button-label: "Calendrier"; calendar-accessible-name: "Ouvrir le calendrier de l’événement"; previous-month-label: "Mois précédent"; next-month-label: "Mois suivant"; theme: root.theme; label: "Date de l’événement"; text: "2026-10-17"; accepted-date: { year: 2026, month: 10, day: 17 }; title: "Octobre 2026"; days: [{ year: 2026, month: 9, day: 28, label: "28", accessible-label: "28 septembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 9, day: 29, label: "29", accessible-label: "29 septembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 9, day: 30, label: "30", accessible-label: "30 septembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 1, label: "1", accessible-label: "1er octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 2, label: "2", accessible-label: "2 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 3, label: "3", accessible-label: "3 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 4, label: "4", accessible-label: "4 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 5, label: "5", accessible-label: "5 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 6, label: "6", accessible-label: "6 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 7, label: "7", accessible-label: "7 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 8, label: "8", accessible-label: "8 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 9, label: "9", accessible-label: "9 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 10, label: "10", accessible-label: "10 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 11, label: "11", accessible-label: "11 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 12, label: "12", accessible-label: "12 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 13, label: "13", accessible-label: "13 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 14, label: "14", accessible-label: "14 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 15, label: "15", accessible-label: "15 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 16, label: "16", accessible-label: "16 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 17, label: "17", accessible-label: "17 octobre 2026", enabled: true, selected: true, today: true, in-range: false }, { year: 2026, month: 10, day: 18, label: "18", accessible-label: "18 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 19, label: "19", accessible-label: "19 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 20, label: "20", accessible-label: "20 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 21, label: "21", accessible-label: "21 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 22, label: "22", accessible-label: "22 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 23, label: "23", accessible-label: "23 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 24, label: "24", accessible-label: "24 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 25, label: "25", accessible-label: "25 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 26, label: "26", accessible-label: "26 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 27, label: "27", accessible-label: "27 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 28, label: "28", accessible-label: "28 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 29, label: "29", accessible-label: "29 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 30, label: "30", accessible-label: "30 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 10, day: 31, label: "31", accessible-label: "31 octobre 2026", enabled: true, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 1, label: "1", accessible-label: "1er novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 2, label: "2", accessible-label: "2 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 3, label: "3", accessible-label: "3 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 4, label: "4", accessible-label: "4 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 5, label: "5", accessible-label: "5 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 6, label: "6", accessible-label: "6 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 7, label: "7", accessible-label: "7 novembre 2026", enabled: false, selected: false, today: false, in-range: false }, { year: 2026, month: 11, day: 8, label: "8", accessible-label: "8 novembre 2026", enabled: false, selected: false, today: false, in-range: false }]; weekdays: ["L", "M", "M", "J", "V", "S", "D"]; }
if root.status != "": Text { text: root.status; color: root.theme.text-secondary; font-size: 12px * root.theme.text-scale; wrap: word-wrap; }
}
}
API publique
Hérite de Rectangle.
Propriétés
| Nom | Type | Sens |
|---|---|---|
required |
bool |
in |
min |
string |
in |
max |
string |
in |
step |
string |
in |
default-value |
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-date |
CivilDateView |
in |
days |
[CalendarDay] |
in |
weekdays |
[string] |
in |
active-index |
int |
in |
title |
string |
in |
label |
string |
in |
placeholder |
string |
in |
description |
string |
in |
calendar-button-label |
string |
in |
calendar-accessible-name |
string |
in |
previous-month-label |
string |
in |
next-month-label |
string |
in |
error |
string |
in |
text |
string |
in-out |
open |
bool |
in-out |
Types et variantes
| Type | Déclaration |
|---|---|
struct CalendarDay |
year: int, month: int, day: int, label: string, accessible-label: string, enabled: bool, selected: bool, in-range: bool, today: bool, |
enum CalendarNavigation |
previous-day, next-day, previous-week, next-week, week-start, week-end, previous-month, next-month, previous-year, next-year |
struct CivilDateView |
year: int, month: int, day: int |
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, |
Signaux
invalid()edited(string)commit-requested(string)navigate(CalendarNavigation)choose(CalendarDay)cancelled()opened()
Fonctions
check-validity() → boolreport-validity() → boolset-custom-validity(message: string)reset-validation()take-focus()