UMG - 18:尺寸框里的按钮属性,按钮包含文本框,以此来了解按钮的尺寸属性与颜色属性

(81)场景搭建

在这里插入图片描述

(82) 按钮中填充的含义

在这里插入图片描述

(83)按钮只有这外部填充与内部填充(普通与压下填充两种区分)这两个属性

在这里插入图片描述

(84)以下依然是取消按钮的内外填充,开始测试其中文本框的填充属性(这样是为了避免填充尺寸之间的互相干扰)

在这里插入图片描述

++可见,文本框也一样存在外部填充与内部填充
++ 文本框的行高百分比数值

在这里插入图片描述

++ 资料

在这里插入图片描述

++再记录文本框的两个数值属性
在这里插入图片描述

(85)接下来继续讨论按钮与其中文本的颜色

在这里插入图片描述

(86)以下把按钮中文本的颜色归零重置,开始测试按钮里的颜色属性

在这里插入图片描述

(87)接着把按钮中的文本换成图片,看看效果

在这里插入图片描述

++按钮里图片的颜色

在这里插入图片描述

(88)把按钮里的图片撤换,换上文本子控件, 0 填充,测试按钮的背景属性

++ 按钮的图片背景渲染是总会覆盖填充区的
在这里插入图片描述

++ 按钮轮廓
在这里插入图片描述

(89) 至此,按钮自身的尺寸与颜色属性,及按钮子控件的尺寸与颜色属性,都总结与测试完了。而且这些尺寸与颜色属性很常见,很多别的控件也有同样的这些属性。 以最简单的按钮开始,由易到难,来了解界面里控件的属性。

(90)

谢谢

