Aller au contenu principal

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

CasForme recommandée
Valeur fixe connue à la constructionDataSource.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