Données statiques et dynamiques
Les widgets qui affichent une valeur variable (labels, barres de progression, lignes clé-valeur) acceptent un DataSource<T> plutôt qu'une valeur brute. Ce mécanisme unifie les valeurs fixes et les valeurs calculées à la volée.
Pourquoi DataSource ?
Sans DataSource, un widget devrait être reconstruit chaque fois que la donnée change. Avec DataSource, le widget appelle get() à chaque frame et obtient toujours la valeur courante — même après un update().
Forme statique — DataSource.of(valeur)
Quand la valeur ne changera pas pendant la durée de vie de l'écran, utilise la fabrique statique :
import ca.tawess123.apocalyinterface.api.data.DataSource;
import ca.tawess123.apocalyinterface.api.widget.KeyValueRow;
// Valeur connue à la construction — jamais mise à jour
KeyValueRow.builder()
.key("monmod.row.clan")
.value(DataSource.of("Les Loups"))
.build();
// Valeur calculée une fois à la construction
int total = membres + invites;
KeyValueRow.builder()
.key("monmod.row.total")
.value(DataSource.of(total + " joueurs"))
.build();
DataSource.of(x) capture la valeur x au moment de l'appel. Si x change plus tard, le widget continue d'afficher l'ancienne valeur. Pour une valeur qui évolue, utilise la forme dynamique.
Forme dynamique — lambda () -> expression
Quand la valeur peut changer (champ mis à jour par un packet, compteur, état toggle), fournis un lambda :
// Champ d'instance mis à jour par un packet S2C
private int points = 0;
KeyValueRow.builder()
.key("monmod.row.points")
.value(() -> points + " pts") // réévalué à chaque frame
.build();
Le lambda () -> points + " pts" est une implémentation de DataSource<String> via une interface fonctionnelle. Il est appelé par le widget à chaque render(), donc il reflète toujours la valeur courante du champ points.
// Barre de progression dynamique
private float xpRatio = 0.0f;
ProgressBar.builder()
.value(() -> xpRatio) // DataSource<Float>
.labelKey("monmod.bar.xp")
.build();
Le pattern update() — rafraîchir sans fermer l'écran
Quand un packet S2C apporte de nouvelles données, tu mets à jour les champs puis appelles update(). L'écran se reconstruit (appel à buildLayout) sans se fermer, et le scroll est préservé.
public final class StatScreen extends ApocalyScreen {
private String clanName = "…";
private int points = 0;
private int membres = 0;
public StatScreen() {
super(Component.translatable("monmod.screen.stats"), 240);
}
@Override
protected void buildLayout(Column root) {
root.add(Card.create()
.add(KeyValueRow.builder()
.key("monmod.row.clan")
.value(DataSource.of(clanName)) // statique : capturé au buildLayout
.build())
.add(KeyValueRow.builder()
.key("monmod.row.membres")
.value(DataSource.of(membres + " / 15"))
.build()));
}
// Appelé quand un nouveau packet S2C arrive
public void onDataReceived(String newClanName, int newPoints, int newMembres) {
this.clanName = newClanName;
this.points = newPoints;
this.membres = newMembres;
update(); // ← relance buildLayout avec les nouvelles valeurs
}
}
// Dans le handler du packet côté client :
if (Minecraft.getInstance().screen instanceof StatScreen s) {
s.onDataReceived(payload.clanName(), payload.points(), payload.membres());
}
DataSource.of() vs lambda avec update()Avec DataSource.of(clanName), la valeur est capturée au moment du buildLayout. Elle est mise à jour correctement lors du update() (le lambda est réexécuté). La différence avec () -> clanName n'est visible que si le widget est réutilisé entre deux buildLayout — ce qui n'arrive qu'avec ScrollableColumn (voir layout).
Tableau récapitulatif
| Cas | Forme recommandée |
|---|---|
| Valeur fixe connue à la construction | DataSource.of(valeur) |
Valeur formatée une fois (ex. pts + " pts") | DataSource.of(pts + " pts") |
| Champ mis à jour entre deux frames | () -> champ (lambda) |
Valeur rafraîchie via update() | DataSource.of(champ) (champ lu au buildLayout) |
Voir aussi
- Référence DataSource — interface et signatures complètes
- Référence Label —
text()+value() - Référence KeyValueRow
- Référence ProgressBar
- Recette liste de joueurs — DataSource lambda en pratique