void UTFGroupItemList::RefreshGroupItemList() { if (RefreshHandle.IsValid()) { return; } RefreshGroupItems(); RefreshHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateWeakLambda(this, [this](float DeltaTime) { if (!GetVisibleItems().IsEmpty()) { InventoryItems->SetListItems(VisibleListItems); ChangeSelectedSlot(); RefreshHandle.Reset(); return true; } return false; })); } 这是刷新道具列表的具体代码实现,讲解一下这段代码,下面还提供调用堆栈: > UnrealEditor-Force.dll!UTFGroupItemList::RefreshGroupItemList() 行 18 C++ UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) 行 7192 C++ UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) 行 2175 C++ UnrealEditor-UnLua.dll!FFunctionDesc::CallUE(lua_State * L, int NumParams, void * Userdata) 行 229 C++ UnrealEditor-UnLua.dll!Class_CallUFunction(lua_State * L) 行 1280 C++ [外部代码] UnrealEditor-UnLua.dll!FFunctionDesc::CallLuaInternal(lua_State * L, void * InParams, FOutParmRec * OutParams, void * RetValueAddress) 行 459 C++ UnrealEditor-UnLua.dll!FFunctionDesc::CallLua(lua_State * L, int LuaRef, void * Params, UObject * Self) 行 164 C++ UnrealEditor-UnLua.dll!UnLua::FDelegateRegistry::Execute(const ULuaDelegateHandler * Handler, void * Params) 行 220 C++ UnrealEditor-UnLua.dll!ULuaDelegateHandler::ProcessEvent(UFunction * Function, void * Parms) 行 79 C++ [内联架] UnrealEditor-CommonUI.dll!TScriptDelegate<FNotThreadSafeNotCheckedDelegateMode>::ProcessDelegate(void *) 行 448 C++ UnrealEditor-CommonUI.dll!TMulticastScriptDelegate<FNotThreadSafeDelegateMode>::ProcessMulticastDelegate<UObject>(void * Parameters) 行 913 C++ [内联架] UnrealEditor-CommonUI.dll!UCommonTabListWidgetBase::FOnTabSelected_DelegateWrapper(const TMulticastScriptDelegate<FNotThreadSafeDelegateMode> &) 行 159 C++ [内联架] UnrealEditor-CommonUI.dll!UCommonTabListWidgetBase::FOnTabSelected::Broadcast(FName) 行 51 C++ UnrealEditor-CommonUI.dll!UCommonTabListWidgetBase::HandleTabButtonSelected(UCommonButtonBase * SelectedTabButton, int ButtonIndex) 行 445 C++ UnrealEditor-CommonUI.dll!UCommonTabListWidgetBase::execHandleTabButtonSelected(UObject * Context, FFrame & Stack, void * const Z_Param__Result) 行 829 C++ UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) 行 7192 C++ UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) 行 2175 C++ [内联架] UnrealEditor-CommonUI.dll!TScriptDelegate<FNotThreadSafeNotCheckedDelegateMode>::ProcessDelegate(void *) 行 448 C++ UnrealEditor-CommonUI.dll!TMulticastScriptDelegate<FNotThreadSafeDelegateMode>::ProcessMulticastDelegate<UObject>(void * Parameters) 行 913 C++ [内联架] UnrealEditor-CommonUI.dll!FSimpleButtonBaseGroupDelegate_DelegateWrapper(const TMulticastScriptDelegate<FNotThreadSafeDelegateMode> &) 行 71 C++ [内联架] UnrealEditor-CommonUI.dll!FSimpleButtonBaseGroupDelegate::Broadcast(UCommonButtonBase *) 行 10 C++ UnrealEditor-CommonUI.dll!UCommonButtonGroupBase::OnSelectionStateChangedBase(UCommonButtonBase * BaseButton, bool bIsSelected) 行 335 C++ UnrealEditor-CommonUI.dll!UCommonButtonGroupBase::execOnSelectionStateChangedBase(UObject * Context, FFrame & Stack, void * const Z_Param__Result) 行 780 C++ UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) 行 7192 C++ UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) 行 2175 C++ [内联架] UnrealEditor-CommonUI.dll!TScriptDelegate<FNotThreadSafeNotCheckedDelegateMode>::ProcessDelegate(void *) 行 448 C++ UnrealEditor-CommonUI.dll!TMulticastScriptDelegate<FNotThreadSafeDelegateMode>::ProcessMulticastDelegate<UObject>(void * Parameters) 行 913 C++ [内联架] UnrealEditor-CommonUI.dll!FCommonSelectedStateChangedBase_DelegateWrapper(const TMulticastScriptDelegate<FNotThreadSafeDelegateMode> &) 行 1284 C++ [内联架] UnrealEditor-CommonUI.dll!FCommonSelectedStateChangedBase::Broadcast(UCommonButtonBase *) 行 283 C++ UnrealEditor-CommonUI.dll!UCommonButtonBase::NativeOnSelected(bool bBroadcast) 行 1437 C++ UnrealEditor-CommonUI.dll!UCommonButtonBase::SetSelectedInternal(bool bInSelected, bool bAllowSound, bool bBroadcast) 行 963 C++ UnrealEditor-CommonUI.dll!UCommonButtonGroupBase::OnWidgetAdded(UWidget * NewWidget) 行 224 C++ UnrealEditor-CommonUI.dll!UCommonWidgetGroupBase::AddWidget(UWidget * InWidget) 行 17 C++ UnrealEditor-CommonUI.dll!UCommonTabListWidgetBase::RegisterTab(FName TabNameID, TSubclassOf<UCommonButtonBase> ButtonWidgetType, UWidget * ContentWidget, const int TabIndex) 行 107 C++ UnrealEditor-Force.dll!UTFTabListWidgetBase::RegisterDynamicTab(const FTFTabDescriptor & TabDescriptor) 行 77 C++ UnrealEditor-Force.dll!UTFTabListWidgetBase::execRegisterDynamicTab(UObject * Context, FFrame & Stack, void * const Z_Param__Result) 行 684 C++ UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) 行 7192 C++ UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) 行 2175 C++ UnrealEditor-UnLua.dll!FFunctionDesc::CallUE(lua_State * L, int NumParams, void * Userdata) 行 229 C++ UnrealEditor-UnLua.dll!Class_CallUFunction(lua_State * L) 行 1280 C++ [外部代码] UnrealEditor-UnLua.dll!FFunctionDesc::CallLuaInternal(lua_State * L, void * InParams, FOutParmRec * OutParams, void * RetValueAddress) 行 459 C++ UnrealEditor-UnLua.dll!FFunctionDesc::CallLua(lua_State * L, __int64 FunctionRef, __int64 SelfRef, FFrame & Stack, void * const Z_Param__Result) 行 148 C++ UnrealEditor-UnLua.dll!UnLua::FFunctionRegistry::Invoke(ULuaFunction * Function, UObject * Context, FFrame & Stack, void * const Z_Param__Result) 行 84 C++ UnrealEditor-UnLua.dll!ULuaFunction::execCallLua(UObject * Context, FFrame & Stack, void * const Z_Param__Result) 行 36 C++ UnrealEditor-CoreUObject.dll!UFunction::Invoke(UObject * Obj, FFrame & Stack, void * const Z_Param__Result) 行 7192 C++ UnrealEditor-CoreUObject.dll!UObject::ProcessEvent(UFunction * Function, void * Parms) 行 2175 C++ [内联架] UnrealEditor-UMG.dll!UUserWidget::Construct() 行 1052 C++ UnrealEditor-UMG.dll!UUserWidget::NativeConstruct() 行 1794 C++ UnrealEditor-CommonUI.dll!UCommonActivatableWidget::NativeConstruct() 行 34 C++ UnrealEditor-CommonUI.dll!UCommonUserWidget::OnWidgetRebuilt() 行 184 C++ UnrealEditor-UMG.dll!UWidget::TakeWidget_Private(TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructMethod) 行 1054 C++ UnrealEditor-CommonUI.dll!UWidget::TakeDerivedWidget<SObjectWidget>(TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructMethod) 行 828 C++ UnrealEditor-CommonUI.dll!FUserWidgetPool::AddActiveWidgetInternal<UCommonActivatableWidget>(TSubclassOf<UCommonActivatableWidget> WidgetClass, TFunctionRef<TSharedPtr<SObjectWidget,1> __cdecl(UUserWidget *,TSharedRef<SWidget,1>)> ConstructWidgetFunc) 行 146 C++ [内联架] UnrealEditor-CommonUI.dll!FUserWidgetPool::GetOrCreateInstance(TSubclassOf<UCommonActivatableWidget>) 行 62 C++ UnrealEditor-CommonUI.dll!UCommonActivatableWidgetContainerBase::AddWidgetInternal(TSubclassOf<UCommonActivatableWidget> ActivatableWidgetClass, TFunctionRef<void __cdecl(UCommonActivatableWidget &)> InitFunc) 行 178 C++ [内联架] UnrealEditor-Force.dll!UCommonActivatableWidgetContainerBase::AddWidget(TSubclassOf<UCommonActivatableWidget>) 行 52 C++ UnrealEditor-Force.dll!UPrimaryGameLayout::PushWidgetToLayerStack<UCommonActivatableWidget>(FGameplayTag LayerName, UClass * ActivatableWidgetClass, FName GlobalName, TFunctionRef<void __cdecl(UCommonActivatableWidget &)> InitInstanceFunc) 行 120 C++ UnrealEditor-Force.dll!UPrimaryGameLayout::PushWidgetToLayerStackAsync::__l2::<lambda_1>::operator()() 行 77 C++ [内联架] UnrealEditor-Force.dll!Invoke(UPrimaryGameLayout::PushWidgetToLayerStackAsync::__l2::<lambda_1> &) 行 47 C++ [内联架] UnrealEditor-Force.dll!UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int>>::ApplyAfter(UPrimaryGameLayout::PushWidgetToLayerStackAsync::__l2::<lambda_1> &) 行 317 C++ UnrealEditor-Force.dll!TWeakBaseFunctorDelegateInstance<UPrimaryGameLayout,void __cdecl(void),FDefaultDelegateUserPolicy,`UPrimaryGameLayout::PushWidgetToLayerStackAsync<UCommonActivatableWidget>'::`2'::<lambda_1>>::ExecuteIfSafe() 行 971 C++ [内联架] UnrealEditor-Engine.dll!TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfBound() 行 634 C++ UnrealEditor-Engine.dll!FStreamableDelegateDelayHelper::Tick(float DeltaTime) 行 179 C++ UnrealEditor-Engine.dll!FTickableGameObject::TickObjects(UWorld * World, ELevelTick LevelTickType, bool bIsPaused, float DeltaSeconds) 行 196 C++ UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) 行 2193 C++ UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) 行 550 C++ UnrealEditor.exe!FEngineLoop::Tick() 行 5869 C++ [内联架] UnrealEditor.exe!EngineTick() 行 69 C++ UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) 行 188 C++ UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) 行 266 C++ UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) 行 317 C++ [外部代码] 最后解释一下,调用堆栈中[外部代码]和[内联架]这两个标识代表什么意思?
10-22
这段代码 `UTFGroupItemList::RefreshGroupItemList()` 是一个典型的 **延迟刷新(Deferred Refresh)** 机制,常见于 Unreal Engine 的 UI 系统中,尤其是在处理列表项更新时,需要等待某些异步条件满足后再执行实际的 UI 更新操作。下面我将逐行解析代码逻辑,并结合调用堆栈分析其上下文行为。 --- ### ✅ 函数代码详解 ```cpp void UTFGroupItemList::RefreshGroupItemList() { if (RefreshHandle.IsValid()) { return; } RefreshGroupItems(); RefreshHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateWeakLambda(this, [this](float DeltaTime) { if (!GetVisibleItems().IsEmpty()) { InventoryItems->SetListItems(VisibleListItems); ChangeSelectedSlot(); RefreshHandle.Reset(); return true; } return false; })); } ``` #### 🔍 逐行解释: 1. **防止重复注册 Ticker** ```cpp if (RefreshHandle.IsValid()) { return; } ``` - `RefreshHandle` 是一个 `FTSTicker::FDelegateHandle` 类型的对象,用于保存通过 `FTSTicker` 注册的定时回调句柄。 - 如果已经存在有效的句柄,说明当前已经有正在运行的刷新任务,直接返回,避免重复添加 ticker 回调。 2. **触发数据层刷新** ```cpp RefreshGroupItems(); ``` - 调用私有函数 `RefreshGroupItems()`,这个函数的作用通常是重新计算或填充 `VisibleListItems` 这类可见项的数据。 - 注意:此时可能还没有完成数据准备,尤其是如果依赖异步加载或布局尚未构建完毕。 3. **注册 Core Ticker 回调** ```cpp RefreshHandle = FTSTicker::GetCoreTicker().AddTicker(...) ``` - 使用 `FTSTicker::GetCoreTicker()` 获取主线程核心 ticker(每帧执行一次),并添加一个 lambda 回调。 - `FTickerDelegate::CreateWeakLambda(this, [...])` 创建一个弱引用 Lambda,防止因对象生命周期问题导致崩溃(即当 `this` 被销毁后不会调用无效指针)。 4. **Lambda 回调逻辑** ```cpp [this](float DeltaTime) -> bool { if (!GetVisibleItems().IsEmpty()) { InventoryItems->SetListItems(VisibleListItems); ChangeSelectedSlot(); RefreshHandle.Reset(); // 清理句柄 return true; // 返回 true 表示继续保留该 ticker(但这不会再执行) } return false; // 返回 false 表示移除此 ticker } ``` - 每一帧都会检查是否已有可见项(`GetVisibleItems().IsEmpty()`)。 - 只有在 `VisibleListItems` 非空时才进行真正的 UI 列表设置。 - 成功设置后: - `SetListItems(...)`:将数据绑定到 UMG 列表组件(如 `UListView` 或 `UniformGridPanel`); - `ChangeSelectedSlot()`:可能用于选中某个默认槽位; - `RefreshHandle.Reset()`:主动清除句柄,确保这个 ticker 不再被调用; - 返回 `true`:虽然返回了 true,但由于我们手动调用了 `Reset()`,下一次 ticker 执行前就会被引擎自动移除(因为句柄无效了); - 若条件不满足(列表仍为空),则返回 `false`,ticker 继续等待下一帧。 > ⚠️ 注意:这种模式非常典型地用于解决“**UI 组件未初始化却尝试赋值**”的问题。例如,在蓝图构造或 Lua 初始化过程中,`InventoryItems` 可能还未被正确创建(Slate 层未生成),直接调用 `SetListItems` 会失败甚至崩溃。因此使用 ticker 延迟到“可用时”再设置。 --- ### 🧩 调用堆栈分析 从你的调用堆栈可以看出: - 最初是由 Lua 脚本(通过 UnLua 插件)调用了某个函数; - 触发了一个多播委托(Multicast Delegate)`FOnTabSelected`; - 最终进入 `UTFGroupItemList::RefreshGroupItemList()` 的 C++ 实现; - 整个流程是典型的 **Unreal + Lua 混合开发路径**,其中事件由用户交互(切换标签页)触发,最终驱动道具列表刷新。 关键节点包括: | 调用层级 | 说明 | |--------|------| | `ULuaDelegateHandler::ProcessEvent` → `UnLua::FDelegateRegistry::Execute` | Lua 绑定的 delegate 被触发 | | `UCommonTabListWidgetBase::HandleTabButtonSelected` | Tab 切换事件处理 | | `UCommonButtonGroupBase::OnSelectionStateChangedBase` | 按钮选择状态变更通知 | | `UCommonButtonBase::NativeOnSelected` | 某个按钮被选中 | | `UTFTabListWidgetBase::RegisterDynamicTab` | 动态注册 Tab(可能是初始化的一部分) | | `PushWidgetToLayerStackAsync` → 构造 Widget | 异步加载 UI 控件,构造期间可能无法立即访问子控件 | | `TakeWidget_Private` / `Construct` | UMG 构建过程开始 | 这表明: 👉 `RefreshGroupItemList()` 很可能是在一个新的 UI 页面被打开(比如背包、技能栏等)时被调用,但由于 UI 尚未完全构建好(Slate 层未 Ready),所以不能立刻设置 `InventoryItems` 的内容,必须延迟到下一帧甚至几帧之后。 --- ### ❓ `[外部代码]` 和 `[内联架]` 的含义 这两个标识出现在 Visual Studio 调用堆栈中,属于调试器对调用链的简化显示方式: #### 1. `[外部代码]` - **含义**:表示当前帧位于你没有源码或符号信息的模块中(如系统库、第三方 DLL、引擎内部非公开代码)。 - **出现位置**:通常出现在 Lua VM 内部(如 `lua_call`)、操作系统调度、或者 UE4 引擎底层汇编/模板展开处。 - **作用**:VS 自动折叠你不关心的部分,提升可读性。 - **能否展开?** 一般不能看到具体源码,除非你有完整 `.pdb` 符号文件并配置了源服务器。 #### 2. `[内联架]` - **含义**:这是一个被 **编译器内联优化过** 的函数调用。 - 当函数体很小(如 getter/setter),编译器会将其“展开”到调用处以提高性能,不再产生真正的函数调用栈帧。 - 因此调试器无法为它分配独立栈帧,但仍能追踪到原始代码位置。 - **举例**: ```cpp FORCEINLINE void Broadcast(FName TabName) { Super::Broadcast(TabName); } ``` 上面这种宏定义或内联函数就可能显示为 `[内联架]`。 > 💡 提示:你可以右键调用堆栈 → “显示外部代码”,来展开 `[外部代码]` 查看更多细节(但多数情况下只是汇编或无意义中间层)。 --- ### ✅ 总结:为什么这样设计? | 问题 | 解决方案 | |------|----------| | 构造期无法访问 UMG 子控件(如 `InventoryItems == nullptr`) | 使用 `FTSTicker` 延迟到下一帧 | | 多次刷新请求应合并防重入 | 使用 `RefreshHandle.IsValid()` 防止重复注册 | | 对象析构时避免野指针回调 | 使用 `CreateWeakLambda(this, ...)` 安全捕获 `this` | | 数据准备 UI 显示不同步 | 在 ticker 中轮询 `GetVisibleItems()` 是否就绪 | 这是一种 **安全、健壮、适用于异步场景的标准做法**,广泛应用于 UEUMG 开发中。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangzhangkeji

谢谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值