---
title: Pages Personnalisées
type: docs
weight: 2
---

Les pages personnalisées permettent aux plugins de créer des overlays UI interactifs qui peuvent recevoir des entrées utilisateur et afficher du contenu dynamique.

**Package:** `com.hypixel.hytale.server.core.entity.entities.player.pages`

## Durées de Vie des Pages

Contrôle comment les pages peuvent être fermées :

| Durée de Vie | Description |
|--------------|-------------|
| `CustomPageLifetime.CantClose` | La page ne peut pas être fermée par le joueur |
| `CustomPageLifetime.CanDismiss` | Le joueur peut fermer avec Échap |
| `CustomPageLifetime.CanDismissOrCloseThroughInteraction` | Peut fermer via Échap ou interaction |

## Créer une Page Personnalisée

Étendez `CustomUIPage` :

```java
public class WelcomePage extends CustomUIPage {

    public WelcomePage(PlayerRef playerRef) {
        super(playerRef, CustomPageLifetime.CanDismiss);
    }

    @Override
    public void build(Ref<EntityStore> ref, UICommandBuilder builder,
                      UIEventBuilder events, Store<EntityStore> store) {
        // Charger un template UI depuis les assets (extension .ui OBLIGATOIRE)
        builder.append("Pages/WelcomeScreen.ui");

        // Définir des valeurs dynamiques
        builder.set("#player_name", playerRef.getUsername());
        builder.set("#server_name", "Mon Serveur");
    }
}
```

## UICommandBuilder

Construire du contenu UI dynamiquement :

### Ajouter du Contenu

```java
// Ajouter un template UI (extension .ui OBLIGATOIRE)
builder.append("Pages/TemplatePath.ui");

// Ajouter à un sélecteur spécifique
builder.append("#container", "Pages/ChildTemplate.ui");

// Ajouter du YAML/JSON inline
builder.appendInline("#list", "{ type: text, content: 'Bonjour' }");
```

### Définir des Valeurs

```java
// Définir du texte
builder.set("#title", "Bienvenue !");
builder.set("#description", Message.translation("my.key"));

// Définir des nombres
builder.set("#score", 1500);
builder.set("#progress", 0.75f);

// Définir un booléen
builder.set("#visible", true);

// Mettre à null
builder.setNull("#optional_field");
```

### Définir des Objets

```java
// Définir un ItemStack
ItemStack item = ...;
builder.setObject("#item_display", item);

// Définir une Zone UI
Area area = new Area(0, 0, 100, 50);
builder.setObject("#bounds", area);

// Définir des tableaux/listes
builder.set("#items", itemStackArray);
builder.set("#options", stringList);
```

### Supprimer du Contenu

```java
// Vider les enfants d'un élément
builder.clear("#container");

// Supprimer un élément entièrement
builder.remove("#element_id");

// Insérer avant un élément
builder.insertBefore("#existing", "Pages/NewElement.ui");
```

## UIEventBuilder

Lier des événements aux éléments UI avec `addEventBinding` :

```java
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
import com.hypixel.hytale.server.core.ui.builder.EventData;

@Override
public void build(Ref<EntityStore> ref, UICommandBuilder builder,
                  UIEventBuilder events, Store<EntityStore> store) {
    builder.append("Pages/Menu.ui");

    // Lier des événements de clic avec addEventBinding
    events.addEventBinding(CustomUIEventBindingType.Activating, "#button_start",
        EventData.of("Action", "start_game"));
    events.addEventBinding(CustomUIEventBindingType.Activating, "#button_settings",
        EventData.of("Action", "open_settings"));
}
```

### Types de Liaison d'Événements

| Type | Description |
|------|-------------|
| `Activating` | Clic/activation de bouton |
| `RightClicking` | Action clic droit |
| `DoubleClicking` | Action double-clic |
| `MouseEntered` | Entrée du survol souris |
| `MouseExited` | Sortie du survol souris |
| `ValueChanged` | Valeur d'entrée changée |
| `FocusGained` | Élément focus |
| `FocusLost` | Élément perd le focus |

## Gérer les Événements

Surchargez `handleDataEvent` pour traiter les interactions utilisateur. Le paramètre `rawData` contient du JSON avec les données de l'événement :

```java
public class MenuPage extends CustomUIPage {

    public MenuPage(PlayerRef ref) {
        super(ref, CustomPageLifetime.CanDismiss);
    }

    @Override
    public void build(Ref<EntityStore> ref, UICommandBuilder builder,
                      UIEventBuilder events, Store<EntityStore> store) {
        builder.append("Pages/MainMenu.ui");

        events.addEventBinding(CustomUIEventBindingType.Activating, "#play_button",
            EventData.of("Action", "play"));
        events.addEventBinding(CustomUIEventBindingType.Activating, "#quit_button",
            EventData.of("Action", "quit"));
    }

    @Override
    public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, String rawData) {
        // rawData contient du JSON comme : {"Action":"play"}
        if (rawData.contains("\"Action\":\"play\"")) {
            startGame();
        } else if (rawData.contains("\"Action\":\"quit\"")) {
            close();
        }
    }

    private void startGame() {
        // Logique de jeu...
        close();  // Fermer la page
    }
}
```

