// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "SettingRowWidget.generated.h" class UButton; class UProgressBar; class USlider; class UTextBlock; DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnSettingRowIndexChanged, int32, NewIndex); /** * Une ligne de reglage : un libelle, une fleche gauche, une valeur, une fleche * droite, et une barre de remplissage optionnelle derriere la valeur. * * TOUT reglage est ramene a un index dans une liste de paliers, y compris les * nombres : "Champ de vision, 70 a 120 par pas de 5" devient un index de 0 a 10. * La ligne ne connait donc jamais que CurrentIndex et StepCount, et c'est ce qui * permet de n'avoir qu'UN SEUL WBP a dessiner pour tout le menu, qu'il s'agisse * d'un choix (Maintien / Bascule) ou d'une valeur continue. * * Elle ne sait pas ce qu'elle regle : elle previent via OnIndexChanged, et c'est * l'onglet qui traduit l'index en appel sur USurvivalUserSettings. Meme principe * que UConfirmDialogWidget, qui ignore ce qu'il confirme. * * Classe Abstract : on cree un WBP_SettingRow qui en herite. */ UCLASS(Abstract) class SURVIVAL_PROJET_API USettingRowWidget : public UUserWidget { GENERATED_BODY() public: virtual void NativeOnInitialized() override; /** Previent d'un changement venant du joueur, jamais d'un rafraichissement. */ UPROPERTY(BlueprintAssignable, Category = "Reglages") FOnSettingRowIndexChanged OnIndexChanged; /** * Mode choix : la valeur affichee est prise telle quelle dans Options. * Sert aussi bien a un oui/non qu'a une liste de qualites. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetupAsChoice(const FText& Label, const TArray& Options, int32 Index); /** * Mode nombre : la valeur affichee est calculee a partir de l'index. * * @param Step pas fin, celui du glissement. C'est lui qui definit la * granularite de l'index. * @param ArrowStep bond des fleches, forcement un multiple de Step. Les * fleches se calent sur le multiple suivant plutot que * d'ajouter betement leur bond : depuis 37 avec un bond de * 5 on arrive a 40, pas a 42. * @param Suffix unite ("%", "°"), peut rester vide. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetupAsNumber(const FText& Label, float Min, float Max, float Step, float ArrowStep, float Value, const FText& Suffix); UFUNCTION(BlueprintPure, Category = "Reglages") int32 GetIndex() const { return CurrentIndex; } /** Reconvertit l'index en valeur reelle. Sans objet en mode choix. */ UFUNCTION(BlueprintPure, Category = "Reglages") float GetNumericValue() const; /** * Repositionne la ligne sans rien diffuser. * * A utiliser pour tout rafraichissement venant des reglages : diffuser ici * ferait boucler l'onglet sur lui-meme (il reecrit le reglage, qui rafraichit * la ligne, qui rediffuse...). */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetIndexSilently(int32 NewIndex); /** Repositionne la ligne sur une valeur reelle, sans rien diffuser. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetNumericValueSilently(float Value); /** * Derniere valeur que le JOUEUR peut atteindre, les suivantes restant * affichables par le code. * * Sert a "Personnalise" en qualite globale : c'est un etat dans lequel on * tombe en bougeant un curseur individuel, jamais un choix qu'on fait. * Sans ca la fleche droite proposerait de le "selectionner", ce qui ne veut * rien dire. INDEX_NONE pour lever la limite. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetMaxSelectableIndex(int32 InMaxSelectableIndex); protected: UPROPERTY(meta = (BindWidget)) TObjectPtr LabelText; UPROPERTY(meta = (BindWidget)) TObjectPtr ValueText; UPROPERTY(meta = (BindWidget)) TObjectPtr LeftArrowButton; UPROPERTY(meta = (BindWidget)) TObjectPtr RightArrowButton; /** * Le REMPLISSAGE visible, et rien d'autre. * * Le Slider d'Unreal ne sait pas dessiner de portion remplie : il rend une * barre uniforme plus une poignee. On empile donc les deux -- cette * ProgressBar pour le rendu, le Slider transparent par-dessus pour le * geste. C'est le montage habituel, pas un bricolage. */ UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr ValueBar; /** * Le GESTE, et rien d'autre : a styler entierement transparent dans le WBP. * * Sa course est exprimee en INDEX de palier (0 a StepCount-1) et non dans * l'unite du reglage -- c'est ce qui garantit qu'un glissement ne peut pas * produire une valeur hors palier, que le setter refuserait ensuite. * * Les deux sont facultatifs : une ligne de choix (Maintien / Bascule) les * replie tous les deux, il n'y a rien a doser. */ UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr ValueSlider; private: UFUNCTION() void HandleLeftClicked(); UFUNCTION() void HandleRightClicked(); /** Glissement souris : on arrondit sur le palier le plus proche. */ UFUNCTION() void HandleSliderValueChanged(float NewValue); /** Deplace l'index de Delta paliers et diffuse, si le mouvement est possible. */ void StepIndex(int32 Delta); /** Index vise par une fleche : le multiple de son bond suivant ou precedent. */ int32 ArrowTargetIndex(int32 Direction) const; /** Plus grand index atteignable par le joueur. */ int32 MaxSelectable() const; /** Reecrit le texte, la barre et l'etat des deux fleches. */ void Refresh(); /** Vide en mode nombre. */ TArray ChoiceLabels; FText NumericSuffix; float NumericMin = 0.f; float NumericStep = 1.f; int32 CurrentIndex = 0; /** Nombre de paliers fins, donc d'index valides. Toujours au moins 1. */ int32 StepCount = 1; /** Bond des fleches, exprime en nombre de paliers fins. 1 en mode choix. */ int32 ArrowIndexDelta = 1; /** INDEX_NONE = aucune limite, tous les paliers sont selectionnables. */ int32 MaxSelectableIndex = INDEX_NONE; bool bNumeric = false; /** * Vrai pendant qu'on repositionne le curseur depuis le code. * * USlider::SetValue ne rediffuse pas son delegue aujourd'hui, mais s'en * remettre a ce detail d'implementation coute une boucle infinie le jour ou * il change. */ bool bRefreshingSlider = false; };