Files
PalUIExtension/Api/UserWidget.hpp

176 lines
7.8 KiB
C++
Raw Normal View History

2026-09-07 19:33:58 -04:00
#pragma once
#include <Unreal/NameTypes.hpp>
#include "Widget.hpp"
#include "WidgetTree.hpp"
namespace PalUIExtension::Api
{
// ------------------------------------------------------------------
// UUserWidget. Base for every WBP_..._C generated class.
//
// NAMESPACE: this file previously declared itself in
// PalStatusOverlay::Api while every other header in the tree is in
// PalUIExtension::Api. Nothing included it, so nothing caught it -- the
// first #include from dllmain.cpp would have failed to find any of
// these types. Fixed; if you have local code referring to
// PalStatusOverlay::Api::UserWidget, that is why it no longer resolves.
// Static/WidgetRegistry.hpp had the same split and is fixed to match.
//
// GetWidgetFromName is NOT available on this build. It does not appear
// in the UserWidget.h you pulled from UMG.zip, which is UHT's own
// generated listing of every UFUNCTION this class has. Stock UE ships
// it BlueprintPure; this build either stripped it or the dump is
// incomplete.
//
// That is no longer an obstacle rather than a caveat: FindWidget below
// walks the widget tree instead of calling it. See WidgetTree.hpp for
// why walking beats both the missing UFUNCTION and the global-object
// scan that UnrealApiCall::FindInstance does.
// ------------------------------------------------------------------
class UserWidget : public Widget
{
public:
static auto ClassPath() -> const TCHAR* { return STR("/Script/UMG.UserWidget"); }
static auto DebugTag() -> const TCHAR* { return STR("UserWidget"); }
UserWidget() = default;
explicit UserWidget(UObject* Obj)
: Widget(Obj, ClassOf<UserWidget>(), DebugTag()) {}
// ---- the widget tree ------------------------------------------
//
// WidgetTree is a plain UPROPERTY (Transient/DuplicateTransient,
// not a bitfield) -- direct property read, no ProcessEvent frame.
auto GetWidgetTree() const -> WidgetTree
{
return WidgetTree::FromUserWidget(m_obj);
}
// Find a widget by its designer name anywhere in THIS widget's
// tree. Replaces the GetWidgetFromName call this class used to make
// opportunistically.
//
// Scope defaults to ThisTreeOnly -- names are unique per tree, not
// globally, and a nested WBP has its own "Text_PalName" just like
// its parent does. See WidgetTree::EScope.
auto FindWidget(const TCHAR* Name,
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> Widget
{
return GetWidgetTree().Find(Name, Scope);
}
auto FindWidgetObject(const TCHAR* Name,
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> UObject*
{
return GetWidgetTree().FindObjectByName(Name, Scope);
}
// Typed variant: UserWidget{W}.FindWidgetAs<CanvasPanel>(STR("Canvas_Base"))
template <typename T>
auto FindWidgetAs(const TCHAR* Name,
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T
{
return GetWidgetTree().FindAs<T>(Name, Scope);
}
// ---- bound widget properties ------------------------------------
//
// The OTHER way to reach a child, and the cheaper one when it
// applies. UMG promotes any widget marked "Is Variable" in the
// designer to a UPROPERTY on the generated class, so the pointer
// sits at a fixed offset -- no walk at all. PalDetailWidget's
// CanvasBase()/TextPalName()/etc. are exactly this.
//
// The catch, and the reason FindWidget still earns its place: this
// only works for widgets that were marked as variables. Anything
// left unpromoted (most auto-named CanvasPanel_N containers) has no
// property and can only be found by walking.
auto BoundObject(const TCHAR* Name) const -> UObject*
{
return TryObjectProperty(Name);
}
// Property first, tree walk second. Use this when you do not know
// whether a given child was promoted to a variable, and do not want
// to care.
auto ChildObject(const TCHAR* Name,
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> UObject*
{
if (UObject* Bound = BoundObject(Name)) return Bound;
return FindWidgetObject(Name, Scope);
}
template <typename T>
auto Child(const TCHAR* Name,
WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T
{
UObject* O = ChildObject(Name, Scope);
if (!O)
{
Output::send<LogLevel::Warning>(
STR("[{}] no child named '{}' (neither a bound variable nor in the tree)\n"),
m_tag, Name);
return T{};
}
if (!ObjectIsA<T>(O))
{
Output::send<LogLevel::Warning>(
STR("[{}] child '{}' resolved to {}, which is not a {}\n"),
m_tag, Name, O->GetFullName(), T::DebugTag());
return T{};
}
return T{ O };
}
// ---- viewport lifecycle -------------------------------------------
auto AddToViewport(int32 ZOrder) -> bool
{
struct P { int32 ZOrder; } p{ ZOrder };
return Call(STR("AddToViewport"), p);
}
auto RemoveFromViewport() -> bool { return CallVoid(STR("RemoveFromViewport")); }
auto GetIsVisible() -> std::optional<bool> { return CallForReturn<bool>(STR("GetIsVisible")); }
// BlueprintImplementableEvent. Dispatch is by the object's own
// class chain regardless of native-vs-event, so this call works
// the same either way -- but if no Blueprint graph overrides it,
// the engine's default thunk hands back bool's zero value (false),
// not an error. A false here does not prove non-interactable.
auto IsInteractable() -> std::optional<bool> { return CallForReturn<bool>(STR("IsInteractable")); }
// ---- cosmetic setters ----------------------------------------------
auto SetPadding(FMargin_ InPadding) -> bool
{
struct P { FMargin_ InPadding; } p{ InPadding };
return Call(STR("SetPadding"), p);
}
auto SetColorAndOpacity(FLinearColor_ InColorAndOpacity) -> bool
{
struct P { FLinearColor_ InColorAndOpacity; } p{ InColorAndOpacity };
return Call(STR("SetColorAndOpacity"), p);
}
auto SetForegroundColor(FSlateColor_ InForegroundColor) -> bool
{
struct P { FSlateColor_ InForegroundColor; } p{ InForegroundColor };
return Call(STR("SetForegroundColor"), p);
}
// ---- plain properties ------------------------------------------
auto GetPriority() const -> std::optional<int32> { return PropertyValue<int32>(STR("Priority")); }
auto GetColorAndOpacity()const -> std::optional<FLinearColor_>{ return PropertyValue<FLinearColor_>(STR("ColorAndOpacity")); }
auto GetPaddingValue() const -> std::optional<FMargin_> { return PropertyValue<FMargin_>(STR("Padding")); }
// bIsFocusable / bStopAction / bHasScriptImplementedTick /
// bHasScriptImplementedPaint are packed uint8:1 bitfields -- not
// exposed, same reasoning as UWidget::bIsEnabled in Widget.hpp.
// No UFUNCTION getter exists for any of them either; there is no
// clean substitute, only a byte read and a manual mask.
protected:
UserWidget(UObject* Obj, UClass* Expected, const TCHAR* Tag)
: Widget(Obj, Expected, Tag) {}
};
}