{{< callout type="info" >}}
**Conseil :** Pour une gestion d'événements plus complexe, envisagez d'utiliser une bibliothèque de parsing JSON ou d'étendre `InteractiveCustomUIPage<T>` avec une classe de données typée.
{{< /callout >}}

## Ouvrir des Pages

Utilisez `PageManager` pour ouvrir des pages :

```java
Player player = ...;
PageManager pageManager = player.getPageManager();

// Ouvrir une page personnalisée
pageManager.openCustomPage(
    player.getReference(),
    player.getReference().getStore(),
    new MenuPage(player.getPlayerRef())
);
```

## Fermer des Pages

```java
// Depuis l'intérieur de la page
protected void close() {
    // Méthode héritée - ferme cette page
}

// Depuis l'extérieur
pageManager.setPage(ref, store, Page.None);
```

## Mettre à Jour des Pages

Mettre à jour le contenu sans reconstruire :

```java
// Envoyer une mise à jour avec de nouvelles commandes
UICommandBuilder update = new UICommandBuilder();
update.set("#score", newScore);
sendUpdate(update);

// Reconstruction complète
rebuild();

// Vider et mettre à jour
sendUpdate(update, true);  // clear=true
```

## Page avec Fenêtres

Ouvrir des pages avec des fenêtres d'inventaire :

```java
// Ouvrir une page avec des fenêtres
pageManager.openCustomPageWithWindows(
    ref, store,
    new CraftingPage(playerRef),
    craftingWindow,
    playerInventoryWindow
);
```

## Exemple de Page Interactive

```java
public class ShopPage extends CustomUIPage {

    private int selectedItem = -1;
    private final List<ShopItem> items;

    public ShopPage(PlayerRef ref, List<ShopItem> items) {
        super(ref, CustomPageLifetime.CanDismiss);
        this.items = items;
    }

    @Override
    public void build(Ref<EntityStore> ref, UICommandBuilder builder,
                      UIEventBuilder events, Store<EntityStore> store) {
        builder.append("Pages/Shop.ui");

        // Peupler les items
        for (int i = 0; i < items.size(); i++) {
            ShopItem item = items.get(i);
            builder.set("#item_" + i + "_name", item.getName());
            builder.set("#item_" + i + "_price", item.getPrice());
            events.addEventBinding(CustomUIEventBindingType.Activating, "#item_" + i,
                EventData.of("Action", "select").append("Index", String.valueOf(i)));
        }

        events.addEventBinding(CustomUIEventBindingType.Activating, "#buy_button",
            EventData.of("Action", "buy"));
        events.addEventBinding(CustomUIEventBindingType.Activating, "#close_button",
            EventData.of("Action", "close"));
    }

    @Override
    public void handleDataEvent(Ref<EntityStore> ref, Store<EntityStore> store, String rawData) {
        // Parser les données JSON de l'événement
        if (rawData.contains("\"Action\":\"select\"")) {
            // Extraire l'index du JSON
            int indexStart = rawData.indexOf("\"Index\":\"") + 9;
            int indexEnd = rawData.indexOf("\"", indexStart);
            selectedItem = Integer.parseInt(rawData.substring(indexStart, indexEnd));
            updateSelection();
        } else if (rawData.contains("\"Action\":\"buy\"") && selectedItem >= 0) {
            purchaseItem(selectedItem);
        } else if (rawData.contains("\"Action\":\"close\"")) {
            close();
        }
    }

    private void updateSelection() {
        UICommandBuilder update = new UICommandBuilder();
        update.set("#selected_index", selectedItem);
        update.set("#selected_name", items.get(selectedItem).getName());
        sendUpdate(update);
    }

    private void purchaseItem(int index) {
        // Logique d'achat...
        rebuild();  // Rafraîchir l'UI
    }
}
```

## Pages Intégrées

Types de pages disponibles :

| Page | Description |
|------|-------------|
| `Page.None` | Pas de page ouverte (fermer la page courante) |
| `Page.Bench` | Interface d'établi/crafting |
| `Page.Inventory` | Inventaire du joueur |
| `Page.ToolsSettings` | Paramètres des outils |
| `Page.Map` | Carte du monde |
| `Page.MachinimaEditor` | Éditeur de machinima |
| `Page.ContentCreation` | Outils de création de contenu |
| `Page.Custom` | Page UI personnalisée |

```java
// Ouvrir une page intégrée
pageManager.setPage(ref, store, Page.Inventory);
```

## Bonnes Pratiques

{{< callout type="info" >}}
**Directives des Pages Personnalisées :**
- Utilisez `CanDismiss` sauf si la page doit être modale
- Gérez le callback `onDismiss` pour le nettoyage
- Utilisez `sendUpdate()` pour les petits changements, `rebuild()` pour les changements majeurs
- Stockez l'état de la page dans des champs d'instance
- Fermez correctement les pages en utilisant `close()` ou `setPage(Page.None)`
{{< /callout >}}

{{< callout type="warning" >}}
**Important :** Vérifiez toujours si `playerRef.getReference()` est null avant d'accéder au store, car le joueur peut s'être déconnecté.
{{< /callout >}}
