claude-code-arcane 2.3.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/README.md +131 -41
- package/agents/game/ue-blueprint-specialist.md +125 -0
- package/agents/game/ue-gas-specialist.md +133 -0
- package/agents/game/ue-umg-specialist.md +131 -0
- package/dist/cli.js +642 -478
- package/docs/SKILLS-CATALOG.md +42 -3
- package/docs/USER-GUIDE.md +28 -54
- package/hooks/detect-unreal.sh +90 -0
- package/package.json +2 -1
- package/profiles/agile.yaml +1 -1
- package/profiles/ai.yaml +1 -1
- package/profiles/android-native.yaml +1 -1
- package/profiles/audio.yaml +1 -1
- package/profiles/backend-dotnet.yaml +1 -1
- package/profiles/backend-go.yaml +1 -1
- package/profiles/backend-nestjs.yaml +1 -1
- package/profiles/backend-nextjs.yaml +1 -1
- package/profiles/backend-ts.yaml +1 -1
- package/profiles/business.yaml +1 -1
- package/profiles/clevel.yaml +1 -1
- package/profiles/clickup.yaml +1 -1
- package/profiles/core.yaml +1 -1
- package/profiles/database.yaml +1 -1
- package/profiles/design.yaml +1 -1
- package/profiles/docs.yaml +1 -1
- package/profiles/ecommerce.yaml +1 -1
- package/profiles/finance.yaml +1 -1
- package/profiles/flutter.yaml +1 -1
- package/profiles/freelance.yaml +1 -1
- package/profiles/frontend.yaml +1 -1
- package/profiles/infra.yaml +1 -1
- package/profiles/integrations.yaml +1 -1
- package/profiles/ios-native.yaml +1 -1
- package/profiles/jira.yaml +1 -1
- package/profiles/job-hunt.yaml +1 -1
- package/profiles/marketing.yaml +1 -1
- package/profiles/mobile.yaml +1 -1
- package/profiles/regulatory.yaml +1 -1
- package/profiles/second-brain.yaml +1 -1
- package/profiles/security.yaml +1 -1
- package/profiles/self-improving.yaml +1 -1
- package/profiles/statusline.yaml +1 -1
- package/profiles/testing.yaml +1 -1
- package/profiles/unity-design.yaml +1 -1
- package/profiles/unity-dev.yaml +1 -1
- package/profiles/unreal-dev.yaml +97 -0
- package/profiles/visual-novel.yaml +1 -1
- package/rules/gamedev/unreal-code.md +125 -0
- package/skills/arcane-list/SKILL.md +12 -7
- package/skills/install-mcp/SKILL.md +135 -46
- package/skills/install-mcp/references/unreal-operations.md +55 -0
- package/skills/ue-actor-component-architecture/SKILL.md +509 -0
- package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
- package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
- package/skills/ue-ai-navigation/SKILL.md +526 -0
- package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
- package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
- package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
- package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
- package/skills/ue-animation-system/SKILL.md +521 -0
- package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
- package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
- package/skills/ue-async-threading/SKILL.md +433 -0
- package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
- package/skills/ue-async-threading/references/threading-patterns.md +345 -0
- package/skills/ue-audio-system/SKILL.md +527 -0
- package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
- package/skills/ue-blueprints/SKILL.md +134 -0
- package/skills/ue-blueprints/references/communication.md +82 -0
- package/skills/ue-character-movement/SKILL.md +475 -0
- package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
- package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
- package/skills/ue-cpp-foundations/SKILL.md +533 -0
- package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
- package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
- package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
- package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
- package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
- package/skills/ue-data-assets-tables/SKILL.md +527 -0
- package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
- package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
- package/skills/ue-editor-tools/SKILL.md +523 -0
- package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
- package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
- package/skills/ue-game-features/SKILL.md +477 -0
- package/skills/ue-game-features/references/experience-system.md +222 -0
- package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
- package/skills/ue-gameplay-abilities/SKILL.md +518 -0
- package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
- package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
- package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
- package/skills/ue-gameplay-framework/SKILL.md +466 -0
- package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
- package/skills/ue-input-system/SKILL.md +394 -0
- package/skills/ue-input-system/references/cpp-setup.md +131 -0
- package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
- package/skills/ue-input-system/references/input-action-reference.md +731 -0
- package/skills/ue-mass-entity/SKILL.md +476 -0
- package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
- package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
- package/skills/ue-materials-rendering/SKILL.md +526 -0
- package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
- package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
- package/skills/ue-module-build-system/SKILL.md +487 -0
- package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
- package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
- package/skills/ue-naming-conventions/SKILL.md +142 -0
- package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
- package/skills/ue-naming-conventions/references/content-structure.md +83 -0
- package/skills/ue-networking-replication/SKILL.md +452 -0
- package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
- package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
- package/skills/ue-niagara-effects/SKILL.md +531 -0
- package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
- package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
- package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
- package/skills/ue-packaging/SKILL.md +116 -0
- package/skills/ue-physics-collision/SKILL.md +423 -0
- package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
- package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
- package/skills/ue-procedural-generation/SKILL.md +505 -0
- package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
- package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
- package/skills/ue-project-context/SKILL.md +305 -0
- package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
- package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
- package/skills/ue-serialization-savegames/SKILL.md +524 -0
- package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
- package/skills/ue-state-trees/SKILL.md +489 -0
- package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
- package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
- package/skills/ue-testing-debugging/SKILL.md +520 -0
- package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
- package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
- package/skills/ue-ui-umg-slate/SKILL.md +431 -0
- package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
- package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
- package/skills/ue-world-level-streaming/SKILL.md +518 -0
- package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
- package/templates/gamedev/unreal-project-context.md +107 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
# Detail Customization Patterns
|
|
2
|
+
|
|
3
|
+
Reference for common `IDetailCustomization` and `IPropertyTypeCustomization` patterns with Slate widget examples. All patterns are based on the real UE source interfaces in `Engine/Source/Editor/PropertyEditor/Public/`.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## IDetailLayoutBuilder Key Methods
|
|
8
|
+
|
|
9
|
+
From `DetailLayoutBuilder.h` (`IDetailLayoutBuilder`):
|
|
10
|
+
|
|
11
|
+
```cpp
|
|
12
|
+
// Edit or create a category
|
|
13
|
+
IDetailCategoryBuilder& EditCategory(
|
|
14
|
+
FName CategoryName,
|
|
15
|
+
const FText& NewLocalizedDisplayName = FText::GetEmpty(),
|
|
16
|
+
ECategoryPriority::Type CategoryType = ECategoryPriority::Default);
|
|
17
|
+
|
|
18
|
+
// Hide an entire category
|
|
19
|
+
void HideCategory(FName CategoryName);
|
|
20
|
+
|
|
21
|
+
// Get a property handle by member name — use GET_MEMBER_NAME_CHECKED for safety
|
|
22
|
+
TSharedRef<IPropertyHandle> GetProperty(
|
|
23
|
+
const FName PropertyPath,
|
|
24
|
+
const UStruct* ClassOutermost = nullptr,
|
|
25
|
+
FName InstanceName = NAME_None) const;
|
|
26
|
+
|
|
27
|
+
// Hide a specific property
|
|
28
|
+
void HideProperty(const TSharedPtr<IPropertyHandle> PropertyHandle);
|
|
29
|
+
|
|
30
|
+
// Get the objects currently being customized
|
|
31
|
+
void GetObjectsBeingCustomized(TArray<TWeakObjectPtr<UObject>>& OutObjects) const;
|
|
32
|
+
|
|
33
|
+
// Force rebuild of the entire detail layout
|
|
34
|
+
void ForceRefreshDetails();
|
|
35
|
+
|
|
36
|
+
// Add a property to its auto-detected category (preserves original category)
|
|
37
|
+
IDetailPropertyRow& AddPropertyToCategory(TSharedPtr<IPropertyHandle> InPropertyHandle);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Category Priority Reference
|
|
43
|
+
|
|
44
|
+
`ECategoryPriority` controls sort order in the details panel (lower value = higher position):
|
|
45
|
+
|
|
46
|
+
| Enum Value | Sort Position |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `Variable` | Highest (top) |
|
|
49
|
+
| `Transform` | Near top |
|
|
50
|
+
| `Important` | High |
|
|
51
|
+
| `TypeSpecific` | Middle |
|
|
52
|
+
| `Default` | Standard |
|
|
53
|
+
| `Uncommon` | Bottom |
|
|
54
|
+
|
|
55
|
+
```cpp
|
|
56
|
+
IDetailCategoryBuilder& CoreCat =
|
|
57
|
+
DetailBuilder.EditCategory("Core", FText::GetEmpty(), ECategoryPriority::Important);
|
|
58
|
+
IDetailCategoryBuilder& AdvancedCat =
|
|
59
|
+
DetailBuilder.EditCategory("Advanced", FText::GetEmpty(), ECategoryPriority::Uncommon);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## IDetailPropertyRow — Fluent Modifier API
|
|
65
|
+
|
|
66
|
+
From `IDetailPropertyRow.h`. Methods return `IDetailPropertyRow&` for chaining:
|
|
67
|
+
|
|
68
|
+
```cpp
|
|
69
|
+
IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("Settings");
|
|
70
|
+
IDetailPropertyRow& Row = Category.AddProperty(
|
|
71
|
+
DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, MyFloat)));
|
|
72
|
+
|
|
73
|
+
Row.DisplayName(FText::FromString("Speed"))
|
|
74
|
+
.ToolTip(FText::FromString("Movement speed in cm/s"))
|
|
75
|
+
.ShowPropertyButtons(false) // hide reset-to-default arrow
|
|
76
|
+
.EditCondition(
|
|
77
|
+
TAttribute<bool>::Create([this]() { return bIsEnabled; }),
|
|
78
|
+
FOnBooleanValueChanged::CreateLambda([this](bool bNewValue)
|
|
79
|
+
{
|
|
80
|
+
bIsEnabled = bNewValue;
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
.IsEnabled(TAttribute<bool>::Create([]() { return true; }))
|
|
84
|
+
.Visibility(TAttribute<EVisibility>::Create([]()
|
|
85
|
+
{
|
|
86
|
+
return EVisibility::Visible;
|
|
87
|
+
}));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Custom Widget Row Patterns
|
|
93
|
+
|
|
94
|
+
### Full Custom Widget Row
|
|
95
|
+
|
|
96
|
+
```cpp
|
|
97
|
+
Category.AddCustomRow(FText::FromString("SearchFilter"))
|
|
98
|
+
[
|
|
99
|
+
SNew(SHorizontalBox)
|
|
100
|
+
+ SHorizontalBox::Slot()
|
|
101
|
+
.FillWidth(1.f)
|
|
102
|
+
.VAlign(VAlign_Center)
|
|
103
|
+
[
|
|
104
|
+
SNew(STextBlock)
|
|
105
|
+
.Text(FText::FromString("Custom Label"))
|
|
106
|
+
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
107
|
+
]
|
|
108
|
+
+ SHorizontalBox::Slot()
|
|
109
|
+
.AutoWidth()
|
|
110
|
+
.Padding(FMargin(4.f, 0.f))
|
|
111
|
+
[
|
|
112
|
+
SNew(SButton)
|
|
113
|
+
.Text(FText::FromString("Action"))
|
|
114
|
+
.OnClicked(FOnClicked::CreateLambda([]()
|
|
115
|
+
{
|
|
116
|
+
return FReply::Handled();
|
|
117
|
+
}))
|
|
118
|
+
]
|
|
119
|
+
];
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### NameContent / ValueContent Split Row
|
|
123
|
+
|
|
124
|
+
The detail panel uses a two-column layout. Use `NameContent()` and `ValueContent()` to align with standard property rows:
|
|
125
|
+
|
|
126
|
+
```cpp
|
|
127
|
+
Category.AddCustomRow(FText::FromString("MyCustomProp"))
|
|
128
|
+
.NameContent()
|
|
129
|
+
[
|
|
130
|
+
SNew(STextBlock)
|
|
131
|
+
.Text(FText::FromString("Custom Property"))
|
|
132
|
+
.Font(IDetailLayoutBuilder::GetDetailFont())
|
|
133
|
+
]
|
|
134
|
+
.ValueContent()
|
|
135
|
+
.MinDesiredWidth(125.f)
|
|
136
|
+
.MaxDesiredWidth(400.f)
|
|
137
|
+
[
|
|
138
|
+
SNew(SEditableTextBox)
|
|
139
|
+
.Text_Lambda([]() { return FText::FromString("value"); })
|
|
140
|
+
.OnTextCommitted_Lambda([](const FText& NewText, ETextCommit::Type CommitType)
|
|
141
|
+
{
|
|
142
|
+
// handle commit
|
|
143
|
+
})
|
|
144
|
+
];
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Override Default Property Widget While Keeping Children
|
|
148
|
+
|
|
149
|
+
```cpp
|
|
150
|
+
TSharedRef<IPropertyHandle> Handle =
|
|
151
|
+
DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, MyProp));
|
|
152
|
+
IDetailPropertyRow& Row = Category.AddProperty(Handle);
|
|
153
|
+
TSharedPtr<SWidget> DefaultNameWidget;
|
|
154
|
+
TSharedPtr<SWidget> DefaultValueWidget;
|
|
155
|
+
Row.GetDefaultWidgets(DefaultNameWidget, DefaultValueWidget);
|
|
156
|
+
|
|
157
|
+
Row.CustomWidget(/*bShowChildren=*/true)
|
|
158
|
+
.NameContent()
|
|
159
|
+
[
|
|
160
|
+
DefaultNameWidget.ToSharedRef()
|
|
161
|
+
]
|
|
162
|
+
.ValueContent()
|
|
163
|
+
[
|
|
164
|
+
SNew(SHorizontalBox)
|
|
165
|
+
+ SHorizontalBox::Slot().FillWidth(1.f)
|
|
166
|
+
[
|
|
167
|
+
DefaultValueWidget.ToSharedRef()
|
|
168
|
+
]
|
|
169
|
+
+ SHorizontalBox::Slot().AutoWidth().Padding(4.f, 0.f)
|
|
170
|
+
[
|
|
171
|
+
SNew(SButton)
|
|
172
|
+
.Text(FText::FromString("..."))
|
|
173
|
+
]
|
|
174
|
+
];
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## IPropertyTypeCustomization — Struct Header Patterns
|
|
180
|
+
|
|
181
|
+
From `IPropertyTypeCustomization.h`. `CustomizeHeader` sets up the collapsed/inline row; `CustomizeChildren` sets up the expanded rows.
|
|
182
|
+
|
|
183
|
+
### Compact Inline Display
|
|
184
|
+
|
|
185
|
+
Show all struct fields inline in the header, no expansion needed:
|
|
186
|
+
|
|
187
|
+
```cpp
|
|
188
|
+
void FMyVectorCustomization::CustomizeHeader(
|
|
189
|
+
TSharedRef<IPropertyHandle> PropertyHandle,
|
|
190
|
+
FDetailWidgetRow& HeaderRow,
|
|
191
|
+
IPropertyTypeCustomizationUtils& CustomizationUtils)
|
|
192
|
+
{
|
|
193
|
+
// Get child handles for X, Y, Z
|
|
194
|
+
TSharedPtr<IPropertyHandle> XHandle = PropertyHandle->GetChildHandle(
|
|
195
|
+
GET_MEMBER_NAME_CHECKED(FMyVector, X));
|
|
196
|
+
TSharedPtr<IPropertyHandle> YHandle = PropertyHandle->GetChildHandle(
|
|
197
|
+
GET_MEMBER_NAME_CHECKED(FMyVector, Y));
|
|
198
|
+
|
|
199
|
+
HeaderRow
|
|
200
|
+
.NameContent()
|
|
201
|
+
[
|
|
202
|
+
PropertyHandle->CreatePropertyNameWidget()
|
|
203
|
+
]
|
|
204
|
+
.ValueContent()
|
|
205
|
+
.MinDesiredWidth(200.f)
|
|
206
|
+
[
|
|
207
|
+
SNew(SHorizontalBox)
|
|
208
|
+
+ SHorizontalBox::Slot().Padding(2.f)
|
|
209
|
+
[
|
|
210
|
+
XHandle->CreatePropertyValueWidget()
|
|
211
|
+
]
|
|
212
|
+
+ SHorizontalBox::Slot().Padding(2.f)
|
|
213
|
+
[
|
|
214
|
+
YHandle->CreatePropertyValueWidget()
|
|
215
|
+
]
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
void FMyVectorCustomization::CustomizeChildren(
|
|
220
|
+
TSharedRef<IPropertyHandle> PropertyHandle,
|
|
221
|
+
IDetailChildrenBuilder& ChildBuilder,
|
|
222
|
+
IPropertyTypeCustomizationUtils& CustomizationUtils)
|
|
223
|
+
{
|
|
224
|
+
// Leave empty to suppress expanded child rows
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Summary Text in Header, Children in Expansion
|
|
229
|
+
|
|
230
|
+
```cpp
|
|
231
|
+
void FMyRangeCustomization::CustomizeHeader(
|
|
232
|
+
TSharedRef<IPropertyHandle> PropertyHandle,
|
|
233
|
+
FDetailWidgetRow& HeaderRow,
|
|
234
|
+
IPropertyTypeCustomizationUtils& CustomizationUtils)
|
|
235
|
+
{
|
|
236
|
+
TSharedPtr<IPropertyHandle> MinHandle =
|
|
237
|
+
PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyRange, Min));
|
|
238
|
+
TSharedPtr<IPropertyHandle> MaxHandle =
|
|
239
|
+
PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyRange, Max));
|
|
240
|
+
|
|
241
|
+
HeaderRow
|
|
242
|
+
.NameContent()
|
|
243
|
+
[
|
|
244
|
+
PropertyHandle->CreatePropertyNameWidget()
|
|
245
|
+
]
|
|
246
|
+
.ValueContent()
|
|
247
|
+
[
|
|
248
|
+
SNew(STextBlock)
|
|
249
|
+
.Text_Lambda([MinHandle, MaxHandle]()
|
|
250
|
+
{
|
|
251
|
+
float MinVal = 0.f, MaxVal = 0.f;
|
|
252
|
+
MinHandle->GetValue(MinVal);
|
|
253
|
+
MaxHandle->GetValue(MaxVal);
|
|
254
|
+
return FText::Format(
|
|
255
|
+
FText::FromString("[{0} .. {1}]"),
|
|
256
|
+
FText::AsNumber(MinVal),
|
|
257
|
+
FText::AsNumber(MaxVal));
|
|
258
|
+
})
|
|
259
|
+
.Font(IPropertyTypeCustomizationUtils::GetRegularFont())
|
|
260
|
+
];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
void FMyRangeCustomization::CustomizeChildren(
|
|
264
|
+
TSharedRef<IPropertyHandle> PropertyHandle,
|
|
265
|
+
IDetailChildrenBuilder& ChildBuilder,
|
|
266
|
+
IPropertyTypeCustomizationUtils& CustomizationUtils)
|
|
267
|
+
{
|
|
268
|
+
uint32 NumChildren = 0;
|
|
269
|
+
PropertyHandle->GetNumChildren(NumChildren);
|
|
270
|
+
for (uint32 i = 0; i < NumChildren; ++i)
|
|
271
|
+
{
|
|
272
|
+
ChildBuilder.AddProperty(PropertyHandle->GetChildHandle(i).ToSharedRef());
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## IPropertyHandle — Reading and Writing Values
|
|
280
|
+
|
|
281
|
+
`IPropertyHandle` provides type-safe access to property values across one or more selected objects:
|
|
282
|
+
|
|
283
|
+
```cpp
|
|
284
|
+
TSharedRef<IPropertyHandle> Handle =
|
|
285
|
+
DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, Speed));
|
|
286
|
+
|
|
287
|
+
// Read
|
|
288
|
+
float SpeedValue = 0.f;
|
|
289
|
+
FPropertyAccess::Result Result = Handle->GetValue(SpeedValue);
|
|
290
|
+
if (Result == FPropertyAccess::MultipleValues)
|
|
291
|
+
{
|
|
292
|
+
// multiple objects with different values selected
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Write — automatically handles transactions and PostEditChange
|
|
296
|
+
Handle->SetValue(150.f);
|
|
297
|
+
|
|
298
|
+
// Get as display string
|
|
299
|
+
FString DisplayStr;
|
|
300
|
+
Handle->GetValueAsDisplayString(DisplayStr);
|
|
301
|
+
|
|
302
|
+
// Enumerate per-object values
|
|
303
|
+
Handle->EnumerateRawData([](void* RawData, const int32 DataIndex, const int32 NumDatas) -> bool
|
|
304
|
+
{
|
|
305
|
+
float* SpeedPtr = static_cast<float*>(RawData);
|
|
306
|
+
*SpeedPtr = 100.f;
|
|
307
|
+
return true; // continue iteration
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Notify post-change
|
|
311
|
+
Handle->NotifyPostChange(EPropertyChangeType::ValueSet);
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Fonts
|
|
317
|
+
|
|
318
|
+
Always use `IDetailLayoutBuilder` or `IPropertyTypeCustomizationUtils` static helpers for fonts to stay visually consistent with the details panel:
|
|
319
|
+
|
|
320
|
+
```cpp
|
|
321
|
+
FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")) // regular body
|
|
322
|
+
FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")) // bold label
|
|
323
|
+
FAppStyle::GetFontStyle(TEXT("PropertyWindow.ItalicFont")) // italic/dimmed
|
|
324
|
+
|
|
325
|
+
// Shorthand statics:
|
|
326
|
+
IDetailLayoutBuilder::GetDetailFont()
|
|
327
|
+
IDetailLayoutBuilder::GetDetailFontBold()
|
|
328
|
+
IDetailLayoutBuilder::GetDetailFontItalic()
|
|
329
|
+
IPropertyTypeCustomizationUtils::GetRegularFont()
|
|
330
|
+
IPropertyTypeCustomizationUtils::GetBoldFont()
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Registration Reference
|
|
336
|
+
|
|
337
|
+
All registrations go in `StartupModule`; all unregistrations in `ShutdownModule`.
|
|
338
|
+
|
|
339
|
+
```cpp
|
|
340
|
+
// In StartupModule
|
|
341
|
+
FPropertyEditorModule& PropertyModule =
|
|
342
|
+
FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
343
|
+
|
|
344
|
+
// Class customization
|
|
345
|
+
PropertyModule.RegisterCustomClassLayout(
|
|
346
|
+
UMyClass::StaticClass()->GetFName(),
|
|
347
|
+
FOnGetDetailCustomizationInstance::CreateStatic(
|
|
348
|
+
&FMyClassCustomization::MakeInstance));
|
|
349
|
+
|
|
350
|
+
// Struct / property type customization
|
|
351
|
+
PropertyModule.RegisterCustomPropertyTypeLayout(
|
|
352
|
+
FMyStruct::StaticStruct()->GetFName(),
|
|
353
|
+
FOnGetPropertyTypeCustomizationInstance::CreateStatic(
|
|
354
|
+
&FMyStructCustomization::MakeInstance));
|
|
355
|
+
|
|
356
|
+
// Notify the editor that customizations changed
|
|
357
|
+
PropertyModule.NotifyCustomizationModuleChanged();
|
|
358
|
+
|
|
359
|
+
// In ShutdownModule
|
|
360
|
+
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
|
|
361
|
+
{
|
|
362
|
+
FPropertyEditorModule& PM =
|
|
363
|
+
FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
|
364
|
+
PM.UnregisterCustomClassLayout(UMyClass::StaticClass()->GetFName());
|
|
365
|
+
PM.UnregisterCustomPropertyTypeLayout(FMyStruct::StaticStruct()->GetFName());
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Instanced Property Type Layout (Scoped)
|
|
372
|
+
|
|
373
|
+
Register a property type customization for a specific details panel instance only, not globally. Useful when you want different presentation in different contexts:
|
|
374
|
+
|
|
375
|
+
```cpp
|
|
376
|
+
void FMyClassCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
|
377
|
+
{
|
|
378
|
+
// Register a type customization only within this details panel
|
|
379
|
+
DetailBuilder.RegisterInstancedCustomPropertyTypeLayout(
|
|
380
|
+
FMyStruct::StaticStruct()->GetFName(),
|
|
381
|
+
FOnGetPropertyTypeCustomizationInstance::CreateStatic(
|
|
382
|
+
&FMyStructInContextCustomization::MakeInstance));
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## Common Patterns Checklist
|
|
389
|
+
|
|
390
|
+
- Use `GET_MEMBER_NAME_CHECKED(ClassName, MemberName)` — compile-time property name validation
|
|
391
|
+
- Always store `TWeakPtr<IDetailLayoutBuilder>` if calling `ForceRefreshDetails()` asynchronously
|
|
392
|
+
- For `SNew` lambdas that reference `DetailBuilder`, capture by `TWeakPtr` to avoid dangling references
|
|
393
|
+
- Use `IDetailCategoryBuilder::AddProperty` for standard rows, `AddCustomRow` for fully custom Slate rows
|
|
394
|
+
- `bShowChildren = true` in `CustomWidget()` to show child properties while overriding the parent row's display
|
|
395
|
+
- Call `PropertyModule.NotifyCustomizationModuleChanged()` after registrations so any open detail panels refresh
|