// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "Blueprint/UserWidget.h" #include "Containers/Ticker.h" #include "ConfirmDialogWidget.generated.h" class UButton; class UTextBlock; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogConfirmed); DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnConfirmDialogCancelled); /** * Boite de dialogue "es-tu sur ?", volontairement generique. * * Elle ne sait pas ce qu'elle confirme : elle pose la question et previent via * ses delegues. C'est l'appelant qui decide de l'action. Ainsi la meme boite * servira pour quitter, effacer une sauvegarde ou abandonner une partie. * * Classe Abstract : on cree un WBP_ConfirmDialog qui en herite. */ UCLASS(Abstract) class EMBERWILD_API UConfirmDialogWidget : public UUserWidget { GENERATED_BODY() public: UConfirmDialogWidget(const FObjectInitializer& ObjectInitializer); virtual void NativeConstruct() override; virtual void NativeDestruct() override; virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override; UPROPERTY(BlueprintAssignable, Category = "Dialogue") FOnConfirmDialogConfirmed OnConfirmed; UPROPERTY(BlueprintAssignable, Category = "Dialogue") FOnConfirmDialogCancelled OnCancelled; /** * Affiche la boite et lui donne le focus clavier. * * Passe par ici et jamais par SetVisibility directement : sans le focus, * la boite ne recevrait aucune touche et Echap resterait sans effet. */ UFUNCTION(BlueprintCallable, Category = "Dialogue") void Open(const FText& InMessage); /** * Meme chose, mais avec un retour automatique passe le delai. * * @param InMessageFormat doit contenir {0}, remplace par les secondes * restantes. Exemple : "Garder ce mode ?\nRetour * automatique dans {0} s." * * A l'expiration, OnCancelled est diffuse -- comme si le joueur avait * refuse. C'est le seul filet quand la question porte sur un changement qui * peut rendre l'ecran illisible. */ UFUNCTION(BlueprintCallable, Category = "Dialogue") void OpenWithCountdown(const FText& InMessageFormat, float Seconds); /** Referme la boite. Ne previent personne : l'appelant sait ce qu'il fait. */ UFUNCTION(BlueprintCallable, Category = "Dialogue") void Close(); protected: /** Echap equivaut a cliquer Annuler. Decoche pour une question incontournable. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") bool bCancelOnEscape = true; /** * Entree equivaut a cliquer Confirmer. * * Desactive par defaut, volontairement : sur une question destructrice, un * joueur qui martele Entree validerait sans avoir lu. */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Dialogue") bool bConfirmOnEnter = false; UPROPERTY(meta = (BindWidget)) TObjectPtr ConfirmButton; UPROPERTY(meta = (BindWidget)) TObjectPtr CancelButton; /** Optionnel : une boite dont le texte est fige dans le Blueprint reste valide. */ UPROPERTY(meta = (BindWidgetOptional)) TObjectPtr MessageText; private: UFUNCTION() void HandleConfirmClicked(); UFUNCTION() void HandleCancelClicked(); /** Un tick par frame tant que le rebours tourne. Renvoie false pour s'arreter. */ bool TickCountdown(float DeltaTime); void StopCountdown(); void RefreshCountdownText(); /** * Le rebours passe par le ticker du moteur et non par un timer du monde. * * Deux raisons : l'ecran de reglages s'ouvre depuis le menu de PAUSE, ou les * timers du monde sont geles -- le rebours ne repartirait jamais ; et un * changement de resolution rate doit se defaire meme si le jeu bloque. */ FTSTicker::FDelegateHandle CountdownTickerHandle; FText CountdownMessageFormat; float CountdownRemaining = 0.f; /** Evite de reecrire le texte 60 fois par seconde pour rien. */ int32 LastDisplayedSeconds = -1; };