Calendrier pour Slint
KitCalendar · Slint Kit 0.3.1
Grille mensuelle pour choisir une date civile.

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 { KitCalendar,KitThemes,CalendarDay,CalendarNavigation,CivilDateView,DateRangePhase } from "@kit/kit.slint";
export {CalendarDay,CalendarNavigation,CivilDateView,DateRangePhase}
export component App inherits Window {
title:"calendar";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();
VerticalLayout {padding:24px;spacing:16px;alignment:start;
control:=KitCalendar{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();}}
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());
}
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());
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;
publish(&app, &state);
}
}
});
let weak = app.as_weak();
let shared = state.clone();
app.on_cancel(move || {
if let Some(app) = weak.upgrade() {
publish(&app, &shared.borrow());
}
});
app.run()
}
Cargo.toml et build.rs
Cargo.toml
[package]
name = "slint-kit-calendar-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
Construisez le modèle des jours avec CalendarController puis connectez sélection et navigation CalendarNavigation.
L’exemple reconstruit la grille lors de la navigation et affiche le CivilDate sélectionné. La semaine commence le lundi.
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é
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 { KitCalendar, KitTheme, KitThemes } from "@kit/kit.slint";
export component Example inherits Window {
title: "KitCalendar — exemple";
preferred-width: 360px;
preferred-height: 440px;
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: "Calendrier"; color: root.theme.text-primary; font-size: 20px * root.theme.text-scale; font-weight: 600; }
KitCalendar { previous-month-label: "Mois précédent"; next-month-label: "Mois suivant"; label: "Choisir une date"; weekdays: ["L", "M", "M", "J", "V", "S", "D"]; theme: root.theme; 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 }]; choose(day) => { root.status = "Jour demandé " + day.day; } }
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 |
|---|---|---|
min |
string |
in |
max |
string |
in |
step |
string |
in |
default-value |
string |
in |
theme |
KitTheme |
in |
control-size |
ControlSize |
in |
access |
FieldAccess |
in |
days |
[CalendarDay] |
in |
weekdays |
[string] |
in |
active-index |
int |
in |
title |
string |
in |
label |
string |
in |
previous-month-label |
string |
in |
next-month-label |
string |
in |
request-focus |
bool |
in |
has-focus |
bool |
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 |
enum ControlSize |
compact, medium, comfortable |
enum FieldAccess |
editable, read-only, disabled |
Signaux
navigate(CalendarNavigation)choose(CalendarDay)cancelled()
Fonctions
take-focus()