Files
integration/Source/Integration/RootCanvas.h

164 lines
5.8 KiB
C
Raw Normal View History

2026-04-21 21:26:06 -04:00
////////////////////////////////////////////////////////////
//
// 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
2026-04-21 21:26:06 -04:00
//
////////////////////////////////////////////////////////////
#pragma once
#include "CoreMinimal.h"
#include "Common.h"
2026-04-22 22:52:04 -04:00
#include "UObject/ObjectKey.h"
2026-04-21 21:26:06 -04:00
#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;
2026-04-22 07:22:55 -04:00
// 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;
}
2026-04-21 21:26:06 -04:00
};
////////////////////////////////////////////////////////////
//
// 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.
2026-04-21 21:26:06 -04:00
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.
2026-04-21 21:26:06 -04:00
TArray<UlxRootCanvasSlot*> GetSortedUserWidgets();
// Return the highest-priority UserWidget slot, or nullptr if there are none.
UlxRootCanvasSlot* GetTopWidget();
2026-04-21 21:26:06 -04:00
// 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();
2026-04-21 21:26:06 -04:00
// 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.
2026-04-21 21:26:06 -04:00
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;
2026-04-21 21:26:06 -04:00
};
////////////////////////////////////////////////////////////
//
// 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()
};