176 lines
7.8 KiB
C++
176 lines
7.8 KiB
C++
#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) {}
|
|
};
|
|
}
|