diff --git a/Source/StevesUEHelpers/Private/StevesUI/MenuBase.cpp b/Source/StevesUEHelpers/Private/StevesUI/MenuBase.cpp index d93421b..208685e 100644 --- a/Source/StevesUEHelpers/Private/StevesUI/MenuBase.cpp +++ b/Source/StevesUEHelpers/Private/StevesUI/MenuBase.cpp @@ -5,6 +5,7 @@ #include "StevesUEHelpers.h" #include "StevesUI/MenuStack.h" #include "Components/ContentWidget.h" +#include "Kismet/GameplayStatics.h" void UMenuBase::Close(bool bWasCancel) { @@ -13,26 +14,19 @@ void UMenuBase::Close(bool bWasCancel) if (ParentStack.IsValid()) { ParentStack->PopMenuIfTop(this, bWasCancel); + } else + { + // standalone mode + RemoveFromParent(); + PreviousFocusWidget.Reset(); } } void UMenuBase::AddedToStack(UMenuStack* Parent) { ParentStack = MakeWeakObjectPtr(Parent); - if (bEmbedInParentContainer) - EmbedInParent(); - else - AddToViewport(); - SetVisibility(ESlateVisibility::Visible); - auto GS = GetStevesGameSubsystem(GetWorld()); - if (bRequestFocus && - GS && (GS->GetLastInputModeUsed() != EInputMode::Gamepad || GS->GetLastInputModeUsed() != EInputMode::Keyboard)) - { - SetFocusProperly(); - } - - + Open(false); } @@ -73,16 +67,7 @@ void UMenuBase::SupercededInStack() void UMenuBase::RegainedFocusInStack() { - if (bEmbedInParentContainer) - EmbedInParent(); - else - AddToViewport(); - SetVisibility(ESlateVisibility::Visible); - - if (bRequestFocus) - { - SetFocusProperly(); - } + Open(true); } @@ -97,3 +82,61 @@ void UMenuBase::EmbedInParent() UE_LOG(LogStevesUI, Error, TEXT("Cannot embed %s in parent, missing container"), *this->GetName()) } + +void UMenuBase::Open(bool bIsRegain) +{ + if (ParentStack.IsValid() && bEmbedInParentContainer) + EmbedInParent(); + else + AddToViewport(); + SetVisibility(ESlateVisibility::Visible); + + auto PC = GetOwningPlayer(); + switch (InputModeSetting) + { + case EInputModeChange::DoNotChange: + break; + case EInputModeChange::UIOnly: + PC->SetInputMode(FInputModeUIOnly()); + break; + case EInputModeChange::GameAndUI: + PC->SetInputMode(FInputModeGameAndUI()); + break; + case EInputModeChange::GameOnly: + PC->SetInputMode(FInputModeGameOnly()); + break; + } + + switch (MousePointerVisibility) + { + case EMousePointerVisibilityChange::DoNotChange: + break; + case EMousePointerVisibilityChange::Visible: + PC->bShowMouseCursor = true; + break; + case EMousePointerVisibilityChange::Hidden: + PC->bShowMouseCursor = false; + break; + } + + switch (GamePauseSetting) + { + case EGamePauseChange::DoNotChange: + break; + case EGamePauseChange::Paused: + UGameplayStatics::SetGamePaused(GetWorld(), true); + break; + case EGamePauseChange::Unpaused: + UGameplayStatics::SetGamePaused(GetWorld(), false); + break; + } + + auto GS = GetStevesGameSubsystem(GetWorld()); + if (bRequestFocus && + GS && (GS->GetLastInputModeUsed() != EInputMode::Gamepad || GS->GetLastInputModeUsed() != EInputMode::Keyboard)) + { + SetFocusProperly(); + } + + +} diff --git a/Source/StevesUEHelpers/Public/StevesUI/MenuBase.h b/Source/StevesUEHelpers/Public/StevesUI/MenuBase.h index fafc78b..135648d 100644 --- a/Source/StevesUEHelpers/Public/StevesUI/MenuBase.h +++ b/Source/StevesUEHelpers/Public/StevesUI/MenuBase.h @@ -10,10 +10,38 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnMenuClosed, UMenuBase*, Menu, bool, bWasCancelled); -/// This class is one element of the UUiMenuStack and represents one level in the chain of -/// an assumed modal stack. It is responsible for implementing how it gets added -/// to the viewport or parent container, and removed again, and what it does when receiving -/// focus. + +UENUM(BlueprintType) +enum class EInputModeChange : uint8 +{ + DoNotChange UMETA(DisplayName="No Change"), + UIOnly UMETA(DisplayName="UI Only"), + GameAndUI UMETA(DisplayName="Game And UI"), + GameOnly UMETA(DisplayName="Game Only") +}; + +UENUM(BlueprintType) +enum class EMousePointerVisibilityChange : uint8 +{ + DoNotChange UMETA(DisplayName="No Change"), + Visible UMETA(DisplayName="Pointer Visible"), + Hidden UMETA(DisplayName="Pointer Hidden") +}; + +UENUM(BlueprintType) +enum class EGamePauseChange : uint8 +{ + DoNotChange UMETA(DisplayName="No Change"), + Paused UMETA(DisplayName="Pause Game"), + Unpaused UMETA(DisplayName="Unpause Game") +}; + +/// This class is a type of focusable panel designed for menus or other dialogs. +/// It can be added to a UMenuStack to put it in context of a larger navigable group, +/// and if so represents one level in the chain of an assumed modal stack. Use UMenuStack::PushMenuByClass/Object +/// to add an entry of this type to the stack +/// If you use this class standalone instead without a stack, then you must call Open() on this instance to +/// make it add itself to the viewport. UCLASS(Abstract, BlueprintType) class STEVESUEHELPERS_API UMenuBase : public UFocusablePanel { @@ -29,23 +57,48 @@ protected: /// Whether this menu should request focus when it is displayed /// The widget which is focussed will either be the InitialFocusWidget on newly displayed, or /// the previously selected widget if regaining focus - UPROPERTY(EditAnywhere, BlueprintReadWrite) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Focus") bool bRequestFocus = true; - /// Set this property to true if you want this menu to embed itself in the parent UUiMenuStack's MenuContainer + /// Set this property to true if you want this menu to embed itself in the parent UMenuStack's MenuContainer /// If false, this Menu will be added to the viewport independently and can float over other menus in the stack - UPROPERTY(EditAnywhere, BlueprintReadWrite) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Layout") bool bEmbedInParentContainer = true; /// Whether to hide this menu when it's superceded by another in the stack. This property is only relevant /// when bEmbedInParentContainer = false, since only one menu can be embedded at once. - UPROPERTY(EditAnywhere, BlueprintReadWrite) + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior") bool bHideWhenSuperceded = true; - void EmbedInParent(); + /// How this menu should set the input mode when it becomes the top of the stack + /// This can be useful if your menus have variable input settings between levels in the stack + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior") + EInputModeChange InputModeSetting = EInputModeChange::DoNotChange; + + /// How this menu should set the mouse pointer visibility when it becomes the top of the stack + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior") + EMousePointerVisibilityChange MousePointerVisibility = EMousePointerVisibilityChange::DoNotChange; + + /// How this menu should set the game pause state when it becomes top of the stack + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Behavior") + EGamePauseChange GamePauseSetting = EGamePauseChange::DoNotChange; + + virtual void EmbedInParent(); public: - /// Close this menu level + + /** + * @brief Open this menu. You should only call this if you're NOT using this in a UMenuStack, because the stack will + * call it for you when you add this menu to it + + * @param bIsRegainedFocus Set this to true if the reason this menu is opening is that it regained focus in a stack + */ + UFUNCTION(BlueprintCallable) + void Open(bool bIsRegainedFocus = false); + /** + * @brief Close this menu. + * @param bWasCancel Set this to true if the reason for closure was a cancellation action + */ UFUNCTION(BlueprintCallable) void Close(bool bWasCancel);