// Fill out your copyright notice in the Description page of Project Settings. #include "ConfirmDialogWidget.h" #include "Components/Button.h" #include "Components/TextBlock.h" #include "InputCoreTypes.h" UConfirmDialogWidget::UConfirmDialogWidget(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { // Sans ce drapeau le widget ne peut pas prendre le focus clavier, et // NativeOnKeyDown ne serait jamais appele. Il ne se change qu'a la // construction, d'ou sa presence ici et pas dans Open(). SetIsFocusable(true); } void UConfirmDialogWidget::NativeConstruct() { Super::NativeConstruct(); if (ConfirmButton) { ConfirmButton->OnClicked.AddDynamic(this, &UConfirmDialogWidget::HandleConfirmClicked); } if (CancelButton) { CancelButton->OnClicked.AddDynamic(this, &UConfirmDialogWidget::HandleCancelClicked); } } void UConfirmDialogWidget::Open(const FText& InMessage) { // Une ouverture simple annule un rebours laisse par la precedente. StopCountdown(); if (MessageText) { MessageText->SetText(InMessage); } SetVisibility(ESlateVisibility::Visible); // Le focus doit etre repris a CHAQUE ouverture : le refermer le rend au // menu, et une boite visible mais sans focus ignorerait le clavier. SetKeyboardFocus(); } void UConfirmDialogWidget::OpenWithCountdown(const FText& InMessageFormat, float Seconds) { StopCountdown(); CountdownMessageFormat = InMessageFormat; CountdownRemaining = FMath::Max(Seconds, 1.f); LastDisplayedSeconds = -1; RefreshCountdownText(); SetVisibility(ESlateVisibility::Visible); SetKeyboardFocus(); CountdownTickerHandle = FTSTicker::GetCoreTicker().AddTicker( FTickerDelegate::CreateUObject(this, &UConfirmDialogWidget::TickCountdown)); } void UConfirmDialogWidget::Close() { StopCountdown(); SetVisibility(ESlateVisibility::Collapsed); } void UConfirmDialogWidget::StopCountdown() { if (CountdownTickerHandle.IsValid()) { FTSTicker::GetCoreTicker().RemoveTicker(CountdownTickerHandle); CountdownTickerHandle.Reset(); } } void UConfirmDialogWidget::RefreshCountdownText() { if (!MessageText) { return; } // CeilToInt : on affiche "10" pendant la premiere seconde et "1" pendant la // derniere. Avec un arrondi classique on afficherait "0" une demi-seconde // avant que ca se declenche, ce qui donne l'impression d'un bug. const int32 Seconds = FMath::CeilToInt(CountdownRemaining); if (Seconds == LastDisplayedSeconds) { return; } LastDisplayedSeconds = Seconds; MessageText->SetText(FText::Format(CountdownMessageFormat, FText::AsNumber(Seconds))); } bool UConfirmDialogWidget::TickCountdown(float DeltaTime) { CountdownRemaining -= DeltaTime; if (CountdownRemaining > 0.f) { RefreshCountdownText(); // true : on veut etre rappele a la frame suivante. return true; } // On coupe le ticker AVANT de diffuser : un abonne qui rouvre une boite // dans son handler doit pouvoir relancer un rebours. StopCountdown(); OnCancelled.Broadcast(); return false; } FReply UConfirmDialogWidget::NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) { const FKey Key = InKeyEvent.GetKey(); // La touche "retour" de la manette en plus d'Echap : Slate y fait pointer le // bouton retour de la plateforme (B sur Xbox, Rond sur PlayStation). Une // seule ligne et le clavier comme la manette sont couverts, sans avoir a // enumerer les touches de chaque constructeur. // // GetVirtualKey() et non la constante EKeys::Virtual_Back, depreciee en 5.8 : // la resolution se fait desormais a l'appel, donc a chaud selon la manette // reellement branchee. if (bCancelOnEscape && (Key == EKeys::Escape || Key == EKeys::Virtual_Gamepad_Back.GetVirtualKey())) { StopCountdown(); OnCancelled.Broadcast(); // Handled et non Unhandled : sans ca la touche continue de remonter et // serait consommee une seconde fois par ce qui se trouve derriere. return FReply::Handled(); } if (bConfirmOnEnter && (Key == EKeys::Enter || Key == EKeys::Virtual_Gamepad_Accept.GetVirtualKey())) { StopCountdown(); OnConfirmed.Broadcast(); return FReply::Handled(); } return Super::NativeOnKeyDown(InGeometry, InKeyEvent); } void UConfirmDialogWidget::HandleConfirmClicked() { // Le joueur a repondu : le rebours n'a plus lieu d'etre, et le laisser // tourner ferait arriver un OnCancelled apres coup. StopCountdown(); OnConfirmed.Broadcast(); } void UConfirmDialogWidget::HandleCancelClicked() { StopCountdown(); OnCancelled.Broadcast(); } void UConfirmDialogWidget::NativeDestruct() { // Le ticker du moteur survit au widget : sans ce retrait, il rappellerait // une methode sur un objet detruit. StopCountdown(); if (ConfirmButton) { ConfirmButton->OnClicked.RemoveDynamic(this, &UConfirmDialogWidget::HandleConfirmClicked); } if (CancelButton) { CancelButton->OnClicked.RemoveDynamic(this, &UConfirmDialogWidget::HandleCancelClicked); } Super::NativeDestruct(); }