//////////////////////////////////////////////////////////// // // RootCanvas.h // // Luprex provides a "window management system" for root // widgets. In this system, all top-level widgets have to go // into the root canvas (instead of the viewport). The // window management system monitors the widgets within the // root canvas and continuously updates the z-orders, the // pointer visibility, the mouse capture mode, the keyboard // focus, and enhanced input event routing based on hints // and directives given by the widgets. // // To learn more, read Docs/Luprex-Window-Management.md // //////////////////////////////////////////////////////////// #pragma once #include "CoreMinimal.h" #include "Common.h" #include "UObject/ObjectKey.h" #include "Blueprint/UserWidget.h" #include "Components/CanvasPanel.h" #include "Components/CanvasPanelSlot.h" #include "RootCanvas.generated.h" class UWidget; //////////////////////////////////////////////////////////// // // UlxRootCanvasSlot // //////////////////////////////////////////////////////////// UCLASS() class INTEGRATION_API UlxRootCanvasSlot : public UCanvasPanelSlot { GENERATED_BODY() public: UlxRootCanvasSlot(const FObjectInitializer& ObjectInitializer); // When this window is in front, the mouse pointer is shown and the // viewport does not capture the mouse. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode") bool ShowPointer = false; // When this window is in front, this window's input component blocks // lower-priority input components in the input stack. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode") bool BlockInput = false; // If true, this widget's input component is enabled, which is to say, // that enhanced input events are enabled. If false, enhanced input // events in the Event Graph are deactivated. UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Luprex|Input Mode") bool EnableEnhancedInput = true; // Sequence number assigned when this slot was last brought to front. int32 BringToFrontCount = 0; // Widget Z Ordering: widgets that show the pointer go on top of windows // that don't, and within a group, widgets that have greater BringToFrontCount // go on top. Sorting an array with this puts the top windows at the end // of the array. bool operator<(const UlxRootCanvasSlot &Other) const { if (ShowPointer != Other.ShowPointer) return !ShowPointer; return BringToFrontCount < Other.BringToFrontCount; } }; //////////////////////////////////////////////////////////// // // UlxRootCanvasPanel // //////////////////////////////////////////////////////////// UCLASS() class INTEGRATION_API UlxRootCanvasPanel : public UCanvasPanel { GENERATED_BODY() public: // Add a child to the canvas. Also brings the child to the front // for the first time, ensuring that every widget has a unique // BringToFront counter. UFUNCTION() UlxRootCanvasSlot* AddChildToRootCanvas(UWidget* Content); // Find children of type UserWidget. Return them in a sorted // order, with the bottom window first and the top window last. TArray GetSortedUserWidgets(); // Return the highest-priority UserWidget slot, or nullptr if there are none. UlxRootCanvasSlot* GetTopWidget(); // This function updates several window-management-related properties // which are stored in the UserWidget and the lxRootCanvasSlot. Note that // it is perfectly legal to edit these properties by other means: this // function is one of many valid setters. See the documentation in // Docs/Keyboard-Focus-and-Input-Modes.md for information about how Luprex // window management works. UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management", meta = (DefaultToSelf = "Widget", EnableEnhancedInput = "true")) static void SetWidgetWindowManagement(class UUserWidget *Widget, bool ShowPointer, bool BlockInput, bool EnableEnhancedInput, bool BringToFront, UWidget *DesiredFocusWidget); // Bring the widget to the front of the Z order within its group. // This is implemented using the BringToFront counter. UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management", meta = (DefaultToSelf = "Widget")) static void BringToFront(class UUserWidget *Widget); // Recompute and apply ZOrder values for all slots based on ShowPointer and // BringToFrontCount. Must be called from a context where SetZOrder is safe - // i.e. not during OnConstruct. void UpdateZOrders(); // Fetch the UlxRootCanvasSlot for a widget that is parented to a // UlxRootCanvasPanel. Returns nullptr via the WrongType exec pin // if the widget isn't a root widget (no slot, or slot is not a // UlxRootCanvasSlot). UFUNCTION(BlueprintCallable, Category = "Luprex|Window Management", meta = (DefaultToSelf = "Widget", ExpandEnumAsExecs = "Result")) static UlxRootCanvasSlot *GetRootCanvasSlot(class UUserWidget *Widget, ElxSuccessOrWrongType &Result); protected: // We inherit most of our code from CanvasPanel. This causes the // CanvasPanel code to allocate slots of type UlxRootCanvasSlot. virtual UClass* GetSlotClass() const override; // We override OnSlotAdded in order to be able to be able to // fully finish initializing the slot before the widget // OnConstruct method has a chance to execute. virtual void OnSlotAdded(UPanelSlot* InSlot) override; // Monotonic counter incremented each time any slot is brought to front. int32 BringToFrontCounter = 0; }; //////////////////////////////////////////////////////////// // // UlxRootWidget // // A trivial concrete UUserWidget subclass used by the // PlayerController to host the root UlxRootCanvasPanel. // UUserWidget itself is marked Abstract in UMG, so we need // a non-abstract class to instantiate. // //////////////////////////////////////////////////////////// UCLASS() class INTEGRATION_API UlxRootWidget : public UUserWidget { GENERATED_BODY() };