#pragma once #include #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(), 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(STR("Canvas_Base")) template auto FindWidgetAs(const TCHAR* Name, WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T { return GetWidgetTree().FindAs(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 auto Child(const TCHAR* Name, WidgetTree::EScope Scope = WidgetTree::EScope::ThisTreeOnly) -> T { UObject* O = ChildObject(Name, Scope); if (!O) { Output::send( STR("[{}] no child named '{}' (neither a bound variable nor in the tree)\n"), m_tag, Name); return T{}; } if (!ObjectIsA(O)) { Output::send( 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 { return CallForReturn(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 { return CallForReturn(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 { return PropertyValue(STR("Priority")); } auto GetColorAndOpacity()const -> std::optional{ return PropertyValue(STR("ColorAndOpacity")); } auto GetPaddingValue() const -> std::optional { return PropertyValue(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) {} }; }