Suivez une image rendue, de l'idée jusqu'aux pixels
Vous savez déjà construire une petite application. Soulevons maintenant le capot en douceur pour voir comment Ailloli UI concentre le travail au bon endroit.
À la fin, vous saurez ce qui est reconstruit, recalculé ou simplement repeint, et à quelle couche appartient chaque API publique.
01
Un arbre qui se souvient
Ailloli UI réconcilie chaque nouvelle vue déclarative avec un arbre d'éléments retenu. Des identités stables permettent au runtime de garder la mise en page, les entrées et les relations réactives attachées aux bons éléments.
Identités retenues
Le runtime conserve l'identité des éléments au lieu de traiter chaque frame comme un dessin sans lien avec le précédent.
Abonnements précis
Une lecture de State pendant Build, Layout ou Paint abonne exactement le consommateur actif.
Lectures conditionnelles
Seule la branche terminée avec succès reste observée. Les branches inutilisées cessent donc de demander du travail.
02
Trois niveaux, une requête la plus forte
Les requêtes sont regroupées avant la prochaine frame. Si Paint et Build sont demandés ensemble, Build l'emporte, car il inclut déjà la mise en page et la peinture.
Paint
Réutiliser l'arbre réconcilié et la mise en page actuelle, puis produire une nouvelle scène.
Layout
Recalculer la géométrie, puis peindre avec les nouvelles positions et dimensions.
Build
Reconstruire le composant propriétaire, le réconcilier, calculer sa mise en page, puis le peindre.
Un raccourci mental utile
Pensez Paint < Layout < Build. Plusieurs requêtes se réduisent à la plus forte, tandis que les racines exactes restent internes au runtime.
03
State bouge, Memo dérive
State possède une valeur d'interface mutable. Memo possède un calcul paresseux. Ensemble, ils rendent les libellés dérivés agréables sans masquer leur coût.
Paresseux, sans cache
Memo exécute sa fonction de calcul à chaque lecture. Utilisez-le pour dériver facilement une valeur, pas comme cache de calcul général.
Réactif par la lecture
Un memo dérivé de State observe ce State lorsqu'il est lu dans un callback retenu.
Local au thread UI
State et Memo utilisent Rc en interne. Ils ne sont ni Send ni Sync et restent sur leur thread UI propriétaire.
Mutations explicites
State::set et State::update notifient les consommateurs. Affecter une valeur égale invalide tout de même en beta.2.
Deux fenêtres, une valeur réactive
Lisez l'application vérifiée comme trois mécanismes séparés : une action différée visible, une valeur dérivée et deux fenêtres de démarrage.
Construisez par petites étapes
Chaque bloc de code change une seule idée de l'application de référence vérifiée.
Mécanisme 1 : une action différée visible
AddOne change le score et le statut, puis DispatchAfter émet Celebrate pour que le statut revienne à Ready.
fn update(
state: &mut DashboardState,
_services: &mut (),
action: DashboardAction,
) -> Commands<DashboardAction> {
match action {
DashboardAction::AddOne => {
state.count.update(|count| *count += 1);
state.status.set("Saved".to_owned());
Commands::dispatch_after(DashboardAction::Celebrate, Duration::from_millis(250))
}
DashboardAction::Celebrate => {
state.status.set("Ready".to_owned());
Commands::none()
}
}
}Mécanisme 2 : une valeur dérivée
map recalcule le résumé lorsque count change. Memo est paresseux et réactif, mais ce n'est pas un calcul mis en cache.
let state = DashboardState {
count: State::new(1),
status: State::new("Ready".to_owned()),
};
let main_state = state.clone();
let doubled = state
.count
.map(|count| format!("Two windows see {} points", count * 2));Mécanisme 3 : deux fenêtres au démarrage
Windows préserve l'ordre de déclaration. main est un alias lisible du premier push, pas une fenêtre spéciale du runtime.
let windows = Windows::new()
.main(
Window::<DashboardAction>::new("main")
.title("Retained dashboard")
.size(800.0, 600.0)
.ailloli_ui_chrome()
.content(move || {
Column::new()
.padding(24.0)
.gap(16.0)
.child(
Text::new(
main_state
.count
.to_text_with(|count| format!("Score: {count}")),
)
.size(28.0),
)
.child(Text::new(main_state.status.clone()))
.child(Button::with_label("Add one").on_click(DashboardAction::AddOne))
}),
)
.push(
Window::<DashboardAction>::new("summary")
.title("Live summary")
.size(420.0, 240.0)
.content(move || Text::new(doubled.clone()).size(22.0)),
);Vérifié avec cargo +1.88.0 check sur la source beta.2.
04
Les commandes quittent le reducer dans l'ordre
Un gestionnaire update renvoie un lot Commands ordonné. L'hôte exécute chaque commande publique dans son ordre d'insertion.
Quit
Demander à la boucle d'événements native de se terminer.
Redraw
Demander un nouveau rendu pour toutes les fenêtres déclarées.
Dispatch
Mettre une autre action typée en file pour le prochain cycle de traitement.
DispatchAfter
Mettre une action typée en file après un délai relatif, y compris avec un délai nul.
Gardez une frontière honnête
Beta.2 ne possède pas de commande générique pour lancer une tâche asynchrone. Pour des actions inter-threads bornées, utilisez RuntimeInbox et gardez la propriété des entrées-sorties hors du reducer UI.
RuntimeInbox franchit la frontière des threads
Une RuntimeInbox bornée permet aux threads de travail de proposer des actions ou des redessins au thread d'interface. Une file pleine ou fermée produit une erreur explicite, et chaque callback de l'hôte traite au maximum 256 messages.
05
Les fenêtres multiples se déclarent au démarrage
Windows est une collection ordonnée de définitions Window. Chaque fenêtre reçoit sa propre surface native tout en partageant l'état applicatif et le type d'action.
Déclarées avant run
Utilisez Windows::new, main et push, puis transmettez la collection à App::windows.
Identifiants logiques uniques
Le builder conserve les doublons, puis les refuse avant la création de toute fenêtre native.
Limite actuelle de la beta
Aucune Command publique n'ouvre ou ne ferme dynamiquement une fenêtre en beta.2. La collection décrit les fenêtres de démarrage.
06
Le parcours complet
Le code applicatif déclare l'intention. Le runtime retient et planifie. Winit possède les événements du bureau et les surfaces. WGPU transforme la Scene en pixels.
Quatre responsabilités, une frame
Chaque couche possède une mission précise. Garder ces frontières visibles simplifie beaucoup le raisonnement sur les intégrations avancées.
- 01 · Application
Déclarer l'intention
State, actions, commandes, widgets et fenêtres.
- 02 · Runtime
Retenir et planifier
Réconciliation, invalidation ciblée, mise en page, entrées et production de la Scene.
- 03 · Hôte natif
Relier le bureau
Fenêtres winit, événements, presse-papiers, réveils et surfaces.
- 04 · GPU
Rendre la scène
Le moteur WGPU par défaut exécute les couches et les commandes de dessin.
Un hôte personnalisé peut remplacer les deux dernières couches sans changer le modèle de widgets retenu.
À vous de jouer
Exercice : ajoutez une remise à zéro différée
Prolongez le motif d'action différée visible au lieu de le reconstruire.
- 1Ajoutez une action Reset qui place le score à zéro et le statut sur Reset.
- 2Renvoyez DispatchAfter afin que Celebrate restaure Ready après 500 ms.
Laissez le bouton émettre Reset. Gardez les deux modifications de State et la commande suivante dans update.
Reset affiche immédiatement zéro et le statut Reset, puis Ready revient environ une demi-seconde plus tard.