// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "SettingsMenuWidget.generated.h" class UButton; class UConfirmDialogWidget; class UKeyCaptureWidget; class USettingsTabWidget; class UWidgetSwitcher; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnSettingsCloseRequested); /** Reponse a une demande de confirmation. bConfirmed est faux sur un refus ou une expiration. */ DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsConfirmationResult, bool, bConfirmed); /** Touche capturee. Invalide si le joueur a annule avec Echap. */ DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSettingsKeyCaptureResult, FKey, CapturedKey); /** * L'ecran de reglages : une barre d'onglets, un WidgetSwitcher, Retour et * Reinitialiser. * * Il ne sait PAS qui l'a ouvert. Il demande a se fermer via OnCloseRequested et * c'est l'appelant -- menu principal ou menu de pause -- qui decide de ce que * "Retour" veut dire. Sans ca il faudrait un ecran par contexte. * * Il ne connait pas davantage le contenu de ses onglets : il appelle * RefreshFromSettings et ResetToDefaults sur USettingsTabWidget. Ajouter * l'onglet Graphismes ne demandera aucune ligne ici. * * Classe Abstract : on cree un WBP_Settings qui en herite. */ UCLASS(Abstract) class SURVIVAL_PROJET_API USettingsMenuWidget : public UUserWidget { GENERATED_BODY() public: USettingsMenuWidget(const FObjectInitializer& ObjectInitializer); virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override; UPROPERTY(BlueprintAssignable, Category = "Reglages") FOnSettingsCloseRequested OnCloseRequested; /** * Affiche l'ecran, rafraichit l'onglet visible et prend le focus clavier. * * Passe par ici et jamais par SetVisibility : sans le focus, Echap ne * refermerait pas l'ecran. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void Open(); /** Masque l'ecran et ecrit le fichier de reglages. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void Close(); /** * ZOrder de la boite de confirmation, impose par celui qui ouvre l'ecran. * * L'ecran ne le choisit PAS lui-meme : il est ouvert depuis deux contextes * dont les empilements n'ont rien a voir (50 au menu principal, 150 en jeu). * Un numero code en dur ici obligerait le widget a savoir qui l'affiche, * exactement ce qu'on evite avec OnCloseRequested. A appeler AVANT Open(). */ UFUNCTION(BlueprintCallable, Category = "Reglages") void SetDialogZOrder(int32 InZOrder) { DialogZOrder = InZOrder; } /** * Pose une question au joueur pour le compte d'un onglet. * * L'ecran est le seul a posseder une boite : un onglet qui creerait la * sienne devrait a son tour se demander a quel ZOrder la poser, et on * repartirait pour un tour. * * @param MessageOrFormat texte simple, ou format contenant {0} si * CountdownSeconds est superieur a zero. * @param CountdownSeconds 0 pour une question sans limite de temps. * @return false si aucune boite n'est configuree -- l'appelant doit alors * decider seul, sa reponse n'arrivera jamais. */ bool RequestConfirmation(const FText& MessageOrFormat, float CountdownSeconds, const FOnSettingsConfirmationResult& OnResult); /** * Ouvre le voile de capture de touche pour le compte d'un onglet. * * Meme raison que la boite : un voile pose DANS l'onglet serait clippe a la * zone du WidgetSwitcher au lieu de couvrir l'ecran, et devrait a son tour * se trouver un ZOrder. * * @return false si aucun voile n'est configure -- la reponse n'arrivera * jamais, l'appelant doit renoncer. */ bool RequestKeyCapture(const FText& ActionLabel, const FOnSettingsKeyCaptureResult& OnResult); /** * Bascule le voile deja ouvert sur la question de conflit. * * Le voile RESTE ouvert entre la capture et la reponse : c'est le meme geste * pour le joueur, il n'a pas a le voir disparaitre puis revenir. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void ShowKeyCaptureConflict(const FText& Message, const FOnSettingsConfirmationResult& OnAnswer); /** Referme le voile. A appeler quand le remappage est termine ou abandonne. */ UFUNCTION(BlueprintCallable, Category = "Reglages") void CloseKeyCapture(); protected: UPROPERTY(meta = (BindWidget)) TObjectPtr TabSwitcher; UPROPERTY(meta = (BindWidget)) TObjectPtr BackButton; UPROPERTY(meta = (BindWidget)) TObjectPtr ResetButton; /** * Les cinq onglets sont OBLIGATOIRES : un WBP_Settings sans eux ne compile pas. * * BindWidgetOptional echoue en silence -- un nom mal orthographie donnait un * bouton parfaitement inerte, sans le moindre message. Pour un element dont * l'absence n'a aucun sens, on veut l'erreur a la compilation du Blueprint. */ UPROPERTY(meta = (BindWidget)) TObjectPtr GameplayTabButton; UPROPERTY(meta = (BindWidget)) TObjectPtr DisplayTabButton; UPROPERTY(meta = (BindWidget)) TObjectPtr GraphicsTabButton; UPROPERTY(meta = (BindWidget)) TObjectPtr ControlsTabButton; UPROPERTY(meta = (BindWidget)) TObjectPtr AudioTabButton; /** * Ton WBP_ConfirmDialog, a renseigner dans les Class Defaults de * WBP_Settings. Laisse-le vide et "Reinitialiser" agit sans rien demander. */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages") TSubclassOf ResetDialogClass; /** La question posee avant de reinitialiser l'onglet visible. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Reglages") FText ResetDialogMessage; /** Ton WBP_KeyCapture. Sans lui, l'onglet Touches ne peut rien remapper. */ UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Reglages") TSubclassOf KeyCaptureClass; UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages") TObjectPtr ResetDialog; UPROPERTY(Transient, BlueprintReadOnly, Category = "Reglages") TObjectPtr KeyCapture; private: UFUNCTION() void HandleBackClicked(); UFUNCTION() void HandleResetClicked(); /** Reponse a la question du bouton Reinitialiser. */ UFUNCTION() void HandleResetAnswered(bool bConfirmed); UFUNCTION() void HandleDialogConfirmed(); UFUNCTION() void HandleDialogCancelled(); /** Referme la boite et rend la reponse a qui a pose la question. */ void FinishConfirmation(bool bConfirmed); /** Cree la boite au premier besoin. Renvoie nullptr si aucune classe n'est assignee. */ UConfirmDialogWidget* EnsureDialog(); UFUNCTION() void HandleGameplayTabClicked(); UFUNCTION() void HandleDisplayTabClicked(); UFUNCTION() void HandleGraphicsTabClicked(); UFUNCTION() void HandleControlsTabClicked(); UFUNCTION() void HandleAudioTabClicked(); UFUNCTION() void HandleKeyCaptured(FKey CapturedKey); UFUNCTION() void HandleKeyCaptureCancelled(); UFUNCTION() void HandleKeyConflictAnswered(bool bConfirmed); /** A qui rendre la reponse du conflit affiche dans le voile. */ FOnSettingsConfirmationResult PendingConflictAnswer; /** Change de page et rafraichit la nouvelle. Ignore un index inexistant. */ void ShowTab(int32 Index); /** La page actuellement affichee, ou nullptr si le switcher est vide. */ USettingsTabWidget* GetActiveTab() const; /** Grise les boutons dont la page n'existe pas encore. */ void UpdateTabButtonStates(); /** A qui rendre la reponse de la boite ouverte. Vide quand rien n'est pose. */ FOnSettingsConfirmationResult PendingConfirmation; /** A qui rendre la touche capturee. Vide quand le voile est ferme. */ FOnSettingsKeyCaptureResult PendingKeyCapture; bool bResetDialogOpen = false; bool bKeyCaptureOpen = false; /** Valeur de repli si l'appelant oublie SetDialogZOrder : devant le fondu exclu. */ int32 DialogZOrder = 500; };