├── .gitignore ├── Binaries └── Win64 │ ├── UnrealEditor-SettingsWidgetConstructor.dll │ ├── UnrealEditor-SettingsWidgetConstructor.pdb │ ├── UnrealEditor-SettingsWidgetConstructorEditor.dll │ ├── UnrealEditor-SettingsWidgetConstructorEditor.pdb │ └── UnrealEditor.modules ├── Config ├── BaseSettingsDataTable.json ├── BaseSettingsWidgetConstructor.ini └── FilterPlugin.ini ├── Content ├── Data │ └── DR_SettingsDataTables.uasset ├── Fonts │ ├── FontFace_RobotoBold.uasset │ ├── FontFace_RobotoItalic.uasset │ ├── FontFace_RobotoLight.uasset │ ├── FontFace_RobotoRegular.uasset │ └── Font_Menu.uasset ├── Subwidgets │ ├── WBP_SettingsButton.uasset │ ├── WBP_SettingsCheckBox.uasset │ ├── WBP_SettingsComboItem.uasset │ ├── WBP_SettingsCombobox.uasset │ ├── WBP_SettingsMenuColumn.uasset │ ├── WBP_SettingsSlider.uasset │ ├── WBP_SettingsTextInput.uasset │ ├── WBP_SettingsTextLine.uasset │ └── WBP_SettingsToolTip.uasset ├── Textures │ ├── M_Settings_Background.uasset │ ├── T_Settings_Button.uasset │ ├── T_Settings_Checkbox_checked.uasset │ ├── T_Settings_Checkbox_unchecked.uasset │ ├── T_Settings_ComboBox_Arrow.uasset │ ├── T_Settings_DialogBox.uasset │ ├── T_Settings_MenuBorder.uasset │ └── T_Settings_Slider_Handle.uasset └── WBP_SettingsWidgetConstructor.uasset ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── SettingsWidgetConstructor.uplugin └── Source ├── SettingsWidgetConstructor ├── Private │ ├── Data │ │ ├── SettingArchetypesData.cpp │ │ ├── SettingFunction.cpp │ │ ├── SettingTag.cpp │ │ ├── SettingsDataAsset.cpp │ │ ├── SettingsDataTable.cpp │ │ ├── SettingsRow.cpp │ │ └── SettingsThemeData.cpp │ ├── FunctionPickerData │ │ ├── FunctionPicker.cpp │ │ ├── SWCFunctionPicker.h │ │ └── SWCFunctionPickerTemplate.h │ ├── MyDataTable │ │ ├── MyDataTable.cpp │ │ └── SWCMyDataTable.h │ ├── MyUtilsLibraries │ │ ├── SWCWidgetUtilsLibrary.h │ │ ├── SettingsUtilsLibrary.cpp │ │ └── WidgetUtilsLibrary.cpp │ ├── SettingsCheatExtension.cpp │ ├── SettingsWidgetConstructorModule.cpp │ └── UI │ │ ├── SettingSubWidget.cpp │ │ └── SettingsWidget.cpp ├── Public │ ├── Data │ │ ├── SettingArchetypesData.h │ │ ├── SettingFunction.h │ │ ├── SettingTag.h │ │ ├── SettingTypes.h │ │ ├── SettingsDataAsset.h │ │ ├── SettingsDataTable.h │ │ ├── SettingsRow.h │ │ └── SettingsThemeData.h │ ├── MyUtilsLibraries │ │ └── SettingsUtilsLibrary.h │ ├── SettingsCheatExtension.h │ ├── SettingsWidgetConstructorLibrary.h │ ├── SettingsWidgetConstructorModule.h │ └── UI │ │ ├── SettingSubWidget.h │ │ └── SettingsWidget.h └── SettingsWidgetConstructor.Build.cs └── SettingsWidgetConstructorEditor ├── Private ├── AssetTypeActions_SettingsDataTable.cpp ├── AssetTypeActions_SettingsWidget.cpp ├── FunctionPickerType │ ├── FunctionPickerCustomization.cpp │ └── FunctionPickerCustomization.h ├── MyAssets │ ├── AssetTypeActions_MyDataTable.cpp │ ├── AssetTypeActions_MyUserWidget.cpp │ ├── SWCAssetTypeActions_MyDataTable.h │ └── SWCAssetTypeActions_MyUserWidget.h ├── MyPropertyType │ ├── MyPropertyTypeCustomization.cpp │ ├── MyPropertyTypeCustomization.h │ ├── PropertyData.cpp │ └── PropertyData.h ├── SettingTagCustomization.cpp ├── SettingsPickerCustomization.cpp └── SettingsWidgetConstructorEditorModule.cpp ├── Public ├── AssetTypeActions_SettingsDataTable.h ├── AssetTypeActions_SettingsWidget.h ├── SettingTagCustomization.h ├── SettingsPickerCustomization.h └── SettingsWidgetConstructorEditorModule.h └── SettingsWidgetConstructorEditor.Build.cs /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 user specific files 2 | .vs/ 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | *.ipa 33 | 34 | # These project files can be generated by the engine 35 | *.xcodeproj 36 | *.xcworkspace 37 | *.sln 38 | *.suo 39 | *.opensdf 40 | *.sdf 41 | *.VC.db 42 | *.VC.opendb 43 | 44 | # Precompiled Assets 45 | SourceArt/**/*.png 46 | SourceArt/**/*.tga 47 | 48 | # Binary Files 49 | Binaries/* 50 | Plugins/*/Binaries/* 51 | 52 | # Builds 53 | Build/* 54 | 55 | # Whitelist PakBlacklist-.txt files 56 | !Build/*/ 57 | Build/*/** 58 | !Build/*/PakBlacklist*.txt 59 | 60 | # Don't ignore icon files in Build 61 | !Build/**/*.ico 62 | 63 | # Built data for maps 64 | *_BuiltData.uasset 65 | 66 | # Configuration files generated by the Editor 67 | Saved/* 68 | 69 | # Compiled source files for the engine to use 70 | Intermediate/* 71 | Plugins/*/Intermediate/* 72 | 73 | # Cache files for the editor to use 74 | DerivedDataCache/* 75 | -------------------------------------------------------------------------------- /Binaries/Win64/UnrealEditor-SettingsWidgetConstructor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Binaries/Win64/UnrealEditor-SettingsWidgetConstructor.dll -------------------------------------------------------------------------------- /Binaries/Win64/UnrealEditor-SettingsWidgetConstructor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Binaries/Win64/UnrealEditor-SettingsWidgetConstructor.pdb -------------------------------------------------------------------------------- /Binaries/Win64/UnrealEditor-SettingsWidgetConstructorEditor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Binaries/Win64/UnrealEditor-SettingsWidgetConstructorEditor.dll -------------------------------------------------------------------------------- /Binaries/Win64/UnrealEditor-SettingsWidgetConstructorEditor.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Binaries/Win64/UnrealEditor-SettingsWidgetConstructorEditor.pdb -------------------------------------------------------------------------------- /Binaries/Win64/UnrealEditor.modules: -------------------------------------------------------------------------------- 1 | { 2 | "BuildId": "33043543", 3 | "Modules": 4 | { 5 | "SettingsWidgetConstructor": "UnrealEditor-SettingsWidgetConstructor.dll", 6 | "SettingsWidgetConstructorEditor": "UnrealEditor-SettingsWidgetConstructorEditor.dll" 7 | } 8 | } -------------------------------------------------------------------------------- /Config/BaseSettingsDataTable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "Settings.TextLine", 4 | "SettingsPicker": 5 | { 6 | "SettingsType": "TextLine", 7 | "PrimaryData": 8 | { 9 | "Tag": 10 | { 11 | "TagName": "Settings.TextLine" 12 | }, 13 | "Owner": 14 | { 15 | "FunctionClass": "None", 16 | "FunctionName": "None" 17 | }, 18 | "Setter": 19 | { 20 | "FunctionClass": "None", 21 | "FunctionName": "None" 22 | }, 23 | "Getter": 24 | { 25 | "FunctionClass": "None", 26 | "FunctionName": "None" 27 | }, 28 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"9B5F34484FB5155A7E20C0B96B5D76AB\", \"SETTINGS\")", 29 | "Tooltip": "NSLOCTEXT(\"Core\", \"None\", \"None\")", 30 | "Padding": 31 | { 32 | "Left": 0, 33 | "Top": 0, 34 | "Right": 0, 35 | "Bottom": 48 36 | }, 37 | "LineHeight": 48, 38 | "bStartOnNextColumn": false, 39 | "SettingsToUpdate": 40 | { 41 | "GameplayTags": [], 42 | "ParentTags": [] 43 | }, 44 | "ShowNextToSettingOverride": 45 | { 46 | "TagName": "None" 47 | } 48 | }, 49 | "Button": 50 | { 51 | "VerticalAlignment": "Content", 52 | "HorizontalAlignment": "HAlign_Fill" 53 | }, 54 | "Checkbox": 55 | { 56 | }, 57 | "Combobox": 58 | { 59 | "SetMembers": 60 | { 61 | "FunctionClass": "None", 62 | "FunctionName": "None" 63 | }, 64 | "GetMembers": 65 | { 66 | "FunctionClass": "None", 67 | "FunctionName": "None" 68 | }, 69 | "Members": [], 70 | "TextJustify": "Center" 71 | }, 72 | "Slider": 73 | { 74 | }, 75 | "TextLine": 76 | { 77 | "VerticalAlignment": "Header", 78 | "HorizontalAlignment": "HAlign_Center" 79 | }, 80 | "UserInput": 81 | { 82 | "MaxCharactersNumber": 0 83 | }, 84 | "CustomWidget": 85 | { 86 | "CustomWidgetClass": "None" 87 | } 88 | } 89 | }, 90 | { 91 | "Name": "Settings.Combobox", 92 | "SettingsPicker": 93 | { 94 | "SettingsType": "Combobox", 95 | "PrimaryData": 96 | { 97 | "Tag": 98 | { 99 | "TagName": "Settings.Combobox" 100 | }, 101 | "Owner": 102 | { 103 | "FunctionClass": "/Script/CoreUObject.Class'/Script/SettingsWidgetConstructor.SettingsUtilsLibrary'", 104 | "FunctionName": "GetGameUserSettings" 105 | }, 106 | "Setter": 107 | { 108 | "FunctionClass": "/Script/CoreUObject.Class'/Script/Engine.GameUserSettings'", 109 | "FunctionName": "SetOverallScalabilityLevel" 110 | }, 111 | "Getter": 112 | { 113 | "FunctionClass": "/Script/CoreUObject.Class'/Script/Engine.GameUserSettings'", 114 | "FunctionName": "GetOverallScalabilityLevel" 115 | }, 116 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"3D05344D4AC20700809DC9B76E80BC28\", \"Overall Quality\")", 117 | "Tooltip": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"9F345551422A4511C85D2B8E8D740C23\", \"Choose the overall quality level\")", 118 | "Padding": 119 | { 120 | "Left": 0, 121 | "Top": 0, 122 | "Right": 30, 123 | "Bottom": 15 124 | }, 125 | "LineHeight": 48, 126 | "bStartOnNextColumn": false, 127 | "SettingsToUpdate": 128 | { 129 | "GameplayTags": [], 130 | "ParentTags": [] 131 | }, 132 | "ShowNextToSettingOverride": 133 | { 134 | "TagName": "None" 135 | } 136 | }, 137 | "Button": 138 | { 139 | "VerticalAlignment": "Content", 140 | "HorizontalAlignment": "HAlign_Fill" 141 | }, 142 | "Checkbox": 143 | { 144 | }, 145 | "Combobox": 146 | { 147 | "SetMembers": 148 | { 149 | "FunctionClass": "None", 150 | "FunctionName": "None" 151 | }, 152 | "GetMembers": 153 | { 154 | "FunctionClass": "None", 155 | "FunctionName": "None" 156 | }, 157 | "Members": [ 158 | "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"CCA672154B5FEA29315F3EADAEF2019F\", \"Low\")", 159 | "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"3D9EECC54A0F8310659B85A3B3F7BB19\", \"Medium\")", 160 | "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"455615594854614F98882F94A79F65B1\", \"High\")", 161 | "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"80910DF74E3E587682422488BCBBD7A3\", \"Epic\")", 162 | "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"213FC09D4F9042E4C7F73BAE9D693D75\", \"Cinematic\")" 163 | ], 164 | "TextJustify": "Center" 165 | }, 166 | "Slider": 167 | { 168 | }, 169 | "TextLine": 170 | { 171 | "VerticalAlignment": "Content", 172 | "HorizontalAlignment": "HAlign_Fill" 173 | }, 174 | "UserInput": 175 | { 176 | "MaxCharactersNumber": 0 177 | }, 178 | "CustomWidget": 179 | { 180 | "CustomWidgetClass": "None" 181 | } 182 | } 183 | }, 184 | { 185 | "Name": "Settings.Checkbox", 186 | "SettingsPicker": 187 | { 188 | "SettingsType": "Checkbox", 189 | "PrimaryData": 190 | { 191 | "Tag": 192 | { 193 | "TagName": "Settings.Checkbox" 194 | }, 195 | "Owner": 196 | { 197 | "FunctionClass": "/Script/CoreUObject.Class'/Script/SettingsWidgetConstructor.SettingsUtilsLibrary'", 198 | "FunctionName": "GetGameUserSettings" 199 | }, 200 | "Setter": 201 | { 202 | "FunctionClass": "/Script/CoreUObject.Class'/Script/Engine.GameUserSettings'", 203 | "FunctionName": "SetVSyncEnabled" 204 | }, 205 | "Getter": 206 | { 207 | "FunctionClass": "/Script/CoreUObject.Class'/Script/Engine.GameUserSettings'", 208 | "FunctionName": "IsVSyncEnabled" 209 | }, 210 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"4B34951E482BBA92506C3D8A3054B05B\", \"V-Sync\")", 211 | "Tooltip": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"69E51E954CA7C3F7A9DB57A88A5B8BBA\", \"Check to enable the V-Sync\")", 212 | "Padding": 213 | { 214 | "Left": 0, 215 | "Top": 0, 216 | "Right": 30, 217 | "Bottom": 15 218 | }, 219 | "LineHeight": 48, 220 | "bStartOnNextColumn": false, 221 | "SettingsToUpdate": 222 | { 223 | "GameplayTags": [], 224 | "ParentTags": [] 225 | }, 226 | "ShowNextToSettingOverride": 227 | { 228 | "TagName": "None" 229 | } 230 | }, 231 | "Button": 232 | { 233 | "VerticalAlignment": "Content", 234 | "HorizontalAlignment": "HAlign_Fill" 235 | }, 236 | "Checkbox": 237 | { 238 | }, 239 | "Combobox": 240 | { 241 | "SetMembers": 242 | { 243 | "FunctionClass": "None", 244 | "FunctionName": "None" 245 | }, 246 | "GetMembers": 247 | { 248 | "FunctionClass": "None", 249 | "FunctionName": "None" 250 | }, 251 | "Members": [], 252 | "TextJustify": "Center" 253 | }, 254 | "Slider": 255 | { 256 | }, 257 | "TextLine": 258 | { 259 | "VerticalAlignment": "Content", 260 | "HorizontalAlignment": "HAlign_Fill" 261 | }, 262 | "UserInput": 263 | { 264 | "MaxCharactersNumber": 0 265 | }, 266 | "CustomWidget": 267 | { 268 | "CustomWidgetClass": "None" 269 | } 270 | } 271 | }, 272 | { 273 | "Name": "Settings.UserInput", 274 | "SettingsPicker": 275 | { 276 | "SettingsType": "UserInput", 277 | "PrimaryData": 278 | { 279 | "Tag": 280 | { 281 | "TagName": "Settings.UserInput" 282 | }, 283 | "Owner": 284 | { 285 | "FunctionClass": "None", 286 | "FunctionName": "None" 287 | }, 288 | "Setter": 289 | { 290 | "FunctionClass": "None", 291 | "FunctionName": "None" 292 | }, 293 | "Getter": 294 | { 295 | "FunctionClass": "None", 296 | "FunctionName": "None" 297 | }, 298 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"27AF3120412B8FDA02FE3F951893C245\", \"Player Name\")", 299 | "Tooltip": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"002E379E4E5144B9BB5A128E98C49EDD\", \"Enter your nickname\")", 300 | "Padding": 301 | { 302 | "Left": 0, 303 | "Top": 0, 304 | "Right": 30, 305 | "Bottom": 15 306 | }, 307 | "LineHeight": 48, 308 | "bStartOnNextColumn": true, 309 | "SettingsToUpdate": 310 | { 311 | "GameplayTags": [], 312 | "ParentTags": [] 313 | }, 314 | "ShowNextToSettingOverride": 315 | { 316 | "TagName": "None" 317 | } 318 | }, 319 | "Button": 320 | { 321 | "VerticalAlignment": "Content", 322 | "HorizontalAlignment": "HAlign_Fill" 323 | }, 324 | "Checkbox": 325 | { 326 | }, 327 | "Combobox": 328 | { 329 | "SetMembers": 330 | { 331 | "FunctionClass": "None", 332 | "FunctionName": "None" 333 | }, 334 | "GetMembers": 335 | { 336 | "FunctionClass": "None", 337 | "FunctionName": "None" 338 | }, 339 | "Members": [], 340 | "TextJustify": "Center" 341 | }, 342 | "Slider": 343 | { 344 | }, 345 | "TextLine": 346 | { 347 | "VerticalAlignment": "Content", 348 | "HorizontalAlignment": "HAlign_Fill" 349 | }, 350 | "UserInput": 351 | { 352 | "MaxCharactersNumber": 16 353 | }, 354 | "CustomWidget": 355 | { 356 | "CustomWidgetClass": "None" 357 | } 358 | } 359 | }, 360 | { 361 | "Name": "Settings.Slider", 362 | "SettingsPicker": 363 | { 364 | "SettingsType": "Slider", 365 | "PrimaryData": 366 | { 367 | "Tag": 368 | { 369 | "TagName": "Settings.Slider" 370 | }, 371 | "Owner": 372 | { 373 | "FunctionClass": "None", 374 | "FunctionName": "None" 375 | }, 376 | "Setter": 377 | { 378 | "FunctionClass": "None", 379 | "FunctionName": "None" 380 | }, 381 | "Getter": 382 | { 383 | "FunctionClass": "None", 384 | "FunctionName": "None" 385 | }, 386 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"B93D9EA548CA0CFCED454784E1FD16D4\", \"Player Name Scale\")", 387 | "Tooltip": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"921CAF0148DA0D735D5A358549AF2F1C\", \"Set the Player Scale Scale\")", 388 | "Padding": 389 | { 390 | "Left": 0, 391 | "Top": 0, 392 | "Right": 30, 393 | "Bottom": 15 394 | }, 395 | "LineHeight": 48, 396 | "bStartOnNextColumn": false, 397 | "SettingsToUpdate": 398 | { 399 | "GameplayTags": [], 400 | "ParentTags": [] 401 | }, 402 | "ShowNextToSettingOverride": 403 | { 404 | "TagName": "None" 405 | } 406 | }, 407 | "Button": 408 | { 409 | "VerticalAlignment": "Content", 410 | "HorizontalAlignment": "HAlign_Fill" 411 | }, 412 | "Checkbox": 413 | { 414 | }, 415 | "Combobox": 416 | { 417 | "SetMembers": 418 | { 419 | "FunctionClass": "None", 420 | "FunctionName": "None" 421 | }, 422 | "GetMembers": 423 | { 424 | "FunctionClass": "None", 425 | "FunctionName": "None" 426 | }, 427 | "Members": [], 428 | "TextJustify": "Center" 429 | }, 430 | "Slider": 431 | { 432 | }, 433 | "TextLine": 434 | { 435 | "VerticalAlignment": "Content", 436 | "HorizontalAlignment": "HAlign_Fill" 437 | }, 438 | "UserInput": 439 | { 440 | "MaxCharactersNumber": 0 441 | }, 442 | "CustomWidget": 443 | { 444 | "CustomWidgetClass": "None" 445 | } 446 | } 447 | }, 448 | { 449 | "Name": "Settings.Button", 450 | "SettingsPicker": 451 | { 452 | "SettingsType": "Button", 453 | "PrimaryData": 454 | { 455 | "Tag": 456 | { 457 | "TagName": "Settings.Button" 458 | }, 459 | "Owner": 460 | { 461 | "FunctionClass": "/Script/CoreUObject.Class'/Script/SettingsWidgetConstructor.SettingsUtilsLibrary'", 462 | "FunctionName": "GetSettingsWidget" 463 | }, 464 | "Setter": 465 | { 466 | "FunctionClass": "/Script/CoreUObject.Class'/Script/SettingsWidgetConstructor.SettingsWidget'", 467 | "FunctionName": "SaveSettings" 468 | }, 469 | "Getter": 470 | { 471 | "FunctionClass": "None", 472 | "FunctionName": "None" 473 | }, 474 | "Caption": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"2F0E1E0E4E3EC020E6020FB66C005751\", \"Apply\")", 475 | "Tooltip": "NSLOCTEXT(\"[B300B6BE227CC798280E67264B750EDE]\", \"BFD2A32B4EDE7484E5AA3499437BB3D7\", \"Apply and save all settings to config\")", 476 | "Padding": 477 | { 478 | "Left": 0, 479 | "Top": 24, 480 | "Right": 0, 481 | "Bottom": 0 482 | }, 483 | "LineHeight": 48, 484 | "bStartOnNextColumn": false, 485 | "SettingsToUpdate": 486 | { 487 | "GameplayTags": [], 488 | "ParentTags": [] 489 | }, 490 | "ShowNextToSettingOverride": 491 | { 492 | "TagName": "None" 493 | } 494 | }, 495 | "Button": 496 | { 497 | "VerticalAlignment": "Footer", 498 | "HorizontalAlignment": "HAlign_Center" 499 | }, 500 | "Checkbox": 501 | { 502 | }, 503 | "Combobox": 504 | { 505 | "SetMembers": 506 | { 507 | "FunctionClass": "None", 508 | "FunctionName": "None" 509 | }, 510 | "GetMembers": 511 | { 512 | "FunctionClass": "None", 513 | "FunctionName": "None" 514 | }, 515 | "Members": [], 516 | "TextJustify": "Center" 517 | }, 518 | "Slider": 519 | { 520 | }, 521 | "TextLine": 522 | { 523 | "VerticalAlignment": "Content", 524 | "HorizontalAlignment": "HAlign_Fill" 525 | }, 526 | "UserInput": 527 | { 528 | "MaxCharactersNumber": 0 529 | }, 530 | "CustomWidget": 531 | { 532 | "CustomWidgetClass": "None" 533 | } 534 | } 535 | } 536 | ] -------------------------------------------------------------------------------- /Config/BaseSettingsWidgetConstructor.ini: -------------------------------------------------------------------------------- 1 | [/Script/SettingsWidgetConstructorEditor.SettingsWidgetFactory] 2 | SettingsWidgetClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/WBP_SettingsWidgetConstructor.WBP_SettingsWidgetConstructor_C' 3 | 4 | [/Script/SettingsWidgetConstructor.SettingsDataAsset] 5 | SettingsDataTableInternal= 6 | SettingsDataRegistryInternal=/SettingsWidgetConstructor/Data/DR_SettingsDataTables.DR_SettingsDataTables 7 | ButtonClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsButton.WBP_SettingsButton_C' 8 | CheckboxClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsCheckBox.WBP_SettingsCheckBox_C' 9 | ComboboxClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsCombobox.WBP_SettingsCombobox_C' 10 | SliderClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsSlider.WBP_SettingsSlider_C' 11 | TextLineClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsTextLine.WBP_SettingsTextLine_C' 12 | UserInputClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsTextInput.WBP_SettingsTextInput_C' 13 | TooltipClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsToolTip.WBP_SettingsToolTip_C' 14 | ColumnClassInternal=/Script/UMG.WidgetBlueprintGeneratedClass'/SettingsWidgetConstructor/Subwidgets/WBP_SettingsMenuColumn.WBP_SettingsMenuColumn_C' 15 | bAutoConstructInternal=True 16 | bAutoFocusOnOpenInternal=True 17 | SettingsPercentSizeInternal=(X=0.600000,Y=0.400000) 18 | SettingsPaddingInternal=(Left=50.000000,Top=50.000000,Right=50.000000,Bottom=50.000000) 19 | ScrollboxPercentHeightInternal=1.000000 20 | ColumnPaddingInternal=(Left=10.000000,Top=0.000000,Right=10.000000,Bottom=0.000000) 21 | SpaceBetweenColumnsInternal=10.000000 22 | ButtonThemeDataInternal=(PressedPadding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Button.T_Settings_Button"',Size=(X=128.000000,Y=128.000000),DrawAs=Box,Margin=(Left=0.250000,Top=0.250000,Right=0.250000,Bottom=0.250000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)) 23 | CheckboxThemeDataInternal=(CheckedTexture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Checkbox_checked.T_Settings_Checkbox_checked"',UndeterminedTexture=None,Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Checkbox_unchecked.T_Settings_Checkbox_unchecked"',Size=(X=24.000000,Y=24.000000),DrawAs=Image,Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)) 24 | ComboboxThemeDataInternal=(PressedPadding=(Left=4.000000,Top=6.500000,Right=2.000000,Bottom=9.000000),Arrow=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_ComboBox_Arrow.T_Settings_ComboBox_Arrow"',Size=(X=32.000000,Y=32.000000),DrawAs=Image,Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)),Border=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Button.T_Settings_Button"',Size=(X=128.000000,Y=128.000000),DrawAs=Box,Margin=(Left=0.450000,Top=0.450000,Right=0.450000,Bottom=0.450000),Padding=(Left=5.000000,Top=5.000000,Right=5.000000,Bottom=5.000000)),ItemBackgroundColor=(SpecifiedColor=(R=0.000000,G=0.000000,B=0.000000,A=0.000000),ColorUseRule=UseColor_Specified),Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Button.T_Settings_Button"',Size=(X=128.000000,Y=128.000000),DrawAs=Box,Margin=(Left=0.250000,Top=0.250000,Right=0.250000,Bottom=0.250000),Padding=(Left=4.000000,Top=6.500000,Right=2.000000,Bottom=9.000000)) 25 | SliderThemeDataInternal=(Thumb=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Slider_Handle.T_Settings_Slider_Handle"',Size=(X=12.000000,Y=24.000000),DrawAs=Image,Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)),Texture=None,Size=(X=128.000000,Y=32.000000),DrawAs=Box,Margin=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)) 26 | UserInputThemeDataInternal=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_Button.T_Settings_Button"',Size=(X=128.000000,Y=128.000000),DrawAs=Box,Margin=(Left=0.250000,Top=0.250000,Right=0.250000,Bottom=0.250000),Padding=(Left=5.000000,Top=2.500000,Right=5.000000,Bottom=2.500000)) 27 | MiscThemeDataInternal=(ThemeColorNormal=(SpecifiedColor=(R=0.046665,G=0.046665,B=0.046665,A=1.000000),ColorUseRule=UseColor_Specified),ThemeColorHover=(SpecifiedColor=(R=0.168269,G=0.168269,B=0.168269,A=1.000000),ColorUseRule=UseColor_Specified),ThemeColorExtra=(SpecifiedColor=(R=0.168269,G=0.168269,B=0.168269,A=1.000000),ColorUseRule=UseColor_Specified),TextAndCaptionFont=(FontObject=Font'"/SettingsWidgetConstructor/Fonts/Font_Menu.Font_Menu"',FontMaterial=None,OutlineSettings=(OutlineSize=0,bSeparateFillAlpha=False,bApplyOutlineToDropShadows=False,OutlineMaterial=None,OutlineColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000)),TypefaceFontName="",Size=14,LetterSpacing=0),TextAndCaptionColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),TextHeaderFont=(FontObject=Font'"/SettingsWidgetConstructor/Fonts/Font_Menu.Font_Menu"',FontMaterial=None,OutlineSettings=(OutlineSize=0,bSeparateFillAlpha=False,bApplyOutlineToDropShadows=False,OutlineMaterial=None,OutlineColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000)),TypefaceFontName="Roboto (light)",Size=36,LetterSpacing=0),TextHeaderColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),TextFooterFont=(FontObject=Font'"/SettingsWidgetConstructor/Fonts/Font_Menu.Font_Menu"',FontMaterial=None,OutlineSettings=(OutlineSize=0,bSeparateFillAlpha=False,bApplyOutlineToDropShadows=False,OutlineMaterial=None,OutlineColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000)),TypefaceFontName="",Size=25,LetterSpacing=0),TextFooterColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),TextElementFont=(FontObject=Font'"/SettingsWidgetConstructor/Fonts/Font_Menu.Font_Menu"',FontMaterial=None,OutlineSettings=(OutlineSize=0,bSeparateFillAlpha=False,bApplyOutlineToDropShadows=False,OutlineMaterial=None,OutlineColor=(R=0.000000,G=0.000000,B=0.000000,A=1.000000)),TypefaceFontName="Roboto (regular)",Size=14,LetterSpacing=0),TextElementColor=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),TooltipBackground=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_DialogBox.T_Settings_DialogBox"',Size=(X=128.000000,Y=64.000000),DrawAs=Box,Margin=(Left=0.200000,Top=0.200000,Right=0.200000,Bottom=0.200000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)),TooltipBackgroundTint=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),WindowBackground=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_DialogBox.T_Settings_DialogBox"',Size=(X=128.000000,Y=64.000000),DrawAs=Box,Margin=(Left=0.300000,Top=0.300000,Right=0.300000,Bottom=0.300000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)),WindowBackgroundTint=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),MenuBorderData=(Texture=Texture2D'"/SettingsWidgetConstructor/Textures/T_Settings_MenuBorder.T_Settings_MenuBorder"',Size=(X=64.000000,Y=64.000000),DrawAs=Box,Margin=(Left=0.450000,Top=0.450000,Right=0.450000,Bottom=0.450000),Padding=(Left=0.000000,Top=0.000000,Right=0.000000,Bottom=0.000000)),MenuBorderTint=(SpecifiedColor=(R=1.000000,G=1.000000,B=1.000000,A=1.000000),ColorUseRule=UseColor_Specified),MenuBorderVisibility=Visible) 28 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 2 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 3 | 4 | [FilterPlugin] 5 | /Config/BaseSettingsWidgetConstructor.ini 6 | -------------------------------------------------------------------------------- /Content/Data/DR_SettingsDataTables.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Data/DR_SettingsDataTables.uasset -------------------------------------------------------------------------------- /Content/Fonts/FontFace_RobotoBold.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Fonts/FontFace_RobotoBold.uasset -------------------------------------------------------------------------------- /Content/Fonts/FontFace_RobotoItalic.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Fonts/FontFace_RobotoItalic.uasset -------------------------------------------------------------------------------- /Content/Fonts/FontFace_RobotoLight.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Fonts/FontFace_RobotoLight.uasset -------------------------------------------------------------------------------- /Content/Fonts/FontFace_RobotoRegular.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Fonts/FontFace_RobotoRegular.uasset -------------------------------------------------------------------------------- /Content/Fonts/Font_Menu.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Fonts/Font_Menu.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsButton.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsButton.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsCheckBox.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsCheckBox.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsComboItem.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsComboItem.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsCombobox.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsCombobox.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsMenuColumn.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsMenuColumn.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsSlider.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsSlider.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsTextInput.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsTextInput.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsTextLine.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsTextLine.uasset -------------------------------------------------------------------------------- /Content/Subwidgets/WBP_SettingsToolTip.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Subwidgets/WBP_SettingsToolTip.uasset -------------------------------------------------------------------------------- /Content/Textures/M_Settings_Background.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/M_Settings_Background.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_Button.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_Button.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_Checkbox_checked.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_Checkbox_checked.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_Checkbox_unchecked.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_Checkbox_unchecked.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_ComboBox_Arrow.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_ComboBox_Arrow.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_DialogBox.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_DialogBox.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_MenuBorder.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_MenuBorder.uasset -------------------------------------------------------------------------------- /Content/Textures/T_Settings_Slider_Handle.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/Textures/T_Settings_Slider_Handle.uasset -------------------------------------------------------------------------------- /Content/WBP_SettingsWidgetConstructor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Content/WBP_SettingsWidgetConstructor.uasset -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Yevhenii Selivanov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![License](https://img.shields.io/badge/license-MIT-brightgreen.svg) 2 | ![Unreal Engine](https://img.shields.io/badge/Unreal-5.4-dea309?style=flat&logo=unrealengine) 3 | 4 |
5 |

