// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Input/Events.h" #include "Layout/ArrangedWidget.h" #include "Layout/WidgetPath.h" #include "Misc/Optional.h" #include "Types/SlateEnums.h" #define UE_API SLATEREFLECTOR_API class FNavigationReply; class SWidget; class SWindow; /* * HOW TO DEBUG NAVIGATION EVENTS * Test the static result via FSlateNavigationEventSimulator. * If the simulation doesn't work, your widget is probably not be configured properly. * If the simulation does work, use the "WidgetReflector events log" or the "Console Slate Debugger" to get information on the navigation event while playing. * A - If you do not find the event, check it has been consumed by the viewport or by a widget with OnKeyDown. * B - If you find the event, note if the Reply and Consumed widget are different. * i- If they are different, then use the "WidgetReflector routing" tool to understand why another widget has consumed the routing. * For example, the widget can be disabled or it may have a custom OnNavigation event. * ii- If they are not different, then the GameViewportClient or the application may have consumed the event. * You may also check if you received the focus event with the "WidgetReflector events log" or with the "Console Slate Debugger". */ /** * Simulate navigation attempt and collect the result for later display. * Some elements may steel the navigation attempt dynamically and can be evaluated until evaluated in game. * A list of elements that can still the navigation attempt and modify the result. * 1. The viewport may consume the Navigation. * 2. A widget may override the OnNavigation function and return a different result dynamically. * 3. A widget can change its "enabled" and "supports keyboard focus" flags dynamically during gameplay. * 4. The GameViewportClient may consume the event via the CustomNavigationEvent. * 5. The application or a widget can consume the "set focus" event. Resulting in different behavior. */ class FSlateNavigationEventSimulator { public: DECLARE_DELEGATE_RetVal_TwoParams(TOptional, FHandleNavigation, uint32 /*UserIndex*/, const TSharedPtr& /*InDestination*/); enum class ENavigationStyle { FourCardinalDirections, ConceptualNextAndPrevious, }; static UE_API FText ToText(ENavigationStyle NavigationStyle); enum class ERoutedReason { BoundaryRule, Window, LastWidget, }; static UE_API FText ToText(ERoutedReason RoutedReason); struct FSimulatedReply { FSimulatedReply() = default; FSimulatedReply(const FNavigationReply&); TSharedPtr EventHandler; TSharedPtr FocusRecipient; EUINavigationRule BoundaryRule; }; struct FSimulationResult { /** From where the navigation started. */ FWidgetPath NavigationSource; /** The result of the navigation simulation. */ FWidgetPath NavigationDestination; // The navigation type EUINavigation NavigationType = EUINavigation::Invalid; /** The reply of received from the routed widget. */ FSimulatedReply NavigationReply; /** The reason we choose that boundary widget. */ ERoutedReason RoutedReason = ERoutedReason::LastWidget; /** Base on the boundary rule, the widget that should receive the focus. */ TSharedPtr WidgetThatShouldReceivedFocus; /** The widget that will received the focus. */ TSharedPtr FocusedWidgetPath; /** Can not return a result since it would involve calling a dynamic callback. */ bool bIsDynamic = false; /** The event is handle, even if the destination widget is nullptr or the Viewport->HandleNavigation return false. */ bool bAlwaysHandleNavigationAttempt = false; /** Was able to find the widget to focus. */ bool bCanFindWidgetForSetFocus = false; /** Does the routed widget has navigation meta data. */ bool bRoutedHandlerHasNavigationMeta = false; /** Handled by the viewport. See OnViewportHandleNavigation. */ bool bHandledByViewport = false; bool IsHandled() const { return NavigationDestination.IsValid() || bAlwaysHandleNavigationAttempt || bHandledByViewport; } bool IsValid() const { return NavigationType != EUINavigation::Invalid; } }; public: /** * Simulate for each widgets that are enabled and support keyboard focus */ UE_API TArray SimulateForEachWidgets(const FWidgetPath& WidgetPath, int32 UserIndex, ENavigationGenesis Genesis, ENavigationStyle Navigation); UE_API TArray SimulateForEachWidgets(const TSharedRef& Window, int32 UserIndex, ENavigationGenesis Genesis, ENavigationStyle Navigation); UE_API TArray SimulateForEachWidgets(const FWidgetPath& WidgetPath, int32 UserIndex, ENavigationGenesis Genesis, EUINavigation Navigation); UE_API TArray SimulateForEachWidgets(const TSharedRef& Window, int32 UserIndex, ENavigationGenesis Genesis, EUINavigation Navigation); /** * Simulate a navigation event for a widget */ UE_API TArray Simulate(const FWidgetPath& WidgetPath, int32 UserIndex, ENavigationGenesis Genesis, ENavigationStyle Navigation); UE_API FSimulationResult Simulate(const FWidgetPath& WidgetPath, int32 UserIndex, ENavigationGenesis Genesis, EUINavigation Navigation); public: /** Once the destination widget has been decided, the viewport may handled the event. */ FHandleNavigation OnViewportHandleNavigation; private: FSimulationResult InterpretResult(const FWidgetPath& WidgetPath, const FNavigationEvent& NavigationEvent, const FNavigationReply& Reply, const FArrangedWidget& BoundaryWidget); }; #undef UE_API