Système de layout
La lib calcule automatiquement les positions de chaque widget. Tu ne passes jamais de coordonnées x/y — tu déclares uniquement la structure (qui contient quoi, dans quel ordre).
Principe général
buildLayout(Column root) reçoit la colonne racine du panneau. Chaque widget ajouté s'empile vers le bas. Le LayoutEngine :
- Demande à chaque enfant sa hauteur préférée (
getPreferredHeight). - Assigne les positions absolues de haut en bas.
- Appelle
render()sur chaque widget avec ces positions.
Cela fonctionne de façon récursive : une Card demande la hauteur de ses propres enfants, etc.
Vue d'ensemble d'un écran typique
ApocalyScreen
└── Column (root) ← reçue dans buildLayout()
├── Card ← sous-panneau avec fond/bordure
│ ├── Label ← texte avec valeur dynamique
│ └── KeyValueRow ← clé à gauche, valeur à droite
├── Spacer(4) ← espace vertical supplémentaire
├── Row ← layout horizontal
│ ├── Button ← occupe 50% de la Row
│ └── Button ← occupe 50% de la Row
└── ScrollableColumn ← zone défilante à hauteur fixe
├── Button
├── Button
└── Button
Column
Conteneur vertical. Empile ses enfants vers le bas avec un gap configurable (défaut : 4 px).
import ca.tawess123.apocalyinterface.api.layout.Column;
// Gap par défaut (4 px)
Column col = Column.create();
// Gap personnalisé
Column col = Column.withGap(8);
// Ajout de widgets (chaînable)
col.add(label).add(button).add(card);
La Column root passée à buildLayout utilise le gap par défaut.
Hauteur totale = somme des hauteurs des enfants + (N-1) × gap.
Quand l'utiliser : toujours. C'est le conteneur de base. Toute hiérarchie commence par une Column.
Row
Conteneur horizontal. Distribue la largeur disponible équitablement entre ses enfants.
import ca.tawess123.apocalyinterface.api.layout.Row;
import ca.tawess123.apocalyinterface.api.widget.Label;
// Deux labels côte à côte, chacun à 50% de la largeur
Row.create()
.add(Label.builder().text("monmod.label.membres").value(DataSource.of("8/15")).build())
.add(Label.builder().text("monmod.label.rang").value(DataSource.of("#2")).build());
Hauteur = hauteur maximale parmi les enfants.
Quand l'utiliser : pour aligner deux ou plusieurs widgets côte à côte — deux labels, deux boutons, une icône et un texte.
La largeur est toujours divisée équitablement. Si tu as 3 enfants dans une Row de 210 px, chacun reçoit 70 px. Il n'existe pas d'option de largeur relative en v1.
Card
Sous-panneau avec fond (SUB_BG), bordure fine (SUB_BORDER, 1 px) et padding interne de 6 px. Regroupe visuellement des widgets liés.
import ca.tawess123.apocalyinterface.api.layout.Card;
import ca.tawess123.apocalyinterface.api.theme.ApocalyColors;
// Card standard
Card infoCard = Card.create();
infoCard.add(Label.builder().text("monmod.titre.infos").title().build());
infoCard.add(KeyValueRow.builder().key("monmod.row.points").value(DataSource.of("4200")).build());
root.add(infoCard);
// Card avec bordure de couleur (succès, danger, etc.)
Card alertCard = Card.create().borderColor(ApocalyColors.DANGER);
Hauteur = hauteur du contenu + 2 × padding (12 px au total).
Quand l'utiliser : pour regrouper visuellement des informations liées — infos clan, stats joueur, résumé d'une transaction.
Grid
Grille à N colonnes. Distribue les enfants en lignes successives.
import ca.tawess123.apocalyinterface.api.layout.Grid;
import ca.tawess123.apocalyinterface.api.widget.ItemIcon;
import net.minecraft.world.item.Items;
// Grille 3 colonnes — affiche 5 items (dernière ligne incomplète)
Grid recompenses = Grid.builder()
.columns(3)
.gap(4)
.add(ItemIcon.builder().item(Items.DIAMOND).build())
.add(ItemIcon.builder().item(Items.EMERALD).build())
.add(ItemIcon.builder().item(Items.GOLD_INGOT).build())
.add(ItemIcon.builder().item(Items.IRON_INGOT).build())
.add(ItemIcon.builder().item(Items.COAL).build())
.build();
Hauteur = (nombre de lignes × hauteur de cellule) + (lignes - 1) × gap.
Quand l'utiliser : afficher une collection d'objets identiques — récompenses de quête, items de boutique, icônes de membres.
ScrollableColumn
Zone à hauteur fixe qui scrolle son contenu en interne (molette + mini scrollbar). Indispensable quand le nombre d'éléments varie ou dépasse l'espace disponible.
import ca.tawess123.apocalyinterface.api.layout.ScrollableColumn;
import ca.tawess123.apocalyinterface.api.theme.ApocalyDimensions;
// Calcul de la hauteur : 5 boutons visibles × 20 px + 4 gaps × 4 px = 116 px
private static final int SCROLL_H =
5 * ApocalyDimensions.BUTTON_HEIGHT + 4 * Column.DEFAULT_GAP;
// Construire UNE SEULE FOIS dans le constructeur (pas dans buildLayout)
private final ScrollableColumn buttonScroll;
public MonEcran() {
super(Component.translatable("monmod.screen.hub"), 260);
buttonScroll = ScrollableColumn.builder().fixedHeight(SCROLL_H).build();
buttonScroll.add(Button.builder().text("monmod.btn.action1").command("cmd1").build());
buttonScroll.add(Button.builder().text("monmod.btn.action2").command("cmd2").build());
// ... autant de boutons que nécessaire
}
@Override
protected void buildLayout(Column root) {
root.add(buttonScroll); // ajouter (ne pas recréer)
}
Si tu crées la ScrollableColumn dans buildLayout, l'offset de scroll est remis à zéro à chaque update() ou redimensionnement. Construis-la dans le constructeur et ajoute-la à root dans buildLayout.
Hauteur retournée = fixedHeight (constante, indépendante du contenu).
Quand l'utiliser : liste de boutons, liste de membres, historique de transactions — dès que le contenu peut dépasser l'espace visible.
Spacer
Espace vide de hauteur fixe. Ajoute un espacement ponctuel entre deux groupes logiques.
import ca.tawess123.apocalyinterface.api.layout.Spacer;
root.add(card);
root.add(Spacer.of(8)); // 8 px de vide supplémentaire
root.add(button);
Différence avec le gap de Column : le gap de Column s'applique uniformément entre tous les enfants. Le Spacer ajoute un espacement supplémentaire à un endroit précis, en complément du gap.
Divider
Ligne de séparation horizontale de 1 px (ApocalyColors.DIVIDER, blanc 33 % alpha).
import ca.tawess123.apocalyinterface.api.layout.Divider;
import ca.tawess123.apocalyinterface.api.theme.ApocalyColors;
// Divider standard
root.add(Spacer.of(4));
root.add(Divider.create());
root.add(Spacer.of(4));
// Divider avec couleur personnalisée
root.add(Divider.builder().color(ApocalyColors.BORDER).build());
Quand l'utiliser : pour séparer deux sections logiques dans un panneau — infos générales vs actions, stats vs historique.
Récapitulatif
| Conteneur | Direction | Hauteur | Scroll interne |
|---|---|---|---|
Column | Vertical | Auto (somme enfants + gaps) | Non |
Row | Horizontal | Auto (max enfants) | Non |
Card | Vertical | Auto + 12 px padding | Non |
Grid | Grille N colonnes | Auto (lignes × cellule) | Non |
ScrollableColumn | Vertical | Fixe (paramètre) | Oui |
Spacer | — | Fixe (paramètre) | — |
Divider | Horizontal | 1 px | — |
Le scroll global du panneau (quand le contenu total dépasse la hauteur de l'écran) est géré automatiquement par ApocalyScreen — tu n'as rien à coder.
Voir aussi
- Screen vs ContainerScreen — quel type d'écran choisir
- Référence Column — API complète
- Référence ScrollableColumn — API complète
- Recette Hub à boutons — exemple réel avec Card + ScrollableColumn