6 | 7 | Logo 8 | 9 |

⚙️ Settings Widget Constructor

10 |

11 | Automatically generate UI for game settings easily 12 |
13 |
14 | Join our Discord ›› 15 |
16 | Releases 17 | · 18 | Docs 19 |

20 | 21 | ## 🌟 About 22 | 23 | Settings Widget Constructor is a handy plugin for Unreal Engine 5 that lets you automatically generate user interface (UI) for your game settings. All it takes is a few clicks and adding a new row to the Data Table for each desired option. 24 | 25 | ![Settings UI](https://user-images.githubusercontent.com/20540872/147825296-ce7d33da-dfda-4757-b070-bfd08f700134.jpg) 26 | 27 | image 28 | 29 | ## 🎓 Sample Projects 30 | 31 | Check out our [Release](https://github.com/JanSeliv/SettingsWidgetConstructor/releases) page for a sample project showcasing the Settings Widget Constructor. 32 | 33 | Also, explore this [game project repository](https://github.com/JanSeliv/Bomber) to view the Settings Widget Constructor in action. 34 | 35 | ## 📅 Changelog 36 | #### 2024-12-26 37 | - Updated to **Unreal Engine 5.4**. 38 | - Moved a significant chunk of the logic from blueprints to code and simplified amount of widgets used. 39 | #### 2023-10-12 40 | - Updated to **Unreal Engine 5.3** 41 | - Added **multiple Settings Data Tables** support ([see doc](https://docs.google.com/document/d/1IXnOqrgaXTClP-0cIo28a9f6GHc9N1BCgTNnMk-X9VQ/edit#heading=h.cix3vjszb2vm)). 42 | - Implemented **Deferred Bindings**: now Getters and Setters are automatically rebound for failed settings. 43 | - Added **Blueprint Function Library** support to allow any its blueprint function to be used as an _Owner_ in a setting row. 44 | #### 2023-05-26 45 | - 🎉 Initial public release on Unreal Engine 5.2 46 | 47 | ## 📫 Feedback & Contribution 48 | 49 | Feedback and contributions from the community are highly appreciated! 50 | 51 | If you'd like to contribute, please fork the project and create a pull request targeting the `develop` branch. 52 | 53 | If you've found a bug or have an idea for a new feature, please open a new issue on GitHub or join our [Discord](https://discord.gg/jbWgwDefnE). Thank you! 54 | 55 | ## 📜 License 56 | 57 | This project is licensed under the terms of the MIT license. See [LICENSE](LICENSE) for more details. 58 | 59 | We hope you find this plugin useful and we look forward to your feedback and contributions. 60 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JanSeliv/SettingsWidgetConstructor/18df6ac4e28e7ed2b2110b98f5c64466c6c0db99/Resources/Icon128.png -------------------------------------------------------------------------------- /SettingsWidgetConstructor.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.0", 5 | "FriendlyName": "Settings Widget Constructor", 6 | "Description": "Plugin allows automatically generate settings on the UI from data table", 7 | "Category": "UI", 8 | "CreatedBy": "Yevhenii Selivanov", 9 | "CreatedByURL": "https://github.com/JanSeliv/SettingsWidgetConstructor", 10 | "DocsURL": "", 11 | "MarketplaceURL": "", 12 | "SupportURL": "mailto:janseliw@gmail.com", 13 | "EngineVersion": "5.4.0", 14 | "EnabledByDefault": true, 15 | "CanContainContent": true, 16 | "IsBetaVersion": false, 17 | "Installed": false, 18 | "Modules": [ 19 | { 20 | "Name": "SettingsWidgetConstructor", 21 | "Type": "Runtime", 22 | "LoadingPhase": "Default" 23 | }, 24 | { 25 | "Name": "SettingsWidgetConstructorEditor", 26 | "Type": "UncookedOnly", 27 | "LoadingPhase": "Default" 28 | } 29 | ], 30 | "Plugins": [ 31 | { 32 | "Name": "GameplayTagsEditor", 33 | "Enabled": true 34 | }, 35 | { 36 | "Name": "DataRegistry", 37 | "Enabled": true 38 | } 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingArchetypesData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "Data/SettingArchetypesData.h" 4 | //--- 5 | #include "Data/SettingsDataAsset.h" 6 | #include "Data/SettingTag.h" 7 | #include "UI/SettingSubWidget.h" 8 | #include "UI/SettingsWidget.h" 9 | //--- 10 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingArchetypesData) 11 | 12 | /********************************************************************************************* 13 | * FSettingsButton 14 | ********************************************************************************************* */ 15 | 16 | // Returns the sub-widget class of this setting type 17 | TSubclassOf FSettingsButton::GetSubWidgetClass() const 18 | { 19 | return USettingsDataAsset::Get().GetButtonClass(); 20 | } 21 | 22 | // Calls the Set function of the Settings Widget of this setting type 23 | void FSettingsButton::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 24 | { 25 | SettingsWidget.SetSettingButtonPressed(Tag); 26 | } 27 | 28 | // Calls the Bind function of the Settings Widget of this setting type 29 | void FSettingsButton::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 30 | { 31 | SettingsWidget.BindButton(PrimaryData, *this); 32 | } 33 | 34 | /********************************************************************************************* 35 | * FSettingsCheckbox 36 | ********************************************************************************************* */ 37 | 38 | // Returns the sub-widget class of this setting type 39 | TSubclassOf FSettingsCheckbox::GetSubWidgetClass() const 40 | { 41 | return USettingsDataAsset::Get().GetCheckboxClass(); 42 | } 43 | 44 | // Calls the Get function of the Settings Widget of this setting type 45 | void FSettingsCheckbox::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 46 | { 47 | const bool Value = SettingsWidget.GetCheckboxValue(Tag); 48 | OutResult = Value ? TEXT("true") : TEXT("false"); 49 | } 50 | 51 | // Calls the Set function of the Settings Widget of this setting type 52 | void FSettingsCheckbox::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 53 | { 54 | const bool NewValue = Value.ToBool(); 55 | SettingsWidget.SetSettingCheckbox(Tag, NewValue); 56 | } 57 | 58 | // Calls the Bind function of the Settings Widget of this setting type 59 | void FSettingsCheckbox::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 60 | { 61 | SettingsWidget.BindCheckbox(PrimaryData, *this); 62 | } 63 | 64 | /********************************************************************************************* 65 | * FSettingsCombobox 66 | ********************************************************************************************* */ 67 | 68 | // Returns the sub-widget class of this setting type 69 | TSubclassOf FSettingsCombobox::GetSubWidgetClass() const 70 | { 71 | return USettingsDataAsset::Get().GetComboboxClass(); 72 | } 73 | 74 | // Calls the Get function of the Settings Widget of this setting type 75 | void FSettingsCombobox::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 76 | { 77 | const int32 Value = SettingsWidget.GetComboboxIndex(Tag); 78 | OutResult = FString::Printf(TEXT("%d"), Value); 79 | } 80 | 81 | // Calls the Set function of the Settings Widget of this setting type 82 | void FSettingsCombobox::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 83 | { 84 | if (Value.IsNumeric()) 85 | { 86 | const int32 NewValue = FCString::Atoi(*Value); 87 | SettingsWidget.SetSettingComboboxIndex(Tag, NewValue); 88 | } 89 | else 90 | { 91 | static const FString Delimiter = TEXT(","); 92 | TArray SeparatedStrings; 93 | Value.ParseIntoArray(SeparatedStrings, *Delimiter); 94 | 95 | TArray NewMembers; 96 | NewMembers.Reserve(SeparatedStrings.Num()); 97 | for (FString& StringIt : SeparatedStrings) 98 | { 99 | NewMembers.Emplace(FText::FromString(MoveTemp(StringIt))); 100 | } 101 | SettingsWidget.SetSettingComboboxMembers(Tag, NewMembers); 102 | } 103 | } 104 | 105 | // Calls the Bind function of the Settings Widget of this setting type 106 | void FSettingsCombobox::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 107 | { 108 | SettingsWidget.BindCombobox(PrimaryData, *this); 109 | } 110 | 111 | /********************************************************************************************* 112 | * FSettingsSlider 113 | ********************************************************************************************* */ 114 | 115 | // Returns the sub-widget class of this setting type 116 | TSubclassOf FSettingsSlider::GetSubWidgetClass() const 117 | { 118 | return USettingsDataAsset::Get().GetSliderClass(); 119 | } 120 | 121 | // Calls the Get function of the Settings Widget of this setting type 122 | void FSettingsSlider::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 123 | { 124 | const double Value = SettingsWidget.GetSliderValue(Tag); 125 | OutResult = FString::Printf(TEXT("%f"), Value); 126 | } 127 | 128 | // Calls the Set function of the Settings Widget of this setting type 129 | void FSettingsSlider::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 130 | { 131 | const double NewValue = FCString::Atod(*Value); 132 | SettingsWidget.SetSettingSlider(Tag, NewValue); 133 | } 134 | 135 | // Calls the Bind function of the Settings Widget of this setting type 136 | void FSettingsSlider::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 137 | { 138 | SettingsWidget.BindSlider(PrimaryData, *this); 139 | } 140 | 141 | /********************************************************************************************* 142 | * FSettingsTextLine 143 | ********************************************************************************************* */ 144 | 145 | // Returns the sub-widget class of this setting type 146 | TSubclassOf FSettingsTextLine::GetSubWidgetClass() const 147 | { 148 | return USettingsDataAsset::Get().GetTextLineClass(); 149 | } 150 | 151 | // Calls the Get function of the Settings Widget of this setting type 152 | void FSettingsTextLine::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 153 | { 154 | FText OutText; 155 | SettingsWidget.GetTextLineValue(Tag, OutText); 156 | OutResult = OutText.ToString(); 157 | } 158 | 159 | // Calls the Set function of the Settings Widget of this setting type 160 | void FSettingsTextLine::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 161 | { 162 | const FText NewValue = FText::FromString(Value); 163 | SettingsWidget.SetSettingTextLine(Tag, NewValue); 164 | } 165 | 166 | // Calls the Bind function of the Settings Widget of this setting type 167 | void FSettingsTextLine::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 168 | { 169 | SettingsWidget.BindTextLine(PrimaryData, *this); 170 | } 171 | 172 | /********************************************************************************************* 173 | * FSettingsUserInput 174 | ********************************************************************************************* */ 175 | 176 | // Returns the sub-widget class of this setting type 177 | TSubclassOf FSettingsUserInput::GetSubWidgetClass() const 178 | { 179 | return USettingsDataAsset::Get().GetUserInputClass(); 180 | } 181 | 182 | // Calls the Get function of the Settings Widget of this setting type 183 | void FSettingsUserInput::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 184 | { 185 | const FName Value = SettingsWidget.GetUserInputValue(Tag); 186 | OutResult = Value.ToString(); 187 | } 188 | 189 | // Calls the Set function of the Settings Widget of this setting type 190 | void FSettingsUserInput::SetSettingValue(USettingsWidget& SettingsWidget, const FSettingTag& Tag, const FString& Value) 191 | { 192 | const FName NewValue = *Value; 193 | SettingsWidget.SetSettingUserInput(Tag, NewValue); 194 | } 195 | 196 | // Calls the Bind function of the Settings Widget of this setting type 197 | void FSettingsUserInput::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 198 | { 199 | SettingsWidget.BindUserInput(PrimaryData, *this); 200 | } 201 | 202 | /********************************************************************************************* 203 | * FSettingsCustomWidget 204 | ********************************************************************************************* */ 205 | 206 | // Returns the sub-widget class of this setting type 207 | TSubclassOf FSettingsCustomWidget::GetSubWidgetClass() const 208 | { 209 | return CustomWidgetClass; 210 | } 211 | 212 | // Calls the Get function of the Settings Widget of this setting type 213 | void FSettingsCustomWidget::GetSettingValue(const USettingsWidget& SettingsWidget, const FSettingTag& Tag, FString& OutResult) const 214 | { 215 | const TSoftObjectPtr CustomWidget = SettingsWidget.GetCustomWidget(Tag); 216 | OutResult = CustomWidget.IsValid() ? CustomWidget.ToSoftObjectPath().ToString() : TEXT(""); 217 | } 218 | 219 | // Calls the Bind function of the Settings Widget of this setting type 220 | void FSettingsCustomWidget::BindSetting(USettingsWidget& SettingsWidget, const FSettingsPrimary& PrimaryData) 221 | { 222 | SettingsWidget.BindCustomWidget(PrimaryData, *this); 223 | } 224 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingFunction.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "Data/SettingFunction.h" 4 | //--- 5 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingFunction) 6 | 7 | // Empty setting function data 8 | const FSettingFunctionPicker FSettingFunctionPicker::EmptySettingFunction = FSettingFunctionPicker(); 9 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingTag.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "Data/SettingTag.h" 4 | //--- 5 | #include "GameplayTagsManager.h" 6 | //--- 7 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingTag) 8 | 9 | // Setting gag that contains nothing by default 10 | const FSettingTag FSettingTag::EmptySettingTag = EmptyTag; 11 | 12 | // The global of Setting tag categories 13 | FGlobalSettingTags FGlobalSettingTags::GSettingTags; 14 | 15 | // Automatically adds native setting tags at startup 16 | void FGlobalSettingTags::AddTags() 17 | { 18 | UGameplayTagsManager& Manager = UGameplayTagsManager::Get(); 19 | 20 | ButtonSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.Button")); 21 | CheckboxSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.Checkbox")); 22 | ComboboxSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.Combobox")); 23 | ScrollboxSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.Scrollbox")); 24 | SliderSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.Slider")); 25 | TextLineSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.TextLine")); 26 | UserInputSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.UserInput")); 27 | CustomWidgetSettingTag = Manager.AddNativeGameplayTag(TEXT("Settings.CustomWidget")); 28 | } 29 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingsDataAsset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "Data/SettingsDataAsset.h" 4 | //--- 5 | #include "Data/SettingsDataTable.h" 6 | #include "MyUtilsLibraries/SettingsUtilsLibrary.h" 7 | //--- 8 | #include "DataRegistry.h" 9 | #include "Engine/Engine.h" 10 | #include "Misc/CoreDelegates.h" 11 | //--- 12 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsDataAsset) 13 | 14 | // Returns the data table, it has to be set manually 15 | const USettingsDataTable* USettingsDataAsset::GetSettingsDataTable() const 16 | { 17 | return SettingsDataTableInternal.LoadSynchronous(); 18 | } 19 | 20 | // Returns the Settings Data Registry asset, is automatically set by default to which 'Settings Data Table' is added by itself 21 | UDataRegistry* USettingsDataAsset::GetSettingsDataRegistry() const 22 | { 23 | return SettingsDataRegistryInternal.LoadSynchronous(); 24 | } 25 | 26 | // Overrides post init to register Settings Data Table by default on startup 27 | void USettingsDataAsset::PostInitProperties() 28 | { 29 | Super::PostInitProperties(); 30 | 31 | if (!GEngine || !GEngine->IsInitialized()) 32 | { 33 | FCoreDelegates::OnPostEngineInit.AddUObject(this, &ThisClass::OnPostEngineInit); 34 | } 35 | else 36 | { 37 | OnPostEngineInit(); 38 | } 39 | } 40 | 41 | // Is called once Engine is initialized, so we can register Settings Data Table by default on startup 42 | void USettingsDataAsset::OnPostEngineInit() 43 | { 44 | USettingsUtilsLibrary::RegisterDataTable(SettingsDataTableInternal); 45 | } 46 | 47 | #if WITH_EDITOR 48 | // Overrides property change events to handle picking Settings Data Table 49 | void USettingsDataAsset::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 50 | { 51 | Super::PostEditChangeProperty(PropertyChangedEvent); 52 | 53 | static const FName PropertyName = GET_MEMBER_NAME_CHECKED(USettingsDataAsset, SettingsDataTableInternal); 54 | const FProperty* Property = PropertyChangedEvent.Property; 55 | if (Property && Property->GetFName() == PropertyName) 56 | { 57 | // The Settings Data Table has been changed, so we have to register it 58 | USettingsUtilsLibrary::RegisterDataTable(SettingsDataTableInternal); 59 | } 60 | } 61 | #endif // WITH_EDITOR 62 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingsDataTable.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "Data/SettingsDataTable.h" 4 | //--- 5 | #include "Data/SettingsRow.h" 6 | //--- 7 | #if WITH_EDITOR 8 | #include "DataTableEditorUtils.h" // FDataTableEditorUtils::RenameRow 9 | #include "Misc/DataValidation.h" // IsDataValid func 10 | #endif 11 | //--- 12 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsDataTable) 13 | 14 | // Default constructor to set members as FSettingsRow 15 | USettingsDataTable::USettingsDataTable() 16 | { 17 | RowStruct = FSettingsRow::StaticStruct(); 18 | } 19 | 20 | // Returns the table rows 21 | void USettingsDataTable::GetSettingRows(TMap& OutRows) const 22 | { 23 | GetRows(OutRows); 24 | } 25 | 26 | #if WITH_EDITOR 27 | // Called on every change in this data table to automatic set the key name by specified setting tag 28 | void USettingsDataTable::OnThisDataTableChanged(FName RowKey, const uint8& RowData) 29 | { 30 | Super::OnThisDataTableChanged(RowKey, RowData); 31 | 32 | // Set row name by specified tag 33 | const FSettingsRow& Row = reinterpret_cast(RowData); 34 | const FName RowValueTag = Row.SettingsPicker.PrimaryData.Tag.GetTagName(); 35 | if (!RowValueTag.IsNone() // Tag is not empty 36 | && RowKey != RowValueTag // New tag name 37 | && !RowMap.Contains(RowValueTag)) // Unique key 38 | { 39 | FDataTableEditorUtils::RenameRow(this, RowKey, RowValueTag); 40 | } 41 | } 42 | 43 | // Is called to validate the data table setup 44 | EDataValidationResult USettingsDataTable::IsDataValid(FDataValidationContext& Context) const 45 | { 46 | EDataValidationResult Result = CombineDataValidationResults(Super::IsDataValid(Context), EDataValidationResult::Valid); 47 | 48 | int32 RowIndex = 1; // Tables indexing starts from 1 49 | TMap SettingsRows; 50 | GetSettingRows(SettingsRows); 51 | for (const TTuple& RowIt : SettingsRows) 52 | { 53 | const EDataValidationResult RowResult = RowIt.Value.SettingsPicker.IsDataValid(Context); 54 | Result = CombineDataValidationResults(Result, RowResult); 55 | if (RowResult == EDataValidationResult::Invalid) 56 | { 57 | Context.AddError(FText::FromString(FString::Printf(TEXT("ERROR: Next setting row is invalid: index [%d], name: '%s'"), RowIndex, *RowIt.Key.ToString()))); 58 | } 59 | 60 | ++RowIndex; 61 | } 62 | 63 | return Result; 64 | } 65 | #endif // WITH_EDITOR 66 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingsRow.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "Data/SettingsRow.h" 4 | //--- 5 | #if WITH_EDITOR 6 | #include "Misc/DataValidation.h" // IsDataValid func 7 | #endif 8 | //--- 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsRow) 10 | 11 | // Empty settings primary row 12 | const FSettingsPrimary FSettingsPrimary::EmptyPrimary = FSettingsPrimary(); 13 | 14 | // Empty settings row 15 | const FSettingsPicker FSettingsPicker::Empty = FSettingsPicker(); 16 | 17 | // Compares for equality 18 | bool FSettingsPrimary::operator==(const FSettingsPrimary& Other) const 19 | { 20 | return GetTypeHash(*this) == GetTypeHash(Other); 21 | } 22 | 23 | #if WITH_EDITOR 24 | // Validates chosen data 25 | EDataValidationResult FSettingsPrimary::IsDataValid(FDataValidationContext& Context) const 26 | { 27 | EDataValidationResult Result = EDataValidationResult::Valid; 28 | 29 | if (!Tag.IsValid()) 30 | { 31 | Context.AddError(FText::FromString(TEXT("`Tag` is not set for the setting, it can't be displayed!"))); 32 | Result = EDataValidationResult::Invalid; 33 | } 34 | 35 | // Validate Owner if it has Setter or Getter function and validate set function itself 36 | const bool bHasSetterFunc = Setter != FSettingFunctionPicker::EmptySettingFunction; 37 | const bool bHasGetterFunc = Getter != FSettingFunctionPicker::EmptySettingFunction; 38 | if (bHasSetterFunc || bHasGetterFunc) 39 | { 40 | const EDataValidationResult OwnerResult = Owner.IsDataValid(Context); 41 | Result = CombineDataValidationResults(Result, OwnerResult); 42 | 43 | if (bHasSetterFunc) 44 | { 45 | const EDataValidationResult SetterResult = Setter.IsDataValid(Context); 46 | Result = CombineDataValidationResults(Result, SetterResult); 47 | } 48 | 49 | if (bHasGetterFunc) 50 | { 51 | const EDataValidationResult GetterResult = Getter.IsDataValid(Context); 52 | Result = CombineDataValidationResults(Result, GetterResult); 53 | } 54 | } 55 | 56 | return Result; 57 | } 58 | #endif // WITH_EDITOR 59 | 60 | // Is executed to obtain holding object 61 | UObject* FSettingsPrimary::GetSettingOwner(const UObject* WorldContext) const 62 | { 63 | if (OwnerFunc.IsBound()) 64 | { 65 | return OwnerFunc.Execute(WorldContext); 66 | } 67 | 68 | return nullptr; 69 | } 70 | 71 | // Creates a hash value 72 | uint32 GetTypeHash(const FSettingsPrimary& Other) 73 | { 74 | const uint32 TagHash = GetTypeHash(Other.Tag); 75 | const uint32 ObjectContextHash = GetTypeHash(Other.Owner); 76 | const uint32 SetterHash = GetTypeHash(Other.Setter); 77 | const uint32 GetterHash = GetTypeHash(Other.Getter); 78 | const uint32 CaptionHash = GetTypeHash(Other.Caption.ToString()); 79 | const uint32 TooltipHash = GetTypeHash(Other.Tooltip.ToString()); 80 | const uint32 PaddingLeftHash = GetTypeHash(Other.Padding.Left); 81 | const uint32 PaddingTopHash = GetTypeHash(Other.Padding.Top); 82 | const uint32 PaddingRightHash = GetTypeHash(Other.Padding.Right); 83 | const uint32 PaddingBottomHash = GetTypeHash(Other.Padding.Bottom); 84 | const uint32 LineHeightHash = GetTypeHash(Other.LineHeight); 85 | const uint32 StartOnNextColumnHash = GetTypeHash(Other.bStartOnNextColumn); 86 | const uint32 SettingsToUpdateHash = GetTypeHash(Other.SettingsToUpdate.ToStringSimple()); 87 | return HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(HashCombine(TagHash, ObjectContextHash), SetterHash), GetterHash), CaptionHash), TooltipHash), PaddingLeftHash), PaddingTopHash), PaddingRightHash), PaddingBottomHash), LineHeightHash), StartOnNextColumnHash), SettingsToUpdateHash); 88 | } 89 | 90 | // Returns the pointer to one of the chosen in-game type 91 | FSettingsDataBase* FSettingsPicker::GetChosenSettingsData() const 92 | { 93 | if (!SettingsType.IsNone()) 94 | { 95 | static const UScriptStruct* const& SettingsPickerStruct = StaticStruct(); 96 | const FProperty* FoundProperty = SettingsPickerStruct ? SettingsPickerStruct->FindPropertyByName(SettingsType) : nullptr; 97 | const FStructProperty* FoundStructProperty = CastField(FoundProperty); 98 | const FSettingsDataBase* FoundSetting = FoundStructProperty ? FoundStructProperty->ContainerPtrToValuePtr(this, 0) : nullptr; 99 | return const_cast(FoundSetting); 100 | } 101 | return nullptr; 102 | } 103 | 104 | // Compares for equality 105 | bool FSettingsPicker::operator==(const FSettingsPicker& Other) const 106 | { 107 | return GetChosenSettingsData() == Other.GetChosenSettingsData() 108 | && GetTypeHash(*this) == GetTypeHash(Other); 109 | } 110 | 111 | // Creates a hash value 112 | uint32 GetTypeHash(const FSettingsPicker& Other) 113 | { 114 | return GetTypeHash(Other.PrimaryData); 115 | } 116 | 117 | #if WITH_EDITOR 118 | // Validates chosen data 119 | EDataValidationResult FSettingsPicker::IsDataValid(FDataValidationContext& Context) const 120 | { 121 | EDataValidationResult Result = EDataValidationResult::Valid; 122 | 123 | const EDataValidationResult PrimaryDataResult = PrimaryData.IsDataValid(Context); 124 | Result = CombineDataValidationResults(Result, PrimaryDataResult); 125 | 126 | const FSettingsDataBase* ChosenData = GetChosenSettingsData(); 127 | if (ChosenData) 128 | { 129 | const EDataValidationResult ChosenDataResult = ChosenData->IsDataValid(Context); 130 | Result = CombineDataValidationResults(Result, ChosenDataResult); 131 | } 132 | else 133 | { 134 | Context.AddError(FText::FromString(TEXT("`SettingsType` is not set"))); 135 | Result = EDataValidationResult::Invalid; 136 | } 137 | 138 | return Result; 139 | } 140 | #endif // WITH_EDITOR 141 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/Data/SettingsThemeData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "Data/SettingsThemeData.h" 4 | //--- 5 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsThemeData) 6 | 7 | // Default constructor to set default values 8 | FButtonThemeData::FButtonThemeData() 9 | { 10 | Size = FVector2D(128.f); 11 | Margin = 0.25f; 12 | } 13 | 14 | // Default constructor to set default values 15 | FCheckboxThemeData::FCheckboxThemeData() 16 | { 17 | Size = FVector2D(24.f); 18 | DrawAs = ESlateBrushDrawType::Image; 19 | } 20 | 21 | // Default constructor to set default values 22 | FComboboxThemeData::FComboboxThemeData() 23 | { 24 | PressedPadding = FMargin(4.f, 6.5f, 2.f, 9.f); 25 | 26 | Arrow.Size = FVector2D(32.f); 27 | Arrow.DrawAs = ESlateBrushDrawType::Image; 28 | 29 | Border.Size = FVector2D(128.f); 30 | Border.Margin = 0.45f; 31 | Border.Padding = 5.f; 32 | 33 | Size = FVector2D(128.f); 34 | Margin = 0.25f; 35 | Padding = FMargin(4.f, 6.5f, 2.f, 9.f); 36 | } 37 | 38 | // Default constructor to set default values 39 | FSliderThemeData::FSliderThemeData() 40 | { 41 | Thumb.Size = FVector2D(12.f, 24.f); 42 | Thumb.DrawAs = ESlateBrushDrawType::Image; 43 | 44 | Size = FVector2D(128.f, 32.f); 45 | } 46 | 47 | // Default constructor to set default values 48 | FMiscThemeData::FMiscThemeData() 49 | { 50 | static const FLinearColor BlackOlive(FVector(0.046665f)); 51 | ThemeColorNormal = BlackOlive; 52 | 53 | static const FLinearColor Nickel(FVector(0.168269f)); 54 | ThemeColorHover = Nickel; 55 | ThemeColorExtra = Nickel; 56 | 57 | TooltipBackground.Size = FVector2D(128.f, 64.f); 58 | TooltipBackground.Margin = 0.2f; 59 | 60 | WindowBackground.Size = FVector2D(128.f, 64.f); 61 | WindowBackground.Margin = 0.3f; 62 | 63 | MenuBorderData.Margin = 0.45f; 64 | } 65 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/FunctionPickerData/FunctionPicker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "FunctionPickerData/SWCFunctionPicker.h" 4 | //--- 5 | #if WITH_EDITOR 6 | #include "Misc/DataValidation.h" // IsDataValid func 7 | #endif 8 | //--- 9 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SWCFunctionPicker) 10 | 11 | // Empty settings function 12 | const FSWCFunctionPicker FSWCFunctionPicker::Empty = FSWCFunctionPicker(); 13 | 14 | // Custom constructor to set all members values 15 | FSWCFunctionPicker::FSWCFunctionPicker(UClass* InFunctionClass, FName InFunctionName) 16 | : FunctionClass(InFunctionClass) 17 | , FunctionName(InFunctionName) {} 18 | 19 | // Returns the function pointer based on set data to this structure 20 | UFunction* FSWCFunctionPicker::GetFunction() const 21 | { 22 | if (CachedFunctionInternal.IsValid()) 23 | { 24 | return CachedFunctionInternal.Get(); 25 | } 26 | 27 | if (FunctionClass 28 | && !FunctionName.IsNone()) 29 | { 30 | UFunction* FoundFunction = FunctionClass->FindFunctionByName(FunctionName, EIncludeSuperFlag::ExcludeSuper); 31 | CachedFunctionInternal = FoundFunction; 32 | return FoundFunction; 33 | } 34 | 35 | return nullptr; 36 | } 37 | 38 | // Returns string in text format: Class::Function 39 | FString FSWCFunctionPicker::ToDisplayString() const 40 | { 41 | return IsValid() ? FString::Printf(TEXT("%s::%s"), *FunctionClass->GetName(), *FunctionName.ToString()) : FString(); 42 | } 43 | 44 | // Creates a hash value 45 | uint32 GetTypeHash(const FSWCFunctionPicker& Other) 46 | { 47 | const uint32 FunctionClassHash = GetTypeHash(Other.FunctionClass); 48 | const uint32 FunctionNameHash = GetTypeHash(Other.FunctionName); 49 | return HashCombine(FunctionClassHash, FunctionNameHash); 50 | } 51 | 52 | #if WITH_EDITOR 53 | // Validates chosen data 54 | EDataValidationResult FSWCFunctionPicker::IsDataValid(FDataValidationContext& Context) const 55 | { 56 | if (!FunctionClass) 57 | { 58 | Context.AddError(FText::FromString(TEXT("Function class is not set"))); 59 | 60 | // Don't process next 61 | return EDataValidationResult::Invalid; 62 | } 63 | 64 | if (FunctionName.IsNone()) 65 | { 66 | static const FText ErrorText = FText::FromString(TEXT("Function name is not set while the class '{0}' is chosen!")); 67 | Context.AddError(FText::Format(ErrorText, FText::FromString(FunctionClass->GetName()))); 68 | 69 | // Don't process next 70 | return EDataValidationResult::Invalid; 71 | } 72 | 73 | // Check UFunction is set 74 | if (!GetFunction()) 75 | { 76 | static const FText ErrorText = FText::FromString(TEXT("Function '{0}' does not exist in the class '{1}'!")); 77 | Context.AddError(FText::Format(ErrorText, FText::FromName(FunctionName), FText::FromString(FunctionClass->GetName()))); 78 | 79 | // Don't process next 80 | return EDataValidationResult::Invalid; 81 | } 82 | 83 | return EDataValidationResult::Valid; 84 | } 85 | #endif // WITH_EDITOR 86 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/FunctionPickerData/SWCFunctionPicker.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "SWCFunctionPicker.generated.h" 6 | 7 | /** 8 | * Allows designer to choose the function in the list. 9 | * 10 | * It's possible to set the template by specifying the function or delegate in the UPROPERTY's meta. 11 | * It will filter the list of all functions by its return type and type of all function arguments. 12 | * If none meta is set, all functions will appear in the list without any filtering. 13 | * 14 | * Meta keys: 15 | * FunctionContextTemplate (only to show static functions or Blueprint Library functions) 16 | * FunctionGetterTemplate 17 | * FunctionSetterTemplate 18 | * 19 | * Meta value (without prefixes): 20 | * /Script/ModuleName.ClassName::FunctionName 21 | * 22 | * Example: 23 | * UPROPERTY(EditDefaultsOnly, meta = (FunctionSetterTemplate = "/Script/FunctionPicker.FunctionPickerTemplate::OnSetMembers__DelegateSignature")) 24 | * FSWCFunctionPicker SetMembers = FSWCFunctionPicker::Empty; 25 | */ 26 | USTRUCT(BlueprintType) 27 | struct SETTINGSWIDGETCONSTRUCTOR_API FSWCFunctionPicker 28 | { 29 | GENERATED_BODY() 30 | 31 | /** Empty function data. */ 32 | static const FSWCFunctionPicker Empty; 33 | 34 | /** Default constructor. */ 35 | FSWCFunctionPicker() = default; 36 | 37 | /** Custom constructor to set all members values. */ 38 | FSWCFunctionPicker(UClass* InFunctionClass, FName InFunctionName); 39 | 40 | /** The class where function can be found. */ 41 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (DisplayName = "Class", AllowAbstract, ShowOnlyInnerProperties)) 42 | TObjectPtr FunctionClass = nullptr; 43 | 44 | /** The function name to choose for specified class.*/ 45 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (DisplayName = "Function", ShowOnlyInnerProperties)) 46 | FName FunctionName = NAME_None; 47 | 48 | /** Returns true if is valid. */ 49 | FORCEINLINE bool IsValid() const { return FunctionClass && !FunctionName.IsNone(); } 50 | 51 | /** Returns the function pointer based on set data to this structure. */ 52 | UFunction* GetFunction() const; 53 | 54 | /** Returns string in text format: Class::Function. */ 55 | FString ToDisplayString() const; 56 | 57 | /** Compares for equality. 58 | * @param Other The other object being compared. */ 59 | FORCEINLINE bool operator==(const FSWCFunctionPicker& Other) const { return GetTypeHash(*this) == GetTypeHash(Other); } 60 | 61 | /** Creates a hash value. 62 | * @param Other the other object to create a hash value for. */ 63 | friend SETTINGSWIDGETCONSTRUCTOR_API uint32 GetTypeHash(const FSWCFunctionPicker& Other); 64 | 65 | /** bool operator */ 66 | FORCEINLINE operator bool() const { return IsValid(); } 67 | 68 | /** FName operator */ 69 | FORCEINLINE operator FName() const { return FunctionName; } 70 | 71 | #if WITH_EDITOR 72 | /** Returns error if function is not valid. 73 | * To make this validation work, override IsDataValid(Context) in any UObject-derived class and call this function. 74 | * See example: https://github.com/JanSeliv/Bomber/commit/a29b933 */ 75 | EDataValidationResult IsDataValid(class FDataValidationContext& Context) const; 76 | #endif // WITH_EDITOR 77 | 78 | protected: 79 | /** Contains cached function ptr for performance reasons. */ 80 | mutable TWeakObjectPtr CachedFunctionInternal = nullptr; 81 | }; 82 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/FunctionPickerData/SWCFunctionPickerTemplate.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "UObject/Object.h" 6 | //--- 7 | #include "SWCFunctionPickerTemplate.generated.h" 8 | 9 | /** 10 | * Delegates wrapper that are used as templates for FFunctionPicker properties. 11 | * Has to have reflection to allow find its members by FFunctionPickerCustomization: 12 | * UFunctionPickerTemplate::StaticClass()->FindFunctionByName("OnGetterObject__DelegateSignature"); 13 | * DECLARE_DYNAMIC_DELEGATE can't be declared under USTRUCT 14 | */ 15 | UCLASS(Abstract, Const, Transient) 16 | class USWCFunctionPickerTemplate : public UObject 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | DECLARE_DYNAMIC_DELEGATE_RetVal_OneParam(UObject*, FOnGetterObject, const UObject*, WorldContext); 22 | 23 | DECLARE_DYNAMIC_DELEGATE(FOnButtonPressed); 24 | 25 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterInt, int32, Param); 26 | 27 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterFloat, double, Param); 28 | 29 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterBool, bool, Param); 30 | 31 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterText, FText, Param); 32 | 33 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetMembers, const TArray&, NewMembers); 34 | 35 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterName, FName, Param); 36 | 37 | DECLARE_DYNAMIC_DELEGATE_RetVal(int32, FOnGetterInt); 38 | 39 | DECLARE_DYNAMIC_DELEGATE_RetVal(double, FOnGetterFloat); 40 | 41 | DECLARE_DYNAMIC_DELEGATE_RetVal(bool, FOnGetterBool); 42 | 43 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnGetterText, FText&, OutParam); 44 | 45 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnGetMembers, TArray&, OutParam); 46 | 47 | DECLARE_DYNAMIC_DELEGATE_RetVal(FName, FOnGetterName); 48 | }; 49 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/MyDataTable/MyDataTable.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "MyDataTable/SWCMyDataTable.h" 4 | //--- 5 | #if WITH_EDITOR 6 | #include "EditorFramework/AssetImportData.h" 7 | #include "Misc/FileHelper.h" 8 | #include "UObject/ObjectSaveContext.h" 9 | #endif // WITH_EDITOR 10 | //--- 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SWCMyDataTable) 12 | 13 | // Default constructor to set RowStruct structure inherit from FSWCMyTableRow 14 | USWCMyDataTable::USWCMyDataTable() 15 | { 16 | RowStruct = FSWCMyTableRow::StaticStruct(); 17 | } 18 | 19 | #if WITH_EDITOR 20 | // Called on every change in this this row 21 | void FSWCMyTableRow::OnDataTableChanged(const UDataTable* InDataTable, const FName InRowName) 22 | { 23 | if (const USWCMyDataTable* SWCMyDataTable = Cast(InDataTable)) 24 | { 25 | const uint8& ThisRowPtr = reinterpret_cast(*this); 26 | USWCMyDataTable* DataTable = const_cast(SWCMyDataTable); 27 | DataTable->OnThisDataTableChanged(InRowName, ThisRowPtr); 28 | } 29 | } 30 | 31 | // Is called on saving the data table 32 | void USWCMyDataTable::PostSaveRoot(FObjectPostSaveRootContext ObjectSaveContext) 33 | { 34 | Super::PostSaveRoot(ObjectSaveContext); 35 | 36 | ReexportToJson(); 37 | } 38 | 39 | // Reexports this table to .json 40 | void USWCMyDataTable::ReexportToJson() 41 | { 42 | if (!AssetImportData) 43 | { 44 | return; 45 | } 46 | 47 | const FString CurrentFilename = AssetImportData->GetFirstFilename(); 48 | if (!CurrentFilename.IsEmpty()) 49 | { 50 | const FString TableAsJSON = GetTableAsJSON(EDataTableExportFlags::UseJsonObjectsForStructs); 51 | FFileHelper::SaveStringToFile(TableAsJSON, *CurrentFilename); 52 | } 53 | } 54 | #endif // WITH_EDITOR 55 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/MyDataTable/SWCMyDataTable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "Engine/DataTable.h" 6 | //--- 7 | #include "SWCMyDataTable.generated.h" 8 | 9 | /** 10 | * Base class for all custom data table row structs to inherit from. 11 | */ 12 | USTRUCT(BlueprintType) 13 | struct FSWCMyTableRow : public FTableRowBase 14 | { 15 | GENERATED_BODY() 16 | 17 | #if WITH_EDITOR 18 | /** Called on every change in this this row. 19 | * @see USWCMyDataTable::OnThisDataTableChanged. */ 20 | virtual void OnDataTableChanged(const UDataTable* InDataTable, const FName InRowName) override; 21 | #endif // WITH_EDITOR 22 | }; 23 | 24 | /** 25 | * Provides additional in-editor functionality like reexporting .json on data table change. 26 | * Its RowStruct structure must inherit from FSWCMyTableRow. 27 | */ 28 | UCLASS(Abstract) 29 | class USWCMyDataTable : public UDataTable 30 | { 31 | GENERATED_BODY() 32 | 33 | public: 34 | /** Default constructor to set RowStruct structure inherit from FSWCMyTableRow. */ 35 | USWCMyDataTable(); 36 | 37 | /** Returns the table rows. */ 38 | template 39 | void GetRows(TMap& OutRows) const { GetRows(*this, OutRows); } 40 | 41 | template 42 | static void GetRows(const UDataTable& DataTable, TMap& OutRows); 43 | 44 | protected: 45 | #if WITH_EDITOR 46 | friend FSWCMyTableRow; 47 | 48 | /** Called on every change in this data table to reexport .json. 49 | * Is created to let child data tables reacts on changes without binding to its delegate, 50 | * can't use UDataTable::HandleDataTableChanged() since it is not virtual. 51 | * Is in runtime module since FDataTableEditor is private. */ 52 | virtual void OnThisDataTableChanged(FName RowName, const uint8& RowData) {} 53 | 54 | /** Is called on saving the data table. */ 55 | virtual void PostSaveRoot(FObjectPostSaveRootContext ObjectSaveContext) override; 56 | 57 | /** Reexports this table to .json. */ 58 | virtual void ReexportToJson(); 59 | #endif // WITH_EDITOR 60 | }; 61 | 62 | /** Returns the table rows. */ 63 | template 64 | void USWCMyDataTable::GetRows(const UDataTable& DataTable, TMap& OutRows) 65 | { 66 | if (!ensureAlwaysMsgf(DataTable.RowStruct && DataTable.RowStruct->IsChildOf(T::StaticStruct()), TEXT("ASSERT: 'RowStruct' is not child of specified struct"))) 67 | { 68 | return; 69 | } 70 | 71 | if (!OutRows.IsEmpty()) 72 | { 73 | OutRows.Empty(); 74 | } 75 | 76 | const TMap& RowMap = DataTable.GetRowMap(); 77 | OutRows.Reserve(RowMap.Num()); 78 | for (const TTuple& RowIt : RowMap) 79 | { 80 | if (const T* FoundRowPtr = reinterpret_cast(RowIt.Value)) 81 | { 82 | OutRows.Emplace(RowIt.Key, *FoundRowPtr); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/MyUtilsLibraries/SWCWidgetUtilsLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "Components/Widget.h" 6 | 7 | class UUserWidget; 8 | 9 | /** 10 | * The common functions library for widgets. 11 | */ 12 | class FSWCWidgetUtilsLibrary 13 | { 14 | public: 15 | /** Return the parent widget of a specific class in the widget tree hierarchy. */ 16 | template 17 | static FORCEINLINE T* GetParentWidgetOfClass(const UUserWidget* ChildWidget) { return Cast(GetParentWidgetOfClass(ChildWidget, T::StaticClass())); } 18 | static UUserWidget* GetParentWidgetOfClass(const UUserWidget* InWidget, TSubclassOf ParentWidgetClass); 19 | 20 | /** Returns first widget found by specified class iterating all widget objects. */ 21 | template 22 | static FORCEINLINE T* FindWidgetOfClass(UObject* WorldContextObject) { return Cast(FindWidgetOfClass(WorldContextObject, T::StaticClass())); } 23 | static UUserWidget* FindWidgetOfClass(UObject* WorldContextObject, TSubclassOf ParentWidgetClass); 24 | 25 | /** Returns the slate widget from UMG widget. 26 | * As an example, it returns SCheckbox slate widget from UCheckBox widget. */ 27 | template 28 | static FORCEINLINE TSharedPtr GetSlateWidget(const UWidget* ForWidget) { return ForWidget ? StaticCastSharedPtr(ForWidget->GetCachedWidget()) : nullptr; } 29 | 30 | /** Completely destroys specified widget. 31 | * Is useful for MGF-modules unloading in runtime. 32 | * In most gameplay cases it should not be used, since it's expensive: prefer collapse widget instead. */ 33 | static void DestroyWidget(UUserWidget& ParentWidget); 34 | }; 35 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/MyUtilsLibraries/SettingsUtilsLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "MyUtilsLibraries/SettingsUtilsLibrary.h" 4 | //--- 5 | #include "MyUtilsLibraries/SWCWidgetUtilsLibrary.h" 6 | #include "GameFramework/GameUserSettings.h" 7 | #include "UI/SettingsWidget.h" 8 | #include "Data/SettingsDataAsset.h" 9 | #include "Data/SettingsDataTable.h" 10 | #include "Data/SettingsRow.h" 11 | //--- 12 | #include "Engine/Engine.h" 13 | #include "Engine/World.h" 14 | #include "DataRegistrySource_DataTable.h" 15 | #include "DataRegistrySubsystem.h" 16 | //--- 17 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsUtilsLibrary) 18 | 19 | // Returns the Settings widget from viewport 20 | USettingsWidget* USettingsUtilsLibrary::GetSettingsWidget(const UObject* WorldContextObject) 21 | { 22 | UWorld* World = GEngine ? GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull) : nullptr; 23 | return FSWCWidgetUtilsLibrary::FindWidgetOfClass(World); 24 | } 25 | 26 | // Returns the Game User Settings object 27 | UGameUserSettings* USettingsUtilsLibrary::GetGameUserSettings(const UObject* OptionalWorldContext/* = nullptr*/) 28 | { 29 | return GEngine ? GEngine->GetGameUserSettings() : nullptr; 30 | } 31 | 32 | // Returns all Settings Rows from project's Settings Data Table and all other additional Data Tables from 'SettingsDataTable' Data Registry 33 | void USettingsUtilsLibrary::GenerateAllSettingRows(TMap& OutSettingRows) 34 | { 35 | if (!OutSettingRows.IsEmpty()) 36 | { 37 | OutSettingRows.Empty(); 38 | } 39 | 40 | TSet OutDataTables; 41 | GetAllSettingDataTables(OutDataTables); 42 | 43 | if (!ensureMsgf(!OutDataTables.IsEmpty(), TEXT("ASSERT: [%i] %s:\n'Settings Data Table' is not set in the 'Project Settings', can't retrieve any settings!"), __LINE__, *FString(__FUNCTION__))) 44 | { 45 | return; 46 | } 47 | 48 | /** 49 | * Sort Setting Rows based on the FSettingsPrimary::ShowNextToSettingOverride property. 50 | * All the next settings after ShowNextToSettingOverride in the same table will be also shown next to it. 51 | * Sort is needed since setting can be shown based on another setting in different Settings Data Table, so we want to fix the order. 52 | */ 53 | 54 | TArray OrderedSettings; 55 | TMap> OverrideBlocks; 56 | 57 | // Collect settings and override blocks 58 | for (const USettingsDataTable* TableIt : OutDataTables) 59 | { 60 | checkf(TableIt, TEXT("ERROR: [%i] %s:\n'TableIt' is null!"), __LINE__, *FString(__FUNCTION__)); 61 | 62 | TMap TableRows; 63 | TableIt->GetSettingRows(TableRows); 64 | 65 | TArray CurrentOverrideBlock; 66 | FSettingTag CurrentOverrideTag; 67 | 68 | for (const TTuple& Pair : TableRows) 69 | { 70 | const FSettingsRow& Row = Pair.Value; 71 | const FSettingTag& OverrideTag = Row.SettingsPicker.PrimaryData.ShowNextToSettingOverride; 72 | 73 | if (OverrideTag.IsValid()) 74 | { 75 | // Store the previous block if any, then start a new block 76 | if (CurrentOverrideBlock.Num() > 0) 77 | { 78 | OverrideBlocks.Emplace(CurrentOverrideTag, MoveTemp(CurrentOverrideBlock)); 79 | } 80 | CurrentOverrideBlock.Empty(); 81 | CurrentOverrideTag = OverrideTag; 82 | } 83 | 84 | if (CurrentOverrideTag.IsValid()) 85 | { 86 | CurrentOverrideBlock.Emplace(Row); 87 | } 88 | else 89 | { 90 | OrderedSettings.Emplace(Row); 91 | } 92 | } 93 | 94 | // Add the last block if any 95 | if (CurrentOverrideBlock.Num() > 0) 96 | { 97 | OverrideBlocks.Emplace(CurrentOverrideTag, MoveTemp(CurrentOverrideBlock)); 98 | } 99 | } 100 | 101 | // Build the final map, handling the override blocks 102 | for (const FSettingsRow& Row : OrderedSettings) 103 | { 104 | const FName Tag = Row.SettingsPicker.PrimaryData.Tag.GetTagName(); 105 | OutSettingRows.Add(Tag, Row.SettingsPicker); 106 | 107 | // Check if there's an override block for this tag 108 | const FSettingTag SettingTag = Row.SettingsPicker.PrimaryData.Tag; 109 | TArray* OverrideBlock = OverrideBlocks.Find(SettingTag); 110 | if (OverrideBlock) 111 | { 112 | // Add the override block next to the current setting 113 | for (const FSettingsRow& OverrideRow : *OverrideBlock) 114 | { 115 | const FName OverrideTag = OverrideRow.SettingsPicker.PrimaryData.Tag.GetTagName(); 116 | OutSettingRows.Add(OverrideTag, OverrideRow.SettingsPicker); 117 | } 118 | } 119 | } 120 | } 121 | 122 | /********************************************************************************************* 123 | * Multiple Data Tables support 124 | ********************************************************************************************* */ 125 | 126 | // Registers the Settings Data Table with the Data Registry 127 | void USettingsUtilsLibrary::RegisterDataTable(const TSoftObjectPtr SettingsDataTable) 128 | { 129 | UDataRegistrySubsystem* DataRegistrySubsystem = UDataRegistrySubsystem::Get(); 130 | checkf(DataRegistrySubsystem, TEXT("ERROR: [%i] %s:\n'DataRegistrySubsystem' is null!"), __LINE__, *FString(__FUNCTION__)); 131 | 132 | const TSoftObjectPtr& SettingsDataRegistry = USettingsDataAsset::Get().GetSettingsDataRegistrySoft(); 133 | if (ensureMsgf(!SettingsDataRegistry.IsNull(), TEXT("ASSERT: 'SettingsDataRegistry' is null, it has to be set automatically, something went wrong!"))) 134 | { 135 | // Initialize the Settings Data Registry 136 | DataRegistrySubsystem->LoadRegistryPath(SettingsDataRegistry.ToSoftObjectPath()); 137 | } 138 | 139 | // If set, add the Settings Data Table to the Settings Data Registry 140 | const FSoftObjectPath DataTablePath = SettingsDataTable.ToSoftObjectPath(); 141 | if (!DataTablePath.IsNull()) 142 | { 143 | TMap> AssetMap; 144 | static const FDataRegistryType RegistryToAddTo{TEXT("SettingsDataTable")}; 145 | TArray& AssetList = AssetMap.Add(RegistryToAddTo); 146 | AssetList.Emplace(DataTablePath); 147 | DataRegistrySubsystem->PreregisterSpecificAssets(AssetMap); 148 | } 149 | } 150 | 151 | // Returns all Settings Data Tables added to 'SettingsDataTable' Data Registry 152 | void USettingsUtilsLibrary::GetAllSettingDataTables(TSet& OutDataTables) 153 | { 154 | if (!OutDataTables.IsEmpty()) 155 | { 156 | OutDataTables.Empty(); 157 | } 158 | 159 | const UDataRegistry* SettingsDataRegistry = USettingsDataAsset::Get().GetSettingsDataRegistry(); 160 | if (!ensureMsgf(SettingsDataRegistry, TEXT("ASSERT: 'SettingsDataRegistry' is not loaded, can't retrieve any settings!"))) 161 | { 162 | return; 163 | } 164 | 165 | const UScriptStruct* SettingStruct = FSettingsRow::StaticStruct(); 166 | TMap OutItemMap; 167 | SettingsDataRegistry->GetAllCachedItems(OutItemMap, SettingStruct); 168 | 169 | // Obtain all Settings Data Tables from the registry 170 | for (const TTuple& ItemIt : OutItemMap) 171 | { 172 | FDataRegistryLookup Lookup; 173 | SettingsDataRegistry->ResolveDataRegistryId(/*out*/Lookup, ItemIt.Key); 174 | 175 | FName ResolvedName; 176 | constexpr int32 LookupIndex = 0; 177 | UDataRegistrySource* LookupSource = SettingsDataRegistry->LookupSource(ResolvedName, Lookup, LookupIndex); 178 | 179 | const UDataRegistrySource_DataTable* DataTableSource = Cast(LookupSource); 180 | if (!DataTableSource || DataTableSource->SourceTable.IsNull()) 181 | { 182 | continue; 183 | } 184 | 185 | const USettingsDataTable* DataTable = Cast(DataTableSource->SourceTable.LoadSynchronous()); 186 | if (ensureMsgf(DataTable, TEXT("ASSERT: [%i] %s:\nNext Settings Data Table is found, but can't be loaded: %s"), __LINE__, *FString(__FUNCTION__), *DataTableSource->SourceTable.GetAssetName())) 187 | { 188 | OutDataTables.Add(DataTable); 189 | } 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/MyUtilsLibraries/WidgetUtilsLibrary.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "MyUtilsLibraries/SWCWidgetUtilsLibrary.h" 4 | //--- 5 | #include "Blueprint/WidgetBlueprintLibrary.h" 6 | #include "Blueprint/WidgetTree.h" 7 | 8 | // Return the parent widget of a specific class in the widget tree hierarchy 9 | UUserWidget* FSWCWidgetUtilsLibrary::GetParentWidgetOfClass(const UUserWidget* InWidget, TSubclassOf ParentWidgetClass) 10 | { 11 | UObject* It = InWidget ? InWidget->GetParent() : nullptr; 12 | if (!It) 13 | { 14 | return nullptr; 15 | } 16 | 17 | UUserWidget* FoundWidget = nullptr; 18 | while ((It = It->GetOuter()) != nullptr) 19 | { 20 | // Check every outer until the desired one is found 21 | if (It->IsA(ParentWidgetClass)) 22 | { 23 | FoundWidget = Cast(It); 24 | break; 25 | } 26 | 27 | if (!It->IsA() 28 | && !It->IsA()) 29 | { 30 | // No sense to iterate non-widget outers 31 | break; 32 | } 33 | } 34 | 35 | return FoundWidget; 36 | } 37 | 38 | // Returns first widget by specified class iterating all widget objects 39 | UUserWidget* FSWCWidgetUtilsLibrary::FindWidgetOfClass(UObject* WorldContextObject, TSubclassOf ParentWidgetClass) 40 | { 41 | TArray FoundWidgets; 42 | UWidgetBlueprintLibrary::GetAllWidgetsOfClass(WorldContextObject, /*out*/FoundWidgets, ParentWidgetClass); 43 | return !FoundWidgets.IsEmpty() ? FoundWidgets[0] : nullptr; 44 | } 45 | 46 | // Completely destroys specified widget 47 | void FSWCWidgetUtilsLibrary::DestroyWidget(UUserWidget& ParentWidget) 48 | { 49 | // Get an array of all child widgets 50 | TArray ChildWidgets; 51 | const UWidgetTree* WidgetTree = ParentWidget.WidgetTree; 52 | WidgetTree->GetAllWidgets(ChildWidgets); 53 | 54 | // Iterate through the child widgets 55 | for (UWidget* ChildWidgetIt : ChildWidgets) 56 | { 57 | UUserWidget* ChildUserWidget = Cast(ChildWidgetIt); 58 | const UWidgetTree* WidgetTreeIt = IsValid(ChildUserWidget) ? ChildUserWidget->WidgetTree : nullptr; 59 | const bool bHasChildWidgets = WidgetTreeIt && WidgetTreeIt->RootWidget; 60 | 61 | if (bHasChildWidgets) 62 | { 63 | // If the child widget has its own child widgets, recursively remove and destroy them 64 | DestroyWidget(*ChildUserWidget); 65 | } 66 | } 67 | 68 | // Hide widget to let last chance react on visibility change 69 | ParentWidget.SetVisibility(ESlateVisibility::Collapsed); 70 | 71 | // Remove the child widget from the viewport 72 | ParentWidget.RemoveFromParent(); 73 | 74 | // RemoveFromParent() does not completely destroy widget, so schedule the child widget for destruction 75 | ParentWidget.ConditionalBeginDestroy(); 76 | } 77 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/SettingsCheatExtension.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "SettingsCheatExtension.h" 4 | //--- 5 | #include "MyUtilsLibraries/SWCWidgetUtilsLibrary.h" 6 | #include "UI/SettingsWidget.h" 7 | //--- 8 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingsCheatExtension) 9 | 10 | // Default constructor 11 | USettingsCheatExtension::USettingsCheatExtension() 12 | { 13 | UCheatManager::RegisterForOnCheatManagerCreated(FOnCheatManagerCreated::FDelegate::CreateUObject(this, &ThisClass::OnCheatManagerCreated)); 14 | } 15 | 16 | // Register a delegate to call whenever a cheat manager is spawned; it will also be called immediately for cheat managers that already exist at this point 17 | void USettingsCheatExtension::OnCheatManagerCreated(UCheatManager* CheatManager) 18 | { 19 | if (!HasAllFlags(RF_ClassDefaultObject) // Continue if only is CDO object 20 | || !CheatManager) 21 | { 22 | return; 23 | } 24 | 25 | // Spawn the extension from its CDO and add it to the cheat manager 26 | USettingsCheatExtension* Extension = NewObject(CheatManager); 27 | CheatManager->AddCheatManagerExtension(Extension); 28 | } 29 | 30 | // Override the setting value with the cheat 31 | void USettingsCheatExtension::CheatSetting(const FString& TagByValue) const 32 | { 33 | USettingsWidget* SettingsWidget = FSWCWidgetUtilsLibrary::FindWidgetOfClass(GetWorld()); 34 | if (!SettingsWidget) 35 | { 36 | return; 37 | } 38 | 39 | if (TagByValue.IsEmpty()) 40 | { 41 | return; 42 | } 43 | 44 | static const FString Delimiter = TEXT("?"); 45 | TArray SeparatedStrings; 46 | TagByValue.ParseIntoArray(SeparatedStrings, *Delimiter); 47 | 48 | static constexpr int32 TagIndex = 0; 49 | FName TagName = NAME_None; 50 | if (SeparatedStrings.IsValidIndex(TagIndex)) 51 | { 52 | TagName = *SeparatedStrings[TagIndex]; 53 | } 54 | 55 | if (TagName.IsNone()) 56 | { 57 | return; 58 | } 59 | 60 | // Extract value 61 | static constexpr int32 ValueIndex = 1; 62 | FString TagValue = TEXT(""); 63 | if (SeparatedStrings.IsValidIndex(ValueIndex)) 64 | { 65 | TagValue = SeparatedStrings[ValueIndex]; 66 | } 67 | 68 | SettingsWidget->SetSettingValue(TagName, TagValue); 69 | SettingsWidget->SaveSettings(); 70 | } 71 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/SettingsWidgetConstructorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "SettingsWidgetConstructorModule.h" 4 | //--- 5 | #include "Modules/ModuleManager.h" 6 | // Called right after the module DLL has been loaded and the module object has been created 7 | void FSettingsWidgetConstructorModule::StartupModule() 8 | { 9 | // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module 10 | } 11 | 12 | // Called before the module is unloaded, right before the module object is destroyed 13 | void FSettingsWidgetConstructorModule::ShutdownModule() 14 | { 15 | // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, 16 | // we call this function before unloading the module. 17 | } 18 | 19 | IMPLEMENT_MODULE(FSettingsWidgetConstructorModule, SettingsWidgetConstructor) 20 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Private/UI/SettingSubWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "UI/SettingSubWidget.h" 4 | //--- 5 | #include "Data/SettingsDataAsset.h" 6 | #include "MyUtilsLibraries/SWCWidgetUtilsLibrary.h" 7 | #include "UI/SettingsWidget.h" 8 | //--- 9 | #include "Components/Button.h" 10 | #include "Components/CheckBox.h" 11 | #include "Components/ComboBoxString.h" 12 | #include "Components/EditableTextBox.h" 13 | #include "Components/HorizontalBox.h" 14 | #include "Components/ScrollBox.h" 15 | #include "Components/SizeBox.h" 16 | #include "Components/Slider.h" 17 | #include "Components/TextBlock.h" 18 | #include "Components/VerticalBox.h" 19 | #include "Widgets/Input/SButton.h" 20 | #include "Widgets/Input/SCheckBox.h" 21 | #include "Widgets/Input/SComboBox.h" 22 | #include "Widgets/Input/SEditableTextBox.h" 23 | #include "Widgets/Input/SSlider.h" 24 | //--- 25 | #include UE_INLINE_GENERATED_CPP_BY_NAME(SettingSubWidget) 26 | 27 | // Set the new setting tag for this widget 28 | void USettingSubWidget::SetSettingPrimaryRow(const FSettingsPrimary& InSettingPrimaryRow) 29 | { 30 | PrimaryDataInternal = InSettingPrimaryRow; 31 | } 32 | 33 | // Returns the main setting widget (the outer of this subwidget) 34 | USettingsWidget* USettingSubWidget::GetSettingsWidget() const 35 | { 36 | if (!SettingsWidgetInternal) 37 | { 38 | // Try to find the parent widget 39 | return FSWCWidgetUtilsLibrary::GetParentWidgetOfClass(this); 40 | } 41 | return SettingsWidgetInternal; 42 | } 43 | 44 | // Returns the main setting widget checked 45 | USettingsWidget& USettingSubWidget::GetSettingsWidgetChecked() const 46 | { 47 | USettingsWidget* SettingsWidget = GetSettingsWidget(); 48 | checkf(SettingsWidget, TEXT("%s: 'SettingsWidgetInternal' is null"), *FString(__FUNCTION__)); 49 | return *SettingsWidget; 50 | } 51 | 52 | // Sets the main settings widget for this subwidget 53 | void USettingSubWidget::SetSettingsWidget(USettingsWidget* InSettingsWidget) 54 | { 55 | SettingsWidgetInternal = InSettingsWidget; 56 | } 57 | 58 | // Sets the parent widget element in hierarchy of this subwidget 59 | UPanelSlot* USettingSubWidget::Attach() 60 | { 61 | if (ParentSlotInternal) 62 | { 63 | // is already attached 64 | return ParentSlotInternal; 65 | } 66 | 67 | const FSettingsDataBase* SettingData = GetSettingData(); 68 | const EMyVerticalAlignment Alignment = SettingData ? SettingData->GetVerticalAlignment() : EMyVerticalAlignment::None; 69 | if (!ensureMsgf(Alignment != EMyVerticalAlignment::None, TEXT("ASSERT: [%i] %s:\n'This widget '%s' can not be attached to the parent widget, because it has no alignment!"), __LINE__, *FString(__FUNCTION__), *GetName())) 70 | { 71 | return nullptr; 72 | } 73 | 74 | UPanelWidget* ParentWidget = nullptr; 75 | switch (Alignment) 76 | { 77 | case EMyVerticalAlignment::Header: 78 | ParentWidget = GetSettingsWidgetChecked().GetHeaderVerticalBox(); 79 | break; 80 | case EMyVerticalAlignment::Content: 81 | if (const USettingColumn* Column = GetSettingsWidgetChecked().GetColumnBySetting(GetSettingTag())) 82 | { 83 | ParentWidget = Column->GetVerticalHolderBox(); 84 | } 85 | break; 86 | case EMyVerticalAlignment::Footer: 87 | ParentWidget = GetSettingsWidgetChecked().GetFooterVerticalBox(); 88 | break; 89 | default: break; 90 | } 91 | 92 | if (!ensureMsgf(ParentWidget, TEXT("ASSERT: [%i] %s:\n'ParentWidget' is not found for the setting '%s'"), __LINE__, *FString(__FUNCTION__), *GetSettingTag().ToString())) 93 | { 94 | return nullptr; 95 | } 96 | 97 | ParentSlotInternal = ParentWidget->AddChild(this); 98 | 99 | ensureMsgf(ParentSlotInternal, TEXT("ASSERT: [%i] %s:\nFailed to attached the Setting subwidget with the next tag: '%s'"), __LINE__, *FString(__FUNCTION__), *GetSettingTag().ToString()); 100 | return ParentSlotInternal; 101 | } 102 | 103 | // Adds given widget as tooltip to this setting 104 | void USettingSubWidget::AddTooltipWidget() 105 | { 106 | if (PrimaryDataInternal.Tooltip.IsEmpty() 107 | || PrimaryDataInternal.Tooltip.EqualToCaseIgnored(FCoreTexts::Get().None)) 108 | { 109 | return; 110 | } 111 | 112 | USettingTooltip* CreatedWidget = CreateWidget(GetOwningPlayer(), USettingsDataAsset::Get().GetTooltipClass()); 113 | checkf(CreatedWidget, TEXT("ERROR: [%i] %s:\n'CreatedWidget' is null!"), __LINE__, *FString(__FUNCTION__)); 114 | 115 | SetToolTip(CreatedWidget); 116 | CreatedWidget->SetToolTipText(PrimaryDataInternal.Tooltip); 117 | CreatedWidget->ApplyTheme(); 118 | } 119 | 120 | // Base method that is called when the underlying slate widget is constructed 121 | void USettingSubWidget::OnAddSetting(const FSettingsPicker& Setting) 122 | { 123 | BPOnAddSetting(); 124 | 125 | AddTooltipWidget(); 126 | 127 | Attach(); 128 | 129 | ApplyTheme(); 130 | } 131 | 132 | // Returns the custom line height for this setting 133 | float USettingSubWidget::GetLineHeight() const 134 | { 135 | return SizeBoxWidget ? SizeBoxWidget->GetMinDesiredHeight() : 0.f; 136 | } 137 | 138 | // Set custom line height for this setting 139 | void USettingSubWidget::SetLineHeight(float NewLineHeight) 140 | { 141 | if (SizeBoxWidget) 142 | { 143 | SizeBoxWidget->SetMinDesiredHeight(NewLineHeight); 144 | } 145 | } 146 | 147 | // Returns the caption text that is shown on UI 148 | void USettingSubWidget::GetCaptionText(FText& OutCaptionText) const 149 | { 150 | if (CaptionWidget) 151 | { 152 | OutCaptionText = CaptionWidget->GetText(); 153 | } 154 | } 155 | 156 | // Set the new caption text on UI for this widget 157 | void USettingSubWidget::SetCaptionText(const FText& NewCaptionText) 158 | { 159 | if (CaptionWidget) 160 | { 161 | CaptionWidget->SetText(NewCaptionText); 162 | } 163 | } 164 | 165 | // Set the new button setting data for this widget 166 | void USettingButton::SetButtonData(const FSettingsButton& InButtonData) 167 | { 168 | ButtonDataInternal = InButtonData; 169 | } 170 | 171 | // Called after the underlying slate widget is constructed 172 | void USettingButton::NativeConstruct() 173 | { 174 | Super::NativeConstruct(); 175 | 176 | if (ButtonWidget) 177 | { 178 | ButtonWidget->SetClickMethod(EButtonClickMethod::PreciseClick); 179 | ButtonWidget->OnClicked.AddUniqueDynamic(this, &ThisClass::USettingButton::OnButtonPressed); 180 | 181 | SlateButtonInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(ButtonWidget); 182 | check(SlateButtonInternal.IsValid()); 183 | } 184 | } 185 | 186 | // Is overridden to construct the button 187 | void USettingButton::OnAddSetting(const FSettingsPicker& Setting) 188 | { 189 | ButtonDataInternal = Setting.Button; 190 | 191 | Super::OnAddSetting(Setting); 192 | } 193 | 194 | // Called when the Button Widget is pressed 195 | void USettingButton::OnButtonPressed() 196 | { 197 | if (!SettingsWidgetInternal) 198 | { 199 | return; 200 | } 201 | 202 | SettingsWidgetInternal->SetSettingButtonPressed(GetSettingTag()); 203 | } 204 | 205 | // Set the new checkbox setting data for this widget 206 | void USettingCheckbox::SetCheckboxData(const FSettingsCheckbox& InCheckboxData) 207 | { 208 | CheckboxDataInternal = InCheckboxData; 209 | } 210 | 211 | // Called after the underlying slate widget is constructed 212 | void USettingCheckbox::NativeConstruct() 213 | { 214 | Super::NativeConstruct(); 215 | 216 | if (CheckboxWidget) 217 | { 218 | CheckboxWidget->OnCheckStateChanged.AddUniqueDynamic(this, &ThisClass::OnCheckStateChanged); 219 | 220 | SlateCheckboxInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(CheckboxWidget); 221 | check(SlateCheckboxInternal.IsValid()); 222 | } 223 | } 224 | 225 | // Called when the checked state has changed 226 | void USettingCheckbox::OnCheckStateChanged(bool bIsChecked) 227 | { 228 | if (!SettingsWidgetInternal) 229 | { 230 | return; 231 | } 232 | 233 | SettingsWidgetInternal->SetSettingCheckbox(GetSettingTag(), bIsChecked); 234 | } 235 | 236 | // Is overridden to construct the checkbox 237 | void USettingCheckbox::OnAddSetting(const FSettingsPicker& Setting) 238 | { 239 | CheckboxDataInternal = Setting.Checkbox; 240 | 241 | Super::OnAddSetting(Setting); 242 | } 243 | 244 | // Set the new combobox setting data for this widget 245 | void USettingCombobox::SetComboboxData(const FSettingsCombobox& InComboboxData) 246 | { 247 | ComboboxDataInternal = InComboboxData; 248 | } 249 | 250 | // Called after the underlying slate widget is constructed 251 | void USettingCombobox::NativeConstruct() 252 | { 253 | Super::NativeConstruct(); 254 | 255 | if (ComboboxWidget) 256 | { 257 | ComboboxWidget->OnSelectionChanged.AddUniqueDynamic(this, &ThisClass::OnSelectionChanged); 258 | 259 | SlateComboboxInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(ComboboxWidget); 260 | check(SlateComboboxInternal.IsValid()); 261 | } 262 | } 263 | 264 | // Is executed every tick when widget is enabled 265 | void USettingCombobox::NativeTick(const FGeometry& MyGeometry, float InDeltaTime) 266 | { 267 | Super::NativeTick(MyGeometry, InDeltaTime); 268 | 269 | if (!ComboboxWidget) 270 | { 271 | return; 272 | } 273 | 274 | const bool bIsComboboxOpenedLast = bIsComboboxOpenedInternal; 275 | bIsComboboxOpenedInternal = ComboboxWidget->IsOpen(); 276 | 277 | if (bIsComboboxOpenedLast != bIsComboboxOpenedInternal) 278 | { 279 | OnMenuOpenChanged(); 280 | } 281 | } 282 | 283 | // Called when the combobox is opened or closed/ 284 | void USettingCombobox::OnMenuOpenChanged() 285 | { 286 | // Play the sound 287 | if (SettingsWidgetInternal) 288 | { 289 | SettingsWidgetInternal->PlayUIClickSFX(); 290 | } 291 | } 292 | 293 | // Is overridden to construct the combobox 294 | void USettingCombobox::OnAddSetting(const FSettingsPicker& Setting) 295 | { 296 | ComboboxDataInternal = Setting.Combobox; 297 | 298 | Super::OnAddSetting(Setting); 299 | } 300 | 301 | // Set the new slider setting data for this widget 302 | void USettingSlider::SetSliderData(const FSettingsSlider& InSliderData) 303 | { 304 | SliderDataInternal = InSliderData; 305 | } 306 | 307 | // Called when a new item is selected in the combobox 308 | void USettingCombobox::OnSelectionChanged(FString SelectedItem, ESelectInfo::Type SelectionType) 309 | { 310 | if (!SettingsWidgetInternal 311 | || !ComboboxWidget) 312 | { 313 | return; 314 | } 315 | 316 | const int32 SelectedIndex = ComboboxWidget->GetSelectedIndex(); 317 | SettingsWidgetInternal->SetSettingComboboxIndex(GetSettingTag(), SelectedIndex); 318 | } 319 | 320 | // Called after the underlying slate widget is constructed 321 | void USettingSlider::NativeConstruct() 322 | { 323 | Super::NativeConstruct(); 324 | 325 | if (SliderWidget) 326 | { 327 | SliderWidget->OnValueChanged.AddUniqueDynamic(this, &ThisClass::OnValueChanged); 328 | SliderWidget->OnMouseCaptureEnd.AddUniqueDynamic(this, &ThisClass::OnMouseCaptureEnd); 329 | 330 | SlateSliderInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(SliderWidget); 331 | check(SlateSliderInternal.IsValid()); 332 | } 333 | } 334 | 335 | // Invoked when the mouse is released and a capture ends 336 | void USettingSlider::OnMouseCaptureEnd() 337 | { 338 | // Play the sound 339 | if (SettingsWidgetInternal) 340 | { 341 | SettingsWidgetInternal->PlayUIClickSFX(); 342 | } 343 | } 344 | 345 | // Called when the value is changed by slider or typing 346 | void USettingSlider::OnValueChanged(float Value) 347 | { 348 | if (!SettingsWidgetInternal) 349 | { 350 | return; 351 | } 352 | 353 | SettingsWidgetInternal->SetSettingSlider(GetSettingTag(), Value); 354 | } 355 | 356 | // Is overridden to construct the slider 357 | void USettingSlider::OnAddSetting(const FSettingsPicker& Setting) 358 | { 359 | SliderDataInternal = Setting.Slider; 360 | 361 | Super::OnAddSetting(Setting); 362 | } 363 | 364 | // Set the new Text Line setting data for this widget 365 | void USettingTextLine::SetTextLineData(const FSettingsTextLine& InTextLineData) 366 | { 367 | TextLineDataInternal = InTextLineData; 368 | } 369 | 370 | // Is overridden to construct the text line 371 | void USettingTextLine::OnAddSetting(const FSettingsPicker& Setting) 372 | { 373 | TextLineDataInternal = Setting.TextLine; 374 | 375 | Super::OnAddSetting(Setting); 376 | } 377 | 378 | // Returns current text set in the Editable Text Box 379 | void USettingUserInput::GetEditableText(FText& OutText) const 380 | { 381 | if (EditableTextBox) 382 | { 383 | OutText = EditableTextBox->GetText(); 384 | } 385 | } 386 | 387 | // Set new text programmatically instead of by the user 388 | void USettingUserInput::SetEditableText(const FText& InText) 389 | { 390 | if (!EditableTextBox 391 | || InText.EqualTo(EditableTextBox->GetText())) 392 | { 393 | return; 394 | } 395 | 396 | EditableTextBox->SetText(InText); 397 | } 398 | 399 | // Set the new user input setting data for this widget 400 | void USettingUserInput::SetUserInputData(const FSettingsUserInput& InUserInputData) 401 | { 402 | UserInputDataInternal = InUserInputData; 403 | } 404 | 405 | // Called after the underlying slate widget is constructed 406 | void USettingUserInput::NativeConstruct() 407 | { 408 | Super::NativeConstruct(); 409 | 410 | if (EditableTextBox) 411 | { 412 | EditableTextBox->OnTextChanged.AddUniqueDynamic(this, &ThisClass::OnTextChanged); 413 | 414 | SlateEditableTextBoxInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(EditableTextBox); 415 | check(SlateEditableTextBoxInternal.IsValid()); 416 | } 417 | } 418 | 419 | // Called whenever the text is changed programmatically or interactively by the user 420 | void USettingUserInput::OnTextChanged(const FText& Text) 421 | { 422 | if (!SettingsWidgetInternal) 423 | { 424 | return; 425 | } 426 | 427 | const FName MewValue(Text.ToString()); 428 | SettingsWidgetInternal->SetSettingUserInput(GetSettingTag(), MewValue); 429 | } 430 | 431 | // Is overridden to construct the user input 432 | void USettingUserInput::OnAddSetting(const FSettingsPicker& Setting) 433 | { 434 | UserInputDataInternal = Setting.UserInput; 435 | 436 | Super::OnAddSetting(Setting); 437 | } 438 | 439 | // Set the new custom widget setting data for this widget 440 | void USettingCustomWidget::SetCustomWidgetData(const FSettingsCustomWidget& InCustomWidgetData) 441 | { 442 | CustomWidgetDataInternal = InCustomWidgetData; 443 | } 444 | 445 | // Is overridden to construct the custom widget 446 | void USettingCustomWidget::OnAddSetting(const FSettingsPicker& Setting) 447 | { 448 | CustomWidgetDataInternal = Setting.CustomWidget; 449 | 450 | Super::OnAddSetting(Setting); 451 | } 452 | 453 | // Is overridden to attach the column to the Settings Widget 454 | UPanelSlot* USettingColumn::Attach() 455 | { 456 | if (ParentSlotInternal) 457 | { 458 | // is already attached 459 | return ParentSlotInternal; 460 | } 461 | 462 | UHorizontalBox* ContentHorizontalBox = GetSettingsWidgetChecked().GetContentHorizontalBox(); 463 | checkf(ContentHorizontalBox, TEXT("ERROR: [%i] %s:\n'ContentHorizontalBox' is null!"), __LINE__, *FString(__FUNCTION__)); 464 | ParentSlotInternal = ContentHorizontalBox->AddChild(this); 465 | return ParentSlotInternal; 466 | } 467 | 468 | // Called after the underlying slate widget is constructed 469 | void USettingColumn::NativeConstruct() 470 | { 471 | Super::NativeConstruct(); 472 | 473 | if (ScrollBoxWidget) 474 | { 475 | SlateScrollBoxInternal = FSWCWidgetUtilsLibrary::GetSlateWidget(ScrollBoxWidget); 476 | check(SlateScrollBoxInternal.IsValid()); 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingFunction.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "SettingsWidgetConstructor/Private/FunctionPickerData/SWCFunctionPicker.h" 6 | #include "SettingsWidgetConstructor/Private/FunctionPickerData/SWCFunctionPickerTemplate.h" 7 | //--- 8 | #include "SettingFunction.generated.h" 9 | 10 | /** 11 | * Is used to select setting function to be called 12 | */ 13 | USTRUCT(BlueprintType) 14 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingFunctionPicker : public FSWCFunctionPicker 15 | { 16 | GENERATED_BODY() 17 | 18 | /** Empty setting function data. */ 19 | static const FSettingFunctionPicker EmptySettingFunction; 20 | 21 | /** Default constructor. */ 22 | FSettingFunctionPicker() = default; 23 | 24 | /** Custom constructor to set all members values. */ 25 | FSettingFunctionPicker(UClass* InFunctionClass, FName InFunctionName) 26 | : FSWCFunctionPicker(InFunctionClass, InFunctionName) {} 27 | }; 28 | 29 | /** 30 | * Delegates wrapper that are used as templates for FSettingFunctionPicker properties. 31 | * see UFunctionPickerTemplate 32 | */ 33 | UCLASS(Abstract, Const, Transient) 34 | class SETTINGSWIDGETCONSTRUCTOR_API USettingFunctionTemplate : public USWCFunctionPickerTemplate 35 | { 36 | GENERATED_BODY() 37 | 38 | public: 39 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSetterWidget, class USettingCustomWidget*, Param); 40 | 41 | DECLARE_DYNAMIC_DELEGATE_RetVal(class USettingCustomWidget*, FOnGetterWidget); 42 | }; 43 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingTag.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "GameplayTagContainer.h" 6 | //--- 7 | #include "SettingTag.generated.h" 8 | 9 | /** 10 | * Used to require all such tags start with 'Settings.X' as it specified in USTRUCT meta. 11 | */ 12 | USTRUCT(BlueprintType, meta = (Categories = "Settings")) 13 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingTag : public FGameplayTag 14 | { 15 | GENERATED_BODY() 16 | 17 | /** Default constructor. */ 18 | FSettingTag() = default; 19 | 20 | /** Custom constructor to set all members values. */ 21 | FSettingTag(const FGameplayTag& Tag) 22 | : FGameplayTag(Tag) {} 23 | 24 | /** Setting gag that contains nothing by default. */ 25 | static const FSettingTag EmptySettingTag; 26 | }; 27 | 28 | /** 29 | * Allows automatically add native setting tags at startup. 30 | */ 31 | struct SETTINGSWIDGETCONSTRUCTOR_API FGlobalSettingTags : public FGameplayTagNativeAdder 32 | { 33 | FSettingTag ButtonSettingTag = FSettingTag::EmptySettingTag; 34 | FSettingTag CheckboxSettingTag = FSettingTag::EmptySettingTag; 35 | FSettingTag ComboboxSettingTag = FSettingTag::EmptySettingTag; 36 | FSettingTag ScrollboxSettingTag = FSettingTag::EmptySettingTag; 37 | FSettingTag SliderSettingTag = FSettingTag::EmptySettingTag; 38 | FSettingTag TextLineSettingTag = FSettingTag::EmptySettingTag; 39 | FSettingTag UserInputSettingTag = FSettingTag::EmptySettingTag; 40 | FSettingTag CustomWidgetSettingTag = FSettingTag::EmptySettingTag; 41 | 42 | /** Returns global setting tags as const ref. 43 | * @see FGlobalSettingTags::GSettingTags. */ 44 | static const FORCEINLINE FGlobalSettingTags& Get() { return GSettingTags; } 45 | 46 | /** Automatically adds native setting tags at startup. */ 47 | virtual void AddTags() override; 48 | 49 | /** Virtual destructor. */ 50 | virtual ~FGlobalSettingTags() = default; 51 | 52 | private: 53 | /** The global of Setting tag categories. */ 54 | static FGlobalSettingTags GSettingTags; 55 | }; 56 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "SettingTypes.generated.h" 6 | 7 | /** ┌───────────────────────────┐ 8 | * │ [SETTINGS] │ Header (title) 9 | * │ [Option 1] [Option 2] │ Content (options) 10 | * │ [GO BACK] │ Footer 11 | * └───────────────────────────┘ */ 12 | UENUM(BlueprintType, meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) 13 | enum class EMyVerticalAlignment : uint8 14 | { 15 | None = 0 UMETA(Hidden), 16 | Header = 1 << 0, 17 | Content = 1 << 1, 18 | Footer = 1 << 2, 19 | Margins = Header | Footer UMETA(Hidden), 20 | All = Header | Content | Footer UMETA(Hidden) 21 | }; 22 | 23 | ENUM_CLASS_FLAGS(EMyVerticalAlignment) 24 | 25 | /** 26 | * All UI states of the button. 27 | */ 28 | UENUM(BlueprintType) 29 | enum class ESettingsButtonState : uint8 30 | { 31 | Normal, 32 | Hovered, 33 | Pressed, 34 | Disabled 35 | }; 36 | 37 | /** 38 | * All UI states of the checkbox. 39 | */ 40 | UENUM(BlueprintType) 41 | enum class ESettingsCheckboxState : uint8 42 | { 43 | UncheckedNormal, 44 | UncheckedHovered, 45 | UncheckedPressed, 46 | CheckedNormal, 47 | CheckedHovered, 48 | CheckedPressed, 49 | UndeterminedNormal, 50 | UndeterminedHovered, 51 | UndeterminedPressed 52 | }; 53 | 54 | /** 55 | * All UI states of the slider. 56 | */ 57 | UENUM(BlueprintType) 58 | enum class ESettingsSliderState : uint8 59 | { 60 | NormalBar, 61 | HoveredBar, 62 | NormalThumb, 63 | HoveredThumb 64 | }; 65 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingsDataTable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "SettingsWidgetConstructor/Private/MyDataTable/SWCMyDataTable.h" 6 | //--- 7 | #include "SettingsDataTable.generated.h" 8 | 9 | /** 10 | * Settings data table with FSettingsRow members. 11 | * Provides additional in-editor functionality like automatic set the key name by specified setting tag. 12 | */ 13 | UCLASS(BlueprintType) 14 | class SETTINGSWIDGETCONSTRUCTOR_API USettingsDataTable : public USWCMyDataTable 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | /** Default constructor to set members as FSettingsRow. */ 20 | USettingsDataTable(); 21 | 22 | /** Returns the table rows. 23 | * @see USettingsDataAsset::SettingsDataTableInternal */ 24 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor") 25 | void GetSettingRows(TMap& OutRows) const; 26 | 27 | protected: 28 | #if WITH_EDITOR 29 | /** Called on every change in this data table to automatic set the key name by specified setting tag. */ 30 | virtual void OnThisDataTableChanged(FName RowKey, const uint8& RowData) override; 31 | 32 | /** Is called to validate the data table setup. */ 33 | virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override; 34 | #endif // WITH_EDITOR 35 | }; 36 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingsRow.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "Data/SettingArchetypesData.h" 6 | #include "Data/SettingFunction.h" 7 | #include "Data/SettingTag.h" 8 | #include "SettingsWidgetConstructor/Private/MyDataTable/SWCMyDataTable.h" 9 | //--- 10 | #include "SettingsRow.generated.h" 11 | 12 | #define TEXT_NONE FCoreTexts::Get().None 13 | 14 | /* --- Settings Data Table dependencies --- 15 | * 16 | * ╔USettingsDataTable 17 | * ╚═══╦FSettingsRow 18 | * ╚═══╦FSettingsPicker 19 | * ╠═══╦FSettingsPrimary 20 | * ║ ╠════FSettingTag 21 | * ║ ╚════FSettingFunctionPicker (Owner, Setter, Getter) 22 | * ╚════FSettingsDataBase 23 | */ 24 | 25 | /** 26 | * The primary data of any setting. 27 | * Does not contain a default states for its value, because it should be set in the DefaultGameUserSettings.ini 28 | */ 29 | USTRUCT(BlueprintType) 30 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingsPrimary 31 | { 32 | GENERATED_BODY() 33 | 34 | /** Empty settings primary row. */ 35 | static const FSettingsPrimary EmptyPrimary; 36 | 37 | /** The tag of the setting. */ 38 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 39 | FSettingTag Tag = FSettingTag::EmptySettingTag; 40 | 41 | /** The static function to obtain object to call Setters and Getters. 42 | * The FunctionContextTemplate meta will contain a name of one UFunctionPickerTemplate delegate. */ 43 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (FunctionContextTemplate)) 44 | FSettingFunctionPicker Owner = FSettingFunctionPicker::EmptySettingFunction; 45 | 46 | /** The Setter function to be called to set the setting value for the Owner object. 47 | * The FunctionSetterTemplate meta will contain a name of one UFunctionPickerTemplate delegate. */ 48 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (FunctionSetterTemplate)) 49 | FSettingFunctionPicker Setter = FSettingFunctionPicker::EmptySettingFunction; 50 | 51 | /** The Getter function to be called to get the setting value from the Owner object. 52 | * The FunctionGetterTemplate meta will contain a name of one UFunctionPickerTemplate delegate. */ 53 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (FunctionGetterTemplate)) 54 | FSettingFunctionPicker Getter = FSettingFunctionPicker::EmptySettingFunction; 55 | 56 | /** The setting name. */ 57 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MultiLine = "true")) 58 | FText Caption = TEXT_NONE; 59 | 60 | /** The description to be shown as tooltip. */ 61 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (MultiLine = "true")) 62 | FText Tooltip = TEXT_NONE; 63 | 64 | /** The padding of this setting. */ 65 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 66 | FMargin Padding = 0.f; 67 | 68 | /** The custom line height for this setting. */ 69 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 70 | float LineHeight = 48.f; 71 | 72 | /** Set true to add new column starting from this setting. */ 73 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 74 | bool bStartOnNextColumn = false; 75 | 76 | /** Contains tags of settings which are needed to update after change of this setting. */ 77 | UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (Categories = "Settings")) 78 | FGameplayTagContainer SettingsToUpdate = FGameplayTagContainer::EmptyContainer; 79 | 80 | /** If set, it will override own position to be shown after specified setting. 81 | * Is useful when should be shown after setting that is created in another Settings Data Table. 82 | * All the next settings after ShowNextToSettingOverride in the same table will be also shown next to it. 83 | * @see 'Data Registr'y category of 'Settings Data Asset'. */ 84 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 85 | FSettingTag ShowNextToSettingOverride = FSettingTag::EmptySettingTag; 86 | 87 | /** Created widget of the chosen setting (button, checkbox, combobox, slider, text line, user input). */ 88 | TWeakObjectPtr SettingSubWidget = nullptr; 89 | 90 | /** Contains all cached functions of the Owner object. */ 91 | TArray OwnerFunctionList; 92 | 93 | /** Returns true if is valid. */ 94 | FORCEINLINE bool IsValid() const { return Tag.IsValid(); } 95 | 96 | /** Compares for equality. 97 | * @param Other The other object being compared. */ 98 | bool operator==(const FSettingsPrimary& Other) const; 99 | 100 | /** Creates a hash value. 101 | * @param Other the other object to create a hash value for. */ 102 | friend SETTINGSWIDGETCONSTRUCTOR_API uint32 GetTypeHash(const FSettingsPrimary& Other); 103 | 104 | #if WITH_EDITOR 105 | /** Validates chosen data. */ 106 | EDataValidationResult IsDataValid(class FDataValidationContext& Context) const; 107 | #endif // WITH_EDITOR 108 | 109 | /********************************************************************************************* 110 | * Setting Owner 111 | * Is living object that contains specific Setter and Getter functions. 112 | ********************************************************************************************* */ 113 | public: 114 | /** Is executed to obtain holding object. */ 115 | UObject* GetSettingOwner(const UObject* WorldContext) const; 116 | 117 | /** The cached bound delegate that returns holding object. */ 118 | USettingFunctionTemplate::FOnGetterObject OwnerFunc; 119 | }; 120 | 121 | /** 122 | * Allows to select in editor only one setting archetype property at once within this struct. 123 | * Is customizable struct, its members were created under FSettingsPicker ... 124 | * (instead of FSettingsRow which implements table row struct and can't be customized), 125 | * to have possibility to be property-customized by FSettingsPickerCustomization. 126 | * @see FSettingsDataBase 127 | */ 128 | USTRUCT(BlueprintType) 129 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingsPicker 130 | { 131 | GENERATED_BODY() 132 | 133 | /** Nothing picked. */ 134 | static const FSettingsPicker Empty; 135 | 136 | /** Contains a in-game settings type to be used - the name of one of these members. */ 137 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 138 | FName SettingsType = NAME_None; 139 | 140 | /** The common setting data. */ 141 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 142 | FSettingsPrimary PrimaryData; 143 | 144 | /** The button setting data. */ 145 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 146 | FSettingsButton Button; 147 | 148 | /** The checkbox setting data. */ 149 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 150 | FSettingsCheckbox Checkbox; 151 | 152 | /** The combobox setting data. */ 153 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 154 | FSettingsCombobox Combobox; 155 | 156 | /** The slider setting data. */ 157 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 158 | FSettingsSlider Slider; 159 | 160 | /** The text line setting data. */ 161 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 162 | FSettingsTextLine TextLine; 163 | 164 | /** The user input setting data. */ 165 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 166 | FSettingsUserInput UserInput; 167 | 168 | /** The custom widget setting data. */ 169 | UPROPERTY(EditAnywhere, BlueprintReadWrite) 170 | FSettingsCustomWidget CustomWidget; 171 | 172 | /** Returns the pointer to one of the chosen in-game type. 173 | * It searches the member property of this struct by a value of SettingsType. 174 | * @see FSettingsPicker::SettingsType */ 175 | FSettingsDataBase* GetChosenSettingsData() const; 176 | 177 | /** Returns true if row is valid. */ 178 | FORCEINLINE bool IsValid() const { return !(*this == Empty); } 179 | 180 | /** Compares for equality. 181 | * @param Other The other object being compared. */ 182 | bool operator==(const FSettingsPicker& Other) const; 183 | 184 | /** Creates a hash value. 185 | * @param Other the other object to create a hash value for. */ 186 | friend SETTINGSWIDGETCONSTRUCTOR_API uint32 GetTypeHash(const FSettingsPicker& Other); 187 | 188 | #if WITH_EDITOR 189 | /** Validates chosen data. */ 190 | EDataValidationResult IsDataValid(class FDataValidationContext& Context) const; 191 | #endif // WITH_EDITOR 192 | }; 193 | 194 | /** 195 | * Row of the settings data table. 196 | * In a row can be specified all UI values, chosen any getter/setter in the list. 197 | * Main features: 198 | * By this row, new setting will be automatically added on UI. 199 | * Executing UI getters/setters will call automatically bounded chosen functions. 200 | */ 201 | USTRUCT(BlueprintType) 202 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingsRow : public FSWCMyTableRow 203 | { 204 | GENERATED_BODY() 205 | 206 | /** The setting row to be customised. */ 207 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) 208 | FSettingsPicker SettingsPicker = FSettingsPicker::Empty; 209 | }; 210 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/Data/SettingsThemeData.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "Components/SlateWrapperTypes.h" 6 | //--- 7 | #include "Fonts/SlateFontInfo.h" 8 | #include "Styling/SlateBrush.h" 9 | //--- 10 | #include "SettingsThemeData.generated.h" 11 | 12 | /* --- Settings Theme Data dependencies --- 13 | * 14 | * ╔USettingsDataAsset 15 | * ╠════FButtonThemeData 16 | * ╠════FCheckboxThemeData 17 | * ╠════FComboboxThemeData 18 | * ╠════FSliderThemeData 19 | * ╚════FMiscThemeData 20 | */ 21 | 22 | /** 23 | * The parent struct of the settings theme data. 24 | */ 25 | USTRUCT(BlueprintType) 26 | struct SETTINGSWIDGETCONSTRUCTOR_API FSettingsThemeData 27 | { 28 | GENERATED_BODY() 29 | 30 | /** The texture image of the setting. */ 31 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 32 | TObjectPtr Texture = nullptr; 33 | 34 | /** The size of the resource in Slate Units. */ 35 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 36 | FVector2D Size = FVector2D(64.f, 64.f); 37 | 38 | /** How to draw the image. */ 39 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 40 | TEnumAsByte DrawAs = ESlateBrushDrawType::Box; 41 | 42 | /** The margin to use in Box and Border modes. */ 43 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 44 | FMargin Margin; 45 | 46 | /** Outside padding of the image. */ 47 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 48 | FMargin Padding; 49 | }; 50 | 51 | /** 52 | * The theme data of the settings button. 53 | */ 54 | USTRUCT(BlueprintType) 55 | struct SETTINGSWIDGETCONSTRUCTOR_API FButtonThemeData : public FSettingsThemeData 56 | { 57 | GENERATED_BODY() 58 | 59 | /** Default constructor to set default values. */ 60 | FButtonThemeData(); 61 | 62 | /** The padding to used when button is pressed. */ 63 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 64 | FMargin PressedPadding; 65 | }; 66 | 67 | /** 68 | * The theme data of the settings checkbox. 69 | * The parent Texture property determines of the unchecked checkbox. 70 | */ 71 | USTRUCT(BlueprintType) 72 | struct SETTINGSWIDGETCONSTRUCTOR_API FCheckboxThemeData : public FSettingsThemeData 73 | { 74 | GENERATED_BODY() 75 | 76 | /** Default constructor to set default values. */ 77 | FCheckboxThemeData(); 78 | 79 | /** The texture image of the toggled checkbox. */ 80 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 81 | TObjectPtr CheckedTexture = nullptr; 82 | 83 | /** The texture image of the undetermined checkbox. */ 84 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 85 | TObjectPtr UndeterminedTexture = nullptr; 86 | }; 87 | 88 | /** 89 | * The theme data of the settings combobox. 90 | */ 91 | USTRUCT(BlueprintType) 92 | struct SETTINGSWIDGETCONSTRUCTOR_API FComboboxThemeData : public FSettingsThemeData 93 | { 94 | GENERATED_BODY() 95 | 96 | /** Default constructor to set default values. */ 97 | FComboboxThemeData(); 98 | 99 | /** The padding to used when combobox is pressed. */ 100 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 101 | FMargin PressedPadding; 102 | 103 | /** The combobox arrow theme data. */ 104 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 105 | FSettingsThemeData Arrow; 106 | 107 | /** The combobox border theme data. */ 108 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 109 | FSettingsThemeData Border; 110 | 111 | /** The combobox background color */ 112 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 113 | FSlateColor ItemBackgroundColor = FColor::Transparent; 114 | }; 115 | 116 | /** 117 | * The theme data of the settings slider. 118 | */ 119 | USTRUCT(BlueprintType) 120 | struct SETTINGSWIDGETCONSTRUCTOR_API FSliderThemeData : public FSettingsThemeData 121 | { 122 | GENERATED_BODY() 123 | 124 | /** Default constructor to set default values. */ 125 | FSliderThemeData(); 126 | 127 | /** The theme of the slider thumb. */ 128 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 129 | FSettingsThemeData Thumb; 130 | }; 131 | 132 | /** 133 | * The common theme data. 134 | */ 135 | USTRUCT(BlueprintType) 136 | struct SETTINGSWIDGETCONSTRUCTOR_API FMiscThemeData 137 | { 138 | GENERATED_BODY() 139 | 140 | /** Default constructor to set default values. */ 141 | FMiscThemeData(); 142 | 143 | /** The common color of normal state for all setting types. */ 144 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 145 | FSlateColor ThemeColorNormal = FColor::White; 146 | 147 | /** The common color of hover state for all setting types. */ 148 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 149 | FSlateColor ThemeColorHover = FColor::White; 150 | 151 | /** The misc colors for all setting types. */ 152 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 153 | FSlateColor ThemeColorExtra = FColor::White; 154 | 155 | /** The font of text and captions. */ 156 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 157 | FSlateFontInfo TextAndCaptionFont; 158 | 159 | /** The color of text and captions. */ 160 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 161 | FSlateColor TextAndCaptionColor = FColor::White; 162 | 163 | /** The font of the header. */ 164 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 165 | FSlateFontInfo TextHeaderFont; 166 | 167 | /** The color of the header. */ 168 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 169 | FSlateColor TextHeaderColor = FColor::White; 170 | 171 | /** The font of the footer. */ 172 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 173 | FSlateFontInfo TextFooterFont; 174 | 175 | /** The color of the footer. */ 176 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 177 | FSlateColor TextFooterColor = FColor::White; 178 | 179 | /** The font of all setting values. */ 180 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 181 | FSlateFontInfo TextElementFont; 182 | 183 | /** The color of all setting values. */ 184 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 185 | FSlateColor TextElementColor = FColor::White; 186 | 187 | /** The theme data of tooltips. */ 188 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 189 | FSettingsThemeData TooltipBackground; 190 | 191 | /** The background color of tooltips. */ 192 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 193 | FSlateColor TooltipBackgroundTint = FColor::White; 194 | 195 | /** The theme data of the window background. */ 196 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 197 | FSettingsThemeData WindowBackground; 198 | 199 | /** The theme color of the window background. */ 200 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 201 | FSlateColor WindowBackgroundTint = FColor::White; 202 | 203 | /** The theme data of the menu border. */ 204 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 205 | FSettingsThemeData MenuBorderData; 206 | 207 | /** Color of the border. */ 208 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 209 | FSlateColor MenuBorderTint = FColor::White; 210 | 211 | /** Visibility of the border. */ 212 | UPROPERTY(EditDefaultsOnly, BlueprintReadWrite) 213 | ESlateVisibility MenuBorderVisibility = ESlateVisibility::Visible; 214 | }; 215 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/MyUtilsLibraries/SettingsUtilsLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | //--- 7 | #include "SettingsUtilsLibrary.generated.h" 8 | 9 | /** 10 | * The common functions library for Settings Widget Constructor. 11 | */ 12 | UCLASS() 13 | class SETTINGSWIDGETCONSTRUCTOR_API USettingsUtilsLibrary : public UBlueprintFunctionLibrary 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | /** Returns the Settings widget from viewport. */ 19 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor", meta = (WorldContext = "WorldContextObject")) 20 | static class USettingsWidget* GetSettingsWidget(const UObject* WorldContextObject); 21 | 22 | /** Returns the Game User Settings object. */ 23 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor", meta = (WorldContext = "OptionalWorldContext", CallableWithoutWorldContext)) 24 | static class UGameUserSettings* GetGameUserSettings(const UObject* OptionalWorldContext = nullptr); 25 | 26 | /** Returns all Settings Rows from project's Settings Data Table and all other additional Data Tables from 'SettingsDataTable' Data Registry. 27 | * Note: Is expensive function, prefer to cache the result once! */ 28 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor") 29 | static void GenerateAllSettingRows(TMap& OutSettingRows); 30 | 31 | /********************************************************************************************* 32 | * Multiple Data Tables support 33 | * Allows to register additional Settings Data Tables if needed 34 | * https://docs.google.com/document/d/1IXnOqrgaXTClP-0cIo28a9f6GHc9N1BCgTNnMk-X9VQ 35 | ********************************************************************************************* */ 36 | public: 37 | /** Registers given Settings Data Table to the Settings Data Registry. Alternatively Game Feature Action can be used to register it. 38 | * It's automatically called on startup for the 'Settings Data Table' set in the Project Settings. 39 | * Can be called manually to register additional Settings Data Tables. */ 40 | UFUNCTION(BlueprintCallable, Category = "C++") 41 | static void RegisterDataTable(const TSoftObjectPtr SettingsDataTable); 42 | 43 | /** Returns all Settings Data Tables added to 'SettingsDataTable' Data Registry including Project's one. */ 44 | static void GetAllSettingDataTables(TSet& OutDataTables); 45 | }; 46 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/SettingsCheatExtension.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "GameFramework/CheatManager.h" 6 | //--- 7 | #include "SettingsCheatExtension.generated.h" 8 | 9 | /** 10 | * Automatically extends any cheat manager with settings-related console commands. 11 | */ 12 | UCLASS() 13 | class SETTINGSWIDGETCONSTRUCTOR_API USettingsCheatExtension : public UCheatManagerExtension 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | /** Default constructor. */ 19 | USettingsCheatExtension(); 20 | 21 | /** Register a delegate to call whenever a cheat manager is spawned; it will also be called immediately for cheat managers that already exist at this point. */ 22 | void OnCheatManagerCreated(UCheatManager* CheatManager); 23 | 24 | /** Override the setting value with the cheat. 25 | * [Types] [Example command] 26 | * Button: CheatSetting Close 27 | * Checkbox: CheatSetting VSync?1 28 | * Combobox index: CheatSetting Shadows?2 29 | * Combobox list: CheatSetting Shadows?Low,Medium,High 30 | * Slider: CheatSetting Audio?0.5 31 | * Text Line: CheatSetting Title?Settings 32 | * User Input: CheatSetting Player?JanSeliv 33 | * 34 | * @param TagByValue Tag?Value */ 35 | UFUNCTION(Exec) 36 | void CheatSetting(const FString& TagByValue) const; 37 | }; 38 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/SettingsWidgetConstructorLibrary.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "Kismet/BlueprintFunctionLibrary.h" 6 | //--- 7 | #include "Data/SettingsRow.h" 8 | //--- 9 | #include "SettingsWidgetConstructorLibrary.generated.h" 10 | 11 | /** 12 | * The settings widget constructor functions library 13 | */ 14 | UCLASS() 15 | class SETTINGSWIDGETCONSTRUCTOR_API USettingsWidgetConstructorLibrary : public UBlueprintFunctionLibrary 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | /** Returns true if row is valid. */ 21 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library", meta = (AutoCreateRefTerm = "SettingsRow")) 22 | static FORCEINLINE bool IsValidSettingsRow(const FSettingsPicker& SettingsRow) { return SettingsRow.IsValid(); } 23 | 24 | /** Returns empty settings row. */ 25 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 26 | static const FORCEINLINE FSettingsPicker& GetEmptySettingsRow() { return FSettingsPicker::Empty; } 27 | 28 | /** Returns empty setting tag. */ 29 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 30 | static const FORCEINLINE FSettingTag& GetEmptySettingTag() { return FSettingTag::EmptySettingTag; } 31 | 32 | /** Returns the 'Settings.Button' tag. */ 33 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 34 | static const FORCEINLINE FSettingTag& GetButtonSettingTag() { return FGlobalSettingTags::Get().ButtonSettingTag; } 35 | 36 | /** Returns the 'Settings.Checkbox' tag. */ 37 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 38 | static const FORCEINLINE FSettingTag& GetCheckboxSettingTag() { return FGlobalSettingTags::Get().CheckboxSettingTag; } 39 | 40 | /** Returns the 'Settings.Combobox' tag. */ 41 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 42 | static const FORCEINLINE FSettingTag& GetComboboxSettingTag() { return FGlobalSettingTags::Get().ComboboxSettingTag; } 43 | 44 | /** Returns the 'Settings.Scrollbox' tag. */ 45 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 46 | static const FORCEINLINE FSettingTag& GetScrollboxSettingTag() { return FGlobalSettingTags::Get().ScrollboxSettingTag; } 47 | 48 | /** Returns the 'Settings.Slider' tag. */ 49 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 50 | static const FORCEINLINE FSettingTag& GetSliderSettingTag() { return FGlobalSettingTags::Get().SliderSettingTag; } 51 | 52 | /** Returns the 'Settings.TextLine' tag. */ 53 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 54 | static const FORCEINLINE FSettingTag& GetTextLineSettingTag() { return FGlobalSettingTags::Get().TextLineSettingTag; } 55 | 56 | /** Returns the 'Settings.UserInput' tag. */ 57 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 58 | static const FORCEINLINE FSettingTag& GetUserInputSettingTag() { return FGlobalSettingTags::Get().UserInputSettingTag; } 59 | 60 | /** Returns the 'Settings.CustomWidget' tag. */ 61 | UFUNCTION(BlueprintPure, Category = "Settings Widget Constructor|Static Library") 62 | static const FORCEINLINE FSettingTag& GetCustomWidgetSettingTag() { return FGlobalSettingTags::Get().CustomWidgetSettingTag; } 63 | }; 64 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/Public/SettingsWidgetConstructorModule.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | 7 | class SETTINGSWIDGETCONSTRUCTOR_API FSettingsWidgetConstructorModule : public IModuleInterface 8 | { 9 | public: 10 | /** 11 | * Called right after the module DLL has been loaded and the module object has been created. 12 | * Load dependent modules here, and they will be guaranteed to be available during ShutdownModule. 13 | */ 14 | virtual void StartupModule() override; 15 | 16 | /** 17 | * Called before the module is unloaded, right before the module object is destroyed. 18 | * During normal shutdown, this is called in reverse order that modules finish StartupModule(). 19 | * This means that, as long as a module references dependent modules in it's StartupModule(), it 20 | * can safely reference those dependencies in ShutdownModule() as well. 21 | */ 22 | virtual void ShutdownModule() override; 23 | }; 24 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructor/SettingsWidgetConstructor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SettingsWidgetConstructor : ModuleRules 6 | { 7 | public SettingsWidgetConstructor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | CppStandard = CppStandardVersion.Latest; 11 | bEnableNonInlinedGenCppWarnings = true; 12 | 13 | PublicDependencyModuleNames.AddRange(new[] 14 | { 15 | "Core" 16 | , "UMG" // Created USettingsWidget 17 | , "GameplayTags" // Created FSettingTag 18 | , "DeveloperSettings" // Created USettingsDataAsset 19 | } 20 | ); 21 | 22 | PrivateDependencyModuleNames.AddRange(new[] 23 | { 24 | "CoreUObject", "Engine", "Slate", "SlateCore" // Core 25 | , "DataRegistry" // Multiple Data Tables support 26 | } 27 | ); 28 | 29 | if (Target.bBuildEditor) 30 | { 31 | // Include Editor modules that are used in this Runtime module 32 | PrivateDependencyModuleNames.AddRange(new[] 33 | { 34 | "UnrealEd" // FDataTableEditorUtils 35 | } 36 | ); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/AssetTypeActions_SettingsDataTable.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "AssetTypeActions_SettingsDataTable.h" 4 | //--- 5 | #include "SettingsWidgetConstructorEditorModule.h" 6 | #include "Data/SettingsDataTable.h" 7 | //--- 8 | #include "EditorFramework/AssetImportData.h" 9 | #include "Interfaces/IPluginManager.h" 10 | //--- 11 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AssetTypeActions_SettingsDataTable) 12 | 13 | #define LOCTEXT_NAMESPACE "AssetTypeActions" 14 | 15 | USettingsDataTableFactory::USettingsDataTableFactory() 16 | { 17 | SupportedClass = USettingsDataTable::StaticClass(); 18 | } 19 | 20 | FText USettingsDataTableFactory::GetDisplayName() const 21 | { 22 | return LOCTEXT("SettingsDataTableFactory", "Settings Data Table"); 23 | } 24 | 25 | UObject* USettingsDataTableFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) 26 | { 27 | UObject* NewObject = Super::FactoryCreateNew(InClass, InParent, InName, Flags, Context, Warn); 28 | 29 | ImportDefaultSettingsDataTable(NewObject); 30 | 31 | return NewObject; 32 | } 33 | 34 | // Imports default data into new Settings Data Table 35 | void USettingsDataTableFactory::ImportDefaultSettingsDataTable(UObject* NewSettingDataTable) 36 | { 37 | USettingsDataTable* SettingsDataTable = CastChecked(NewSettingDataTable); 38 | UAssetImportData* AssetImportData = SettingsDataTable->AssetImportData; 39 | check(AssetImportData); 40 | 41 | // Find the .json 42 | static FString DataTableGlobalDir = TEXT(""); 43 | if (DataTableGlobalDir.IsEmpty()) 44 | { 45 | const FString ThisPluginName = TEXT("SettingsWidgetConstructor"); 46 | const FString DataTableRelativeDir = TEXT("Config/BaseSettingsDataTable.json"); 47 | const TSharedPtr ThisPlugin = IPluginManager::Get().FindPlugin(ThisPluginName); 48 | checkf(ThisPlugin, TEXT("ASSERT: '%s' plugin is not found"), *ThisPluginName); 49 | DataTableGlobalDir = ThisPlugin->GetBaseDir() / DataTableRelativeDir; 50 | } 51 | 52 | // Import the .json 53 | AssetImportData->UpdateFilenameOnly(DataTableGlobalDir); 54 | Reimport(SettingsDataTable); 55 | 56 | // Clear import path to prevent reimporting default data for already created table 57 | AssetImportData->SourceData = FAssetImportInfo(); 58 | } 59 | 60 | FText FAssetTypeActions_SettingsDataTable::GetName() const 61 | { 62 | return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_SettingsDataTable", "Settings Data Table"); 63 | } 64 | 65 | FColor FAssetTypeActions_SettingsDataTable::GetTypeColor() const 66 | { 67 | constexpr FColor BrownColor(139.f, 69.f, 19.f); 68 | return BrownColor; 69 | } 70 | 71 | UClass* FAssetTypeActions_SettingsDataTable::GetSupportedClass() const 72 | { 73 | return USettingsDataTable::StaticClass(); 74 | } 75 | 76 | uint32 FAssetTypeActions_SettingsDataTable::GetCategories() 77 | { 78 | return FSettingsWidgetConstructorEditorModule::SettingsCategory; 79 | } 80 | 81 | #undef LOCTEXT_NAMESPACE 82 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/AssetTypeActions_SettingsWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "AssetTypeActions_SettingsWidget.h" 4 | //--- 5 | #include "SettingsWidgetConstructorEditorModule.h" 6 | #include "UI/SettingsWidget.h" 7 | //--- 8 | #include UE_INLINE_GENERATED_CPP_BY_NAME(AssetTypeActions_SettingsWidget) 9 | 10 | #define LOCTEXT_NAMESPACE "AssetTypeActions" 11 | 12 | USettingsWidgetFactory::USettingsWidgetFactory() 13 | { 14 | SettingsWidgetClassInternal = USettingsWidget::StaticClass(); 15 | } 16 | 17 | FText USettingsWidgetFactory::GetDisplayName() const 18 | { 19 | return LOCTEXT("SettingsWidget", "Settings Widget"); 20 | } 21 | 22 | TSubclassOf USettingsWidgetFactory::GetWidgetClass() const 23 | { 24 | check(SettingsWidgetClassInternal); 25 | return SettingsWidgetClassInternal; 26 | } 27 | 28 | FText FAssetTypeActions_SettingsWidget::GetName() const 29 | { 30 | return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_SettingsWidget", "Settings Widget"); 31 | } 32 | 33 | FColor FAssetTypeActions_SettingsWidget::GetTypeColor() const 34 | { 35 | constexpr FColor BrownColor(139.f, 69.f, 19.f); 36 | return BrownColor; 37 | } 38 | 39 | uint32 FAssetTypeActions_SettingsWidget::GetCategories() 40 | { 41 | return FSettingsWidgetConstructorEditorModule::SettingsCategory; 42 | } 43 | 44 | #undef LOCTEXT_NAMESPACE 45 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/FunctionPickerType/FunctionPickerCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "FunctionPickerType/FunctionPickerCustomization.h" 4 | //--- 5 | #include "Data/SettingFunction.h" 6 | //--- 7 | #include "PropertyEditorModule.h" 8 | #include "PropertyHandle.h" 9 | #include "Modules/ModuleManager.h" 10 | 11 | // The name of class to be customized: SettingFunctionPicker 12 | const FName FFunctionPickerCustomization::PropertyClassName = FSettingFunctionPicker::StaticStruct()->GetFName(); 13 | 14 | // Default constructor 15 | FFunctionPickerCustomization::FFunctionPickerCustomization() 16 | { 17 | CustomPropertyInternal.PropertyName = GET_MEMBER_NAME_CHECKED(FSettingFunctionPicker, FunctionName); 18 | } 19 | 20 | // Makes a new instance of this detail layout class for a specific detail view requesting it 21 | TSharedRef FFunctionPickerCustomization::MakeInstance() 22 | { 23 | return MakeShareable(new FFunctionPickerCustomization()); 24 | } 25 | 26 | // Called when the header of the property (the row in the details panel where the property is shown) 27 | void FFunctionPickerCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) 28 | { 29 | Super::CustomizeHeader(PropertyHandle, HeaderRow, CustomizationUtils); 30 | } 31 | 32 | // Called when the children of the property should be customized or extra rows added. 33 | void FFunctionPickerCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) 34 | { 35 | Super::CustomizeChildren(PropertyHandle, ChildBuilder, CustomizationUtils); 36 | 37 | InitTemplateMetaKey(); 38 | 39 | RefreshCustomProperty(); 40 | } 41 | 42 | // Creates customization for the Function Picker 43 | void FFunctionPickerCustomization::RegisterFunctionPickerCustomization() 44 | { 45 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 46 | { 47 | return; 48 | } 49 | 50 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 51 | 52 | // Allows to choose ufunction 53 | PropertyModule.RegisterCustomPropertyTypeLayout( 54 | PropertyClassName, 55 | FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFunctionPickerCustomization::MakeInstance) 56 | ); 57 | 58 | PropertyModule.NotifyCustomizationModuleChanged(); 59 | } 60 | 61 | // Removes customization for the Function Picker 62 | void FFunctionPickerCustomization::UnregisterFunctionPickerCustomization() 63 | { 64 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 65 | { 66 | return; 67 | } 68 | 69 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 70 | 71 | PropertyModule.UnregisterCustomPropertyTypeLayout(PropertyClassName); 72 | } 73 | 74 | // Is called for each property on building its row 75 | void FFunctionPickerCustomization::OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData) 76 | { 77 | static const FName FunctionClassPropertyName = TEXT("FunctionClass"); 78 | if (PropertyData.PropertyName == FunctionClassPropertyName) 79 | { 80 | FunctionClassPropertyInternal = PropertyData; 81 | } 82 | 83 | Super::OnCustomizeChildren(ChildBuilder, PropertyData); 84 | } 85 | 86 | // Is called on adding the custom property 87 | void FFunctionPickerCustomization::AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder) 88 | { 89 | // Super will add the searchable combo box 90 | Super::AddCustomPropertyRow(PropertyDisplayText, ChildBuilder); 91 | } 92 | 93 | // Set new values for the list of selectable members 94 | void FFunctionPickerCustomization::RefreshCustomProperty() 95 | { 96 | // Invalidate if class is not chosen 97 | const FName ChosenFunctionClassName = FunctionClassPropertyInternal.GetPropertyValueFromHandle(); 98 | if (ChosenFunctionClassName.IsNone()) 99 | { 100 | InvalidateCustomProperty(); 101 | return; 102 | } 103 | 104 | // Skip if nothing changed 105 | const bool bChosenNewClass = FunctionClassPropertyInternal.PropertyValue != ChosenFunctionClassName; 106 | FunctionClassPropertyInternal.PropertyValue = ChosenFunctionClassName; 107 | const bool bIsNewTemplateFunction = UpdateTemplateFunction(); 108 | if (!bIsNewTemplateFunction && !bChosenNewClass // Settings Type and Class depend on each other 109 | && SearchableComboBoxValuesInternal.Num()) // List is not empty 110 | { 111 | return; 112 | } 113 | 114 | // Invalidate if invalid class 115 | const UClass* ChosenFunctionClass = GetChosenFunctionClass(); 116 | if (!ChosenFunctionClass) 117 | { 118 | InvalidateCustomProperty(); 119 | return; 120 | } 121 | 122 | SetCustomPropertyEnabled(true); 123 | 124 | ResetSearchableComboBox(); 125 | 126 | TArray FoundList; 127 | bool bValidCustomProperty = false; 128 | for (TFieldIterator It(ChosenFunctionClass, EFieldIteratorFlags::ExcludeSuper); It; ++It) 129 | { 130 | const UFunction* FunctionIt = *It; 131 | if (FunctionIt 132 | && FunctionIt != TemplateFunctionInternal 133 | && (!bIsStaticFunctionInternal || (FunctionIt->FunctionFlags & FUNC_Static) != 0) // only static functions if specified 134 | && IsSignatureCompatible(FunctionIt)) 135 | { 136 | FName FunctionNameIt = FunctionIt->GetFName(); 137 | if (FunctionNameIt == CustomPropertyInternal.PropertyValue) 138 | { 139 | bValidCustomProperty = true; 140 | } 141 | 142 | FoundList.AddUnique(MoveTemp(FunctionNameIt)); 143 | } 144 | } 145 | 146 | // Reset function if does not contain in specified class 147 | if (!bValidCustomProperty) 148 | { 149 | SetCustomPropertyValue(NAME_None); 150 | } 151 | 152 | for (const FName& ItemData : FoundList) 153 | { 154 | const FString ItemDataStr = ItemData.ToString(); 155 | const bool bAlreadyContains = SearchableComboBoxValuesInternal.ContainsByPredicate([&ItemDataStr](const TSharedPtr& SearchableComboBoxValue) 156 | { 157 | return SearchableComboBoxValue && SearchableComboBoxValue->Equals(ItemDataStr); 158 | }); 159 | 160 | if (bAlreadyContains) 161 | { 162 | continue; 163 | } 164 | 165 | // Add this to the searchable text box as an FString so users can type and find it 166 | SearchableComboBoxValuesInternal.Emplace(MakeShareable(new FString(ItemData.ToString()))); 167 | } 168 | 169 | // Will refresh searchable combo box 170 | Super::RefreshCustomProperty(); 171 | } 172 | 173 | // Is called to deactivate custom property 174 | void FFunctionPickerCustomization::InvalidateCustomProperty() 175 | { 176 | Super::InvalidateCustomProperty(); 177 | 178 | FunctionClassPropertyInternal.PropertyValue = NAME_None; 179 | } 180 | 181 | // Returns true if changing custom property currently is not forbidden 182 | bool FFunctionPickerCustomization::IsAllowedEnableCustomProperty() const 183 | { 184 | return !FunctionClassPropertyInternal.PropertyValue.IsNone(); 185 | } 186 | 187 | // Returns the currently chosen class of functions to display 188 | const UClass* FFunctionPickerCustomization::GetChosenFunctionClass() const 189 | { 190 | const IPropertyHandle* ChildHandleIt = FunctionClassPropertyInternal.PropertyHandle.Get(); 191 | const FProperty* ChildProperty = ChildHandleIt ? ChildHandleIt->GetProperty() : nullptr; 192 | const FObjectProperty* const ObjectProperty = ChildProperty ? CastField(ChildProperty) : nullptr; 193 | uint8* Base = reinterpret_cast(MyPropertyOuterInternal.Get()); 194 | const uint8* Data = ObjectProperty ? ChildHandleIt->GetValueBaseAddress(Base) : nullptr; 195 | return Data ? Cast(ObjectProperty->GetObjectPropertyValue(Data)) : nullptr; 196 | } 197 | 198 | // Check if all signatures of specified function are compatible with current template function 199 | bool FFunctionPickerCustomization::IsSignatureCompatible(const UFunction* Function) const 200 | { 201 | if (!Function) 202 | { 203 | return false; 204 | } 205 | 206 | const UFunction* TemplateFunction = TemplateFunctionInternal.Get(); 207 | if (!TemplateFunction) 208 | { 209 | // Template was not specified, so is compatible 210 | return true; 211 | } 212 | 213 | auto ArePropertiesTheSame = [](const FProperty* A, const FProperty* B) 214 | { 215 | if (A == B) 216 | { 217 | return true; 218 | } 219 | 220 | if (!A || !B) 221 | { 222 | return false; 223 | } 224 | 225 | if (A->GetSize() != B->GetSize()) 226 | { 227 | return false; 228 | } 229 | 230 | if (A->GetOffset_ForGC() != B->GetOffset_ForGC()) 231 | { 232 | return false; 233 | } 234 | 235 | if (!A->SameType(B)) // A->GetClass() == B->GetClass() 236 | { 237 | // --- That part is implemented: if is return param with the same flags 238 | // Will return true for any derived UObject 239 | 240 | if (!(A->PropertyFlags & B->PropertyFlags & CPF_OutParm)) 241 | { 242 | return false; 243 | } 244 | 245 | if (!A->IsA() || !B->IsA()) 246 | { 247 | return false; 248 | } 249 | } 250 | 251 | return true; 252 | }; 253 | 254 | const uint64 IgnoreFlags = CPF_ReturnParm | UFunction::GetDefaultIgnoredSignatureCompatibilityFlags(); 255 | 256 | // Run through the parameter property chains to compare each property 257 | TFieldIterator IteratorA(TemplateFunction); 258 | TFieldIterator IteratorB(Function); 259 | 260 | while (IteratorA && (IteratorA->PropertyFlags & CPF_Parm)) 261 | { 262 | if (IteratorB && (IteratorB->PropertyFlags & CPF_Parm)) 263 | { 264 | // Compare the two properties to make sure their types are identical 265 | // Note: currently this requires both to be strictly identical and wouldn't allow functions that differ only by how derived a class is, 266 | // which might be desirable when binding delegates, assuming there is directionality in the SignatureIsCompatibleWith call 267 | const FProperty* PropA = *IteratorA; 268 | const FProperty* PropB = *IteratorB; 269 | 270 | // Check the flags as well 271 | const uint64 PropertyMash = PropA->PropertyFlags ^ PropB->PropertyFlags; 272 | if ((PropertyMash & ~IgnoreFlags) != 0) 273 | { 274 | return false; 275 | } 276 | 277 | if (!ArePropertiesTheSame(PropA, PropB)) 278 | { 279 | // Type mismatch between an argument of A and B 280 | return false; 281 | } 282 | } 283 | else 284 | { 285 | // B ran out of arguments before A did 286 | return false; 287 | } 288 | ++IteratorA; 289 | ++IteratorB; 290 | } 291 | 292 | // They matched all the way through A's properties, but it could still be a mismatch if B has remaining parameters 293 | return true; 294 | } 295 | 296 | // Set Template Function once 297 | bool FFunctionPickerCustomization::UpdateTemplateFunction() 298 | { 299 | // Skip if meta was not changed 300 | const FName TemplateMetaValue = ParentPropertyInternal.GetMetaDataValue(TemplateMetaKeyInternal); 301 | if (TemplateMetaValue == TemplateMetaValueInternal) 302 | { 303 | return true; 304 | } 305 | TemplateMetaValueInternal = TemplateMetaValue; 306 | 307 | // Clear if meta is empty (none settings type is chosen) 308 | if (TemplateMetaValue.IsNone()) 309 | { 310 | TemplateFunctionInternal.Reset(); 311 | TemplateMetaValueInternal = NAME_None; 312 | return true; 313 | } 314 | 315 | // Parse into class name and function name 316 | TArray ParsedStrArray; 317 | static const FString Delimiter(TEXT("::")); 318 | TemplateMetaValue.ToString().ParseIntoArray(ParsedStrArray, *Delimiter); 319 | 320 | // Find class 321 | static constexpr int32 ClassNameIndex = 0; 322 | const FString ClassPathName = ParsedStrArray.IsValidIndex(ClassNameIndex) ? ParsedStrArray[ClassNameIndex] : TEXT(""); 323 | const UClass* ScopeClass = !ClassPathName.IsEmpty() ? UClass::TryFindTypeSlow(ClassPathName, EFindFirstObjectOptions::ExactClass) : nullptr; 324 | if (!ensureMsgf(ScopeClass, TEXT("ASSERT: 'ScopeClass' is not valid"))) 325 | { 326 | return false;; 327 | } 328 | 329 | // Find function to filter by its signature 330 | static constexpr int32 FunctionNameIndex = 1; 331 | const FString FunctionName = ParsedStrArray.IsValidIndex(FunctionNameIndex) ? ParsedStrArray[FunctionNameIndex] : TEXT(""); 332 | TemplateFunctionInternal = !FunctionName.IsEmpty() ? ScopeClass->FindFunctionByName(*FunctionName) : nullptr; 333 | if (!ensureMsgf(TemplateFunctionInternal.IsValid(), TEXT("ASSERT: Template function was not found"))) 334 | { 335 | return false; 336 | } 337 | 338 | return true; 339 | } 340 | 341 | // Will set once the meta key of this property 342 | void FFunctionPickerCustomization::InitTemplateMetaKey() 343 | { 344 | if (!TemplateMetaKeyInternal.IsNone() // set only once since the key never changes 345 | || !ParentPropertyInternal.IsValid()) 346 | { 347 | return; 348 | } 349 | 350 | // Will set once the meta key of this property. 351 | for (const FName MetaKeyIt : TemplateMetaKeys) 352 | { 353 | if (ParentPropertyInternal.IsMetaKeyExists(MetaKeyIt)) 354 | { 355 | TemplateMetaKeyInternal = MetaKeyIt; 356 | break; 357 | } 358 | } 359 | 360 | // Set if function is static 361 | static const FName FunctionContextTemplate = TEXT("FunctionContextTemplate"); 362 | bIsStaticFunctionInternal = TemplateMetaKeyInternal == FunctionContextTemplate; 363 | } 364 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/FunctionPickerType/FunctionPickerCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "MyPropertyType/MyPropertyTypeCustomization.h" 6 | 7 | /** 8 | * Allow to choose the function in the list instead of manually typing a name. 9 | */ 10 | class FFunctionPickerCustomization final : public FMyPropertyTypeCustomization 11 | { 12 | public: 13 | /* --------------------------------------------------- 14 | * Public functions 15 | * --------------------------------------------------- */ 16 | 17 | /** The name of class to be customized: SettingFunctionPicker */ 18 | static const FName PropertyClassName; 19 | 20 | /** Fixed-size TemplateMetaKeys which contains names of metas used by this property. */ 21 | inline static const TArray> TemplateMetaKeys = { 22 | TEXT("FunctionContextTemplate"), 23 | TEXT("FunctionSetterTemplate"), 24 | TEXT("FunctionGetterTemplate") 25 | }; 26 | 27 | /** Default constructor. */ 28 | FFunctionPickerCustomization(); 29 | 30 | /** Makes a new instance of this detail layout class for a specific detail view requesting it. */ 31 | static TSharedRef MakeInstance(); 32 | 33 | /** 34 | * Called when the header of the property (the row in the details panel where the property is shown) 35 | * If nothing is added to the row, the header is not displayed 36 | * @param PropertyHandle Handle to the property being customized 37 | * @param HeaderRow A row that widgets can be added to 38 | * @param CustomizationUtils Utilities for customization 39 | */ 40 | virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 41 | 42 | /** 43 | * Called when the children of the property should be customized or extra rows added. 44 | * @param PropertyHandle Handle to the property being customized 45 | * @param ChildBuilder A builder for adding children 46 | * @param CustomizationUtils Utilities for customization 47 | */ 48 | virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 49 | 50 | /** Creates customization for the Function Picker. */ 51 | static void RegisterFunctionPickerCustomization(); 52 | 53 | /** Removes customization for the Function Picker. */ 54 | static void UnregisterFunctionPickerCustomization(); 55 | 56 | protected: 57 | /* --------------------------------------------------- 58 | * Protected properties 59 | * --------------------------------------------------- */ 60 | 61 | /** Contains the function to be compared with all other functions of a class to show in the list only compatible functions. 62 | * @see FFunctionPickerCustomization::RefreshCustomProperty() */ 63 | TWeakObjectPtr TemplateFunctionInternal = nullptr; 64 | 65 | /** One of the TemplateMetaKeys. 66 | * UPROPERTY(meta = (Key="Value")) FFunctionPicker SomeProperty 67 | * It stores the name of Key. 68 | * Is set once on init and never changes. 69 | * @see FFunctionPickerCustomization::InitTemplateMetaKey(). */ 70 | FName TemplateMetaKeyInternal = NAME_None; 71 | 72 | /** Dynamic value, is set for meta when new setting type is chose. 73 | * @see FSettingsPickerCustomization::CopyMetas() */ 74 | FName TemplateMetaValueInternal = NAME_None; 75 | 76 | /** Contains property data about FFunctionPicker::FunctionClass */ 77 | FPropertyData FunctionClassPropertyInternal = FPropertyData::Empty; 78 | 79 | /** If true, will be added FUNC_Static flag to show only static function in the list. */ 80 | bool bIsStaticFunctionInternal = false; 81 | 82 | /* --------------------------------------------------- 83 | * Protected functions 84 | * --------------------------------------------------- */ 85 | 86 | /** Is called for each property on building its row. */ 87 | virtual void OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData) override; 88 | 89 | /** Is called on adding the custom property. 90 | * @see FMyPropertyTypeCustomization::CustomPropertyNameInternal */ 91 | virtual void AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder) override; 92 | 93 | /** Set new values for the list of selectable members. 94 | * @see FMyPropertyTypeCustomization::SearchableComboBoxValuesInternal */ 95 | virtual void RefreshCustomProperty() override; 96 | 97 | /** Is called to deactivate custom property. */ 98 | virtual void InvalidateCustomProperty() override; 99 | 100 | /** Returns true if changing custom property currently is not forbidden. */ 101 | virtual bool IsAllowedEnableCustomProperty() const override; 102 | 103 | /** Returns the currently chosen class of functions to display. 104 | * @see FFunctionPickerCustomization::FunctionClassHandleInternal 105 | * @see FFunctionPicker::Class */ 106 | const UClass* GetChosenFunctionClass() const; 107 | 108 | /** 109 | * Check if all signatures of specified function are compatible with current template function. 110 | * Contains the logic UFunction::IsSignatureCompatibleWith but implements some part to return true for any derived UObject 111 | * @param Function The function to check. 112 | * @return true if compatible. 113 | * @see FFunctionPickerCustomization::TemplateFunctionInternal 114 | */ 115 | bool IsSignatureCompatible(const UFunction* Function) const; 116 | 117 | /** Set Template Function once. 118 | * @see FFunctionPickerCustomization::TemplateFunctionInternal. 119 | * @return true if new template function was set. */ 120 | bool UpdateTemplateFunction(); 121 | 122 | /** Will set once the meta key of this property. 123 | * @see FFunctionPickerCustomization::TemplateMetaKeyInternal. */ 124 | void InitTemplateMetaKey(); 125 | }; 126 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyAssets/AssetTypeActions_MyDataTable.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "MyAssets/SWCAssetTypeActions_MyDataTable.h" 4 | //--- 5 | #include "DataTableEditorModule.h" 6 | #include "DesktopPlatformModule.h" 7 | #include "ToolMenus.h" 8 | #include "EditorFramework/AssetImportData.h" 9 | #include "Engine/DataTable.h" 10 | #include "Framework/Application/SlateApplication.h" 11 | #include "Misc/FileHelper.h" 12 | #include "Misc/MessageDialog.h" 13 | 14 | #define LOCTEXT_NAMESPACE "AssetTypeActions" 15 | 16 | USWCMyDataTableFactory::USWCMyDataTableFactory() 17 | { 18 | bCreateNew = true; 19 | bEditAfterNew = true; 20 | bEditorImport = true; 21 | 22 | // Has to be set in child factory 23 | SupportedClass = UDataTable::StaticClass(); 24 | } 25 | 26 | FText USWCMyDataTableFactory::GetDisplayName() const 27 | { 28 | return LOCTEXT("MyDataTableFactory", "My Data Table"); 29 | } 30 | 31 | UObject* USWCMyDataTableFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) 32 | { 33 | check(DoesSupportClass(InClass)); 34 | return NewObject(InParent, InClass, InName, Flags); 35 | } 36 | 37 | bool USWCMyDataTableFactory::DoesSupportClass(UClass* Class) 38 | { 39 | return Class && Class->IsChildOf(GetSupportedClass()); 40 | } 41 | 42 | FText FAssetTypeActions_MyDataTable::GetName() const 43 | { 44 | return LOCTEXT("AssetTypeActions_MyDataTable", "My Data Table"); 45 | } 46 | 47 | UClass* FAssetTypeActions_MyDataTable::GetSupportedClass() const 48 | { 49 | return UDataTable::StaticClass(); 50 | } 51 | 52 | // Shows 'Export as JSON' option in the context menu 53 | void FAssetTypeActions_MyDataTable::GetActions(const TArray& InObjects, FToolMenuSection& Section) 54 | { 55 | const TArray> Tables = GetTypedWeakObjectPtrs(InObjects); 56 | 57 | Section.AddMenuEntry( 58 | "DataTable_ExportAsJSON", 59 | LOCTEXT("DataTable_ExportAsJSON", "Export as JSON"), 60 | LOCTEXT("DataTable_ExportAsJSONTooltip", "Export the data table as a file containing JSON data."), 61 | FSlateIcon(), 62 | FUIAction( 63 | FExecuteAction::CreateSP(this, &FAssetTypeActions_MyDataTable::ExecuteExportAsJSON, Tables), 64 | FCanExecuteAction() 65 | ) 66 | ); 67 | } 68 | 69 | // Is overridden to show 'reimport' options in the contexts menu 70 | void FAssetTypeActions_MyDataTable::GetResolvedSourceFilePaths(const TArray& TypeAssets, TArray& OutSourceFilePaths) const 71 | { 72 | for (UObject* Asset : TypeAssets) 73 | { 74 | const UDataTable* DataTable = CastChecked(Asset); 75 | if (const UAssetImportData* AssetImportData = DataTable->AssetImportData) 76 | { 77 | AssetImportData->ExtractFilenames(OutSourceFilePaths); 78 | } 79 | } 80 | } 81 | 82 | // Opens data within the data table editor 83 | void FAssetTypeActions_MyDataTable::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) 84 | { 85 | TArray DataTablesToOpen; 86 | TArray InvalidDataTables; 87 | 88 | for (UObject* Obj : InObjects) 89 | { 90 | UDataTable* Table = Cast(Obj); 91 | if (Table) 92 | { 93 | if (Table->GetRowStruct()) 94 | { 95 | DataTablesToOpen.Add(Table); 96 | } 97 | else 98 | { 99 | InvalidDataTables.Add(Table); 100 | } 101 | } 102 | } 103 | 104 | if (InvalidDataTables.Num() > 0) 105 | { 106 | FTextBuilder DataTablesListText; 107 | DataTablesListText.Indent(); 108 | for (const UDataTable* Table : InvalidDataTables) 109 | { 110 | const FTopLevelAssetPath ResolvedRowStructName = Table->GetRowStructPathName(); 111 | DataTablesListText.AppendLineFormat(LOCTEXT("DataTable_MissingRowStructListEntry", "* {0} (Row Structure: {1})"), FText::FromString(Table->GetName()), FText::FromString(ResolvedRowStructName.ToString())); 112 | } 113 | 114 | const EAppReturnType::Type DlgResult = FMessageDialog::Open( 115 | EAppMsgType::YesNoCancel, 116 | FText::Format(LOCTEXT("DataTable_MissingRowStructMsg", "The following Data Tables are missing their row structure and will not be editable.\n\n{0}\n\nDo you want to open these data tables?"), DataTablesListText.ToText()), 117 | LOCTEXT("DataTable_MissingRowStructTitle", "Continue?") 118 | ); 119 | 120 | switch (DlgResult) 121 | { 122 | case EAppReturnType::Yes: 123 | DataTablesToOpen.Append(InvalidDataTables); 124 | break; 125 | case EAppReturnType::Cancel: 126 | return; 127 | default: 128 | break; 129 | } 130 | } 131 | 132 | FDataTableEditorModule& DataTableEditorModule = FModuleManager::LoadModuleChecked("DataTableEditor"); 133 | for (UDataTable* Table : DataTablesToOpen) 134 | { 135 | DataTableEditorModule.CreateDataTableEditor(EToolkitMode::Standalone, EditWithinLevelEditor, Table); 136 | } 137 | } 138 | 139 | // Low-level .json exporter, is called when 'Export as JSON' actions was pressed 140 | void FAssetTypeActions_MyDataTable::ExecuteExportAsJSON(TArray> Objects) 141 | { 142 | IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); 143 | 144 | const void* ParentWindowWindowHandle = FSlateApplication::Get().FindBestParentWindowHandleForDialogs(nullptr); 145 | 146 | for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt) 147 | { 148 | const UDataTable* DataTable = ObjIt ? Cast(ObjIt->Get()) : nullptr; 149 | if (DataTable) 150 | { 151 | const FText Title = FText::Format(LOCTEXT("DataTable_ExportJSONDialogTitle", "Export '{0}' as JSON..."), FText::FromString(*DataTable->GetName())); 152 | const FString CurrentFilename = DataTable->AssetImportData->GetFirstFilename(); 153 | const FString FileTypes = TEXT("Data Table JSON (*.json)|*.json"); 154 | 155 | TArray OutFilenames; 156 | DesktopPlatform->SaveFileDialog( 157 | ParentWindowWindowHandle, 158 | Title.ToString(), 159 | (CurrentFilename.IsEmpty()) ? TEXT("") : FPaths::GetPath(CurrentFilename), 160 | (CurrentFilename.IsEmpty()) ? TEXT("") : FPaths::GetBaseFilename(CurrentFilename) + TEXT(".json"), 161 | FileTypes, 162 | EFileDialogFlags::None, 163 | OutFilenames 164 | ); 165 | 166 | if (OutFilenames.Num() > 0) 167 | { 168 | FFileHelper::SaveStringToFile(DataTable->GetTableAsJSON(EDataTableExportFlags::UseJsonObjectsForStructs), *OutFilenames[0]); 169 | } 170 | } 171 | } 172 | } 173 | 174 | #undef LOCTEXT_NAMESPACE 175 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyAssets/AssetTypeActions_MyUserWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "MyAssets/SWCAssetTypeActions_MyUserWidget.h" 4 | //--- 5 | #include "UMGEditorProjectSettings.h" 6 | #include "WidgetBlueprint.h" 7 | #include "WidgetBlueprintEditor.h" 8 | #include "Blueprint/WidgetTree.h" 9 | #include "Components/CanvasPanel.h" 10 | #include "Kismet2/KismetEditorUtilities.h" 11 | #include "Misc/MessageDialog.h" 12 | 13 | void USWCMyUserWidgetBlueprint::GetReparentingRules(TSet& AllowedChildrenOfClasses, TSet& DisallowedChildrenOfClasses) const 14 | { 15 | check(ParentClass); 16 | AllowedChildrenOfClasses.Empty(); 17 | AllowedChildrenOfClasses.Add(ParentClass); 18 | } 19 | 20 | USWCMyUserWidgetFactory::USWCMyUserWidgetFactory() 21 | { 22 | bCreateNew = true; 23 | bEditAfterNew = true; 24 | SupportedClass = USWCMyUserWidgetBlueprint::StaticClass(); 25 | } 26 | 27 | FText USWCMyUserWidgetFactory::GetDisplayName() const 28 | { 29 | return NSLOCTEXT("AssetTypeActions", "MyUserWidget", "My User Widget"); 30 | } 31 | 32 | UObject* USWCMyUserWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) 33 | { 34 | // Make sure we are trying to factory a blueprint, then create and init one 35 | check(Class->IsChildOf(USWCMyUserWidgetBlueprint::StaticClass())); 36 | 37 | const TSubclassOf ParentClass = GetWidgetClass(); 38 | if (ParentClass == nullptr || !FKismetEditorUtilities::CanCreateBlueprintOfClass(ParentClass)) 39 | { 40 | FFormatNamedArguments Args; 41 | Args.Add(TEXT("ClassName"), ParentClass != nullptr ? FText::FromString(ParentClass->GetName()) : NSLOCTEXT("UnrealEd", "Null", "(null)")); 42 | FMessageDialog::Open(EAppMsgType::Ok, FText::Format(NSLOCTEXT("UnrealEd", "CannotCreateBlueprintFromClass", "Cannot create a blueprint based on the class '{0}'."), Args)); 43 | return nullptr; 44 | } 45 | 46 | // If the root widget selection dialog is not enabled, use a canvas panel as the root by default 47 | TSubclassOf RootWidgetClass = nullptr; 48 | const UUMGEditorProjectSettings* UMGProjectSettings = GetDefault(); 49 | check(UMGProjectSettings); 50 | if (!UMGProjectSettings->bUseWidgetTemplateSelector) 51 | { 52 | RootWidgetClass = UMGProjectSettings->DefaultRootWidget; 53 | } 54 | 55 | USWCMyUserWidgetBlueprint* NewBP = CastChecked(FKismetEditorUtilities::CreateBlueprint(ParentClass, InParent, Name, BPTYPE_Normal, USWCMyUserWidgetBlueprint::StaticClass(), UWidgetBlueprintGeneratedClass::StaticClass(), NAME_None)); 56 | 57 | // Create the selected root widget 58 | if (NewBP->WidgetTree->RootWidget == nullptr) 59 | { 60 | if (const TSubclassOf RootWidgetPanel = RootWidgetClass) 61 | { 62 | UWidget* Root = NewBP->WidgetTree->ConstructWidget(RootWidgetPanel); 63 | NewBP->WidgetTree->RootWidget = Root; 64 | } 65 | } 66 | 67 | return NewBP; 68 | } 69 | 70 | UObject* USWCMyUserWidgetFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) 71 | { 72 | return FactoryCreateNew(Class, InParent, Name, Flags, Context, Warn, NAME_None); 73 | } 74 | 75 | TSubclassOf USWCMyUserWidgetFactory::GetWidgetClass() const 76 | { 77 | return UUserWidget::StaticClass(); 78 | } 79 | 80 | FText FAssetTypeActions_MyUserWidget::GetName() const 81 | { 82 | return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_MyUserWidget", "My User Widget"); 83 | } 84 | 85 | void FAssetTypeActions_MyUserWidget::OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) 86 | { 87 | const EToolkitMode::Type Mode = EditWithinLevelEditor.IsValid() ? EToolkitMode::WorldCentric : EToolkitMode::Standalone; 88 | 89 | for (auto ObjIt = InObjects.CreateConstIterator(); ObjIt; ++ObjIt) 90 | { 91 | USWCMyUserWidgetBlueprint* Blueprint = Cast(*ObjIt); 92 | if (Blueprint && Blueprint->SkeletonGeneratedClass && Blueprint->GeneratedClass) 93 | { 94 | const TSharedRef NewBlueprintEditor(new FWidgetBlueprintEditor()); 95 | 96 | TArray Blueprints; 97 | Blueprints.Add(Blueprint); 98 | NewBlueprintEditor->InitWidgetBlueprintEditor(Mode, EditWithinLevelEditor, Blueprints, true); 99 | } 100 | else 101 | { 102 | FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("AssetTypeActions", "FailedToLoadWidgetBlueprint", "Widget Blueprint could not be loaded because it derives from an invalid class.\nCheck to make sure the parent class for this blueprint hasn't been removed!")); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyAssets/SWCAssetTypeActions_MyDataTable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "AssetTypeActions_CSVAssetBase.h" 6 | #include "Factories/ReimportDataTableFactory.h" 7 | //--- 8 | #include "SWCAssetTypeActions_MyDataTable.generated.h" 9 | 10 | /** 11 | * Is responsible for creating new custom Data Table asset in the 'Add' context menu. 12 | * Is abstract to ignore this factory by !CurrentClass->HasAnyClassFlags(CLASS_Abstract), 13 | * child should not be abstracted 14 | */ 15 | UCLASS(Abstract) 16 | class USWCMyDataTableFactory : public UReimportDataTableFactory 17 | { 18 | GENERATED_BODY() 19 | 20 | public: 21 | #pragma region OverrideInChild 22 | USWCMyDataTableFactory(); 23 | virtual FText GetDisplayName() const override; 24 | #pragma endregion OverrideInChild 25 | 26 | virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 27 | virtual bool ConfigureProperties() override { return true; } 28 | virtual bool ShouldShowInNewMenu() const override { return true; } 29 | virtual bool DoesSupportClass(UClass* Class) override; 30 | }; 31 | 32 | /** 33 | * Shows additional actions in the Context Menu of the custom data table assets. 34 | * Basically, is taken from FAssetTypeActions_DataTable since we can't derive to private include, 35 | * but need its functionality like import and exporting .json files. 36 | */ 37 | class FAssetTypeActions_MyDataTable : public FAssetTypeActions_CSVAssetBase 38 | { 39 | public: 40 | virtual ~FAssetTypeActions_MyDataTable() override = default; 41 | 42 | #pragma region OverrideInChild 43 | virtual FText GetName() const override; 44 | virtual FColor GetTypeColor() const override { return FColor::Blue; } 45 | virtual UClass* GetSupportedClass() const override; 46 | virtual uint32 GetCategories() override { return EAssetTypeCategories::Misc; } 47 | #pragma endregion OverrideInChild 48 | 49 | #pragma region FAssetTypeActions_DataTable 50 | /** Shows 'Export as JSON' option in the context menu. */ 51 | virtual void GetActions(const TArray& InObjects, struct FToolMenuSection& Section) override; 52 | 53 | /** Is overridden to show 'reimport' options in the contexts menu. */ 54 | virtual void GetResolvedSourceFilePaths(const TArray& TypeAssets, TArray& OutSourceFilePaths) const override; 55 | 56 | /** Opens data within the data table editor. */ 57 | virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor = TSharedPtr()) override; 58 | 59 | /** Low-level .json exporter, is called when 'Export as JSON' actions was pressed. */ 60 | virtual void ExecuteExportAsJSON(TArray> Objects); 61 | #pragma endregion FAssetTypeActions_DataTable 62 | }; 63 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyAssets/SWCAssetTypeActions_MyUserWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "AssetTypeActions_Base.h" 6 | #include "WidgetBlueprint.h" 7 | #include "Factories/Factory.h" 8 | //--- 9 | #include "SWCAssetTypeActions_MyUserWidget.generated.h" 10 | 11 | /** 12 | * The widget blueprint enables extending custom user widget. 13 | */ 14 | UCLASS() 15 | class USWCMyUserWidgetBlueprint : public UWidgetBlueprint 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | virtual bool AllowEditorWidget() const override { return true; } 21 | virtual void GetReparentingRules(TSet& AllowedChildrenOfClasses, TSet& DisallowedChildrenOfClasses) const override; 22 | }; 23 | 24 | /** 25 | * Is responsible for creating new custom user widget asset in the 'Add' context menu. 26 | * Basically, is taken from FAssetTypeActions_WidgetBlueprint since we can't derive to private include 27 | * Is abstract to ignore this factory by !CurrentClass->HasAnyClassFlags(CLASS_Abstract), 28 | * child should not be abstracted 29 | */ 30 | UCLASS(Abstract) 31 | class USWCMyUserWidgetFactory : public UFactory 32 | { 33 | GENERATED_BODY() 34 | 35 | public: 36 | USWCMyUserWidgetFactory(); 37 | 38 | #pragma region OverrideInChild 39 | virtual FText GetDisplayName() const override; 40 | virtual TSubclassOf GetWidgetClass() const; 41 | #pragma endregion OverrideInChild 42 | 43 | virtual bool CanCreateNew() const override { return true; } 44 | virtual bool ShouldShowInNewMenu() const override { return true; } 45 | 46 | /** Is copied from UWidgetBlueprintFactory since it's private. */ 47 | virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn, FName CallingContext) override; 48 | virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 49 | }; 50 | 51 | /** 52 | * Shows additional actions in the Context Menu of the custom user widget. 53 | * Basically, is taken from FAssetTypeActions_WidgetBlueprint since we can't derive to private include 54 | */ 55 | class FAssetTypeActions_MyUserWidget : public FAssetTypeActions_Base 56 | { 57 | public: 58 | virtual ~FAssetTypeActions_MyUserWidget() override = default; 59 | 60 | #pragma region OverrideInChild 61 | virtual FText GetName() const override; 62 | virtual FColor GetTypeColor() const override { return FColor::Blue; } 63 | virtual uint32 GetCategories() override { return EAssetTypeCategories::UI; } 64 | #pragma endregion OverrideInChild 65 | 66 | /** Does not require to be overridden since UMyUserWidgetFactory::GetWidgetClass() is known by UMyUserWidgetBlueprint that is specified here. */ 67 | virtual UClass* GetSupportedClass() const override { return USWCMyUserWidgetBlueprint::StaticClass(); } 68 | 69 | /** Is copied from FAssetTypeActions_WidgetBlueprint since it's private. */ 70 | virtual void OpenAssetEditor(const TArray& InObjects, TSharedPtr EditWithinLevelEditor) override; 71 | }; 72 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyPropertyType/MyPropertyTypeCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "MyPropertyType/MyPropertyTypeCustomization.h" 4 | //--- 5 | #include "DetailLayoutBuilder.h" 6 | #include "DetailWidgetRow.h" 7 | #include "IDetailChildrenBuilder.h" 8 | #include "SSearchableComboBox.h" 9 | 10 | // Called when the header of the property (the row in the details panel where the property is shown) 11 | void FMyPropertyTypeCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) 12 | { 13 | // Use default the header details panel 14 | HeaderRow 15 | .NameContent() 16 | [ 17 | PropertyHandle->CreatePropertyNameWidget() 18 | ] 19 | .ValueContent() 20 | [ 21 | PropertyHandle->CreatePropertyValueWidget() 22 | ]; 23 | } 24 | 25 | // Called when the children of the property should be customized or extra rows added. 26 | void FMyPropertyTypeCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) 27 | { 28 | // Find outer 29 | TArray OuterObjects; 30 | PropertyHandle/*ref*/->GetOuterObjects(OuterObjects); 31 | MyPropertyOuterInternal = OuterObjects.IsValidIndex(0) ? OuterObjects[0] : nullptr; 32 | 33 | // Set parent property 34 | ParentPropertyInternal = FPropertyData(PropertyHandle); 35 | const TDelegate& RefreshCustomPropertyFunction = FSimpleDelegate::CreateSP(this, &FMyPropertyTypeCustomization::RefreshCustomProperty); 36 | PropertyHandle/*ref*/->SetOnPropertyValueChanged(RefreshCustomPropertyFunction); 37 | PropertyHandle/*ref*/->SetOnChildPropertyValueChanged(RefreshCustomPropertyFunction); 38 | 39 | // Set children properties 40 | uint32 NumChildren; 41 | PropertyHandle/*ref*/->GetNumChildren(NumChildren); 42 | for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) 43 | { 44 | FPropertyData PropertyData(PropertyHandle/*ref*/->GetChildHandle(ChildIndex).ToSharedRef()); 45 | OnCustomizeChildren(ChildBuilder, PropertyData); 46 | } 47 | } 48 | 49 | // Set the FName value into the property 50 | void FMyPropertyTypeCustomization::SetCustomPropertyValue(FName Value) 51 | { 52 | const FString StringToSet = Value.ToString(); 53 | 54 | // Set value into property 55 | CustomPropertyInternal.PropertyValue = Value; 56 | CustomPropertyInternal.SetPropertyValueToHandle(Value); 57 | 58 | if (const TSharedPtr& RowTextWidget = RowTextWidgetInternal.Pin()) 59 | { 60 | // Update value on displayed widget 61 | RowTextWidget->SetText(FText::FromString(StringToSet)); 62 | } 63 | } 64 | 65 | // Set true to activate property, false to grey out it (read-only) 66 | void FMyPropertyTypeCustomization::SetCustomPropertyEnabled(bool bEnabled) 67 | { 68 | const bool bIsAllowedEnableCustomProperty = IsAllowedEnableCustomProperty(); 69 | if (bEnabled && !bIsAllowedEnableCustomProperty) 70 | { 71 | // Enable is forbidden 72 | return; 73 | } 74 | 75 | if (const TSharedPtr& SearchableComboBox = SearchableComboBoxInternal.Pin()) 76 | { 77 | SearchableComboBox->SetEnabled(bEnabled); 78 | } 79 | 80 | CustomPropertyInternal.bIsEnabled = bEnabled; 81 | } 82 | 83 | // Is called for each property on building its row 84 | void FMyPropertyTypeCustomization::OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData) 85 | { 86 | if (!ensureMsgf(PropertyData.IsValid(), TEXT("ASSERT: 'PropertyData.PropertyHandle' is not valid"))) 87 | { 88 | return; 89 | } 90 | 91 | if (PropertyData.PropertyName != CustomPropertyInternal.PropertyName) 92 | { 93 | // Add each another property to the Details Panel without customization 94 | ChildBuilder.AddProperty(PropertyData.PropertyHandle.ToSharedRef()) 95 | .ShouldAutoExpand(true) 96 | .IsEnabled(PropertyData.bIsEnabled) 97 | .Visibility(PropertyData.Visibility); 98 | DefaultPropertiesInternal.Emplace(PropertyData); 99 | return; 100 | } 101 | 102 | // --- Is custom property --- 103 | 104 | CustomPropertyInternal = PropertyData; 105 | 106 | // Add as searchable combo box by default 107 | AddCustomPropertyRow(PropertyData.PropertyHandle->GetPropertyDisplayName(), ChildBuilder); 108 | } 109 | 110 | // Will add the default searchable combo box 111 | void FMyPropertyTypeCustomization::AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder) 112 | { 113 | InitSearchableComboBox(); 114 | 115 | RefreshCustomProperty(); 116 | 117 | // Will add the searchable combo box by default 118 | const TSharedRef TextRowWidgetRef = 119 | SNew(STextBlock) 120 | .Text(GetCustomPropertyValue()); 121 | RowTextWidgetInternal = TextRowWidgetRef; 122 | 123 | const TSharedRef SearchableComboBoxRef = 124 | SNew(SSearchableComboBox) 125 | .OptionsSource(&SearchableComboBoxValuesInternal) 126 | .OnGenerateWidget_Lambda([](const TSharedPtr InItem) -> TSharedRef 127 | { 128 | return SNew(STextBlock).Text(FText::FromString(*InItem)); 129 | }) 130 | .OnSelectionChanged(this, &FMyPropertyTypeCustomization::OnCustomPropertyChosen) 131 | .ContentPadding(2.f) 132 | .MaxListHeight(200.f) 133 | .IsEnabled(CustomPropertyInternal.bIsEnabled) 134 | .Content() 135 | [ 136 | TextRowWidgetRef 137 | ]; 138 | SearchableComboBoxInternal = SearchableComboBoxRef; 139 | 140 | ChildBuilder.AddCustomRow(PropertyDisplayText) 141 | .Visibility(CustomPropertyInternal.Visibility) 142 | .NameContent() 143 | [ 144 | SNew(STextBlock) 145 | .Text(PropertyDisplayText) 146 | .Font(IDetailLayoutBuilder::GetDetailFont()) 147 | ] 148 | .ValueContent() 149 | [ 150 | SNew(SVerticalBox) 151 | + SVerticalBox::Slot() 152 | .AutoHeight() 153 | .VAlign(VAlign_Fill) 154 | .Padding(0.f) 155 | [ 156 | SearchableComboBoxRef 157 | ] 158 | ]; 159 | } 160 | 161 | //Set new values for the list of selectable members 162 | void FMyPropertyTypeCustomization::RefreshCustomProperty() 163 | { 164 | if (const TSharedPtr& SearchableComboBox = SearchableComboBoxInternal.Pin()) 165 | { 166 | SearchableComboBox->RefreshOptions(); 167 | } 168 | } 169 | 170 | // Is called to deactivate custom property 171 | void FMyPropertyTypeCustomization::InvalidateCustomProperty() 172 | { 173 | SetCustomPropertyEnabled(false); 174 | 175 | SetCustomPropertyValue(NAME_None); 176 | } 177 | 178 | // Called when the children of the property should be customized or extra rows added 179 | void FMyPropertyTypeCustomization::OnCustomPropertyChosen(TSharedPtr SelectedStringPtr, ESelectInfo::Type SelectInfo) 180 | { 181 | if (const FString* SelectedString = SelectedStringPtr.Get()) 182 | { 183 | SetCustomPropertyValue(**SelectedString); 184 | } 185 | } 186 | 187 | // Add an empty row once, so the users can clear the selection if they want 188 | void FMyPropertyTypeCustomization::InitSearchableComboBox() 189 | { 190 | if (!NoneStringInternal.IsValid()) 191 | { 192 | TSharedPtr NoneStringPtr(MakeShareable(new FString(FPropertyData::NoneString))); 193 | NoneStringInternal = NoneStringPtr; 194 | SearchableComboBoxValuesInternal.EmplaceAt(0, MoveTemp(NoneStringPtr)); 195 | } 196 | } 197 | 198 | // Reset and remove all shared strings in array except 'None' string 199 | void FMyPropertyTypeCustomization::ResetSearchableComboBox() 200 | { 201 | const int32 ValuesNum = SearchableComboBoxValuesInternal.Num(); 202 | for (int32 Index = ValuesNum - 1; Index >= 0; --Index) 203 | { 204 | if (!SearchableComboBoxValuesInternal.IsValidIndex(Index)) 205 | { 206 | continue; 207 | } 208 | 209 | TSharedPtr& StringPtrIt = SearchableComboBoxValuesInternal[Index]; 210 | if (StringPtrIt != NoneStringInternal) 211 | { 212 | StringPtrIt.Reset(); 213 | SearchableComboBoxValuesInternal.RemoveAt(Index); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyPropertyType/MyPropertyTypeCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "IPropertyTypeCustomization.h" 6 | //--- 7 | #include "MyPropertyType/PropertyData.h" 8 | 9 | typedef class FMyPropertyTypeCustomization Super; 10 | 11 | /** 12 | * Overrides some property to make better experience avoiding any errors in properties by manual typing etc. 13 | * The FName Property is customised as button to select the value in a list. 14 | */ 15 | class FMyPropertyTypeCustomization : public IPropertyTypeCustomization 16 | { 17 | public: 18 | /** Is used to load and unload the Property Editor Module. */ 19 | inline static const FName PropertyEditorModule = TEXT("PropertyEditor"); 20 | 21 | /* --------------------------------------------------- 22 | * Public functions 23 | * --------------------------------------------------- */ 24 | 25 | /** 26 | * Called when the header of the property (the row in the details panel where the property is shown) 27 | * If nothing is added to the row, the header is not displayed 28 | * @param PropertyHandle Handle to the property being customized 29 | * @param HeaderRow A row that widgets can be added to 30 | * @param CustomizationUtils Utilities for customization 31 | */ 32 | virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 33 | 34 | /** 35 | * Called when the children of the property should be customized or extra rows added. 36 | * @param PropertyHandle Handle to the property being customized 37 | * @param ChildBuilder A builder for adding children 38 | * @param CustomizationUtils Utilities for customization 39 | */ 40 | virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 41 | 42 | /** Get cached value contained in the property to be customized. */ 43 | FORCEINLINE FText GetCustomPropertyValue() const { return FText::FromString(CustomPropertyInternal.PropertyValue.ToString()); } 44 | 45 | /** Set the FName value into the property. 46 | * @see FMyPropertyTypeCustomization::MyPropertyHandleInternal. */ 47 | void SetCustomPropertyValue(FName Value); 48 | 49 | /** Set true to activate property, false to grey out it (read-only). */ 50 | void SetCustomPropertyEnabled(bool bEnabled); 51 | 52 | protected: 53 | /* --------------------------------------------------- 54 | * Protected properties 55 | * --------------------------------------------------- */ 56 | 57 | /** Contains data of hierarchically upper property which is chosen in editor module to customize its child property. */ 58 | FPropertyData ParentPropertyInternal = FPropertyData::Empty; 59 | 60 | /** Property data to be customized. It's property name has to be set in children's constructors. */ 61 | FPropertyData CustomPropertyInternal = FPropertyData::Empty; 62 | 63 | /** Contains data about all not custom child properties. */ 64 | TArray DefaultPropertiesInternal; 65 | 66 | /** The outer uobject of a property to be customized. */ 67 | TWeakObjectPtr MyPropertyOuterInternal = nullptr; 68 | 69 | /** The text widget that displays the chosen property value. */ 70 | TWeakPtr RowTextWidgetInternal = nullptr; 71 | 72 | /** Strings list of displayed values to be selected. */ 73 | TArray> SearchableComboBoxValuesInternal; 74 | 75 | /** Contains the widget row that displays values to be selected. 76 | * @see FMyPropertyTypeCustomization::SearchableComboBoxValuesInternal */ 77 | TWeakPtr SearchableComboBoxInternal = nullptr; 78 | 79 | /** Shared none string. Is selectable value in the searchable box. */ 80 | TWeakPtr NoneStringInternal = nullptr; 81 | 82 | /* --------------------------------------------------- 83 | * Protected functions 84 | * --------------------------------------------------- */ 85 | 86 | /** 87 | * Is called for each property on building its row. 88 | * @param ChildBuilder A builder for adding children. 89 | * @param PropertyData Data of a property to be customized. 90 | */ 91 | virtual void OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData); 92 | 93 | /** 94 | * Is called on adding the custom property. 95 | * @param PropertyDisplayText The formatted (with spacers) title name of a row to be shown. 96 | * @param ChildBuilder A builder for adding children. 97 | * @see FMyPropertyTypeCustomization::CustomPropertyNameInternal 98 | */ 99 | virtual void AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder); 100 | 101 | /** Set new values for the list of selectable members. 102 | * @see FMyPropertyTypeCustomization::SearchableComboBoxValuesInternal */ 103 | virtual void RefreshCustomProperty(); 104 | 105 | /** Is called to deactivate custom property. */ 106 | virtual void InvalidateCustomProperty(); 107 | 108 | /** Returns true if changing custom property currently is not forbidden. */ 109 | virtual bool IsAllowedEnableCustomProperty() const { return true; } 110 | 111 | /** 112 | * Callback for when the function selection has changed from the dropdown. Will call setter of custom property. 113 | * @param SelectedStringPtr The chosen string. 114 | * @param SelectInfo Additional information about a selection event. 115 | * @see FMyPropertyTypeCustomization::SetCustomPropertyValue(FName). 116 | */ 117 | void OnCustomPropertyChosen(TSharedPtr SelectedStringPtr, ESelectInfo::Type SelectInfo); 118 | 119 | /** Add an empty row once, so the users can clear the selection if they want. */ 120 | void InitSearchableComboBox(); 121 | 122 | /** Reset and remove all shared strings in array except 'None' string. */ 123 | void ResetSearchableComboBox(); 124 | }; 125 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyPropertyType/PropertyData.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "MyPropertyType/PropertyData.h" 4 | //--- 5 | #include "PropertyHandle.h" 6 | 7 | // Empty property data 8 | const FPropertyData FPropertyData::Empty = FPropertyData(); 9 | 10 | // Custom constructor, is not required, but fully init property data. 11 | FPropertyData::FPropertyData(TSharedRef InPropertyHandle) 12 | : PropertyHandle(MoveTemp(InPropertyHandle)) 13 | { 14 | PropertyName = GetPropertyNameFromHandle(); 15 | PropertyValue = GetPropertyValueFromHandle(); 16 | } 17 | 18 | // Get property from handle 19 | FProperty* FPropertyData::GetProperty() const 20 | { 21 | return PropertyHandle ? PropertyHandle->GetProperty() : nullptr; 22 | } 23 | 24 | // Get property name by handle 25 | FName FPropertyData::GetPropertyNameFromHandle() const 26 | { 27 | const FProperty* CurrentProperty = GetProperty(); 28 | return CurrentProperty ? CurrentProperty->GetFName() : NAME_None; 29 | } 30 | 31 | // Get FName value by property handle 32 | FName FPropertyData::GetPropertyValueFromHandle() const 33 | { 34 | FName ValueName = NAME_None; 35 | if (PropertyHandle.IsValid()) 36 | { 37 | FString ValueString; 38 | PropertyHandle->GetValueAsDisplayString(/*Out*/ValueString); 39 | if (ValueString.Len() < NAME_SIZE) 40 | { 41 | ValueName = *ValueString; 42 | } 43 | } 44 | return ValueName; 45 | } 46 | 47 | // Get property ptr to the value by handle 48 | void* FPropertyData::GetPropertyValuePtrFromHandle() const 49 | { 50 | void* FoundData = nullptr; 51 | if (PropertyHandle.IsValid()) 52 | { 53 | PropertyHandle->GetValueData(/*Out*/FoundData); 54 | } 55 | return FoundData; 56 | } 57 | 58 | // Set FName value by property handle 59 | void FPropertyData::SetPropertyValueToHandle(FName NewValue) 60 | { 61 | if (PropertyHandle.IsValid()) 62 | { 63 | PropertyHandle->SetValue(NewValue); 64 | } 65 | } 66 | 67 | // Returns the meta value by specified ke 68 | FName FPropertyData::GetMetaDataValue(FName Key) const 69 | { 70 | const FProperty* Property = !Key.IsNone() ? GetProperty() : nullptr; 71 | const FString* FoundKey = Property ? Property->FindMetaData(Key) : nullptr; 72 | return FoundKey ? **FoundKey : *NoneString; 73 | } 74 | 75 | // Returns true if specified key exist 76 | bool FPropertyData::IsMetaKeyExists(FName Key) const 77 | { 78 | const FProperty* Property = !Key.IsNone() ? GetProperty() : nullptr; 79 | return Property && Property->FindMetaData(Key); 80 | } 81 | 82 | // Set the meta value by specified key 83 | void FPropertyData::SetMetaDataValue(FName Key, FName NewValue, bool bNotifyPostChange/* = false*/) 84 | { 85 | FProperty* Property = !Key.IsNone() ? GetProperty() : nullptr; 86 | if (!Property) 87 | { 88 | return; 89 | } 90 | 91 | const FName PrevMetaValue = GetMetaDataValue(Key); 92 | if (PrevMetaValue == NewValue) 93 | { 94 | return; 95 | } 96 | 97 | Property->SetMetaData(Key, NewValue.ToString()); 98 | 99 | if (bNotifyPostChange) 100 | { 101 | PropertyHandle->NotifyPostChange(EPropertyChangeType::ValueSet); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/MyPropertyType/PropertyData.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Layout/Visibility.h" 7 | #include "Misc/Attribute.h" 8 | 9 | /** 10 | * Contains data that describes property. 11 | */ 12 | struct FPropertyData 13 | { 14 | /** Empty property data. */ 15 | static const FPropertyData Empty; 16 | 17 | /** The 'None' string. */ 18 | inline static const FString& NoneString = FCoreTexts::Get().None.ToString(); 19 | 20 | /** Default empty constructor. */ 21 | FPropertyData() = default; 22 | 23 | /** Custom constructor, is not required, but fully init property data. */ 24 | explicit FPropertyData(TSharedRef InPropertyHandle); 25 | 26 | /** The name of a property. */ 27 | FName PropertyName = NAME_None; 28 | 29 | /** The last cached value of a property. */ 30 | FName PropertyValue = NAME_None; 31 | 32 | /** The handle of a property. */ 33 | TSharedPtr PropertyHandle = nullptr; 34 | 35 | /** Determines if property is active (not greyed out). */ 36 | TAttribute bIsEnabled = true; 37 | 38 | /** Determines if property is visible. */ 39 | TAttribute Visibility = EVisibility::Visible; 40 | 41 | /** Returns true is property is not empty. */ 42 | FORCEINLINE bool IsValid() const { return !PropertyName.IsNone() && PropertyHandle != nullptr; } 43 | 44 | /** Get property from handle.*/ 45 | FProperty* GetProperty() const; 46 | 47 | /** Get property name by handle. 48 | * It returns current name of the property. 49 | * Is cheaper to use cached one. 50 | * @see FPropertyData::PropertyName. */ 51 | FName GetPropertyNameFromHandle() const; 52 | 53 | /** Get property value by handle. 54 | * It returns current value contained in property. 55 | * Is cheaper to use cached one. 56 | * @see FPropertyData::PropertyValue. */ 57 | FName GetPropertyValueFromHandle() const; 58 | 59 | /** Get property ptr to the value by handle. */ 60 | void* GetPropertyValuePtrFromHandle() const; 61 | 62 | /** 63 | * Set new template value to property handle. 64 | * @tparam T Template param, is used to as to set simple types as well as set whole FProperty* 65 | * @param NewValue Value to set. 66 | */ 67 | void SetPropertyValueToHandle(FName NewValue); 68 | 69 | /** Returns the meta value by specified key. 70 | * @param Key The key of a meta to find. */ 71 | FName GetMetaDataValue(FName Key) const; 72 | 73 | /** Returns true if specified key exist. 74 | * @param Key The key of a meta to check. */ 75 | bool IsMetaKeyExists(FName Key) const; 76 | 77 | /** Set the meta value by specified key. 78 | * @param Key The key of a meta. 79 | * @param NewValue The value of a meta to set. 80 | * @param bNotifyPostChange Set true to notify property about change. */ 81 | void SetMetaDataValue(FName Key, FName NewValue, bool bNotifyPostChange = false); 82 | }; 83 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/SettingTagCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "SettingTagCustomization.h" 4 | //--- 5 | #include "Data/SettingTag.h" 6 | //--- 7 | #include "PropertyEditorModule.h" 8 | 9 | /** The name of class to be customized: SettingTag */ 10 | const FName FSettingTagCustomization::PropertyClassName = FSettingTag::StaticStruct()->GetFName(); 11 | 12 | // Makes a new instance of this detail layout class for a specific detail view requesting it 13 | TSharedRef FSettingTagCustomization::MakeInstance() 14 | { 15 | return FGameplayTagCustomizationPublic::MakeInstance(); 16 | } 17 | 18 | // Creates customization for the Settings Tag 19 | void FSettingTagCustomization::RegisterSettingsTagCustomization() 20 | { 21 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 22 | { 23 | return; 24 | } 25 | 26 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 27 | 28 | // Use default GameplayTag customization for inherited SettingTag to show Tags list 29 | PropertyModule.RegisterCustomPropertyTypeLayout( 30 | PropertyClassName, 31 | FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSettingTagCustomization::MakeInstance) 32 | ); 33 | 34 | PropertyModule.NotifyCustomizationModuleChanged(); 35 | } 36 | 37 | // Removes customization for the Settings Tag 38 | void FSettingTagCustomization::UnregisterSettingsTagCustomization() 39 | { 40 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 41 | { 42 | return; 43 | } 44 | 45 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 46 | 47 | PropertyModule.UnregisterCustomPropertyTypeLayout(PropertyClassName); 48 | } -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/SettingsPickerCustomization.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #include "SettingsPickerCustomization.h" 4 | //--- 5 | #include "Data/SettingsRow.h" 6 | #include "FunctionPickerType/FunctionPickerCustomization.h" 7 | //--- 8 | #include "PropertyEditorModule.h" 9 | #include "PropertyHandle.h" 10 | #include "Modules/ModuleManager.h" 11 | 12 | // The name of class to be customized: SettingsPicker 13 | const FName FSettingsPickerCustomization::PropertyClassName = FSettingsPicker::StaticStruct()->GetFName(); 14 | 15 | /** The name of the settings data base struct: /Script/SettingsWidgetConstructor.SettingsDataBase. */ 16 | const FName FSettingsPickerCustomization::SettingsDataBasePathName = *FSettingsDataBase::StaticStruct()->GetPathName(); 17 | 18 | /** The name of the Settings Primary struct: /Script/SettingsWidgetConstructor.SettingsPrimary. */ 19 | const FName FSettingsPickerCustomization::SettingsPrimaryPathName = *FSettingsPrimary::StaticStruct()->GetPathName(); 20 | 21 | /** The name of the Function Picker struct: /Script/FunctionPicker.FSettingFunctionPicker. */ 22 | const FName FSettingsPickerCustomization::FunctionPickerPathName = *FSettingFunctionPicker::StaticStruct()->GetPathName(); 23 | 24 | // Default constructor 25 | FSettingsPickerCustomization::FSettingsPickerCustomization() 26 | { 27 | CustomPropertyInternal.PropertyName = GET_MEMBER_NAME_CHECKED(FSettingsPicker, SettingsType); 28 | 29 | SettingsFunctionProperties.Reserve(FFunctionPickerCustomization::TemplateMetaKeys.Num()); 30 | for (FName MetaNameIt : FFunctionPickerCustomization::TemplateMetaKeys) 31 | { 32 | SettingsFunctionProperties.Emplace(MoveTemp(MetaNameIt), FPropertyData::Empty); 33 | } 34 | } 35 | 36 | // Makes a new instance of this detail layout class for a specific detail view requesting it 37 | TSharedRef FSettingsPickerCustomization::MakeInstance() 38 | { 39 | return MakeShareable(new FSettingsPickerCustomization()); 40 | } 41 | 42 | // Called when the header of the property (the row in the details panel where the property is shown) 43 | void FSettingsPickerCustomization::CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) 44 | { 45 | // Do not use the header panel at all 46 | } 47 | 48 | // Called when the children of the property should be customized or extra rows added. 49 | void FSettingsPickerCustomization::CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) 50 | { 51 | Super::CustomizeChildren(PropertyHandle, ChildBuilder, CustomizationUtils); 52 | 53 | // Display added values in the picker list 54 | Super::RefreshCustomProperty(); 55 | ClearPrevChosenProperty(); 56 | } 57 | 58 | // Creates customization for the Settings Picker 59 | void FSettingsPickerCustomization::RegisterSettingsPickerCustomization() 60 | { 61 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 62 | { 63 | return; 64 | } 65 | 66 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 67 | 68 | // Is customized to show only selected in-game option 69 | PropertyModule.RegisterCustomPropertyTypeLayout( 70 | PropertyClassName, 71 | FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FSettingsPickerCustomization::MakeInstance) 72 | ); 73 | 74 | PropertyModule.NotifyCustomizationModuleChanged(); 75 | } 76 | 77 | // Removes customization for the Settings Picker 78 | void FSettingsPickerCustomization::UnregisterSettingsPickerCustomization() 79 | { 80 | if (!FModuleManager::Get().IsModuleLoaded(PropertyEditorModule)) 81 | { 82 | return; 83 | } 84 | 85 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked(PropertyEditorModule); 86 | 87 | PropertyModule.UnregisterCustomPropertyTypeLayout(PropertyClassName); 88 | } 89 | 90 | // Is called for each property on building its row 91 | void FSettingsPickerCustomization::OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData) 92 | { 93 | /* ╔FSettingsPicker 94 | * ╠════╦FSettingsPrimary (1) 95 | * ║ ╚════FSettingFunctionPicker (2) 96 | * ╚═════FSettingsDataBase (3) 97 | */ 98 | 99 | if (!SettingsDataBaseStructInternal.IsValid()) 100 | { 101 | SettingsDataBaseStructInternal = !SettingsDataBasePathName.IsNone() ? UClass::TryFindTypeSlow(SettingsDataBasePathName.ToString(), EFindFirstObjectOptions::ExactClass) : nullptr; 102 | } 103 | 104 | if (!SettingsPrimaryStructInternal.IsValid()) 105 | { 106 | SettingsPrimaryStructInternal = !SettingsDataBasePathName.IsNone() ? UClass::TryFindTypeSlow(SettingsPrimaryPathName.ToString(), EFindFirstObjectOptions::ExactClass) : nullptr; 107 | } 108 | 109 | if (!FunctionPickerStructInternal.IsValid()) 110 | { 111 | FunctionPickerStructInternal = !SettingsDataBasePathName.IsNone() ? UClass::TryFindTypeSlow(FunctionPickerPathName.ToString(), EFindFirstObjectOptions::ExactClass) : nullptr; 112 | } 113 | 114 | const auto StructProperty = CastField(PropertyData.GetProperty()); 115 | const UScriptStruct* StructClass = StructProperty ? StructProperty->Struct : nullptr; 116 | if (StructClass) 117 | { 118 | if (StructClass->IsChildOf(SettingsPrimaryStructInternal.Get())) //(1) 119 | { 120 | // Add lambda for visibility attribute to show\hide function properties when nothing is chosen 121 | PropertyData.Visibility = MakeAttributeLambda([InCustomProperty = CustomPropertyInternal]() -> EVisibility 122 | { 123 | const FName CustomPropertyValueName = InCustomProperty.GetPropertyValueFromHandle(); 124 | return !CustomPropertyValueName.IsNone() ? EVisibility::Visible : EVisibility::Collapsed; 125 | }); 126 | 127 | const TSharedRef& SettingsPrimaryHandle = PropertyData.PropertyHandle.ToSharedRef(); 128 | uint32 NumChildren; 129 | SettingsPrimaryHandle->GetNumChildren(NumChildren); 130 | for (uint32 ChildIndex = 0; ChildIndex < NumChildren; ++ChildIndex) 131 | { 132 | FPropertyData SettingsPrimaryData(SettingsPrimaryHandle->GetChildHandle(ChildIndex).ToSharedRef()); 133 | const FStructProperty* ChildProperty = CastField(SettingsPrimaryData.GetProperty()); 134 | const UScriptStruct* ChildClass = ChildProperty ? ChildProperty->Struct : nullptr; 135 | if (ChildClass 136 | && ChildClass->IsChildOf(FunctionPickerStructInternal.Get())) //(2) 137 | { 138 | // Find and set function properties into array 139 | for (TTuple& SettingsFunctionPropertyIt : SettingsFunctionProperties) 140 | { 141 | const FName MetaKey = SettingsFunctionPropertyIt.Key; 142 | if (SettingsPrimaryData.IsMetaKeyExists(MetaKey)) 143 | { 144 | SettingsFunctionPropertyIt.Value = MoveTemp(SettingsPrimaryData); 145 | break; 146 | } 147 | } 148 | } 149 | } 150 | } 151 | else if (StructClass->IsChildOf(SettingsDataBaseStructInternal.Get())) //(3) 152 | { 153 | // Add this to the searchable text box as an FString so users can type and find it 154 | const FString SettingsDataBasePropertyName = PropertyData.PropertyName.ToString(); 155 | SearchableComboBoxValuesInternal.Emplace(MakeShareable(new FString(SettingsDataBasePropertyName))); 156 | 157 | // Add lambda for visibility attribute to show\hide property when is not chosen 158 | PropertyData.Visibility = MakeAttributeLambda([InDefaultProperty = PropertyData, InCustomProperty = CustomPropertyInternal]() -> EVisibility 159 | { 160 | const FName OtherPropertyName = InDefaultProperty.PropertyName; 161 | const FName CustomPropertyValueName = InCustomProperty.GetPropertyValueFromHandle(); 162 | return OtherPropertyName == CustomPropertyValueName ? EVisibility::Visible : EVisibility::Collapsed; 163 | }); 164 | } 165 | } 166 | 167 | Super::OnCustomizeChildren(ChildBuilder, PropertyData); 168 | } 169 | 170 | // Is called on adding the custom property 171 | void FSettingsPickerCustomization::AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder) 172 | { 173 | // Super will add the searchable combo box 174 | Super::AddCustomPropertyRow(PropertyDisplayText, ChildBuilder); 175 | } 176 | 177 | // Set new values for the list of selectable members 178 | void FSettingsPickerCustomization::RefreshCustomProperty() 179 | { 180 | // Super is not called to avoid refreshing searchable combo box, it always has the same values 181 | 182 | // Compare with chosen option 183 | if (ChosenSettingsDataInternal.PropertyName != CustomPropertyInternal.PropertyValue) 184 | { 185 | ClearPrevChosenProperty(); 186 | } 187 | } 188 | 189 | // Will reset the whole property content of last chosen settings type 190 | void FSettingsPickerCustomization::ClearPrevChosenProperty() 191 | { 192 | FPropertyData NewPropertyData = FPropertyData::Empty; 193 | FPropertyData PrevPropertyData = FPropertyData::Empty; 194 | for (const FPropertyData& DefaultPropertyIt : DefaultPropertiesInternal) 195 | { 196 | if (!NewPropertyData.IsValid() 197 | && CustomPropertyInternal.IsValid() 198 | && CustomPropertyInternal.PropertyValue == DefaultPropertyIt.PropertyName) 199 | { 200 | NewPropertyData = DefaultPropertyIt; 201 | continue; 202 | } 203 | 204 | if (!PrevPropertyData.IsValid() 205 | && ChosenSettingsDataInternal.IsValid() 206 | && ChosenSettingsDataInternal.PropertyName == DefaultPropertyIt.PropertyName) 207 | { 208 | PrevPropertyData = DefaultPropertyIt; 209 | } 210 | } 211 | 212 | if (!PrevPropertyData.IsValid() 213 | && !NewPropertyData.IsValid()) 214 | { 215 | return; 216 | } 217 | 218 | ChosenSettingsDataInternal = NewPropertyData; 219 | 220 | // Clear prev value 221 | void* PrevValueData = PrevPropertyData.GetPropertyValuePtrFromHandle(); 222 | const FProperty* PrevProperty = PrevPropertyData.GetProperty(); 223 | if (PrevValueData 224 | && PrevProperty) 225 | { 226 | PrevProperty->ClearValue(PrevValueData); 227 | } 228 | 229 | CopyMetas(); 230 | } 231 | 232 | // Copy meta from chosen option USTRUCT to each UPROPERTY of Settings Functions 233 | void FSettingsPickerCustomization::CopyMetas() 234 | { 235 | const auto StructProperty = CastField(ChosenSettingsDataInternal.GetProperty()); 236 | const UScriptStruct* SettingsDataChildStruct = StructProperty ? StructProperty->Struct : nullptr; 237 | 238 | for (TTuple& PropertyIt : SettingsFunctionProperties) 239 | { 240 | const FName TemplateMetaKey = PropertyIt.Key; 241 | FPropertyData& PropertyDataRef = PropertyIt.Value; 242 | if (!PropertyIt.Value.IsValid()) 243 | { 244 | continue; 245 | } 246 | 247 | if (!SettingsDataChildStruct) 248 | { 249 | PropertyDataRef.SetMetaDataValue(TemplateMetaKey, NAME_None, true); 250 | continue; 251 | } 252 | 253 | // Check meta key in parent struct if not found for child struct 254 | for (const UStruct* StructIt = SettingsDataChildStruct; StructIt; StructIt = StructIt->GetSuperStruct()) 255 | { 256 | // Copy meta from chosen option USTRUCT to each UPROPERTY of SettingsFunctionProperties 257 | if (const FString* FoundMetaData = StructIt->FindMetaData(TemplateMetaKey)) 258 | { 259 | PropertyDataRef.SetMetaDataValue(TemplateMetaKey, **FoundMetaData, true); 260 | break; 261 | } 262 | 263 | if (StructIt == SettingsDataBaseStructInternal) 264 | { 265 | // The meta key was not found 266 | PropertyDataRef.SetMetaDataValue(TemplateMetaKey, NAME_None, true); 267 | break; 268 | } 269 | } 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Private/SettingsWidgetConstructorEditorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #include "SettingsWidgetConstructorEditorModule.h" 4 | //--- 5 | #include "AssetTypeActions_SettingsDataTable.h" 6 | #include "AssetTypeActions_SettingsWidget.h" 7 | #include "SettingsPickerCustomization.h" 8 | #include "SettingTagCustomization.h" 9 | #include "FunctionPickerType/FunctionPickerCustomization.h" 10 | //--- 11 | #include "KismetCompiler.h" 12 | #include "Modules/ModuleManager.h" 13 | 14 | // Called right after the module DLL has been loaded and the module object has been created 15 | void FSettingsWidgetConstructorEditorModule::StartupModule() 16 | { 17 | RegisterPropertyCustomizations(); 18 | RegisterSettingAssets(); 19 | 20 | FKismetCompilerContext::RegisterCompilerForBP(USWCMyUserWidgetBlueprint::StaticClass(), &UWidgetBlueprint::GetCompilerForWidgetBP); 21 | } 22 | 23 | // Called before the module is unloaded, right before the module object is destroyed 24 | void FSettingsWidgetConstructorEditorModule::ShutdownModule() 25 | { 26 | UnregisterPropertyCustomizations(); 27 | UnregisterAssets(RegisteredAssets); 28 | } 29 | 30 | // Removes all custom assets from context menu 31 | void FSettingsWidgetConstructorEditorModule::UnregisterAssets(TArray>& RegisteredAssets) 32 | { 33 | static const FName AssetToolsModuleName = TEXT("AssetTools"); 34 | const FAssetToolsModule* AssetToolsPtr = FModuleManager::GetModulePtr(AssetToolsModuleName); 35 | if (!AssetToolsPtr) 36 | { 37 | return; 38 | } 39 | 40 | IAssetTools& AssetTools = AssetToolsPtr->Get(); 41 | for (TSharedPtr& AssetTypeActionIt : RegisteredAssets) 42 | { 43 | if (AssetTypeActionIt.IsValid()) 44 | { 45 | AssetTools.UnregisterAssetTypeActions(AssetTypeActionIt.ToSharedRef()); 46 | } 47 | } 48 | } 49 | 50 | // Creates all customizations for custom properties 51 | void FSettingsWidgetConstructorEditorModule::RegisterPropertyCustomizations() 52 | { 53 | FFunctionPickerCustomization::RegisterFunctionPickerCustomization(); 54 | FSettingsPickerCustomization::RegisterSettingsPickerCustomization(); 55 | FSettingTagCustomization::RegisterSettingsTagCustomization(); 56 | } 57 | 58 | // Removes all custom property customizations 59 | void FSettingsWidgetConstructorEditorModule::UnregisterPropertyCustomizations() 60 | { 61 | FFunctionPickerCustomization::UnregisterFunctionPickerCustomization(); 62 | FSettingsPickerCustomization::UnregisterSettingsPickerCustomization(); 63 | FSettingTagCustomization::UnregisterSettingsTagCustomization(); 64 | } 65 | 66 | // Adds to context menu custom assets to be created 67 | void FSettingsWidgetConstructorEditorModule::RegisterSettingAssets() 68 | { 69 | RegisterSettingAssetsCategory(); 70 | 71 | RegisterAsset(RegisteredAssets); 72 | RegisterAsset(RegisteredAssets); 73 | } 74 | 75 | // Adds the category of this plugin to the 'Add' context menu 76 | void FSettingsWidgetConstructorEditorModule::RegisterSettingAssetsCategory() 77 | { 78 | static const FName CategoryKey = TEXT("SettingsWidgetConstructor"); 79 | static const FText CategoryDisplayName = NSLOCTEXT("SettingsWidgetConstructorEditorModule", "SettingsWidgetConstructorCategory", "Settings Widget Constructor"); 80 | SettingsCategory = IAssetTools::Get().RegisterAdvancedAssetCategory(CategoryKey, CategoryDisplayName); 81 | } 82 | 83 | IMPLEMENT_MODULE(FSettingsWidgetConstructorEditorModule, SettingsWidgetConstructorEditor) 84 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Public/AssetTypeActions_SettingsDataTable.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "MyAssets/SWCAssetTypeActions_MyDataTable.h" 6 | //--- 7 | #include "AssetTypeActions_SettingsDataTable.generated.h" 8 | 9 | /** 10 | * Is responsible for creating new Settings Data Table asset in the 'Add' context menu. 11 | */ 12 | UCLASS() 13 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API USettingsDataTableFactory : public USWCMyDataTableFactory 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | USettingsDataTableFactory(); 19 | virtual FText GetDisplayName() const override; 20 | virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 21 | 22 | protected: 23 | /** Imports default data into new Settings Data Table. */ 24 | virtual void ImportDefaultSettingsDataTable(UObject* NewSettingDataTable); 25 | }; 26 | 27 | /** 28 | * Shows additional actions in the Context Menu. 29 | */ 30 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API FAssetTypeActions_SettingsDataTable : public FAssetTypeActions_MyDataTable 31 | { 32 | public: 33 | virtual ~FAssetTypeActions_SettingsDataTable() override = default; 34 | 35 | virtual FText GetName() const override; 36 | virtual FColor GetTypeColor() const override; 37 | virtual UClass* GetSupportedClass() const override; 38 | virtual uint32 GetCategories() override; 39 | }; 40 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Public/AssetTypeActions_SettingsWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov 2 | 3 | #pragma once 4 | 5 | #include "MyAssets/SWCAssetTypeActions_MyUserWidget.h" 6 | //--- 7 | #include "AssetTypeActions_SettingsWidget.generated.h" 8 | 9 | /** 10 | * Is responsible for creating new Settings Widget in the 'Add' context menu. 11 | */ 12 | UCLASS(Config = SettingsWidgetConstructor, DefaultConfig) 13 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API USettingsWidgetFactory : public USWCMyUserWidgetFactory 14 | { 15 | GENERATED_BODY() 16 | 17 | public: 18 | USettingsWidgetFactory(); 19 | 20 | virtual FText GetDisplayName() const override; 21 | virtual TSubclassOf GetWidgetClass() const override; 22 | 23 | private: 24 | /** The blueprint parent class is required for widget creation, if is not set in config, then USettingsWidget will be used. 25 | * @TODO Remove this property after USettingsWidget blueprint will be completely moved to code. */ 26 | UPROPERTY(Config) 27 | TSubclassOf SettingsWidgetClassInternal = nullptr; 28 | }; 29 | 30 | /** 31 | * Shows additional actions in the Context Menu. 32 | */ 33 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API FAssetTypeActions_SettingsWidget : public FAssetTypeActions_MyUserWidget 34 | { 35 | public: 36 | virtual ~FAssetTypeActions_SettingsWidget() override = default; 37 | 38 | virtual FText GetName() const override; 39 | virtual FColor GetTypeColor() const override; 40 | virtual uint32 GetCategories() override; 41 | }; 42 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Public/SettingTagCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "GameplayTagsEditorModule.h" 6 | 7 | /** 8 | * Is customized to show only selected in-game option. 9 | */ 10 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API FSettingTagCustomization : public FGameplayTagCustomizationPublic 11 | { 12 | public: 13 | /** Is used to load and unload the Property Editor Module. */ 14 | inline static const FName PropertyEditorModule = TEXT("PropertyEditor"); 15 | 16 | /** The name of class to be customized: SettingTag. */ 17 | static const FName PropertyClassName; 18 | 19 | /** Makes a new instance of this detail layout class for a specific detail view requesting it. */ 20 | static TSharedRef MakeInstance(); 21 | 22 | /** Creates customization for the Settings Tag. */ 23 | static void RegisterSettingsTagCustomization(); 24 | 25 | /** Removes customization for the Settings Tag. */ 26 | static void UnregisterSettingsTagCustomization(); 27 | }; 28 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Public/SettingsPickerCustomization.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "MyPropertyType/MyPropertyTypeCustomization.h" 6 | 7 | /** 8 | * Is customized to show only selected in-game option. 9 | */ 10 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API FSettingsPickerCustomization : public FMyPropertyTypeCustomization 11 | { 12 | public: 13 | /** The name of class to be customized: SettingsPicker */ 14 | static const FName PropertyClassName; 15 | 16 | /** The name of the settings data base struct: /Script/SettingsWidgetConstructor.SettingsDataBase */ 17 | static const FName SettingsDataBasePathName; 18 | 19 | /** The name of the Settings Primary struct: /Script/SettingsWidgetConstructor.SettingsPrimary */ 20 | static const FName SettingsPrimaryPathName; 21 | 22 | /** The name of the Function Picker struct: /Script/FunctionPicker.SWCFunctionPicker */ 23 | static const FName FunctionPickerPathName; 24 | 25 | /* --------------------------------------------------- 26 | * Public functions 27 | * --------------------------------------------------- */ 28 | 29 | /** Default constructor. */ 30 | FSettingsPickerCustomization(); 31 | 32 | /** Makes a new instance of this detail layout class for a specific detail view requesting it. */ 33 | static TSharedRef MakeInstance(); 34 | 35 | /** 36 | * Called when the header of the property (the row in the details panel where the property is shown) 37 | * If nothing is added to the row, the header is not displayed 38 | * @param PropertyHandle Handle to the property being customized 39 | * @param HeaderRow A row that widgets can be added to 40 | * @param CustomizationUtils Utilities for customization 41 | */ 42 | virtual void CustomizeHeader(TSharedRef PropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 43 | 44 | /** 45 | * Called when the children of the property should be customized or extra rows added. 46 | * @param PropertyHandle Handle to the property being customized 47 | * @param ChildBuilder A builder for adding children 48 | * @param CustomizationUtils Utilities for customization 49 | */ 50 | virtual void CustomizeChildren(TSharedRef PropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 51 | 52 | /** Creates customization for the Settings Picker. */ 53 | static void RegisterSettingsPickerCustomization(); 54 | 55 | /** Removes customization for the Settings Picker. */ 56 | static void UnregisterSettingsPickerCustomization(); 57 | 58 | protected: 59 | /* --------------------------------------------------- 60 | * Protected properties 61 | * --------------------------------------------------- */ 62 | 63 | /** One of chosen FSettingsDataBase property. */ 64 | FPropertyData ChosenSettingsDataInternal = FPropertyData::Empty; 65 | 66 | /** Contains property to by meta. 67 | * MetaName: SettingsFunctionContextTemplate, SettingsFunctionSetterTemplate, SettingsFunctionGetterTemplate. 68 | * SettingsFunctionProperty: FSettingsPicker::Owner, FSettingsPicker::Setter, FSettingsPicker::Getter, */ 69 | TMap SettingsFunctionProperties; 70 | 71 | /** Pointer to the FSettingsDataBase struct. */ 72 | TWeakObjectPtr SettingsDataBaseStructInternal = nullptr; 73 | 74 | /** Pointer to the SettingsPrimary struct. */ 75 | TWeakObjectPtr SettingsPrimaryStructInternal = nullptr; 76 | 77 | /** Pointer to the FunctionPicker struct. */ 78 | TWeakObjectPtr FunctionPickerStructInternal = nullptr; 79 | 80 | /* --------------------------------------------------- 81 | * Protected functions 82 | * --------------------------------------------------- */ 83 | 84 | /** Is called for each property on building its row. */ 85 | virtual void OnCustomizeChildren(IDetailChildrenBuilder& ChildBuilder, FPropertyData& PropertyData) override; 86 | 87 | /** Is called on adding the custom property. 88 | * @see FMyPropertyTypeCustomization::CustomPropertyNameInternal */ 89 | virtual void AddCustomPropertyRow(const FText& PropertyDisplayText, IDetailChildrenBuilder& ChildBuilder) override; 90 | 91 | /** Is called when any child property is changed. */ 92 | virtual void RefreshCustomProperty() override; 93 | 94 | /** Will reset the whole property content of last chosen settings type. */ 95 | void ClearPrevChosenProperty(); 96 | 97 | /** Copy meta from chosen option USTRUCT to each UPROPERTY of Settings Functions. 98 | * @see FMyPropertyTypeCustomization::CustomPropertyInternal 99 | * @see FSettingsPickerCustomization::SettingsFunctionProperties */ 100 | void CopyMetas(); 101 | }; 102 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/Public/SettingsWidgetConstructorEditorModule.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | //--- 7 | #include "AssetTypeCategories.h" 8 | #include "AssetTypeActions_Base.h" 9 | 10 | class SETTINGSWIDGETCONSTRUCTOREDITOR_API FSettingsWidgetConstructorEditorModule : public IModuleInterface 11 | { 12 | public: 13 | /** Category of this plugin in the 'Add' context menu. */ 14 | inline static EAssetTypeCategories::Type SettingsCategory = EAssetTypeCategories::None; 15 | 16 | /** 17 | * Called right after the module DLL has been loaded and the module object has been created. 18 | * Load dependent modules here, and they will be guaranteed to be available during ShutdownModule. 19 | */ 20 | virtual void StartupModule() override; 21 | 22 | /** 23 | * Called before the module is unloaded, right before the module object is destroyed. 24 | * During normal shutdown, this is called in reverse order that modules finish StartupModule(). 25 | * This means that, as long as a module references dependent modules in it's StartupModule(), it 26 | * can safely reference those dependencies in ShutdownModule() as well. 27 | */ 28 | virtual void ShutdownModule() override; 29 | 30 | #pragma region EditorExtensions 31 | /** Adds the asset to the context menu 32 | * @param InOutRegisteredAssets Input: the list of registered assets. Output: the list of registered assets + the new one. */ 33 | template 34 | static void RegisterAsset(TArray>& InOutRegisteredAssets); 35 | 36 | /** Removes all custom assets from context menu. */ 37 | static void UnregisterAssets(TArray>& RegisteredAssets); 38 | #pragma endregion EditorExtensions 39 | 40 | protected: 41 | /** Creates all customizations for custom properties. */ 42 | static void RegisterPropertyCustomizations(); 43 | 44 | /** Removes all custom property customizations. */ 45 | static void UnregisterPropertyCustomizations(); 46 | 47 | /** Adds to context menu custom assets to be created. */ 48 | void RegisterSettingAssets(); 49 | 50 | /** Adds the category of this plugin to the 'Add' context menu. */ 51 | static void RegisterSettingAssetsCategory(); 52 | 53 | /** Asset type actions */ 54 | TArray> RegisteredAssets; 55 | }; 56 | 57 | // Adds the asset to the context menu 58 | template 59 | void FSettingsWidgetConstructorEditorModule::RegisterAsset(TArray>& InOutRegisteredAssets) 60 | { 61 | TSharedPtr SettingsDataTableAction = MakeShared(); 62 | IAssetTools::Get().RegisterAssetTypeActions(SettingsDataTableAction.ToSharedRef()); 63 | InOutRegisteredAssets.Emplace(MoveTemp(SettingsDataTableAction)); 64 | } 65 | -------------------------------------------------------------------------------- /Source/SettingsWidgetConstructorEditor/SettingsWidgetConstructorEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Yevhenii Selivanov. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SettingsWidgetConstructorEditor : ModuleRules 6 | { 7 | public SettingsWidgetConstructorEditor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | CppStandard = CppStandardVersion.Latest; 11 | bEnableNonInlinedGenCppWarnings = true; 12 | 13 | PublicDependencyModuleNames.AddRange(new[] 14 | { 15 | "Core" 16 | , "AssetTools" // Created FAssetTypeActions_MyDataTable 17 | , "UnrealEd" // Created UMyDataTableFactory 18 | , "UMGEditor" // Created UMyUserWidgetFactory 19 | } 20 | ); 21 | 22 | PrivateDependencyModuleNames.AddRange(new[] 23 | { 24 | "CoreUObject", "Engine", "Slate", "SlateCore" // Core 25 | , "GameplayTagsEditor" // FGameplayTagCustomizationPublic 26 | , "Projects" // IPluginManager::Get() 27 | , "ToolWidgets" // SSearchableComboBox 28 | , "DataTableEditor", "DesktopPlatform", "EditorFramework", "ToolMenus" // Editor data table 29 | , "UMG", "Kismet", "KismetCompiler" // Editor user widget 30 | // My modules 31 | , "SettingsWidgetConstructor" // USettingsDataTable 32 | } 33 | ); 34 | } 35 | } 36 | --------------------------------------------------------------------------------