├── .gitignore ├── BuildPlugin.bat.template ├── Config └── FilterPlugin.ini ├── Images ├── Logo.png ├── UE4-ContextMenu.png ├── UE4-Details.png ├── UE4-Preview.png ├── UE4-Search.png ├── UE4-ViewOptions.png ├── UE5-ContextMenu.png ├── UE5-NomadHorizontal.png ├── UE5-Preview.png ├── UE5-Search.png ├── UE5-Subobjects.png ├── UE5-SubsystemSettings.png ├── UE5-ViewOptions.png └── UE5-WorldSelection.png ├── LICENSE ├── README.md ├── Resources └── Icon128.png ├── Source ├── SubsystemBrowser │ ├── Model │ │ ├── Category │ │ │ ├── SubsystemBrowserCategory_AudioEngine.cpp │ │ │ ├── SubsystemBrowserCategory_AudioEngine.h │ │ │ ├── SubsystemBrowserCategory_Editor.cpp │ │ │ ├── SubsystemBrowserCategory_Editor.h │ │ │ ├── SubsystemBrowserCategory_Engine.cpp │ │ │ ├── SubsystemBrowserCategory_Engine.h │ │ │ ├── SubsystemBrowserCategory_GameInstance.cpp │ │ │ ├── SubsystemBrowserCategory_GameInstance.h │ │ │ ├── SubsystemBrowserCategory_Player.cpp │ │ │ ├── SubsystemBrowserCategory_Player.h │ │ │ ├── SubsystemBrowserCategory_World.cpp │ │ │ └── SubsystemBrowserCategory_World.h │ │ ├── Column │ │ │ ├── SubsystemBrowserColumn_Config.cpp │ │ │ ├── SubsystemBrowserColumn_Config.h │ │ │ ├── SubsystemBrowserColumn_Module.cpp │ │ │ ├── SubsystemBrowserColumn_Module.h │ │ │ ├── SubsystemBrowserColumn_Name.cpp │ │ │ ├── SubsystemBrowserColumn_Name.h │ │ │ ├── SubsystemBrowserColumn_Plugin.cpp │ │ │ └── SubsystemBrowserColumn_Plugin.h │ │ ├── SubsystemBrowserCategory.cpp │ │ ├── SubsystemBrowserCategory.h │ │ ├── SubsystemBrowserColumn.cpp │ │ ├── SubsystemBrowserColumn.h │ │ ├── SubsystemBrowserDescriptor.cpp │ │ ├── SubsystemBrowserDescriptor.h │ │ ├── SubsystemBrowserModel.cpp │ │ ├── SubsystemBrowserModel.h │ │ └── SubsystemBrowserSorting.h │ ├── SubsystemBrowser.Build.cs │ ├── SubsystemBrowserFlags.h │ ├── SubsystemBrowserModule.cpp │ ├── SubsystemBrowserModule.h │ ├── SubsystemBrowserSettings.cpp │ ├── SubsystemBrowserSettings.h │ ├── SubsystemBrowserStyle.cpp │ ├── SubsystemBrowserStyle.h │ ├── SubsystemBrowserUtils.cpp │ ├── SubsystemBrowserUtils.h │ └── UI │ │ ├── SubsystemBrowserPanel.cpp │ │ ├── SubsystemBrowserPanel.h │ │ ├── SubsystemDetailsCustomizations.cpp │ │ ├── SubsystemDetailsCustomizations.h │ │ ├── SubsystemTableHeader.cpp │ │ ├── SubsystemTableHeader.h │ │ ├── SubsystemTableItem.cpp │ │ ├── SubsystemTableItem.h │ │ ├── SubsystemTableItemTooltip.cpp │ │ ├── SubsystemTableItemTooltip.h │ │ ├── SubsystemTreeWidget.cpp │ │ └── SubsystemTreeWidget.h ├── SubsystemBrowserTests │ ├── SubsystemBrowserTestSubsystem.cpp │ ├── SubsystemBrowserTestSubsystem.h │ ├── SubsystemBrowserTests.Build.cs │ ├── SubsystemBrowserTests.cpp │ ├── SubsystemBrowserTestsModule.cpp │ └── SubsystemBrowserTestsModule.h └── SubsystemSettingsEditor │ ├── SubsystemSettingsEditor.Build.cs │ ├── SubsystemSettingsEditorModule.cpp │ ├── SubsystemSettingsEditorModule.h │ ├── SubsystemSettingsManager.cpp │ ├── SubsystemSettingsManager.h │ └── UI │ ├── SubsystemSettingsWidget.cpp │ └── SubsystemSettingsWidget.h └── SubsystemBrowserPlugin.uplugin /.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 | 76 | # Misc 77 | Releases/* 78 | Marketplace/* 79 | 80 | # my omnibuild/package scripts 81 | /BuildScripts/*.bat 82 | /BuildScripts/*.php 83 | /BuildScripts/*.U* 84 | -------------------------------------------------------------------------------- /BuildPlugin.bat.template: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set PLUGIN_PATH="%cd%\SubsystemBrowserPlugin.uplugin" 4 | set RUNUAT_PATH="%EPIC_LIBRARY%\UE_%VERSION%\Engine\Build\BatchFiles\RunUAT.bat" 5 | set PACKAGE_PATH="%WORK_TEMP%\SBP_%VERSION%" 6 | 7 | set EXTRA_PARAMS= -StrictIncludes 8 | 9 | echo. 10 | echo Compiling for %VERSION% to %PACKAGE_PATH% 11 | echo. 12 | 13 | %RUNUAT_PATH% BuildPlugin -Plugin=%PLUGIN_PATH% -Package=%PACKAGE_PATH% -HostPlatforms=Win64 -NoTargetPlatforms %EXTRA_PARAMS% 14 | -------------------------------------------------------------------------------- /Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | 10 | /README.md 11 | -------------------------------------------------------------------------------- /Images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/Logo.png -------------------------------------------------------------------------------- /Images/UE4-ContextMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE4-ContextMenu.png -------------------------------------------------------------------------------- /Images/UE4-Details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE4-Details.png -------------------------------------------------------------------------------- /Images/UE4-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE4-Preview.png -------------------------------------------------------------------------------- /Images/UE4-Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE4-Search.png -------------------------------------------------------------------------------- /Images/UE4-ViewOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE4-ViewOptions.png -------------------------------------------------------------------------------- /Images/UE5-ContextMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-ContextMenu.png -------------------------------------------------------------------------------- /Images/UE5-NomadHorizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-NomadHorizontal.png -------------------------------------------------------------------------------- /Images/UE5-Preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-Preview.png -------------------------------------------------------------------------------- /Images/UE5-Search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-Search.png -------------------------------------------------------------------------------- /Images/UE5-Subobjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-Subobjects.png -------------------------------------------------------------------------------- /Images/UE5-SubsystemSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-SubsystemSettings.png -------------------------------------------------------------------------------- /Images/UE5-ViewOptions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-ViewOptions.png -------------------------------------------------------------------------------- /Images/UE5-WorldSelection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Images/UE5-WorldSelection.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 aquanox 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 | [![GitHub release](https://img.shields.io/github/release/aquanox/SubsystemBrowserPlugin.svg)](https://github.com/aquanox/SubsystemBrowserPlugin/releases) 2 | [![GitHub license](https://img.shields.io/github/license/aquanox/SubsystemBrowserPlugin)](https://github.com/aquanox/SubsystemBrowserPlugin/blob/main/LICENSE) 3 | [![GitHub forks](https://img.shields.io/github/forks/aquanox/SubsystemBrowserPlugin)](https://github.com/aquanox/SubsystemBrowserPlugin/network) 4 | [![GitHub stars](https://img.shields.io/github/stars/aquanox/SubsystemBrowserPlugin)](https://github.com/aquanox/SubsystemBrowserPlugin/stargazers) 5 | ![UE4](https://img.shields.io/badge/UE4-4.25+-lightgrey) 6 | ![UE5](https://img.shields.io/badge/UE5-5.0+-lightgrey) 7 | 8 | ## Subsystem Browser Plugin for Unreal Engine 9 | 10 | Subsystems provide an easy way to extend engine functionality and implement new features while avoiding the complexity of overriding engine classes or packing multiple independent features into a single class. 11 | Check [Programming Subsystems](https://dev.epicgames.com/documentation/en-us/unreal-engine/programming-subsystems-in-unreal-engine) article if you haven't tried them yet. 12 | 13 | With Subsystems being a great tool, Unreal Engine Editor does not have a default method to visualize them or allow changing their properties at runtime. 14 | 15 | Plugin provides a dedicated Subsystem Browser panel to display active subsystems with property editor. 16 | 17 | New Subsystem Settings panel provides an easy way to interact with config properties within Subsystem-derived classes without need to create a standalone UDeveloperSettings class. 18 | 19 | ## Key Features 20 | 21 | * List of all avaiable subsystem types with search bar and customizable filters 22 | * Engine Subsystems 23 | * Editor Subsystems 24 | * Game Instance Subsystems 25 | * World Subsystems 26 | * Local Player Subsystems 27 | * Display active subsystems in selected worlds 28 | * Display which module or plugin subsystems originate from 29 | * View and modify subsystem properties with Details View (including hidden properties) 30 | * Quick navigation to source file via "Open Source File" context menu action 31 | * Quick actions to work with ini config settings via "Export Config", "Export to Defaults" context menu actions 32 | * Browser panel state (chosen filters, category folders state) is saved to local editor settings 33 | * Support for custom categories: ![Customizing Categories](https://github.com/aquanox/SubsystemBrowserPlugin/wiki/Customizing-Categories) 34 | * Support for custom table colums: ![Customizing Columns](https://github.com/aquanox/SubsystemBrowserPlugin/wiki/Customizing-Columns) 35 | * Support for custom tooltips: ![Customizing Tooltips](https://github.com/aquanox/SubsystemBrowserPlugin/wiki/Customizing-Tooltips) 36 | * Various ![Metadata Specifiers](https://github.com/aquanox/SubsystemBrowserPlugin/wiki/Plugin-Metadata-Specifiers-Reference) for Subsystem classes 37 | * Subsystem Settings Panel to interact with config properties. 38 | 39 | ## Unreal Engine Versions 40 | 41 | Plugin compatible with 4.25-4.27, 5.0+, ue5-main and can be easily modified to work with other engine versions. 42 | 43 | ## Contributing 44 | 45 | Please report any issues with GitHub Issues page for this repository. 46 | 47 | If you want to suggest changes, improvements or updates to the plugin open new GitHub Issue requesting them or Pull Request. 48 | 49 | ## License 50 | 51 | SubsystemBrowserPlugin is available under the MIT license. See the LICENSE file for more info. 52 | 53 | ![](Images/UE4-Preview.png) 54 | ![](Images/UE4-Search.png) 55 | ![](Images/UE4-ContextMenu.png) 56 | ![](Images/UE4-ViewOptions.png) 57 | 58 | ![](Images/UE5-Preview.png) 59 | 60 | --- 61 | 62 | Special Thanks to Unreal Source Community Discord and #cpp 63 | -------------------------------------------------------------------------------- /Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aquanox/SubsystemBrowserPlugin/efe866ad2cfbdc21ac5aa55dfd9e77d9db6848a8/Resources/Icon128.png -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_AudioEngine.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_AudioEngine.h" 4 | 5 | #if UE_VERSION_NEWER_THAN(5, 1, 0) 6 | 7 | #include "AudioDevice.h" 8 | #include "AudioDeviceHandle.h" 9 | #include "Engine/World.h" 10 | #include "Subsystems/AudioEngineSubsystem.h" 11 | 12 | FSubsystemCategory_AudioEngine::FSubsystemCategory_AudioEngine() 13 | { 14 | Name = TEXT("AudioEngineSubsystemCategory"); 15 | SettingsName = TEXT("AudioEngine"); 16 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_AudioEngine", "Audio Engine Subsystems"); 17 | SortOrder = 150; 18 | } 19 | 20 | UClass* FSubsystemCategory_AudioEngine::GetSubsystemClass() const 21 | { 22 | return UAudioEngineSubsystem::StaticClass(); 23 | } 24 | 25 | void FSubsystemCategory_AudioEngine::Select(UWorld* InContext, TArray& OutData) const 26 | { 27 | if (IsValid(InContext)) 28 | { 29 | FAudioDeviceHandle AudioDeviceHandle = InContext->GetAudioDevice(); 30 | if (AudioDeviceHandle.IsValid()) 31 | { 32 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 33 | OutData.Append(AudioDeviceHandle->GetSubsystemArray()); 34 | #else 35 | OutData.Append(AudioDeviceHandle->GetSubsystemArrayCopy()); 36 | #endif 37 | } 38 | } 39 | } 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_AudioEngine.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "SubsystemBrowserFlags.h" 6 | #include "Model/SubsystemBrowserCategory.h" 7 | 8 | #if UE_VERSION_NEWER_THAN(5, 1, 0) 9 | 10 | /** 11 | * 12 | */ 13 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_AudioEngine : public FSubsystemCategory 14 | { 15 | FSubsystemCategory_AudioEngine(); 16 | virtual UClass* GetSubsystemClass() const override; 17 | virtual bool IsVisibleByDefault() const override { return false; } 18 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Editor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_Editor.h" 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | #include "Editor.h" 7 | #include "EditorSubsystem.h" 8 | 9 | FSubsystemCategory_Editor::FSubsystemCategory_Editor() 10 | { 11 | Name = TEXT("EditorSubsystemCategory"); 12 | SettingsName = TEXT("Editor"); 13 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_Editor", "Editor Subsystems"); 14 | SortOrder = 200; 15 | } 16 | 17 | UClass* FSubsystemCategory_Editor::GetSubsystemClass() const 18 | { 19 | return UEditorSubsystem::StaticClass(); 20 | } 21 | 22 | void FSubsystemCategory_Editor::Select(UWorld* InContext, TArray& OutData) const 23 | { 24 | if (GEditor) 25 | { 26 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 27 | return OutData.Append(GEditor->GetEditorSubsystemArray()); 28 | #else 29 | return OutData.Append(GEditor->GetEditorSubsystemArrayCopy()); 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Editor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserCategory.h" 6 | 7 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_Editor : public FSubsystemCategory 8 | { 9 | FSubsystemCategory_Editor(); 10 | virtual UClass* GetSubsystemClass() const override; 11 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Engine.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_Engine.h" 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | #include "Engine/Engine.h" 7 | #include "Subsystems/EngineSubsystem.h" 8 | 9 | FSubsystemCategory_Engine::FSubsystemCategory_Engine() 10 | { 11 | Name = TEXT("EngineSubsystemCategory"); 12 | SettingsName = TEXT("Engine"); 13 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_Engine", "Engine Subsystems"); 14 | SortOrder = 100; 15 | } 16 | 17 | UClass* FSubsystemCategory_Engine::GetSubsystemClass() const 18 | { 19 | return UEngineSubsystem::StaticClass(); 20 | } 21 | 22 | void FSubsystemCategory_Engine::Select(UWorld* InContext, TArray& OutData) const 23 | { 24 | if (GEngine) 25 | { 26 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 27 | return OutData.Append(GEngine->GetEngineSubsystemArray()); 28 | #else 29 | return OutData.Append(GEngine->GetEngineSubsystemArrayCopy()); 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Engine.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserCategory.h" 6 | 7 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_Engine : public FSubsystemCategory 8 | { 9 | FSubsystemCategory_Engine(); 10 | 11 | virtual UClass* GetSubsystemClass() const override; 12 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 13 | }; 14 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_GameInstance.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_GameInstance.h" 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | #include "Engine/GameInstance.h" 7 | #include "Engine/World.h" 8 | #include "Subsystems/GameInstanceSubsystem.h" 9 | 10 | FSubsystemCategory_GameInstance::FSubsystemCategory_GameInstance() 11 | { 12 | Name = TEXT("GameInstanceCategory"); 13 | SettingsName = TEXT("GameInstance"); 14 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_GameInstance", "Game Instance Subsystems"); 15 | SortOrder = 300; 16 | } 17 | 18 | UClass* FSubsystemCategory_GameInstance::GetSubsystemClass() const 19 | { 20 | return UGameInstanceSubsystem::StaticClass(); 21 | } 22 | 23 | void FSubsystemCategory_GameInstance::Select(UWorld* InContext, TArray& OutData) const 24 | { 25 | if (IsValid(InContext) && InContext->GetGameInstance()) 26 | { 27 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 28 | OutData.Append(InContext->GetGameInstance()->GetSubsystemArray()); 29 | #else 30 | OutData.Append(InContext->GetGameInstance()->GetSubsystemArrayCopy()); 31 | #endif 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_GameInstance.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserCategory.h" 6 | 7 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_GameInstance : public FSubsystemCategory 8 | { 9 | FSubsystemCategory_GameInstance(); 10 | virtual UClass* GetSubsystemClass() const override; 11 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Player.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_Player.h" 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | #include "Engine/GameInstance.h" 7 | #include "Subsystems/LocalPlayerSubsystem.h" 8 | #include "Engine/LocalPlayer.h" 9 | #include "Engine/World.h" 10 | 11 | FSubsystemCategory_Player::FSubsystemCategory_Player() 12 | { 13 | Name = TEXT("PlayerCategory"); 14 | SettingsName = TEXT("LocalPlayer"); 15 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_Player", "Player Subsystems"); 16 | SortOrder = 500; 17 | } 18 | 19 | UClass* FSubsystemCategory_Player::GetSubsystemClass() const 20 | { 21 | return ULocalPlayerSubsystem::StaticClass(); 22 | } 23 | 24 | void FSubsystemCategory_Player::Select(UWorld* InContext, TArray& OutData) const 25 | { 26 | if (IsValid(InContext) && InContext->GetGameInstance()) 27 | { 28 | for (ULocalPlayer* const LocalPlayer : InContext->GetGameInstance()->GetLocalPlayers()) 29 | { 30 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 31 | OutData.Append(LocalPlayer->GetSubsystemArray()); 32 | #else 33 | OutData.Append(LocalPlayer->GetSubsystemArrayCopy()); 34 | #endif 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_Player.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserCategory.h" 6 | 7 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_Player: public FSubsystemCategory 8 | { 9 | FSubsystemCategory_Player(); 10 | virtual UClass* GetSubsystemClass() const override; 11 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_World.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Category/SubsystemBrowserCategory_World.h" 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | #include "Engine/World.h" 7 | #include "Subsystems/WorldSubsystem.h" 8 | 9 | FSubsystemCategory_World::FSubsystemCategory_World() 10 | { 11 | Name = TEXT("WorldSubsystemCategory"); 12 | SettingsName = TEXT("World"); 13 | Label = NSLOCTEXT("SubsystemBrowser", "SubsystemBrowser_World", "World Subsystems"); 14 | SortOrder = 400; 15 | } 16 | 17 | UClass* FSubsystemCategory_World::GetSubsystemClass() const 18 | { 19 | return UWorldSubsystem::StaticClass(); 20 | } 21 | 22 | void FSubsystemCategory_World::Select(UWorld* InContext, TArray& OutData) const 23 | { 24 | if (IsValid(InContext)) 25 | { 26 | #if UE_VERSION_OLDER_THAN(5, 5, 0) 27 | OutData.Append(InContext->GetSubsystemArray()); 28 | #else 29 | OutData.Append(InContext->GetSubsystemArrayCopy()); 30 | #endif 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Category/SubsystemBrowserCategory_World.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserCategory.h" 6 | 7 | struct SUBSYSTEMBROWSER_API FSubsystemCategory_World : public FSubsystemCategory 8 | { 9 | FSubsystemCategory_World(); 10 | virtual UClass* GetSubsystemClass() const override; 11 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Config.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Column/SubsystemBrowserColumn_Config.h" 4 | 5 | #include "UI/SubsystemTableItem.h" 6 | 7 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 8 | 9 | FSubsystemDynamicColumn_Config::FSubsystemDynamicColumn_Config() 10 | { 11 | Name = TEXT("Config"); 12 | TableLabel = LOCTEXT("SubsystemBrowser_Column_Config", "Config"); 13 | ConfigLabel = LOCTEXT("SubsystemBrowser_Column_Config", "Config"); 14 | PreferredWidthRatio = 0.15f; 15 | } 16 | 17 | FText FSubsystemDynamicColumn_Config::ExtractText(TSharedRef Item) const 18 | { 19 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item->GetAsSubsystemDescriptor()) 20 | { 21 | return SubsystemItem->IsConfigExportable() ? FText::FromName(SubsystemItem->ConfigName) : FText::GetEmpty(); 22 | } 23 | 24 | return FText::GetEmpty(); 25 | } 26 | 27 | void FSubsystemDynamicColumn_Config::PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const 28 | { 29 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item.GetAsSubsystemDescriptor()) 30 | { 31 | if (SubsystemItem->IsConfigExportable()) 32 | { 33 | OutSearchStrings.Add(SubsystemItem->ConfigName.ToString()); 34 | } 35 | } 36 | } 37 | 38 | #undef LOCTEXT_NAMESPACE 39 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserColumn.h" 6 | 7 | /** 8 | * "Config" column implementation 9 | */ 10 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicColumn_Config : public FSubsystemDynamicTextColumn 11 | { 12 | using Super = FSubsystemDynamicTextColumn; 13 | 14 | FSubsystemDynamicColumn_Config(); 15 | 16 | virtual bool IsVisibleByDefault() const override { return false; } 17 | 18 | virtual FText ExtractText(TSharedRef Item) const override; 19 | virtual FText ExtractTooltipText(TSharedRef Item) const override { return FText::GetEmpty(); } 20 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const override; 21 | }; 22 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Module.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Column/SubsystemBrowserColumn_Module.h" 4 | 5 | #include "SubsystemBrowserSettings.h" 6 | #include "UI/SubsystemTableItem.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | FSubsystemDynamicColumn_Module::FSubsystemDynamicColumn_Module() 11 | { 12 | Name = TEXT("Module"); 13 | TableLabel = LOCTEXT("SubsystemBrowser_Column_Module", "Module"); 14 | ConfigLabel = LOCTEXT("SubsystemBrowser_Column_Module", "Module"); 15 | PreferredWidthRatio = 0.25f; 16 | } 17 | 18 | FText FSubsystemDynamicColumn_Module::ExtractText(TSharedRef Item) const 19 | { 20 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item->GetAsSubsystemDescriptor()) 21 | { 22 | return FText::FromString(SubsystemItem->ModuleName); 23 | } 24 | 25 | return FText::GetEmpty(); 26 | } 27 | 28 | FSlateColor FSubsystemDynamicColumn_Module::ExtractColor(TSharedRef Item) const 29 | { 30 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 31 | if (Settings->IsColoringEnabled() && !Item->IsStale() && !Item->IsSelected()) 32 | { 33 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item->GetAsSubsystemDescriptor()) 34 | { 35 | return USubsystemBrowserSettings::Get()->GetModuleColor(SubsystemItem->IsGameModule()); 36 | } 37 | } 38 | return Super::ExtractColor(Item); 39 | } 40 | 41 | void FSubsystemDynamicColumn_Module::PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const 42 | { 43 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item.GetAsSubsystemDescriptor()) 44 | { 45 | OutSearchStrings.Add(SubsystemItem->ModuleName); 46 | } 47 | } 48 | 49 | #undef LOCTEXT_NAMESPACE 50 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Module.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserColumn.h" 6 | 7 | /* 8 | * "Module" column implementation 9 | */ 10 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicColumn_Module : public FSubsystemDynamicTextColumn 11 | { 12 | using Super = FSubsystemDynamicTextColumn; 13 | 14 | FSubsystemDynamicColumn_Module(); 15 | 16 | virtual bool IsVisibleByDefault() const override { return true; } 17 | 18 | virtual FText ExtractText(TSharedRef Item) const override; 19 | virtual FSlateColor ExtractColor(TSharedRef Item) const override; 20 | virtual FText ExtractTooltipText(TSharedRef Item) const override { return FText::GetEmpty(); } 21 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Name.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Column/SubsystemBrowserColumn_Name.h" 4 | 5 | #include "SubsystemBrowserSettings.h" 6 | #include "SubsystemBrowserStyle.h" 7 | #include "UI/SubsystemTableItem.h" 8 | #include "Widgets/Images/SImage.h" 9 | #include "Widgets/Text/STextBlock.h" 10 | #include "Widgets/Views/SListView.h" 11 | 12 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 13 | 14 | FSubsystemDynamicColumn_Name::FSubsystemDynamicColumn_Name() 15 | { 16 | Name = TEXT("Name"); 17 | ConfigLabel = LOCTEXT("SubsystemBrowser_Column_Name", "Name"); 18 | TableLabel = LOCTEXT("SubsystemBrowser_Column_Name", "Name"); 19 | SortOrder = -1; 20 | PreferredWidthRatio = 0.6f; 21 | } 22 | 23 | TSharedPtr FSubsystemDynamicColumn_Name::GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const 24 | { 25 | const bool bHasIcon = Item->CanHaveChildren() && ExtractIcon(Item) != nullptr; 26 | 27 | return SNew(SHorizontalBox) 28 | +SHorizontalBox::Slot() 29 | .AutoWidth() 30 | [ 31 | SNew(SExpanderArrow, TableRow) 32 | ] 33 | 34 | +SHorizontalBox::Slot() 35 | .VAlign(VAlign_Center) 36 | .Padding(1, 0, 0, 0) 37 | .AutoWidth() 38 | [ 39 | SNew(SBox) 40 | .VAlign(VAlign_Center) 41 | .HeightOverride(22) 42 | .WidthOverride(bHasIcon ? 16.f : 7.f) 43 | [ 44 | SNew(SImage) 45 | .Image(this, &FSubsystemDynamicColumn_Name::ExtractIcon, Item) 46 | ] 47 | ] 48 | 49 | +SHorizontalBox::Slot() 50 | .VAlign(VAlign_Center) 51 | .Padding(4, 3, 0, 3) 52 | .AutoWidth() 53 | [ 54 | SNew(STextBlock) 55 | .Font(this, &FSubsystemDynamicColumn_Name::ExtractFont, Item) 56 | .ColorAndOpacity(this, &FSubsystemDynamicColumn_Name::ExtractColor, Item) 57 | .Text(this, &FSubsystemDynamicColumn_Name::ExtractText, Item) 58 | .ToolTipText(this, &FSubsystemDynamicColumn_Name::ExtractTooltipText, Item) 59 | .HighlightText(TableRow->HighlightText) 60 | ]; 61 | } 62 | 63 | const FSlateBrush* FSubsystemDynamicColumn_Name::ExtractIcon(TSharedRef Item) const 64 | { 65 | switch (Item->GetType()) 66 | { 67 | case ISubsystemTreeItem::EItemType::Category: 68 | { 69 | return FStyleHelper::GetBrush(Item->bExpanded 70 | ? FSubsystemBrowserStyle::FolderOpenName 71 | : FSubsystemBrowserStyle::FolderClosedName); 72 | } 73 | case ISubsystemTreeItem::EItemType::Subsystem: 74 | { 75 | //if (Item->CanHaveChildren() && Item->GetNumChildren() > 0) 76 | //{ 77 | // return FStyleHelper::GetBrush(Item->bExpanded 78 | // ? FSubsystemBrowserStyle::FolderOpenName 79 | // : FSubsystemBrowserStyle::FolderClosedName); 80 | //} 81 | return Item->GetIcon(); 82 | } 83 | case ISubsystemTreeItem::EItemType::Object: 84 | default: 85 | return Item->GetIcon(); 86 | } 87 | } 88 | 89 | FText FSubsystemDynamicColumn_Name::ExtractText(TSharedRef Item) const 90 | { 91 | FFormatNamedArguments Args; 92 | Args.Add(TEXT("DisplayText"), Item->GetDisplayName()); 93 | Args.Add(TEXT("Stale"), (Item->IsStale() ? LOCTEXT("SubsystemItem_Stale", " (Stale)") : FText::GetEmpty())); 94 | return FText::Format(LOCTEXT("SubsystemItem_Name", "{DisplayText}{Stale}"), Args); 95 | } 96 | 97 | FSlateColor FSubsystemDynamicColumn_Name::ExtractColor(TSharedRef Item) const 98 | { 99 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 100 | if (Settings->IsColoringEnabled() && !Item->IsStale() && !Item->IsSelected()) 101 | { 102 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item->GetAsSubsystemDescriptor()) 103 | { 104 | if (SubsystemItem->UserColor.IsSet()) 105 | { 106 | return SubsystemItem->UserColor.GetValue(); 107 | } 108 | } 109 | } 110 | return Super::ExtractColor(Item); 111 | } 112 | 113 | void FSubsystemDynamicColumn_Name::PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const 114 | { 115 | OutSearchStrings.Add(Item.GetDisplayName().ToString()); 116 | } 117 | 118 | 119 | #undef LOCTEXT_NAMESPACE 120 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Name.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserColumn.h" 6 | 7 | /** 8 | * "Name" column implementation 9 | */ 10 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicColumn_Name : public FSubsystemDynamicTextColumn 11 | { 12 | using Super = FSubsystemDynamicTextColumn; 13 | 14 | FSubsystemDynamicColumn_Name(); 15 | 16 | virtual bool IsVisibleByDefault() const override { return true; } 17 | virtual TSharedPtr GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const override; 18 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const override; 19 | virtual const FSlateBrush* ExtractIcon(TSharedRef Item) const; 20 | virtual FText ExtractText(TSharedRef Item) const override; 21 | virtual FText ExtractTooltipText(TSharedRef Item) const override { return FText::GetEmpty(); } 22 | virtual FSlateColor ExtractColor(TSharedRef Item) const override; 23 | }; 24 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Plugin.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/Column/SubsystemBrowserColumn_Plugin.h" 4 | 5 | #include "UI/SubsystemTableItem.h" 6 | #include "UI/SubsystemTableItemTooltip.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | FSubsystemDynamicColumn_Plugin::FSubsystemDynamicColumn_Plugin() 11 | { 12 | Name = TEXT("Plugin"); 13 | TableLabel = LOCTEXT("SubsystemBrowser_Column_Plugin", "Plugin"); 14 | ConfigLabel = LOCTEXT("SubsystemBrowser_Column_Plugin", "Plugin"); 15 | PreferredWidthRatio = 0.25f; 16 | } 17 | 18 | FText FSubsystemDynamicColumn_Plugin::ExtractText(TSharedRef Item) const 19 | { 20 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item->GetAsSubsystemDescriptor()) 21 | { 22 | return FText::FromString(SubsystemItem->PluginDisplayName); 23 | } 24 | 25 | return FText::GetEmpty(); 26 | } 27 | 28 | void FSubsystemDynamicColumn_Plugin::PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const 29 | { 30 | if (const FSubsystemTreeSubsystemItem* SubsystemItem = Item.GetAsSubsystemDescriptor()) 31 | { 32 | OutSearchStrings.Add(SubsystemItem->PluginDisplayName); 33 | } 34 | } 35 | 36 | #undef LOCTEXT_NAMESPACE 37 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/Column/SubsystemBrowserColumn_Plugin.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserColumn.h" 6 | 7 | /* 8 | * "Plugin" column implementation 9 | */ 10 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicColumn_Plugin : public FSubsystemDynamicTextColumn 11 | { 12 | using Super = FSubsystemDynamicTextColumn; 13 | 14 | FSubsystemDynamicColumn_Plugin(); 15 | 16 | virtual bool IsVisibleByDefault() const override { return false; } 17 | 18 | virtual FText ExtractText(TSharedRef Item) const override; 19 | virtual FText ExtractTooltipText(TSharedRef Item) const override { return FText::GetEmpty(); } 20 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const override; 21 | }; 22 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserCategory.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/SubsystemBrowserCategory.h" 4 | 5 | #include "SubsystemBrowserFlags.h" 6 | #include "Subsystems/Subsystem.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | FSubsystemCategory::FSubsystemCategory(const FName& Name, const FText& Label, int32 SortOrder) 11 | : Name(Name), SettingsName(Name), Label(Label), SortOrder(SortOrder) 12 | { 13 | } 14 | 15 | UClass* FSubsystemCategory::GetSubsystemClass() const 16 | { 17 | return USubsystem::StaticClass(); 18 | } 19 | 20 | FSimpleSubsystemCategory::FSimpleSubsystemCategory(const FName& Name, const FText& Label, const FEnumSubsystemsDelegate& Selector, int32 SortOrder) 21 | : FSubsystemCategory(Name, Label, SortOrder), Selector(Selector) 22 | { 23 | ensure(Selector.IsBound()); 24 | } 25 | 26 | void FSimpleSubsystemCategory::Select(UWorld* InContext, TArray& OutData) const 27 | { 28 | Selector.Execute(InContext, OutData); 29 | } 30 | 31 | #undef LOCTEXT_NAMESPACE 32 | 33 | #if ENABLE_SUBSYSTEM_BROWSER_EXAMPLES 34 | 35 | // 2. Call this in your editor modules StartupModule to register a new category 36 | void RegisterCategoryExample() 37 | { 38 | // Get a reference to Subsystem Browser module instance or load it 39 | FSubsystemBrowserModule& Module = FModuleManager::LoadModuleChecked(TEXT("SubsystemBrowser")); 40 | // Construct category 41 | auto SampleCategory = MakeShared(); 42 | SampleCategory->Name = TEXT("Sample"); 43 | SampleCategory->Label = INVTEXT("Sample Subsystems"); 44 | SampleCategory->SortOrder = 50; 45 | SampleCategory->Selector = FEnumSubsystemsDelegate::CreateLambda([](UWorld* InContext, TArray& OutData) 46 | { 47 | // Fill Data 48 | }); 49 | // Register category in module 50 | Module.RegisterCategory(SampleCategory); 51 | } 52 | 53 | #endif 54 | 55 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserCategory.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Templates/SharedPointer.h" 6 | #include "Engine/World.h" 7 | 8 | /** 9 | * Subsystem data provider delegate 10 | */ 11 | DECLARE_DELEGATE_TwoParams(FEnumSubsystemsDelegate, UWorld* /* InContext */, TArray& /* OutData */); 12 | 13 | /** 14 | * Represents a type of subsystem that will be shown in browser 15 | */ 16 | struct SUBSYSTEMBROWSER_API FSubsystemCategory : public TSharedFromThis 17 | { 18 | /* Category config identifier */ 19 | FName Name; 20 | /* Category display in Settings */ 21 | FName SettingsName; 22 | /* Category display title */ 23 | FText Label; 24 | /* Sort weight for the category (with 0 being topmost, 1000 bottom last) */ 25 | int32 SortOrder = 0; 26 | 27 | FSubsystemCategory() = default; 28 | FSubsystemCategory(const FName& Name, const FText& Label, int32 SortOrder); 29 | virtual ~FSubsystemCategory() = default; 30 | 31 | const FName& GetID() const { return Name; } 32 | const FName& GetSettingsName() const { return SettingsName; } 33 | const FText& GetDisplayName() const { return Label; } 34 | int32 GetSortOrder() const { return SortOrder; } 35 | 36 | virtual bool IsVisibleByDefault() const { return true; } 37 | virtual bool IsVisibleInSettings() const { return true; } 38 | 39 | /* Get primary subsystem category class */ 40 | virtual UClass* GetSubsystemClass() const; 41 | 42 | /* Select subsystems for this category */ 43 | virtual void Select(UWorld* InContext, TArray& OutData) const = 0; 44 | }; 45 | 46 | using SubsystemCategoryPtr = TSharedPtr; 47 | 48 | /** 49 | * Basic implementation of category that take in delegate selector 50 | */ 51 | struct SUBSYSTEMBROWSER_API FSimpleSubsystemCategory : public FSubsystemCategory 52 | { 53 | /* Data supplier function */ 54 | FEnumSubsystemsDelegate Selector; 55 | 56 | FSimpleSubsystemCategory() = default; 57 | FSimpleSubsystemCategory(const FName& Name, const FText& Label, const FEnumSubsystemsDelegate& Selector, int32 SortOrder); 58 | 59 | virtual void Select(UWorld* InContext, TArray& OutData) const override; 60 | }; 61 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserColumn.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/SubsystemBrowserColumn.h" 4 | 5 | #include "SubsystemBrowserFlags.h" 6 | #include "SubsystemBrowserModule.h" 7 | #include "SubsystemBrowserSettings.h" 8 | #include "Model/SubsystemBrowserDescriptor.h" 9 | #include "SubsystemBrowserSorting.h" 10 | #include "UI/SubsystemTableItem.h" 11 | #include "SubsystemBrowserStyle.h" 12 | #include "Widgets/Text/STextBlock.h" 13 | #include "Widgets/Views/SListView.h" 14 | 15 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 16 | 17 | FSubsystemDynamicColumn::FSubsystemDynamicColumn() 18 | { 19 | } 20 | 21 | SHeaderRow::FColumn::FArguments FSubsystemDynamicColumn::GenerateHeaderColumnWidget() const 22 | { 23 | return SHeaderRow::Column( Name ) 24 | .FillWidth(PreferredWidthRatio) 25 | .HeaderContent() 26 | [ 27 | SNew(SBox) 28 | .MinDesiredHeight(24) 29 | [ 30 | SNew(SHorizontalBox) 31 | +SHorizontalBox::Slot() 32 | .VAlign(VAlign_Center) 33 | [ 34 | SNew(STextBlock) 35 | .Text(TableLabel) 36 | ] 37 | ] 38 | ]; 39 | } 40 | 41 | bool FSubsystemDynamicColumn::IsValidColumnName(FName InName) 42 | { 43 | static const FName ColumnID_Name("Name"); 44 | return !InName.IsNone() && InName != ColumnID_Name; 45 | } 46 | 47 | TSharedPtr FSubsystemDynamicTextColumn::GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const 48 | { 49 | return SNew(SHorizontalBox) 50 | + SHorizontalBox::Slot() 51 | .VAlign(VAlign_Center) 52 | .Padding(1, 0, 0, 0) 53 | .AutoWidth() 54 | [ 55 | SNew(STextBlock) 56 | .Font(this, &FSubsystemDynamicTextColumn::ExtractFont, Item) 57 | .ColorAndOpacity(this, &FSubsystemDynamicTextColumn::ExtractColor, Item) 58 | .Text(this, &FSubsystemDynamicTextColumn::ExtractText, Item) 59 | .ToolTipText(this, &FSubsystemDynamicTextColumn::ExtractTooltipText, Item) 60 | .HighlightText(TableRow->HighlightText) 61 | ]; 62 | } 63 | 64 | FText FSubsystemDynamicTextColumn::ExtractTooltipText(TSharedRef Item) const 65 | { 66 | return ExtractText(Item); 67 | } 68 | 69 | FSlateColor FSubsystemDynamicTextColumn::ExtractColor(TSharedRef Item) const 70 | { 71 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 72 | if (Item->IsStale()) 73 | { 74 | return Settings->GetStaleColor(); 75 | } 76 | if (Item->IsSelected()) 77 | { 78 | return Settings->GetSelectedColor(); 79 | } 80 | return FSlateColor::UseForeground(); 81 | } 82 | 83 | FSlateFontInfo FSubsystemDynamicTextColumn::ExtractFont(TSharedRef Item) const 84 | { 85 | return FStyleHelper::GetFontStyle("WorldBrowser.LabelFont"); 86 | } 87 | 88 | void FSubsystemDynamicTextColumn::SortItems(TArray& RootItems, const EColumnSortMode::Type SortMode) const 89 | { 90 | SubsystemBrowser::FSortHelper() 91 | .Primary([this](TSharedPtr Item) { return ExtractText(Item.ToSharedRef()).ToString(); }, SortMode) 92 | .Sort(RootItems); 93 | } 94 | 95 | #undef LOCTEXT_NAMESPACE 96 | 97 | #if ENABLE_SUBSYSTEM_BROWSER_EXAMPLES && !UE_VERSION_OLDER_THAN(4, 27, 0) 98 | 99 | // 1. Create a new struct inheriting FSubsystemDynamicColumn 100 | 101 | struct FSubsystemDynamicColumn_Tick : public FSubsystemDynamicColumn 102 | { 103 | FSubsystemDynamicColumn_Tick() 104 | { 105 | // Configure name and header title 106 | Name = TEXT("IsTickable"); 107 | TableLabel = INVTEXT(""); 108 | ConfigLabel = INVTEXT("Tickable"); 109 | PreferredWidthRatio = 0.05f; 110 | } 111 | 112 | virtual TSharedPtr GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const override 113 | { 114 | // Build a widget to represent value 115 | return SNew(SHorizontalBox) 116 | + SHorizontalBox::Slot() 117 | .HAlign(HAlign_Center) 118 | [ 119 | SNew(SImage) 120 | .ColorAndOpacity(FSlateColor::UseForeground()) 121 | .Image(FStyleHelper::GetBrush(TEXT("GraphEditor.Conduit_16x"))) 122 | .DesiredSizeOverride(FVector2d{16,16}) 123 | .Visibility(this, &FSubsystemDynamicColumn_Tick::ExtractIsTickable, Item) 124 | ]; 125 | } 126 | private: 127 | EVisibility ExtractIsTickable(TSharedRef Item) const 128 | { 129 | auto* Subsystem = Item->GetAsSubsystemDescriptor(); 130 | return Subsystem && Subsystem->Class.IsValid() && Subsystem->Class->IsChildOf(UTickableWorldSubsystem::StaticClass()) 131 | ? EVisibility::Visible : EVisibility::Hidden; 132 | } 133 | }; 134 | 135 | // 2. Call this during module initialization 136 | void RegisterCustomColumns() 137 | { 138 | // Get a reference to Subsystem Browser module instance or load it 139 | FSubsystemBrowserModule& Module = FModuleManager::LoadModuleChecked(TEXT("SubsystemBrowser")); 140 | // Construct and register new column 141 | Module.RegisterDynamicColumn(MakeShared()); 142 | } 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserColumn.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserDescriptor.h" 6 | #include "Widgets/Views/SHeaderRow.h" 7 | 8 | /* Represents a configurable column */ 9 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicColumn : public TSharedFromThis 10 | { 11 | /* Column config identifier */ 12 | FName Name; 13 | /* Column display name for table header */ 14 | FText TableLabel; 15 | /* Column display name for menu */ 16 | FText ConfigLabel; 17 | /* Column preferred width ratio (from 0 to 1) */ 18 | float PreferredWidthRatio = 0.1f; 19 | /* Column sort order */ 20 | int32 SortOrder = 0; 21 | 22 | FSubsystemDynamicColumn(); 23 | virtual ~FSubsystemDynamicColumn() = default; 24 | 25 | const FName& GetID() const { return Name; } 26 | int32 GetSortOrder() const { return SortOrder; } 27 | 28 | /** 29 | * Generate visual representation of column in header row 30 | */ 31 | virtual SHeaderRow::FColumn::FArguments GenerateHeaderColumnWidget() const; 32 | 33 | /** 34 | * Generate visual representation of column in table row 35 | */ 36 | virtual TSharedPtr GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const = 0; 37 | 38 | /** 39 | * Gather searchable strings for column 40 | */ 41 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const {} 42 | 43 | /** 44 | * Does this column support sorting? WIP 45 | */ 46 | virtual bool SupportsSorting() const { return false; } 47 | 48 | /** 49 | * Perform sorting of table items. WIP 50 | */ 51 | virtual void SortItems(TArray& RootItems, const EColumnSortMode::Type SortMode) const {} 52 | 53 | /** 54 | * Test if custom column name is valid (not None or permanent column) 55 | */ 56 | static bool IsValidColumnName(FName InName); 57 | 58 | /** 59 | * Get default column visibility state 60 | */ 61 | virtual bool IsVisibleByDefault() const { return false; } 62 | }; 63 | 64 | using SubsystemColumnPtr = TSharedPtr; 65 | 66 | struct SubsystemColumnSorter 67 | { 68 | bool operator()(const SubsystemColumnPtr& A, const SubsystemColumnPtr& B) const 69 | { 70 | return A->SortOrder < B->SortOrder; 71 | } 72 | }; 73 | 74 | /** 75 | * A prefab type for simple columns that have text representation 76 | */ 77 | struct SUBSYSTEMBROWSER_API FSubsystemDynamicTextColumn : public FSubsystemDynamicColumn 78 | { 79 | using Super = FSubsystemDynamicTextColumn; 80 | 81 | virtual TSharedPtr GenerateColumnWidget(TSharedRef Item, TSharedRef TableRow) const override; 82 | virtual void PopulateSearchStrings(const ISubsystemTreeItem& Item, TArray& OutSearchStrings) const override { } 83 | protected: 84 | /* get text to display for specified item */ 85 | virtual FText ExtractText(TSharedRef Item) const = 0; 86 | /* get tooltip text to display for specified item */ 87 | virtual FText ExtractTooltipText(TSharedRef Item) const; 88 | /* get color and opacity of text for specified item */ 89 | virtual FSlateColor ExtractColor(TSharedRef Item) const; 90 | /* get font of text for specified item */ 91 | virtual FSlateFontInfo ExtractFont(TSharedRef Item) const; 92 | /* text columns support sorting by default */ 93 | virtual bool SupportsSorting() const override { return true; } 94 | virtual void SortItems(TArray& RootItems, const EColumnSortMode::Type SortMode) const override; 95 | }; 96 | 97 | // FSubsystemDynamicImageColumn 98 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserDescriptor.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/SubsystemBrowserDescriptor.h" 4 | 5 | #include "SourceCodeNavigation.h" 6 | #include "SubsystemBrowserSettings.h" 7 | #include "SubsystemBrowserModule.h" 8 | #include "SubsystemBrowserUtils.h" 9 | #include "ToolMenu.h" 10 | #include "Interfaces/IPluginManager.h" 11 | #include "Model/SubsystemBrowserModel.h" 12 | #include "UI/SubsystemTableItemTooltip.h" 13 | #include "SubsystemBrowserStyle.h" 14 | #include "Misc/PackageName.h" 15 | #include "UObject/Package.h" 16 | #include "Widgets/Views/SListView.h" 17 | 18 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 19 | 20 | FSubsystemTreeCategoryItem::FSubsystemTreeCategoryItem(TSharedRef InModel, TSharedRef InCategory) 21 | : Data(InCategory) 22 | { 23 | Model = InModel; 24 | } 25 | 26 | void FSubsystemTreeCategoryItem::GenerateTooltip(FSubsystemTableItemTooltipBuilder& TooltipBuilder) const 27 | { 28 | TArray Subsystems; 29 | Model->GetAllSubsystemsInCategory(SharedThis(this), Subsystems); 30 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_NumSub", "Num Subsystems"), FText::AsNumber(Subsystems.Num())); 31 | } 32 | 33 | FSubsystemTreeObjectItem::FSubsystemTreeObjectItem(TSharedRef InModel, TSharedPtr InParent, UObject* Instance) 34 | { 35 | Model = InModel; 36 | Parent = InParent; 37 | 38 | check(Instance); 39 | Object = Instance; 40 | ObjectClass = Instance->GetClass(); 41 | } 42 | 43 | bool FSubsystemTreeObjectItem::IsSelected() const 44 | { 45 | return Model.IsValid() && Model->IsItemSelected(SharedThis(this)); 46 | } 47 | 48 | bool FSubsystemTreeObjectItem::IsStale() const 49 | { 50 | return Object.IsStale() || ObjectClass.IsStale(); 51 | } 52 | 53 | void FSubsystemTreeObjectItem::GenerateTooltip(class FSubsystemTableItemTooltipBuilder& TooltipBuilder) const 54 | { 55 | FName ClassName = ObjectClass->GetFName(); 56 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Class", "Class"), FText::FromName(ClassName)); 57 | 58 | FString ModuleName = FPackageName::GetShortName(ObjectClass->GetOuterUPackage()->GetName()); 59 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Module", "Module"), FText::FromString(ModuleName)); 60 | } 61 | 62 | FSubsystemTreeItemID FSubsystemTreeObjectItem::GetID() const 63 | { 64 | if (Object.IsValid()) 65 | { 66 | return Object->GetFName(); 67 | } 68 | return FSubsystemTreeItemID(); 69 | } 70 | 71 | FText FSubsystemTreeObjectItem::GetDisplayName() const 72 | { 73 | if (Object.IsValid()) 74 | { 75 | return FText::FromName(Object->GetFName()); 76 | } 77 | return FText::GetEmpty(); 78 | } 79 | 80 | FSubsystemTreeSubsystemItem::FSubsystemTreeSubsystemItem(TSharedRef InModel, TSharedPtr InParent, UObject* Instance) 81 | : FSubsystemTreeObjectItem(InModel, InParent, Instance) 82 | { 83 | UClass* const InClass = Instance->GetClass(); 84 | 85 | DisplayName = InClass->GetDisplayNameText(); 86 | ClassName = InClass->GetFName(); 87 | 88 | Package = InClass->GetOuterUPackage()->GetName(); 89 | if (!FSubsystemBrowserUtils::GetModuleDetailsForClass(InClass, ModuleName, bIsGameModuleClass)) 90 | { 91 | ModuleName = FPackageName::GetShortName(Package); 92 | bIsGameModuleClass = false; 93 | } 94 | 95 | ScriptName = FString::Printf(TEXT("/Script/%s.%s"), *ModuleName, *ClassName.ToString()); 96 | 97 | if (InClass->HasAnyClassFlags(CLASS_Config) && !InClass->ClassConfigName.IsNone()) 98 | { 99 | bConfigExportable = true; 100 | bIsDefaultConfig = InClass->HasAnyClassFlags(CLASS_DefaultConfig); 101 | ConfigName = InClass->ClassConfigName; 102 | } 103 | 104 | FSubsystemBrowserUtils::CollectSourceFiles(InClass, SourceFilePaths); 105 | 106 | OwnerName = FSubsystemBrowserUtils::GetSubsystemOwnerName(Instance); 107 | 108 | if (FSubsystemBrowserUtils::GetPluginDetailsForClass(InClass, PluginName, PluginDisplayName)) 109 | { 110 | bIsPluginClass = true; 111 | } 112 | 113 | PropertyStats = FSubsystemBrowserUtils::GetClassFieldStats(InClass); 114 | 115 | TOptional UserColorValue = FSubsystemBrowserUtils::GetMetadataHierarchical(InClass, FSubsystemBrowserUserMeta::MD_SBColor); 116 | if (UserColorValue.IsSet()) 117 | { 118 | FLinearColor Value(ForceInit); 119 | if (Value.InitFromString(*UserColorValue)) 120 | { 121 | UserColor = Value; 122 | } 123 | } 124 | 125 | TOptional UserTooltipValue = FSubsystemBrowserUtils::GetSmartMetaValue(Instance, FSubsystemBrowserUserMeta::MD_SBTooltip, true); 126 | if (UserTooltipValue.IsSet()) 127 | { 128 | UserTooltip = UserTooltipValue; 129 | } 130 | 131 | bHasSubobjectPicker = FSubsystemBrowserUtils::GetMetadataHierarchical(InClass, FSubsystemBrowserUserMeta::MD_SBGetSubobjects).IsSet() 132 | || FSubsystemBrowserUtils::GetMetadataHierarchical(InClass, FSubsystemBrowserUserMeta::MD_SBAutoGetSubobjects).IsSet(); 133 | } 134 | 135 | FText FSubsystemTreeSubsystemItem::GetDisplayName() const 136 | { 137 | return DisplayName; 138 | } 139 | 140 | bool FSubsystemTreeSubsystemItem::HasViewableElements() const 141 | { 142 | if (PropertyStats.NumProperties && (PropertyStats.NumEditable || PropertyStats.NumVisible)) 143 | return true; 144 | if (PropertyStats.NumCallable) 145 | return true; 146 | return false; 147 | } 148 | 149 | const FSlateBrush* FSubsystemTreeSubsystemItem::GetIcon() const 150 | { 151 | return nullptr; 152 | } 153 | 154 | void FSubsystemTreeSubsystemItem::GenerateTooltip(FSubsystemTableItemTooltipBuilder& TooltipBuilder) const 155 | { 156 | if (TooltipBuilder.IsInAdvancedMode()) 157 | { 158 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_ScriptName", "Script Name"), FText::FromString(ScriptName)); 159 | } 160 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Class", "Class"), FText::FromName(ClassName)); 161 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Module", "Module"), FText::FromString(ModuleName)); 162 | if (IsPluginModule()) 163 | { 164 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Plugin", "Plugin"), FText::FromString(PluginDisplayName)); 165 | } 166 | if (IsConfigExportable()) 167 | { 168 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Config", "Config"), FText::FromName(ConfigName)); 169 | } 170 | if (!OwnerName.IsEmpty()) 171 | { 172 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Owner", "Owned by"), FText::FromString(OwnerName)); 173 | } 174 | 175 | if (TooltipBuilder.IsInAdvancedMode()) 176 | { 177 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Props", "Num Properties"), FText::AsNumber(PropertyStats.NumProperties)); 178 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_PropsEditable", "Num Editable Properties"), FText::AsNumber(PropertyStats.NumEditable)); 179 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_PropsConfig", "Num Config Properties"), FText::AsNumber(PropertyStats.NumConfig)); 180 | TooltipBuilder.AddPrimary(LOCTEXT("SubsystemTooltipItem_Funcs", "Num Callables"), FText::AsNumber(PropertyStats.NumCallable)); 181 | } 182 | 183 | if (UserTooltip.IsSet()) 184 | { 185 | TooltipBuilder.SetUserTooltip(FText::FromString(UserTooltip.GetValue())); 186 | } 187 | } 188 | 189 | void FSubsystemTreeSubsystemItem::GenerateContextMenu(UToolMenu* MenuBuilder) const 190 | { 191 | TWeakPtr Self = SharedThis(this); 192 | 193 | { 194 | FToolMenuSection& Section = MenuBuilder->AddSection("SubsystemContextActions", LOCTEXT("SubsystemContextActions", "Common")); 195 | Section.AddMenuEntry("OpenSourceFile", 196 | LOCTEXT("OpenSourceFile", "Open Source File"), 197 | FText::GetEmpty(), 198 | FStyleHelper::GetSlateIcon("SystemWideCommands.FindInContentBrowser"), 199 | FUIAction( 200 | FExecuteAction::CreateLambda([Self]() 201 | { 202 | if (Self.IsValid()) 203 | { 204 | UObject* ViewedObject = Self.Pin()->GetObjectForDetails(); 205 | if (!ViewedObject || !FSourceCodeNavigation::CanNavigateToClass(ViewedObject->GetClass())) 206 | { 207 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("OpenSourceFile_Failed", "Failed to open source file."), SNotificationItem::CS_Fail); 208 | } 209 | else 210 | { 211 | FSourceCodeNavigation::NavigateToClass(ViewedObject->GetClass()); 212 | } 213 | } 214 | }) 215 | ) 216 | ); 217 | } 218 | 219 | { 220 | FToolMenuSection& Section = MenuBuilder->AddSection("SubsystemReferenceActions", LOCTEXT("SubsystemReferenceActions", "References")); 221 | Section.AddMenuEntry("CopyClassName", 222 | LOCTEXT("CopyClassName", "Copy Class Name"), 223 | FText::GetEmpty(), 224 | FSlateIcon(), 225 | FUIAction( 226 | FExecuteAction::CreateLambda([Self]() 227 | { 228 | if (Self.IsValid()) 229 | { 230 | FSubsystemBrowserUtils::SetClipboardText(FString::Printf(TEXT("U%s"), *Self.Pin()->ClassName.ToString())); 231 | } 232 | }) 233 | ) 234 | ); 235 | Section.AddMenuEntry("CopyPackageName", 236 | LOCTEXT("CopyPackageName", "Copy Module Name"), 237 | FText::GetEmpty(), 238 | FSlateIcon(), 239 | FUIAction( 240 | FExecuteAction::CreateLambda([Self]() 241 | { 242 | if (Self.IsValid()) 243 | { 244 | FSubsystemBrowserUtils::SetClipboardText(Self.Pin()->ModuleName); 245 | } 246 | }) 247 | ) 248 | ); 249 | Section.AddMenuEntry("CopyScriptName", 250 | LOCTEXT("CopyScriptName", "Copy Script Name"), 251 | FText::GetEmpty(), 252 | FSlateIcon(), 253 | FUIAction( 254 | FExecuteAction::CreateLambda([Self]() 255 | { 256 | if (Self.IsValid()) 257 | { 258 | FSubsystemBrowserUtils::SetClipboardText(Self.Pin()->ScriptName); 259 | } 260 | }) 261 | ) 262 | ); 263 | Section.AddMenuEntry("CopyFilePath", 264 | LOCTEXT("CopyFilePath", "Copy File Path"), 265 | FText::GetEmpty(), 266 | FSlateIcon(), 267 | FUIAction( 268 | FExecuteAction::CreateLambda([Self]() 269 | { 270 | if (Self.IsValid()) 271 | { 272 | const TArray& FilePaths = Self.Pin()->SourceFilePaths; 273 | 274 | FString ClipboardText; 275 | if (FilePaths.Num() > 0) 276 | { 277 | const FString* FoundHeader = FilePaths.FindByPredicate([](const FString& S) 278 | { 279 | FString Extension = FPaths::GetExtension(S); 280 | return Extension == TEXT("h") || Extension == TEXT("hpp"); 281 | }); 282 | 283 | if (!FoundHeader) FoundHeader = &FilePaths[0]; 284 | 285 | ClipboardText = FPaths::ConvertRelativePathToFull(*FoundHeader); 286 | } 287 | 288 | FSubsystemBrowserUtils::SetClipboardText(ClipboardText); 289 | } 290 | }) 291 | ) 292 | ); 293 | } 294 | 295 | if (IsConfigExportable()) 296 | { 297 | FToolMenuSection& Section = MenuBuilder->AddSection("SubsystemConfigActions", LOCTEXT("SubsystemConfigActions", "Config")); 298 | 299 | Section.AddMenuEntry("ExportToDefaults", 300 | LOCTEXT("ExportToDefaults", "Export Default Config"), 301 | LOCTEXT("ExportToDefaultTooltip", "Export current values to DefaultConfig (only if class has DefaultConfig specifier)"), 302 | FSlateIcon(), 303 | FUIAction( 304 | FExecuteAction::CreateLambda([Self]() 305 | { 306 | if (Self.IsValid()) 307 | { 308 | if (FSubsystemBrowserUtils::TryUpdateDefaultConfigFile(Self.Pin()->GetObjectForDetails())) 309 | { 310 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SubsystemBrowserDefaultsUpdate_Success", "Successfully updated defaults"), SNotificationItem::CS_Success); 311 | } 312 | else 313 | { 314 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SubsystemBrowserDefaultsUpdate_Failed", "Failed to update defaults"), SNotificationItem::CS_Fail); 315 | } 316 | } 317 | }), 318 | FCanExecuteAction::CreateSP(this, &FSubsystemTreeSubsystemItem::IsDefaultConfig) 319 | ) 320 | ); 321 | Section.AddMenuEntry("ExportModified", 322 | LOCTEXT("ExportModified", "Export Modified Properties"), 323 | LOCTEXT("ExportModifiedTooltip", "Export modified properties as an INI section and store it in clipboard"), 324 | FSlateIcon(), 325 | FUIAction( 326 | FExecuteAction::CreateLambda([Self]() 327 | { 328 | if (Self.IsValid()) 329 | { 330 | FString ClipboardText = FSubsystemBrowserUtils::GenerateConfigExport(Self.Pin().Get(), true); 331 | FSubsystemBrowserUtils::SetClipboardText(ClipboardText); 332 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SubsystemBrowserClipboardCopy_Success", "Copied to clipboard"), SNotificationItem::CS_Success); 333 | } 334 | }), 335 | FCanExecuteAction::CreateSP(this, &FSubsystemTreeSubsystemItem::IsConfigExportable) 336 | ) 337 | ); 338 | Section.AddMenuEntry("ExportAll", 339 | LOCTEXT("ExportAll", "Export All Properties"), 340 | LOCTEXT("ExportAllTooltip", "Export all config properties as an INI section and store it in clipboard"), 341 | FSlateIcon(), 342 | FUIAction( 343 | FExecuteAction::CreateLambda([Self]() 344 | { 345 | if (Self.IsValid()) 346 | { 347 | FString ClipboardText = FSubsystemBrowserUtils::GenerateConfigExport(Self.Pin().Get(), false); 348 | FSubsystemBrowserUtils::SetClipboardText(ClipboardText); 349 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SubsystemBrowserClipboardCopy_Success", "Copied to clipboard"), SNotificationItem::CS_Success); 350 | } 351 | }), 352 | FCanExecuteAction::CreateSP(this, &FSubsystemTreeSubsystemItem::IsConfigExportable) 353 | ) 354 | ); 355 | } 356 | } 357 | 358 | bool FSubsystemTreeSubsystemItem::CanHaveChildren() const 359 | { 360 | return USubsystemBrowserSettings::Get()->ShouldShowSubobjbects() && bHasSubobjectPicker; 361 | } 362 | 363 | #undef LOCTEXT_NAMESPACE 364 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserDescriptor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "SubsystemBrowserUtils.h" 6 | #include "Model/SubsystemBrowserCategory.h" 7 | #include "Misc/TextFilter.h" 8 | #include "Misc/Optional.h" 9 | #include "Templates/SharedPointer.h" 10 | 11 | class FSubsystemModel; 12 | struct ISubsystemTreeItem; 13 | struct FSubsystemTreeSubsystemItem; 14 | struct FSubsystemTreeObjectItem; 15 | struct FSubsystemTreeCategoryItem; 16 | 17 | using FSubsystemTreeItemID = FName; 18 | using SubsystemTreeItemPtr = TSharedPtr; 19 | using SubsystemTreeItemConstPtr = TSharedPtr; 20 | 21 | 22 | /** 23 | * Node base class for Subsystem TreeView 24 | */ 25 | struct SUBSYSTEMBROWSER_API ISubsystemTreeItem : public TSharedFromThis 26 | { 27 | enum class EItemType 28 | { 29 | Category, 30 | Object, 31 | Subsystem 32 | }; 33 | 34 | ISubsystemTreeItem() = default; 35 | virtual ~ISubsystemTreeItem() = default; 36 | 37 | virtual EItemType GetType() const = 0; 38 | virtual FSubsystemTreeItemID GetID() const = 0; 39 | virtual int32 GetSortOrder() const { return 0; } 40 | 41 | TSharedPtr GetModel() const { return Model; } 42 | SubsystemTreeItemPtr GetParent() const { return Parent; } 43 | 44 | virtual bool CanHaveChildren() const { return false; } 45 | virtual const TArray& GetChildren() const { return Children; } 46 | virtual int32 GetNumChildren() const { return Children.Num(); } 47 | virtual void RemoveAllChildren() { Children.Empty(); } 48 | virtual bool IsSelected() const { return false; } 49 | 50 | virtual UObject* GetObjectForDetails() const { return nullptr; } 51 | virtual bool IsStale() const { return false; } 52 | virtual bool IsGameModule() const { return false; } 53 | virtual bool IsPluginModule() const { return false; } 54 | virtual bool HasViewableElements() const { return false; } 55 | 56 | virtual FText GetDisplayName() const = 0; 57 | 58 | virtual const FSubsystemTreeObjectItem* GetAsObjectDescriptor() const { return nullptr; } 59 | virtual const FSubsystemTreeSubsystemItem* GetAsSubsystemDescriptor() const { return nullptr; } 60 | virtual const FSubsystemTreeCategoryItem* GetAsCategoryDescriptor() const { return nullptr; } 61 | 62 | virtual const FSlateBrush* GetIcon() const { return nullptr; } 63 | virtual void GenerateTooltip(class FSubsystemTableItemTooltipBuilder& TooltipBuilder) const {} 64 | virtual void GenerateContextMenu(class UToolMenu* MenuBuilder) const { } 65 | public: 66 | bool bExpanded = true; 67 | bool bVisible = true; 68 | bool bNeedsRefresh = true; 69 | bool bChildrenRequireSort = false; 70 | 71 | TSharedPtr Model; 72 | mutable SubsystemTreeItemPtr Parent; 73 | mutable TArray Children; 74 | }; 75 | 76 | /** 77 | * Node representing a Subsystem Category in TreeView 78 | */ 79 | struct SUBSYSTEMBROWSER_API FSubsystemTreeCategoryItem : public ISubsystemTreeItem 80 | { 81 | FSubsystemTreeCategoryItem() = default; 82 | FSubsystemTreeCategoryItem(TSharedRef InModel, TSharedRef InCategory); 83 | 84 | virtual EItemType GetType() const override { return EItemType::Category; } 85 | virtual FSubsystemTreeItemID GetID() const override { return Data->Name; } 86 | virtual int32 GetSortOrder() const override { return Data->SortOrder; } 87 | virtual FText GetDisplayName() const override { return Data->Label; } 88 | virtual bool CanHaveChildren() const override { return true; } 89 | virtual void GenerateTooltip(class FSubsystemTableItemTooltipBuilder& TooltipBuilder) const override; 90 | const FSubsystemCategory& GetData() const { check(Data.IsValid()); return *Data; } 91 | 92 | virtual const FSubsystemTreeCategoryItem* GetAsCategoryDescriptor() const override { return this; } 93 | public: 94 | TSharedPtr Data; 95 | }; 96 | 97 | /** 98 | * Node representing generic viewable object 99 | */ 100 | struct SUBSYSTEMBROWSER_API FSubsystemTreeObjectItem : public ISubsystemTreeItem 101 | { 102 | FSubsystemTreeObjectItem() = default; 103 | FSubsystemTreeObjectItem(TSharedRef InModel, TSharedPtr InParent, UObject* Instance); 104 | 105 | virtual EItemType GetType() const override { return EItemType::Object; } 106 | virtual FSubsystemTreeItemID GetID() const override; 107 | virtual FText GetDisplayName() const override; 108 | virtual UObject* GetObjectForDetails() const override { return Object.Get(); } 109 | virtual bool IsSelected() const override; 110 | virtual bool IsStale() const override; 111 | 112 | virtual const FSubsystemTreeObjectItem* GetAsObjectDescriptor() const override { return this; } 113 | 114 | //virtual const FSlateBrush* GetIcon() const override; 115 | virtual void GenerateTooltip(class FSubsystemTableItemTooltipBuilder& TooltipBuilder) const override; 116 | public: 117 | // 118 | TWeakObjectPtr Object; 119 | // 120 | TWeakObjectPtr ObjectClass; 121 | }; 122 | 123 | /** 124 | * Node representing a Subsystem in TreeView 125 | */ 126 | struct SUBSYSTEMBROWSER_API FSubsystemTreeSubsystemItem : public FSubsystemTreeObjectItem 127 | { 128 | FSubsystemTreeSubsystemItem() = default; 129 | FSubsystemTreeSubsystemItem(TSharedRef InModel, TSharedPtr InParent, UObject* Instance); 130 | 131 | virtual EItemType GetType() const override { return EItemType::Subsystem; } 132 | virtual FSubsystemTreeItemID GetID() const override { return ClassName; } 133 | 134 | virtual FText GetDisplayName() const override; 135 | 136 | bool IsConfigExportable() const { return bConfigExportable; } 137 | bool IsDefaultConfig() const { return bIsDefaultConfig; } 138 | virtual bool IsGameModule() const override { return bIsGameModuleClass; } 139 | virtual bool IsPluginModule() const override { return bIsPluginClass; } 140 | virtual bool HasViewableElements() const override; 141 | 142 | virtual const FSlateBrush* GetIcon() const override; 143 | virtual void GenerateTooltip(class FSubsystemTableItemTooltipBuilder& TooltipBuilder) const override; 144 | virtual void GenerateContextMenu(class UToolMenu* MenuBuilder) const override; 145 | 146 | virtual bool CanHaveChildren() const override; 147 | 148 | virtual const FSubsystemTreeSubsystemItem* GetAsSubsystemDescriptor() const override { return this; } 149 | 150 | public: 151 | // Friendly display name (Class Name) 152 | FText DisplayName; 153 | // Subsystem class name (ClassName) 154 | FName ClassName; 155 | // Subsystem package name (/Script/ModuleName) 156 | FString Package; 157 | // Full package name (/Script/ModuleName.ClassName) 158 | FString ScriptName; 159 | // Short module name (ModuleName) 160 | FString ModuleName; 161 | // Config category name (Class specifier value of UCLASS) 162 | FName ConfigName; 163 | 164 | // Owning object name for LocalPlayerSS and similar 165 | FString OwnerName; 166 | 167 | // List of source locations associated with subsystem class 168 | TArray SourceFilePaths; 169 | 170 | // Detected plugin name that this subsystem is part of 171 | FString PluginName; 172 | // Friendly name of plugin 173 | FString PluginDisplayName; 174 | 175 | // Optional user color override 176 | TOptional UserColor; 177 | // Optional user extra tooltip text 178 | TOptional UserTooltip; 179 | 180 | using FClassPropertyCounts = FSubsystemBrowserUtils::FClassFieldStats; 181 | FClassPropertyCounts PropertyStats; 182 | 183 | bool bConfigExportable = false; 184 | bool bIsDefaultConfig = false; 185 | bool bIsGameModuleClass = false; 186 | bool bIsPluginClass = false; 187 | 188 | bool bHasSubobjectPicker = false; 189 | //TArray> SubobjectsToDisplay; 190 | }; 191 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserModel.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "Model/SubsystemBrowserModel.h" 4 | 5 | #include "SubsystemBrowserModule.h" 6 | #include "SubsystemBrowserSettings.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | SubsystemCategoryFilter::SubsystemCategoryFilter() 11 | { 12 | // load initial state from config 13 | USubsystemBrowserSettings::Get()->LoadCategoryStates(FilterState); 14 | } 15 | 16 | bool SubsystemCategoryFilter::PassesFilter(const ISubsystemTreeItem& InItem) const 17 | { 18 | return IsCategoryVisible(InItem.GetID()); 19 | } 20 | 21 | void SubsystemCategoryFilter::ShowCategory(FSubsystemTreeItemID InCategory) 22 | { 23 | FilterState.Add(InCategory, true); 24 | USubsystemBrowserSettings::Get()->SetCategoryState(InCategory, true); 25 | OnChangedInternal.Broadcast(); 26 | } 27 | 28 | void SubsystemCategoryFilter::HideCategory(FSubsystemTreeItemID InCategory) 29 | { 30 | FilterState.Add(InCategory, false); 31 | USubsystemBrowserSettings::Get()->SetCategoryState(InCategory, false); 32 | OnChangedInternal.Broadcast(); 33 | } 34 | 35 | bool SubsystemCategoryFilter::IsCategoryVisible(FSubsystemTreeItemID InCategory) const 36 | { 37 | return !FilterState.Contains(InCategory) ? true : FilterState.FindChecked(InCategory); 38 | } 39 | 40 | FSubsystemModel::FSubsystemModel() 41 | { 42 | FSubsystemBrowserModule::AddPermanentColumns(PermanentColumns); 43 | } 44 | 45 | TWeakObjectPtr FSubsystemModel::GetCurrentWorld() const 46 | { 47 | return CurrentWorld; 48 | } 49 | 50 | void FSubsystemModel::SetCurrentWorld(TWeakObjectPtr InWorld) 51 | { 52 | UE_LOG(LogSubsystemBrowser, Log, TEXT("World Switch %s => %s"), *GetNameSafe(CurrentWorld.Get()), *GetNameSafe(InWorld.Get())); 53 | 54 | CurrentWorld = InWorld; 55 | 56 | EmptyModel(); 57 | 58 | PopulateCategories(); 59 | PopulateSubsystems(); 60 | } 61 | 62 | bool FSubsystemModel::IsSubsystemFilterActive() const 63 | { 64 | return SubsystemTextFilter.IsValid() && SubsystemTextFilter->HasText(); 65 | } 66 | 67 | int32 FSubsystemModel::GetNumCategories() const 68 | { 69 | return AllCategories.Num(); 70 | } 71 | 72 | const TArray& FSubsystemModel::GetAllCategories() const 73 | { 74 | return AllCategories; 75 | } 76 | 77 | void FSubsystemModel::GetFilteredCategories(TArray& OutCategories) 78 | { 79 | OutCategories.Empty(); 80 | 81 | for (const SubsystemTreeItemPtr& Item : GetAllCategories()) 82 | { 83 | check(Item->GetAsCategoryDescriptor()); 84 | if (CategoryFilter.IsValid() && !CategoryFilter->PassesFilter(*Item)) 85 | { 86 | continue; 87 | } 88 | 89 | if (USubsystemBrowserSettings::Get()->ShouldHideEmptyCategories() 90 | && !GetNumSubsystemsFromCategory(Item)) 91 | { 92 | continue; 93 | } 94 | 95 | OutCategories.Add(Item); 96 | } 97 | } 98 | 99 | const TArray& FSubsystemModel::GetAllSubsystems() const 100 | { 101 | return AllSubsystems; 102 | } 103 | 104 | void FSubsystemModel::GetAllSubsystemsInCategory(SubsystemTreeItemConstPtr Category, TArray& OutChildren) 105 | { 106 | check(Category->GetAsCategoryDescriptor()); 107 | 108 | OutChildren.Empty(); 109 | if (AllSubsystemsByCategory.Contains(Category->GetID())) 110 | { 111 | for (const SubsystemTreeItemPtr& Item : AllSubsystemsByCategory.FindChecked(Category->GetID())) 112 | { 113 | OutChildren.Add(Item); 114 | } 115 | } 116 | } 117 | 118 | void FSubsystemModel::GetFilteredSubsystems(SubsystemTreeItemConstPtr Category, TArray& OutChildren) 119 | { 120 | const FSubsystemTreeCategoryItem* AsCategory = Category->GetAsCategoryDescriptor(); 121 | check(AsCategory); 122 | 123 | OutChildren.Empty(); 124 | 125 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 126 | 127 | if (AllSubsystemsByCategory.Contains(AsCategory->GetID())) 128 | { 129 | for (const SubsystemTreeItemPtr& Item : AllSubsystemsByCategory.FindChecked(AsCategory->GetID())) 130 | { 131 | if (Settings->ShouldShowOnlyGame() && !Item->IsGameModule()) 132 | continue; 133 | if (Settings->ShouldShowOnlyPlugins() && !Item->IsPluginModule()) 134 | continue; 135 | if (Settings->ShouldShowOnlyViewable() && !Item->HasViewableElements()) 136 | continue; 137 | 138 | if (!SubsystemTextFilter.IsValid() || SubsystemTextFilter->PassesFilter(*Item)) 139 | { 140 | OutChildren.Add(Item); 141 | } 142 | } 143 | } 144 | } 145 | 146 | void FSubsystemModel::GetSubsystemSubobjects(SubsystemTreeItemConstPtr Subsystem, TArray& OutChildren) 147 | { 148 | const FSubsystemTreeSubsystemItem* AsSubsystem = Subsystem->GetAsSubsystemDescriptor(); 149 | check(AsSubsystem); 150 | const FSubsystemTreeCategoryItem* AsCategory = AsSubsystem->GetParent()->GetAsCategoryDescriptor(); 151 | check(AsCategory); 152 | 153 | OutChildren.Empty(); 154 | 155 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 156 | if (!Settings->ShouldShowSubobjbects()) 157 | return; 158 | 159 | TArray Result; 160 | FSubsystemBrowserUtils::DefaultSelectSubsystemSubobjects(AsSubsystem->GetObjectForDetails(), Result); 161 | 162 | for (UObject* Object : Result) 163 | { 164 | auto Item = MakeShared(SharedThis(this), ConstCastSharedPtr(Subsystem), Object); 165 | 166 | OutChildren.Emplace(MoveTemp(Item)); 167 | } 168 | } 169 | 170 | int32 FSubsystemModel::GetNumSubsystemsFromCategory(SubsystemTreeItemConstPtr Category) 171 | { 172 | TArray Subsystems; 173 | GetFilteredSubsystems(Category, Subsystems); 174 | return Subsystems.Num(); 175 | } 176 | 177 | int32 FSubsystemModel::GetNumSubsystemsFromVisibleCategories() 178 | { 179 | int32 Count = 0; 180 | 181 | TArray VisibleCategories; 182 | GetFilteredCategories(VisibleCategories); 183 | 184 | TArray Subsystems; 185 | 186 | for (const SubsystemTreeItemPtr& Category : VisibleCategories) 187 | { 188 | GetAllSubsystemsInCategory(Category, Subsystems); 189 | 190 | Count += Subsystems.Num(); 191 | } 192 | 193 | return Count; 194 | } 195 | 196 | int32 FSubsystemModel::GetNumDynamicColumns() const 197 | { 198 | return FSubsystemBrowserModule::Get().GetDynamicColumns().Num(); 199 | } 200 | 201 | bool FSubsystemModel::ShouldShowColumn(SubsystemColumnPtr Column) const 202 | { 203 | if (PermanentColumns.Contains(Column)) 204 | return true; 205 | 206 | return USubsystemBrowserSettings::Get()->GetTableColumnState(Column->Name); 207 | } 208 | 209 | bool FSubsystemModel::IsItemSelected(TSharedRef Item) 210 | { 211 | return LastSelectedItem == Item; 212 | } 213 | 214 | void FSubsystemModel::NotifySelected(TSharedPtr Item) 215 | { 216 | if (Item.IsValid()) 217 | { 218 | LastSelectedItem = Item; 219 | } 220 | 221 | OnSelectionChanged.Broadcast(Item); 222 | } 223 | 224 | TArray FSubsystemModel::GetSelectedTableColumns() const 225 | { 226 | const USubsystemBrowserSettings* Settings = USubsystemBrowserSettings::Get(); 227 | 228 | TArray Result; 229 | Result.Append(PermanentColumns); 230 | 231 | for (const SubsystemColumnPtr& Column : FSubsystemBrowserModule::Get().GetDynamicColumns()) 232 | { 233 | if (Settings->GetTableColumnState(Column->Name)) 234 | { 235 | Result.Add(Column); 236 | } 237 | } 238 | 239 | Result.StableSort(SubsystemColumnSorter()); 240 | return Result; 241 | } 242 | 243 | TArray FSubsystemModel::GetDynamicTableColumns() const 244 | { 245 | TArray Result; 246 | Result.Append(FSubsystemBrowserModule::Get().GetDynamicColumns()); 247 | Result.StableSort(SubsystemColumnSorter()); 248 | return Result; 249 | } 250 | 251 | SubsystemColumnPtr FSubsystemModel::FindTableColumn(const FName& ColumnName) const 252 | { 253 | TArray> Result; 254 | Result.Append(PermanentColumns); 255 | Result.Append(FSubsystemBrowserModule::Get().GetDynamicColumns()); 256 | 257 | for (const SubsystemColumnPtr& Column : Result) 258 | { 259 | if (Column->Name == ColumnName) 260 | { 261 | return Column; 262 | } 263 | } 264 | return nullptr; 265 | } 266 | 267 | void FSubsystemModel::EmptyModel() 268 | { 269 | for (const SubsystemTreeItemPtr& Category : AllCategories) 270 | { 271 | Category->RemoveAllChildren(); 272 | } 273 | AllCategories.Empty(); 274 | 275 | AllSubsystems.Empty(); 276 | AllSubsystemsByCategory.Empty(); 277 | 278 | LastSelectedItem.Reset(); 279 | } 280 | 281 | void FSubsystemModel::PopulateCategories() 282 | { 283 | FSubsystemBrowserModule& BrowserModule = FSubsystemBrowserModule::Get(); 284 | for (auto& SubsystemCategory : BrowserModule.GetCategories()) 285 | { 286 | auto Category = MakeShared(SharedThis(this), SubsystemCategory.ToSharedRef()); 287 | 288 | AllCategories.Add(MoveTemp(Category)); 289 | } 290 | 291 | // sort categories after populating them 292 | AllCategories.StableSort(SubsystemCategorySorter()); 293 | } 294 | 295 | void FSubsystemModel::PopulateSubsystems() 296 | { 297 | check(!AllSubsystems.Num()); 298 | check(!AllSubsystemsByCategory.Num()); 299 | 300 | UWorld* const LocalWorld = CurrentWorld.Get(); 301 | 302 | for (const auto & Category : AllCategories) 303 | { 304 | const FSubsystemTreeCategoryItem* AsCategory = Category->GetAsCategoryDescriptor(); 305 | 306 | TArray Result; 307 | AsCategory->Data->Select(LocalWorld, Result); 308 | for (UObject* Impl : Result) 309 | { 310 | auto Descriptor = MakeShared(SharedThis(this), Category, Impl); 311 | 312 | AllSubsystems.Add(Descriptor); 313 | AllSubsystemsByCategory.FindOrAdd(AsCategory->GetID()).Add(Descriptor); 314 | } 315 | } 316 | } 317 | 318 | #undef LOCTEXT_NAMESPACE 319 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserModel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Model/SubsystemBrowserDescriptor.h" 6 | #include "Model/SubsystemBrowserColumn.h" 7 | #include "Misc/TextFilter.h" 8 | 9 | /* Subsystem text filter */ 10 | class SubsystemTextFilter : public TTextFilter 11 | { 12 | using Super = TTextFilter; 13 | public: 14 | SubsystemTextFilter(const FItemToStringArray& InTransformDelegate) : Super(InTransformDelegate) { } 15 | 16 | bool HasText() const { return !GetRawFilterText().IsEmpty(); } 17 | }; 18 | 19 | /* Subsystem category filter */ 20 | class SubsystemCategoryFilter : public IFilter 21 | { 22 | public: 23 | SubsystemCategoryFilter(); 24 | 25 | virtual FChangedEvent& OnChanged() override { return OnChangedInternal; } 26 | virtual bool PassesFilter(const ISubsystemTreeItem& InItem) const override; 27 | void ShowCategory(FSubsystemTreeItemID InCategory); 28 | void HideCategory(FSubsystemTreeItemID InCategory); 29 | bool IsCategoryVisible(FSubsystemTreeItemID InCategory) const; 30 | private: 31 | TMap FilterState; 32 | FChangedEvent OnChangedInternal; 33 | }; 34 | 35 | struct SubsystemCategorySorter 36 | { 37 | bool operator()(const SubsystemTreeItemPtr& A, const SubsystemTreeItemPtr& B) const 38 | { 39 | return A->GetSortOrder() < B->GetSortOrder(); 40 | } 41 | }; 42 | 43 | /* Subsystem list data model */ 44 | class FSubsystemModel : public TSharedFromThis 45 | { 46 | public: 47 | FSubsystemModel(); 48 | 49 | TWeakObjectPtr GetCurrentWorld() const; 50 | void SetCurrentWorld(TWeakObjectPtr InWorld); 51 | 52 | bool IsSubsystemFilterActive() const; 53 | 54 | int32 GetNumCategories() const; 55 | const TArray& GetAllCategories() const; 56 | void GetFilteredCategories(TArray& OutCategories); 57 | 58 | const TArray& GetAllSubsystems() const; 59 | void GetAllSubsystemsInCategory(SubsystemTreeItemConstPtr Category, TArray& OutChildren); 60 | 61 | void GetFilteredSubsystems(SubsystemTreeItemConstPtr Category, TArray& OutChildren); 62 | 63 | void GetSubsystemSubobjects(SubsystemTreeItemConstPtr Subsystem, TArray& OutChildren); 64 | 65 | /* get total number of subsystems in category */ 66 | int32 GetNumSubsystemsFromCategory(SubsystemTreeItemConstPtr Category); 67 | /* get total number of subsystems in visible categories */ 68 | int32 GetNumSubsystemsFromVisibleCategories(); 69 | 70 | /* find a permanent or dynamic column by its name */ 71 | SubsystemColumnPtr FindTableColumn(const FName& ColumnName) const; 72 | /* returns all visible permanent and dynamic columns in sorted order */ 73 | TArray GetSelectedTableColumns() const; 74 | 75 | /* returns all dynamic columns in sorted order */ 76 | TArray GetDynamicTableColumns() const; 77 | /* return a total number of dynamic columns registered */ 78 | int32 GetNumDynamicColumns() const; 79 | /* check if dynamic column is enabled by settings */ 80 | bool ShouldShowColumn(SubsystemColumnPtr Column) const; 81 | 82 | bool IsItemSelected(TSharedRef Item); 83 | 84 | DECLARE_MULTICAST_DELEGATE_OneParam(FOnItemSelectionChange, TSharedPtr /* Item */); 85 | /* delegate that is triggered when tree selection changed */ 86 | FOnItemSelectionChange OnSelectionChanged; 87 | 88 | void NotifySelected(TSharedPtr Item); 89 | 90 | DECLARE_MULTICAST_DELEGATE_OneParam(FOnItemDataChanged, TSharedRef /* Item */); 91 | /* delegate that is triggered when one of subsystems in this model is changed and needs possible update */ 92 | FOnItemDataChanged OnDataChanged; 93 | 94 | private: 95 | void EmptyModel(); 96 | void PopulateCategories(); 97 | void PopulateSubsystems(); 98 | 99 | /* Global list of all categories */ 100 | TArray AllCategories; 101 | /* Global list of all subsystems */ 102 | TArray AllSubsystems; 103 | /* Global list of all subsystems by category */ 104 | TMap> AllSubsystemsByCategory; 105 | /* List of permanent columns */ 106 | TArray PermanentColumns; 107 | 108 | /* Pointer to currently browsing world */ 109 | TWeakObjectPtr CurrentWorld; 110 | /* Weak pointer to last selected item */ 111 | TWeakPtr LastSelectedItem; 112 | public: 113 | TSharedPtr CategoryFilter; 114 | TSharedPtr SubsystemTextFilter; 115 | }; 116 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/Model/SubsystemBrowserSorting.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | /** 6 | * Customized SceneOutliner sorting helper 7 | */ 8 | namespace SubsystemBrowser 9 | { 10 | 11 | template 12 | struct FSortHelper : FNoncopyable 13 | { 14 | typedef TFunction FPrimaryFunction; 15 | typedef TFunction FSecondaryFunction; 16 | 17 | FSortHelper() = default; 18 | 19 | /** Sort primarily by the specified function and mode. Beware the function is a reference, so must be valid for the lifetime of this instance. */ 20 | FSortHelper& Primary(FPrimaryFunction&& Function, EColumnSortMode::Type SortMode) 21 | { 22 | PrimarySortMode = SortMode; 23 | PrimaryFunction = MoveTemp(Function); 24 | return *this; 25 | } 26 | 27 | /** Sort secondarily by the specified function and mode. Beware the function is a reference, so must be valid for the lifetime of this instance. */ 28 | FSortHelper& Secondary(FSecondaryFunction&& Function, EColumnSortMode::Type SortMode) 29 | { 30 | SecondarySortMode = SortMode; 31 | SecondaryFunction = MoveTemp(Function); 32 | return *this; 33 | } 34 | 35 | /** Sort the specified array using the current sort settings */ 36 | void Sort(TArray& Array) 37 | { 38 | TArray SortData; 39 | const auto End = Array.Num(); 40 | for (int32 Index = 0; Index < End; ++Index) 41 | { 42 | const auto& Element = Array[Index]; 43 | 44 | PrimaryKeyType PrimaryKey = PrimaryFunction(Element); 45 | 46 | SecondaryKeyType SecondaryKey; 47 | if (SecondarySortMode != EColumnSortMode::None) 48 | { 49 | SecondaryKey = SecondaryFunction(Element); 50 | } 51 | 52 | SortData.Emplace(Index, MoveTemp(PrimaryKey), MoveTemp(SecondaryKey)); 53 | } 54 | 55 | SortData.Sort([&](const FSortPayload& One, const FSortPayload& Two) 56 | { 57 | if (PrimarySortMode == EColumnSortMode::Ascending && One.PrimaryKey != Two.PrimaryKey) 58 | return One.PrimaryKey < Two.PrimaryKey; 59 | else if (PrimarySortMode == EColumnSortMode::Descending && One.PrimaryKey != Two.PrimaryKey) 60 | return One.PrimaryKey > Two.PrimaryKey; 61 | 62 | if (SecondarySortMode == EColumnSortMode::Ascending) 63 | return One.SecondaryKey < Two.SecondaryKey; 64 | else if (SecondarySortMode == EColumnSortMode::Descending) 65 | return One.SecondaryKey > Two.SecondaryKey; 66 | 67 | return false; 68 | }); 69 | 70 | TArray NewArray; 71 | NewArray.Reserve(Array.Num()); 72 | 73 | for (const auto& Element : SortData) 74 | { 75 | NewArray.Add(Array[Element.OriginalIndex]); 76 | } 77 | Array = MoveTemp(NewArray); 78 | } 79 | 80 | private: 81 | 82 | EColumnSortMode::Type PrimarySortMode = EColumnSortMode::None; 83 | EColumnSortMode::Type SecondarySortMode = EColumnSortMode::None; 84 | 85 | FPrimaryFunction PrimaryFunction; 86 | FSecondaryFunction SecondaryFunction; 87 | 88 | /** Aggregated data from the sort methods. We extract the sort data from all elements first, then sort based on the extracted data. */ 89 | struct FSortPayload : FNoncopyable 90 | { 91 | int32 OriginalIndex; 92 | 93 | PrimaryKeyType PrimaryKey; 94 | SecondaryKeyType SecondaryKey; 95 | 96 | FSortPayload(int32 InOriginalIndex, PrimaryKeyType&& InPrimaryKey, SecondaryKeyType&& InSecondaryKey) 97 | : OriginalIndex(InOriginalIndex) 98 | , PrimaryKey(MoveTemp(InPrimaryKey)) 99 | , SecondaryKey(MoveTemp(InSecondaryKey)) {} 100 | 101 | FSortPayload(FSortPayload&& Other) { (*this) = MoveTemp(Other); } 102 | FSortPayload& operator=(FSortPayload&& rhs) 103 | { 104 | OriginalIndex = rhs.OriginalIndex; 105 | PrimaryKey = MoveTemp(rhs.PrimaryKey); 106 | SecondaryKey = MoveTemp(rhs.SecondaryKey); 107 | return *this; 108 | } 109 | }; 110 | }; 111 | 112 | } 113 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowser.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SubsystemBrowser : ModuleRules 6 | { 7 | public bool bStrictIncludesCheck = false; 8 | 9 | public SubsystemBrowser(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 12 | 13 | // This is to emulate engine installation and verify includes during development 14 | // Gives effect similar to BuildPlugin with -StrictIncludes 15 | if (bStrictIncludesCheck) 16 | { 17 | bUseUnity = false; 18 | PCHUsage = PCHUsageMode.NoPCHs; 19 | // Enable additional checks used for Engine modules 20 | bTreatAsEngineModule = true; 21 | } 22 | 23 | // This is to use non-Public/Private folder system 24 | PublicIncludePaths.Add(ModuleDirectory); 25 | // Same as above but exclusively for current module 26 | // PrivateIncludePaths.Add(ModuleDirectory); 27 | 28 | // These are dependencies always needed if planning on expanding plugin 29 | PublicDependencyModuleNames.AddRange(new string[] 30 | { 31 | "Core", 32 | "CoreUObject", 33 | "Engine" 34 | }); 35 | 36 | // These are dependencies that nobody should know of 37 | PrivateDependencyModuleNames.AddRange(new string [] 38 | { 39 | "Slate", 40 | "SlateCore", 41 | "ApplicationCore", 42 | "InputCore", 43 | "UnrealEd", 44 | "EditorStyle", 45 | "EditorSubsystem", 46 | "WorkspaceMenuStructure", 47 | "WorldBrowser", 48 | "LevelEditor", 49 | "ToolMenus", 50 | "SettingsEditor", 51 | "AssetTools", 52 | "Projects" 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserFlags.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Misc/EngineVersionComparison.h" 6 | 7 | // just two little helpers to help reading code 8 | 9 | #define SINCE_UE_VERSION(MajorVersion, MinorVersion, PatchVersion) \ 10 | !UE_VERSION_OLDER_THAN(MajorVersion, MinorVersion, PatchVersion) 11 | 12 | #define BEFORE_UE_VERSION(MajorVersion, MinorVersion, PatchVersion) \ 13 | !UE_VERSION_NEWER_THAN(MajorVersion, MinorVersion, PatchVersion) 14 | 15 | /** 16 | * Toggle example code validation 17 | */ 18 | #define ENABLE_SUBSYSTEM_BROWSER_EXAMPLES 0 19 | 20 | /** 21 | * Internal debugging things flag. 22 | */ 23 | #define ENABLE_SUBSYSTEM_BROWSER_DEBUG_THINGS 0 24 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserModule.h" 4 | 5 | #include "SubsystemBrowserFlags.h" 6 | #include "SubsystemBrowserSettings.h" 7 | #include "SubsystemBrowserStyle.h" 8 | #include "Model/Column/SubsystemBrowserColumn_Name.h" 9 | #include "Model/Column/SubsystemBrowserColumn_Config.h" 10 | #include "Model/Column/SubsystemBrowserColumn_Module.h" 11 | #include "Model/Column/SubsystemBrowserColumn_Plugin.h" 12 | #include "Model/Category/SubsystemBrowserCategory_Editor.h" 13 | #include "Model/Category/SubsystemBrowserCategory_Engine.h" 14 | #include "Model/Category/SubsystemBrowserCategory_GameInstance.h" 15 | #include "Model/Category/SubsystemBrowserCategory_Player.h" 16 | #include "Model/Category/SubsystemBrowserCategory_World.h" 17 | #include "Model/Category/SubsystemBrowserCategory_AudioEngine.h" 18 | #include "UI/SubsystemBrowserPanel.h" 19 | #include "ISettingsModule.h" 20 | #include "ISettingsSection.h" 21 | #include "Widgets/Docking/SDockTab.h" 22 | #include "LevelEditor.h" 23 | #include "ToolMenus.h" 24 | #include "WorkspaceMenuStructure.h" 25 | #include "WorkspaceMenuStructureModule.h" 26 | 27 | IMPLEMENT_MODULE(FSubsystemBrowserModule, SubsystemBrowser); 28 | 29 | DEFINE_LOG_CATEGORY(LogSubsystemBrowser); 30 | 31 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 32 | 33 | const FName FSubsystemBrowserModule::SubsystemBrowserTabName = TEXT("SubsystemBrowserTab"); 34 | const FName FSubsystemBrowserModule::SubsystemBrowserNomadTabName = TEXT("SubsystemBrowserNomadTab"); 35 | const FName FSubsystemBrowserModule::SubsystemBrowserContextMenuName = TEXT("SubsystemBrowser.ContextMenu"); 36 | 37 | FSubsystemBrowserModule::FOnGenerateTooltip FSubsystemBrowserModule::OnGenerateTooltip; 38 | FSubsystemBrowserModule::FOnGenerateMenu FSubsystemBrowserModule::OnGenerateContextMenu; 39 | 40 | void FSubsystemBrowserModule::StartupModule() 41 | { 42 | if (GIsEditor && !IsRunningCommandlet()) 43 | { 44 | FSubsystemBrowserStyle::Register(); 45 | 46 | bNomadModeActive = USubsystemBrowserSettings::Get()->ShouldUseNomadMode(); 47 | 48 | if (!bNomadModeActive) 49 | { // register as a normal panel within level editor 50 | FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); 51 | LevelEditorModule.OnTabManagerChanged().AddLambda([Module = this]() 52 | { 53 | FLevelEditorModule& LevelEditorModule = FModuleManager::GetModuleChecked(TEXT("LevelEditor")); 54 | TSharedPtr LevelEditorTabManager = LevelEditorModule.GetLevelEditorTabManager(); 55 | if (LevelEditorTabManager.IsValid()) 56 | { 57 | LevelEditorTabManager->RegisterTabSpawner(SubsystemBrowserTabName, FOnSpawnTab::CreateRaw(Module, &FSubsystemBrowserModule::HandleSpawnBrowserTab)) 58 | .SetDisplayName(LOCTEXT("SubsystemBrowserTabTitle", "Subsystems")) 59 | .SetTooltipText(LOCTEXT("SubsystemBrowserTabTooltip", "Open the Subsystem Browser tab.")) 60 | .SetGroup(WorkspaceMenu::GetMenuStructure().GetLevelEditorCategory()) 61 | .SetIcon(FStyleHelper::GetSlateIcon(FSubsystemBrowserStyle::PanelIconName)); 62 | } 63 | }); 64 | } 65 | else 66 | { // register as a nomad tab within global tab manager 67 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SubsystemBrowserNomadTabName, FOnSpawnTab::CreateRaw(this, &FSubsystemBrowserModule::HandleSpawnBrowserTab)) 68 | .SetDisplayName(LOCTEXT("SubsystemBrowserNomadTabTitle", "Subsystem Browser")) 69 | .SetTooltipText(LOCTEXT("SubsystemBrowserNomadTabTooltip", "Open the Subsystem Browser tab.")) 70 | #if UE_VERSION_OLDER_THAN(5, 0, 0) 71 | .SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory()) 72 | #else 73 | .SetGroup(WorkspaceMenu::GetMenuStructure().GetToolsCategory()) 74 | #endif 75 | .SetIcon(FStyleHelper::GetSlateIcon(FSubsystemBrowserStyle::PanelIconName)); 76 | } 77 | 78 | // Register default columns and categories on startup 79 | RegisterDefaultDynamicColumns(); 80 | RegisterDefaultCategories(); 81 | 82 | // Register plugin settings 83 | RegisterSettings(); 84 | 85 | // 86 | UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FSubsystemBrowserModule::RegisterMenus)); 87 | } 88 | } 89 | 90 | void FSubsystemBrowserModule::RegisterSettings() 91 | { 92 | ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked("Settings"); 93 | 94 | // Setup plugin settings panel in Editor Settings 95 | USubsystemBrowserSettings* SettingsObject = USubsystemBrowserSettings::Get(); 96 | PluginSettingsSection = SettingsModule.RegisterSettings( 97 | TEXT("Editor"), TEXT("Plugins"), TEXT("SubsystemBrowser"), 98 | LOCTEXT("SubsystemBrowserSettingsName", "Subsystem Browser"), 99 | LOCTEXT("SubsystemBrowserSettingsDescription", "Settings for Subsystem Browser Plugin"), 100 | SettingsObject 101 | ); 102 | PluginSettingsSection->OnSelect().BindUObject(SettingsObject, &USubsystemBrowserSettings::OnSettingsSelected); 103 | PluginSettingsSection->OnResetDefaults().BindUObject(SettingsObject, &USubsystemBrowserSettings::OnSettingsReset); 104 | } 105 | 106 | void FSubsystemBrowserModule::RegisterMenus() 107 | { 108 | UToolMenus* const ToolMenus = UToolMenus::Get(); 109 | 110 | ToolMenus->RegisterMenu(SubsystemBrowserContextMenuName); 111 | } 112 | 113 | void FSubsystemBrowserModule::ShutdownModule() 114 | { 115 | if (GIsEditor && !IsRunningCommandlet()) 116 | { 117 | PluginSettingsSection.Reset(); 118 | 119 | if (!bNomadModeActive) 120 | { 121 | if (FLevelEditorModule* LevelEditorModule = FModuleManager::GetModulePtr(TEXT("LevelEditor"))) 122 | { 123 | LevelEditorModule->GetLevelEditorTabManager()->UnregisterTabSpawner(SubsystemBrowserTabName); 124 | } 125 | } 126 | else 127 | { 128 | FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(SubsystemBrowserNomadTabName); 129 | } 130 | 131 | FSubsystemBrowserStyle::UnRegister(); 132 | } 133 | } 134 | 135 | TSharedRef FSubsystemBrowserModule::HandleSpawnBrowserTab(const FSpawnTabArgs& Args) 136 | { 137 | UWorld* EditorWorld = GEditor ? GEditor->GetEditorWorldContext().World() : nullptr; 138 | 139 | return SNew(SDockTab) 140 | .Label(LOCTEXT("SubsystemBrowserTitle", "Subsystems")) 141 | .TabRole(bNomadModeActive ? ETabRole::NomadTab : ETabRole::PanelTab) 142 | [ 143 | SNew(SBorder) 144 | .BorderImage( FStyleHelper::GetBrush(TEXT("ToolPanel.GroupBorder")) ) 145 | [ 146 | SNew(SSubsystemBrowserPanel).InWorld(EditorWorld) 147 | ] 148 | ]; 149 | } 150 | 151 | void FSubsystemBrowserModule::SummonSubsystemTab() 152 | { 153 | const FName& TabName = bNomadModeActive ? SubsystemBrowserNomadTabName : SubsystemBrowserTabName; 154 | 155 | #if UE_VERSION_OLDER_THAN(4, 26, 0) 156 | FGlobalTabmanager::Get()->InvokeTab(TabName); 157 | #else 158 | FGlobalTabmanager::Get()->TryInvokeTab(TabName); 159 | #endif 160 | } 161 | 162 | void FSubsystemBrowserModule::SummonPluginSettingsTab() 163 | { 164 | ISettingsModule& Module = FModuleManager::GetModuleChecked(TEXT("Settings")); 165 | Module.ShowViewer(TEXT("Editor"), TEXT("Plugins"), TEXT("SubsystemBrowser")); 166 | } 167 | 168 | void FSubsystemBrowserModule::SummonSubsystemSettingsTab() 169 | { 170 | if (USubsystemBrowserSettings::Get()->ShouldUseSubsystemSettings()) 171 | { 172 | ISettingsModule& Module = FModuleManager::GetModuleChecked(TEXT("Settings")); 173 | Module.ShowViewer(TEXT("Subsystem"), TEXT(""), TEXT("")); 174 | } 175 | } 176 | 177 | const TArray& FSubsystemBrowserModule::GetCategories() const 178 | { 179 | return Categories; 180 | } 181 | 182 | void FSubsystemBrowserModule::RegisterDefaultCategories() 183 | { 184 | RegisterCategory(); 185 | RegisterCategory(); 186 | RegisterCategory(); 187 | RegisterCategory(); 188 | // RegisterCategory(); 189 | RegisterCategory(); 190 | 191 | #if UE_VERSION_NEWER_THAN(5, 1, 0) 192 | RegisterCategory(); 193 | #endif 194 | } 195 | 196 | const TArray& FSubsystemBrowserModule::GetDynamicColumns() const 197 | { 198 | return DynamicColumns; 199 | } 200 | 201 | void FSubsystemBrowserModule::AddPermanentColumns(TArray& Columns) 202 | { 203 | Columns.Add(MakeShared()); 204 | } 205 | 206 | void FSubsystemBrowserModule::RegisterDefaultDynamicColumns() 207 | { 208 | RegisterDynamicColumn(MakeShared()); 209 | RegisterDynamicColumn(MakeShared()); 210 | RegisterDynamicColumn(MakeShared()); 211 | } 212 | 213 | void FSubsystemBrowserModule::RegisterCategory(TSharedRef InCategory) 214 | { 215 | if (InCategory->Name.IsNone()) 216 | { 217 | UE_LOG(LogSubsystemBrowser, Error, TEXT("Invalid category being registered")); 218 | return; 219 | } 220 | 221 | for (const SubsystemCategoryPtr& Category : Categories) 222 | { 223 | if (Category->Name == InCategory->Name) 224 | { 225 | UE_LOG(LogSubsystemBrowser, Error, TEXT("Duplicating category with name %s."), *Category->Name.ToString()); 226 | return; 227 | } 228 | } 229 | 230 | Categories.Add(InCategory); 231 | 232 | // Sort categories according to their order 233 | Categories.Sort([](const SubsystemCategoryPtr& A, const SubsystemCategoryPtr& B) 234 | { 235 | return A->GetSortOrder() < B->GetSortOrder(); 236 | }); 237 | } 238 | 239 | void FSubsystemBrowserModule::RemoveCategory(FName CategoryName) 240 | { 241 | for (auto It = Categories.CreateIterator(); It; ++It) 242 | { 243 | if ((*It)->Name == CategoryName) 244 | { 245 | It.RemoveCurrent(); 246 | } 247 | } 248 | } 249 | 250 | void FSubsystemBrowserModule::RegisterDynamicColumn(TSharedRef InColumn) 251 | { 252 | if (!FSubsystemDynamicColumn::IsValidColumnName(InColumn->Name)) 253 | { 254 | UE_LOG(LogSubsystemBrowser, Error, TEXT("Invalid column being registered")); 255 | return; 256 | } 257 | 258 | for (SubsystemColumnPtr& Column : DynamicColumns) 259 | { 260 | if (Column->Name == InColumn->Name) 261 | { 262 | UE_LOG(LogSubsystemBrowser, Error, TEXT("Duplicating column with name %s."), *Column->Name.ToString()); 263 | return; 264 | } 265 | } 266 | 267 | DynamicColumns.Add(InColumn); 268 | 269 | // Sort columns by order 270 | DynamicColumns.StableSort(SubsystemColumnSorter()); 271 | } 272 | 273 | #undef LOCTEXT_NAMESPACE 274 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserModule.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "Modules/ModuleInterface.h" 7 | #include "Modules/ModuleManager.h" 8 | #include "Model/SubsystemBrowserCategory.h" // [no-fwd] 9 | #include "Model/SubsystemBrowserColumn.h" // [no-fwd] 10 | 11 | class FSpawnTabArgs; 12 | class UToolMenu; 13 | class SDockTab; 14 | class IDetailsView; 15 | class ISettingsSection; 16 | struct ISubsystemTreeItem; 17 | 18 | class FSubsystemBrowserModule : public IModuleInterface 19 | { 20 | public: 21 | static const FName SubsystemBrowserTabName; 22 | static const FName SubsystemBrowserNomadTabName; 23 | static const FName SubsystemBrowserContextMenuName; 24 | 25 | SUBSYSTEMBROWSER_API static FSubsystemBrowserModule& Get() 26 | { 27 | return FModuleManager::GetModuleChecked(TEXT("SubsystemBrowser")); 28 | } 29 | 30 | virtual void StartupModule() override; 31 | virtual void ShutdownModule() override; 32 | virtual bool SupportsDynamicReloading() override { return false; } 33 | 34 | /** 35 | * Get list of all registered subsystem categories 36 | */ 37 | SUBSYSTEMBROWSER_API const TArray& GetCategories() const; 38 | /** 39 | * Register default subsystem categories 40 | */ 41 | SUBSYSTEMBROWSER_API void RegisterDefaultCategories(); 42 | /** 43 | * Register a new subsystem category 44 | */ 45 | template 46 | void RegisterCategory(TArgs&&... InArgs); 47 | /** 48 | * Register a new subsystem category to show 49 | */ 50 | SUBSYSTEMBROWSER_API void RegisterCategory(TSharedRef InCategory); 51 | /** 52 | * Remove a category by its name 53 | */ 54 | SUBSYSTEMBROWSER_API void RemoveCategory(FName CategoryName); 55 | 56 | /** 57 | * Get a list of all custom dynamic columns 58 | */ 59 | SUBSYSTEMBROWSER_API const TArray& GetDynamicColumns() const; 60 | /** 61 | * 62 | */ 63 | SUBSYSTEMBROWSER_API void RegisterDefaultDynamicColumns(); 64 | /** 65 | * Register a new custom dynamic column 66 | */ 67 | template 68 | void RegisterDynamicColumn(TArgs&&... InArgs); 69 | /** 70 | * Register a new custom dynamic column 71 | */ 72 | SUBSYSTEMBROWSER_API void RegisterDynamicColumn(TSharedRef InColumn); 73 | /** 74 | * Populate permanent columns 75 | */ 76 | static void AddPermanentColumns(TArray& Columns); 77 | 78 | /** 79 | * Open subsystems tab 80 | */ 81 | SUBSYSTEMBROWSER_API void SummonSubsystemTab(); 82 | 83 | /** 84 | * Open editor settings tab with plugin settings pre-selected 85 | */ 86 | SUBSYSTEMBROWSER_API void SummonPluginSettingsTab(); 87 | 88 | /** 89 | * Open subsystem settings panel 90 | */ 91 | SUBSYSTEMBROWSER_API void SummonSubsystemSettingsTab(); 92 | 93 | /** 94 | * Callback that is called whenever a tooltip for item needs to be generated 95 | * Used to add custom data to tooltips. 96 | */ 97 | DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGenerateTooltip, TSharedRef, class FSubsystemTableItemTooltipBuilder&); 98 | static SUBSYSTEMBROWSER_API FOnGenerateTooltip OnGenerateTooltip; 99 | 100 | /** 101 | * Callback that is called whenever a menu for item needs to be generated. 102 | * Used to add custom menu actions. 103 | */ 104 | DECLARE_MULTICAST_DELEGATE_TwoParams(FOnGenerateMenu, TSharedRef, UToolMenu*); 105 | static SUBSYSTEMBROWSER_API FOnGenerateMenu OnGenerateContextMenu; 106 | 107 | /** 108 | * Apply custom subsystem customizations to provided details view 109 | * @param DetailsView details view instance to patch 110 | * @param Usage 111 | */ 112 | static SUBSYSTEMBROWSER_API void CustomizeDetailsView(TSharedRef DetailsView, FName Usage); 113 | 114 | protected: 115 | void RegisterSettings(); 116 | void RegisterMenus(); 117 | 118 | /** Handles creating the subsystem browser tab. */ 119 | TSharedRef HandleSpawnBrowserTab(const FSpawnTabArgs& Args); 120 | 121 | private: 122 | // Is nomad mode enabled 123 | bool bNomadModeActive = false; 124 | // Instances of subsystem categories 125 | TArray Categories; 126 | // Instances of dynamic subsystem columns 127 | TArray DynamicColumns; 128 | 129 | 130 | // Saved instance of Settings section 131 | TSharedPtr PluginSettingsSection; 132 | }; 133 | 134 | template 135 | void FSubsystemBrowserModule::RegisterCategory(TArgs&&... InArgs) 136 | { 137 | RegisterCategory(MakeShared(Forward(InArgs)...)); 138 | } 139 | 140 | template 141 | void FSubsystemBrowserModule::RegisterDynamicColumn(TArgs&&... InArgs) 142 | { 143 | RegisterDynamicColumn(MakeShared(Forward(InArgs)...)); 144 | } 145 | 146 | #if UE_BUILD_DEBUG || defined(WITH_SB_HOST_PROJECT) 147 | DECLARE_LOG_CATEGORY_EXTERN(LogSubsystemBrowser, Log, All); 148 | #else 149 | DECLARE_LOG_CATEGORY_EXTERN(LogSubsystemBrowser, Log, Warning); 150 | #endif 151 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserSettings.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserSettings.h" 4 | #include "SubsystemBrowserModule.h" 5 | #include "Model/SubsystemBrowserModel.h" 6 | 7 | USubsystemBrowserSettings::FSettingChangedEvent USubsystemBrowserSettings::SettingChangedEvent; 8 | 9 | const FName FSubsystemBrowserConfigMeta::MD_ConfigAffectsView(TEXT("ConfigAffectsView")); 10 | const FName FSubsystemBrowserConfigMeta::MD_ConfigAffectsColumns(TEXT("ConfigAffectsColumns")); 11 | const FName FSubsystemBrowserConfigMeta::MD_ConfigAffectsDetails(TEXT("ConfigAffectsDetails")); 12 | const FName FSubsystemBrowserConfigMeta::MD_ConfigAffectsSettings(TEXT("ConfigAffectsSettings")); 13 | 14 | const FName FSubsystemBrowserUserMeta::MD_SBColor(TEXT("SBColor")); 15 | const FName FSubsystemBrowserUserMeta::MD_SBTooltip(TEXT("SBTooltip")); 16 | const FName FSubsystemBrowserUserMeta::MD_SBOwnerName(TEXT("SBOwnerName")); 17 | const FName FSubsystemBrowserUserMeta::MD_SBHidden(TEXT("SBHidden")); 18 | const FName FSubsystemBrowserUserMeta::MD_SBGetSubobjects(TEXT("SBGetSubobjects")); 19 | const FName FSubsystemBrowserUserMeta::MD_SBAutoGetSubobjects(TEXT("SBAutoGetSubobjects")); 20 | 21 | USubsystemBrowserSettings::USubsystemBrowserSettings() 22 | { 23 | } 24 | 25 | void USubsystemBrowserSettings::OnSettingsSelected() 26 | { 27 | UE_LOG(LogSubsystemBrowser, Log, TEXT("Browser settings being selected")); 28 | 29 | SyncCategorySettings(); 30 | SyncColumnSettings(); 31 | } 32 | 33 | bool USubsystemBrowserSettings::OnSettingsModified() 34 | { 35 | UE_LOG(LogSubsystemBrowser, Log, TEXT("Browser settings being modified")); 36 | return true; 37 | } 38 | 39 | bool USubsystemBrowserSettings::OnSettingsReset() 40 | { 41 | UE_LOG(LogSubsystemBrowser, Log, TEXT("Browser settings being reset")); 42 | 43 | CategoryVisibilityState.Empty(); 44 | TreeExpansionState.Empty(); 45 | TableColumnVisibilityState.Empty(); 46 | 47 | bShowOnlyGameModules = false; 48 | bShowOnlyPluginModules = false; 49 | bShowAnyProperties = false; 50 | bEditAnyProperties = false; 51 | bShowOnlyWithViewableElements = false; 52 | 53 | MaxColumnTogglesToShow = 4; 54 | MaxCategoryTogglesToShow = 6; 55 | 56 | bEnableColoring = false; 57 | 58 | bEnableStaleColor = false; 59 | StaleStateColor = FLinearColor(0.75, 0.75, 0.75, 1.0); 60 | bEnableSelectedColor = false; 61 | SelectedStateColor = FLinearColor(0.828, 0.364, 0.003, 1.0); 62 | bEnableColoringGameModule = false; 63 | GameModuleColor = FLinearColor(0.4, 0.4, 1.0, 1.0); 64 | bEnableColoringEngineModule = false; 65 | EngineModuleColor = FLinearColor(0.75, 0.75, 0.75, 1.0); 66 | 67 | 68 | NotifyPropertyChange(NAME_All); 69 | 70 | return true; 71 | } 72 | 73 | // Sync category settings with categories we have in module 74 | void USubsystemBrowserSettings::SyncCategorySettings() 75 | { 76 | TMap CurrentSettings; 77 | LoadDataFromConfig(CategoryVisibilityState, CurrentSettings); 78 | // clear from possible stale/removed categories 79 | CategoryVisibilityState.Empty(); 80 | 81 | for (const auto& Category : FSubsystemBrowserModule::Get().GetCategories()) 82 | { 83 | if (!CurrentSettings.Contains(Category->Name)) 84 | { 85 | CurrentSettings.Emplace(Category->Name, Category->IsVisibleByDefault()); 86 | } 87 | } 88 | 89 | StoreDataToConfig(CurrentSettings, CategoryVisibilityState); 90 | } 91 | 92 | void USubsystemBrowserSettings::LoadCategoryStates(TMap& States) 93 | { 94 | SyncCategorySettings(); 95 | LoadDataFromConfig(CategoryVisibilityState, States); 96 | } 97 | 98 | void USubsystemBrowserSettings::SetCategoryStates(const TMap& States) 99 | { 100 | StoreDataToConfig(States, CategoryVisibilityState); 101 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, CategoryVisibilityState)); 102 | } 103 | 104 | void USubsystemBrowserSettings::SetCategoryState(FName Category, bool State) 105 | { 106 | SetConfigFlag(CategoryVisibilityState, Category, State); 107 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, CategoryVisibilityState)); 108 | } 109 | 110 | bool USubsystemBrowserSettings::GetTreeExpansionState(FName Category) const 111 | { 112 | const FSubsystemBrowserConfigItem* bFoundState = TreeExpansionState.FindByKey(Category); 113 | return bFoundState ? bFoundState->bValue : true; 114 | } 115 | 116 | void USubsystemBrowserSettings::LoadTreeExpansionStates(TMap& States) 117 | { 118 | LoadDataFromConfig(TreeExpansionState, States); 119 | } 120 | 121 | void USubsystemBrowserSettings::SetTreeExpansionStates(TMap const& States) 122 | { 123 | StoreDataToConfig(States, TreeExpansionState); 124 | //NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, TreeExpansionState)); 125 | } 126 | 127 | ESubsystemBrowserSplitterOrientation USubsystemBrowserSettings::GetSeparatorOrientation() const 128 | { 129 | if (SeparatorOrientation == ESubsystemBrowserSplitterOrientation::Auto) 130 | { 131 | return bUseNomadMode ? ESubsystemBrowserSplitterOrientation::Horizontal : ESubsystemBrowserSplitterOrientation::Vertical; 132 | } 133 | return SeparatorOrientation; 134 | } 135 | 136 | void USubsystemBrowserSettings::SetSeparatorLocation(float NewValue) 137 | { 138 | SeparatorLocation = NewValue; 139 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, SeparatorLocation)); 140 | } 141 | 142 | void USubsystemBrowserSettings::SetColoringEnabled(bool bNewValue) 143 | { 144 | bEnableColoring = bNewValue; 145 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bEnableColoring)); 146 | } 147 | 148 | FSlateColor USubsystemBrowserSettings::GetSelectedColor() const 149 | { 150 | if (bEnableColoring && bEnableSelectedColor) 151 | { 152 | return SelectedStateColor; 153 | } 154 | return FSlateColor::UseForeground(); 155 | } 156 | 157 | FSlateColor USubsystemBrowserSettings::GetStaleColor() const 158 | { 159 | if (bEnableColoring && bEnableStaleColor) 160 | { 161 | return StaleStateColor; 162 | } 163 | return FSlateColor::UseSubduedForeground(); 164 | } 165 | 166 | FSlateColor USubsystemBrowserSettings::GetModuleColor(bool bGameModule) 167 | { 168 | if (bEnableColoring) 169 | { 170 | if (bGameModule && bEnableColoringGameModule) 171 | return GameModuleColor; 172 | if (!bGameModule && bEnableColoringEngineModule) 173 | return EngineModuleColor; 174 | } 175 | return FSlateColor::UseForeground(); 176 | } 177 | 178 | void USubsystemBrowserSettings::SetShowSubobjects(bool bNewValue) 179 | { 180 | bShowSubobjects = bNewValue; 181 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bShowSubobjects)); 182 | } 183 | 184 | void USubsystemBrowserSettings::SetForceHiddenPropertyVisibility(bool bNewValue) 185 | { 186 | bForceHiddenPropertyVisibility = bNewValue; 187 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bForceHiddenPropertyVisibility)); 188 | } 189 | void USubsystemBrowserSettings::SetShouldShowOnlyGame(bool bNewValue) 190 | { 191 | bShowOnlyGameModules = bNewValue; 192 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bShowOnlyGameModules)); 193 | } 194 | 195 | void USubsystemBrowserSettings::SetShouldShowOnlyPlugins(bool bNewValue) 196 | { 197 | bShowOnlyPluginModules = bNewValue; 198 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bShowOnlyPluginModules)); 199 | } 200 | 201 | void USubsystemBrowserSettings::SetShouldShowOnlyViewable(bool bNewValue) 202 | { 203 | bShowOnlyWithViewableElements = bNewValue; 204 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bShowOnlyWithViewableElements)); 205 | } 206 | 207 | void USubsystemBrowserSettings::SetShouldHideEmptyCategories(bool bNewValue) 208 | { 209 | bHideEmptyCategories = bNewValue; 210 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, bHideEmptyCategories)); 211 | } 212 | 213 | void USubsystemBrowserSettings::SyncColumnSettings() 214 | { 215 | TMap CurrentSettings; 216 | LoadDataFromConfig(TableColumnVisibilityState, CurrentSettings); 217 | // clear from possible stale/removed categories 218 | TableColumnVisibilityState.Empty(); 219 | 220 | for (const auto& DynamicColumn : FSubsystemBrowserModule::Get().GetDynamicColumns()) 221 | { 222 | if (!CurrentSettings.Contains(DynamicColumn->Name)) 223 | { 224 | CurrentSettings.Emplace(DynamicColumn->Name, DynamicColumn->IsVisibleByDefault()); 225 | } 226 | } 227 | 228 | StoreDataToConfig(CurrentSettings, TableColumnVisibilityState); 229 | } 230 | 231 | bool USubsystemBrowserSettings::GetTableColumnState(FName Column) const 232 | { 233 | const FSubsystemBrowserConfigItem* bFoundState = TableColumnVisibilityState.FindByKey(Column); 234 | return bFoundState ? bFoundState->bValue : true; 235 | } 236 | 237 | void USubsystemBrowserSettings::SetTableColumnState(FName Column, bool State) 238 | { 239 | SetConfigFlag(TableColumnVisibilityState, Column, State); 240 | NotifyPropertyChange(GET_MEMBER_NAME_CHECKED(ThisClass, TableColumnVisibilityState)); 241 | } 242 | 243 | void USubsystemBrowserSettings::NotifyPropertyChange(FName PropertyName) 244 | { 245 | UE_LOG(LogSubsystemBrowser, Log, TEXT("Property %s changed and called save"), *PropertyName.ToString()); 246 | SaveConfig(); 247 | 248 | SettingChangedEvent.Broadcast(PropertyName); 249 | } 250 | 251 | void USubsystemBrowserSettings::PostLoad() 252 | { 253 | UE_LOG(LogSubsystemBrowser, Log, TEXT("Browser settings being loaded")); 254 | 255 | Super::PostLoad(); 256 | } 257 | 258 | void USubsystemBrowserSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) 259 | { 260 | Super::PostEditChangeProperty(PropertyChangedEvent); 261 | 262 | // Take the class member property name instead of struct member 263 | FName PropertyName = (PropertyChangedEvent.MemberProperty ? PropertyChangedEvent.MemberProperty->GetFName() : PropertyChangedEvent.GetPropertyName()); 264 | 265 | NotifyPropertyChange(PropertyName); 266 | } 267 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserSettings.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "Styling/SlateColor.h" 7 | #include "SubsystemBrowserSettings.generated.h" 8 | 9 | USTRUCT() 10 | struct FSubsystemBrowserConfigItem 11 | { 12 | GENERATED_BODY() 13 | 14 | UPROPERTY(EditAnywhere, Category=Item) 15 | FName Name; 16 | UPROPERTY(EditAnywhere, Category=Item) 17 | bool bValue = true; 18 | 19 | FSubsystemBrowserConfigItem() = default; 20 | FSubsystemBrowserConfigItem(const FName& InName, bool InValue) : Name(InName), bValue(InValue) { } 21 | 22 | bool operator==(const FName& OtherName) const { return Name == OtherName; } 23 | }; 24 | 25 | UENUM() 26 | enum class ESubsystemBrowserSplitterOrientation 27 | { 28 | Horizontal, 29 | Vertical, 30 | // Vertical in Panel mode, Horizontal in Nomad mode 31 | Auto 32 | }; 33 | 34 | DECLARE_DELEGATE_RetVal(FString, FSubsystemBrowserGetStringProperty); 35 | DECLARE_DELEGATE_RetVal(FText, FSubsystemBrowserGetTextProperty); 36 | DECLARE_DELEGATE_RetVal(TArray, FSubsystemBrowserGetSubobjects); 37 | 38 | struct SUBSYSTEMBROWSER_API FSubsystemBrowserUserMeta 39 | { 40 | // Subsystem Browser - Subsystem name color in list 41 | static const FName MD_SBColor; 42 | // Subsystem Browser - Extra tooltip text when hovering 43 | static const FName MD_SBTooltip; 44 | // Subsystem Browser - Owner name provider function/property name (will be called on subsystem) 45 | static const FName MD_SBOwnerName; 46 | // Subsystem Browser Details - Hide property from display 47 | static const FName MD_SBHidden; 48 | 49 | // Subsystem Browser Details - Subobject collection function 50 | // Specifies a function name that will provide list of subobjects to display 51 | // TArray MyGetSubobjectsFunction() const; 52 | static const FName MD_SBGetSubobjects; 53 | // Subsystem Browser Details - Subobject display 54 | // Automatically gather market subobjbects for display 55 | static const FName MD_SBAutoGetSubobjects; 56 | 57 | }; 58 | 59 | struct SUBSYSTEMBROWSER_API FSubsystemBrowserConfigMeta 60 | { 61 | // Config property affects subsystem table view and requires data refresh (no reconstruct) 62 | static const FName MD_ConfigAffectsView; 63 | // Config property affects subsystem table and requires table reconstruct 64 | static const FName MD_ConfigAffectsColumns; 65 | // Config property affects details view and requires details view refresh 66 | static const FName MD_ConfigAffectsDetails; 67 | // Config property affects subsystem settings panel 68 | static const FName MD_ConfigAffectsSettings; 69 | }; 70 | 71 | /** 72 | * Class that holds settings for subsystem browser plugin. 73 | * 74 | * It is possible to register it within ISettingsModule to see in Editor Settings. 75 | */ 76 | UCLASS(config=EditorPerProjectUserSettings, meta=(DisplayName="Subsystem Browser Settings")) 77 | class SUBSYSTEMBROWSER_API USubsystemBrowserSettings : public UObject 78 | { 79 | GENERATED_BODY() 80 | public: 81 | USubsystemBrowserSettings(); 82 | 83 | static USubsystemBrowserSettings* Get() 84 | { 85 | return GetMutableDefault(); 86 | } 87 | 88 | // Called when browser settings being opened 89 | void OnSettingsSelected(); 90 | // Called when browser settings were modified 91 | bool OnSettingsModified(); 92 | // Called when settings reset is requested 93 | bool OnSettingsReset(); 94 | 95 | DECLARE_MULTICAST_DELEGATE_OneParam(FSettingChangedEvent, FName /* InPropertyName */); 96 | static FSettingChangedEvent& OnSettingChanged() { return SettingChangedEvent; } 97 | 98 | virtual void PostLoad() override; 99 | virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; 100 | 101 | void SyncCategorySettings(); 102 | void LoadCategoryStates(TMap& States); 103 | void SetCategoryStates(const TMap& States); 104 | void SetCategoryState(FName Category, bool State); 105 | 106 | void SyncColumnSettings(); 107 | bool GetTableColumnState(FName Column) const; 108 | void SetTableColumnState(FName Column, bool State); 109 | 110 | bool GetTreeExpansionState(FName Category) const; 111 | void LoadTreeExpansionStates(TMap& States); 112 | void SetTreeExpansionStates(const TMap& States); 113 | 114 | ESubsystemBrowserSplitterOrientation GetSeparatorOrientation() const; 115 | float GetSeparatorLocation() const { return SeparatorLocation; } 116 | void SetSeparatorLocation(float NewValue); 117 | 118 | bool IsColoringEnabled() const { return bEnableColoring; } 119 | void SetColoringEnabled(bool bNewValue); 120 | void ToggleColoringEnabled() { SetColoringEnabled(!bEnableColoring); } 121 | 122 | FSlateColor GetSelectedColor() const; 123 | FSlateColor GetStaleColor() const; 124 | FSlateColor GetModuleColor(bool bGameModule); 125 | 126 | bool ShouldShowSubobjbects() const { return bShowSubobjects; } 127 | void SetShowSubobjects(bool bNewValue); 128 | void ToggleShowSubobjbects() { SetShowSubobjects(!bShowSubobjects); } 129 | 130 | bool ShouldForceHiddenPropertyVisibility() const { return bForceHiddenPropertyVisibility; } 131 | void SetForceHiddenPropertyVisibility(bool bNewValue); 132 | void ToggleForceHiddenPropertyVisibility() { SetForceHiddenPropertyVisibility(!bForceHiddenPropertyVisibility); } 133 | 134 | bool ShouldShowAnyProperties() const { return bShowAnyProperties; } 135 | bool ShouldShowAnyConfigProperties() const { return bShowAnyConfigProperties; } 136 | 137 | bool ShouldEditAnyProperties() const { return bEditAnyProperties; } 138 | bool ShouldEditAnyConfigProperties() const { return bEditAnyConfigProperties; } 139 | 140 | bool ShouldShowOnlyGame() const { return bShowOnlyGameModules; } 141 | void SetShouldShowOnlyGame(bool bNewValue); 142 | void ToggleShouldShowOnlyGame() { SetShouldShowOnlyGame(!bShowOnlyGameModules); } 143 | 144 | bool ShouldShowOnlyPlugins() const { return bShowOnlyPluginModules; } 145 | void SetShouldShowOnlyPlugins(bool bNewValue); 146 | void ToggleShouldShowOnlyPlugins() { SetShouldShowOnlyPlugins(!bShowOnlyPluginModules); } 147 | 148 | bool ShouldShowOnlyViewable() const { return bShowOnlyWithViewableElements; } 149 | void SetShouldShowOnlyViewable(bool bNewValue); 150 | void ToggleShouldShowOnlyViewable() { SetShouldShowOnlyViewable(!bShowOnlyWithViewableElements); } 151 | 152 | bool ShouldHideEmptyCategories() const { return bHideEmptyCategories; } 153 | void SetShouldHideEmptyCategories(bool bNewValue); 154 | void ToggleShouldHideEmptyCategories() { SetShouldHideEmptyCategories(!bHideEmptyCategories); } 155 | 156 | int32 GetMaxColumnTogglesToShow() const { return MaxColumnTogglesToShow; } 157 | int32 GetMaxCategoryTogglesToShow() const { return MaxCategoryTogglesToShow; } 158 | 159 | bool ShouldShowDetailsTooltips() const { return bShowDetailedTooltips; } 160 | 161 | bool ShouldUseSubsystemSettings() const { return bUseSubsystemSettings; } 162 | bool ShouldUseCustomSettingsWidget() const { return bUseCustomSettingsWidget; } 163 | bool ShouldUseCustomPropertyFilter() const { return bUseCustomPropertyFilter; } 164 | 165 | bool ShouldUseNomadMode() const { return bUseNomadMode; } 166 | 167 | private: 168 | 169 | template 170 | void LoadDataFromConfig(const TList& InConfigList, TMap& OutMap); 171 | 172 | template 173 | void StoreDataToConfig(const TMap& InMap, TList& OutConfigList); 174 | 175 | template 176 | void SetConfigFlag(TMap& InMap, FName Category, bool State); 177 | 178 | protected: 179 | 180 | // Should spawn Subsystem Browser Panel as a nomad tab insteaf of Level Editor Panel tab? 181 | // Editor Restart is required to apply value change. Default is False. 182 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigRestartRequired=true)) 183 | bool bUseNomadMode = false; 184 | 185 | // Should show subsystems only from Game Modules? 186 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsView)) 187 | bool bShowOnlyGameModules = false; 188 | 189 | // Should show subsystems only from Plugins? 190 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsView)) 191 | bool bShowOnlyPluginModules = false; 192 | 193 | // Should hide categories with no subsystems to show? 194 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsView)) 195 | bool bHideEmptyCategories = false; 196 | 197 | // Should show subsystems that have at least one viewable property or callable function? 198 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsView)) 199 | bool bShowOnlyWithViewableElements = false; 200 | 201 | // Should display subsystem important subobjects? 202 | // List of subobjects controlled by metadata specifiers 203 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsView)) 204 | bool bShowSubobjects = true; 205 | 206 | // Enforces display of all hidden object properties in details panel. Results filtered with options below. 207 | // WARNING: May be unsafe in some use cases. 208 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsDetails)) 209 | bool bForceHiddenPropertyVisibility = false; 210 | 211 | // Should display hidden properties (without Edit property specifier) 212 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsDetails, EditCondition="bForceHiddenPropertyVisibility")) 213 | bool bShowAnyProperties = false; 214 | 215 | // Should display hidden properties with Config property specifier 216 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsDetails, EditCondition="bForceHiddenPropertyVisibility")) 217 | bool bShowAnyConfigProperties = false; 218 | 219 | // Enforces editing of all object properties in details panel. 220 | // WARNING: May be unsafe in some use cases. 221 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsDetails)) 222 | bool bEditAnyProperties = false; 223 | 224 | // Enforces editing of all object properties that have Config specifier. 225 | UPROPERTY(config, EditAnywhere, Category="Browser Panel", meta=(ConfigAffectsDetails)) 226 | bool bEditAnyConfigProperties = false; 227 | 228 | // Maximum number of column toggles to show in menu before folding into submenu 229 | // Specify 0 to always fold 230 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance") 231 | int32 MaxColumnTogglesToShow = 4; 232 | 233 | // Maximum number of category toggles to show in menu before folding into submenu 234 | // Specify 0 to always fold 235 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance") 236 | int32 MaxCategoryTogglesToShow = 6; 237 | 238 | // Should color some data in table? 239 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(ConfigAffectsView)) 240 | bool bEnableColoring = false; 241 | 242 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(InlineEditConditionToggle)) 243 | bool bEnableStaleColor = false; 244 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(EditCondition="bEnableStaleColor")) 245 | FLinearColor StaleStateColor = FLinearColor(0.75, 0.75, 0.75, 1.0); 246 | 247 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(InlineEditConditionToggle)) 248 | bool bEnableSelectedColor = false; 249 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(EditCondition="bEnableSelectedColor")) 250 | FLinearColor SelectedStateColor = FLinearColor(0.828, 0.364, 0.003, 1.0); 251 | 252 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(InlineEditConditionToggle)) 253 | bool bEnableColoringGameModule = false; 254 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(EditCondition="bEnableColoringGameModule")) 255 | FLinearColor GameModuleColor = FLinearColor(0.4, 0.4, 1.0, 1.0); 256 | 257 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(InlineEditConditionToggle)) 258 | bool bEnableColoringEngineModule = false; 259 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(EditCondition="bEnableColoringEngineModule")) 260 | FLinearColor EngineModuleColor = FLinearColor(0.75, 0.75, 0.75, 1.0); 261 | 262 | // Display additional information in subsystem tooltips 263 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance") 264 | bool bShowDetailedTooltips = false; 265 | 266 | // Control TreeView|DetailsView splitter mode 267 | // Respawning panel or restarting editor is required to apply change. 268 | UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance", meta=(ConfigRestartRequired=true)) 269 | ESubsystemBrowserSplitterOrientation SeparatorOrientation = ESubsystemBrowserSplitterOrientation::Auto; 270 | 271 | // 272 | //UPROPERTY(config, EditAnywhere, Category="Browser Panel Appearance") 273 | UPROPERTY(config) 274 | float SeparatorLocation = 0.33f; 275 | 276 | // 277 | //UPROPERTY(config, EditAnywhere, Category="Browser Panel State", meta=(ConfigAffectsView, TitleProperty="Name")) 278 | UPROPERTY(config) 279 | TArray CategoryVisibilityState; 280 | 281 | // 282 | //UPROPERTY(config, VisibleAnywhere, Category="Browser Panel State", meta=(ConfigAffectsView, TitleProperty="Name")) 283 | UPROPERTY(config) 284 | TArray TreeExpansionState; 285 | 286 | // 287 | //UPROPERTY(config, EditAnywhere, Category="Browser Panel State", meta=(ConfigAffectsColumns, TitleProperty="Name")) 288 | UPROPERTY(config) 289 | TArray TableColumnVisibilityState; 290 | 291 | // Enables subsystem settings panel. 292 | // Requires editor restart to apply value change. 293 | UPROPERTY(config, EditAnywhere, Category="Settings Panel", meta=(ConfigRestartRequired=true)) 294 | bool bUseSubsystemSettings = false; 295 | // Enables use of custom settings widget in Settings panel. 296 | // Will enable display of built-in subsystems that are configurable but not editable 297 | UPROPERTY(config, EditAnywhere, Category="Settings Panel", meta=(ConfigAffectsSettings)) 298 | bool bUseCustomSettingsWidget = false; 299 | // Enables use of custom property filter in Settings panel. 300 | // Will display only properties that have Config flag instead of normal "anything editable" behavior 301 | UPROPERTY(config, EditAnywhere, Category="Settings Panel") 302 | bool bUseCustomPropertyFilter = false; 303 | 304 | private: 305 | void NotifyPropertyChange(FName PropertyName); 306 | 307 | // Holds an event delegate that is executed when a setting has changed. 308 | static FSettingChangedEvent SettingChangedEvent; 309 | 310 | }; 311 | 312 | template 313 | void USubsystemBrowserSettings::LoadDataFromConfig(const TList& InConfigList, TMap& OutMap) 314 | { 315 | for (const auto& Option : InConfigList) 316 | { 317 | OutMap.Add(Option.Name, Option.bValue); 318 | } 319 | } 320 | 321 | template 322 | void USubsystemBrowserSettings::StoreDataToConfig(const TMap& InMap, TList& OutConfigList) 323 | { 324 | for (const auto& Option : InMap) 325 | { 326 | if (auto Existing = OutConfigList.FindByKey(Option.Key)) 327 | { 328 | Existing->bValue = Option.Value; 329 | } 330 | else 331 | { 332 | OutConfigList.Emplace(Option.Key, Option.Value); 333 | } 334 | } 335 | } 336 | 337 | template 338 | void USubsystemBrowserSettings::SetConfigFlag(TMap& InMap, FName Category, bool State) 339 | { 340 | if (auto* Existing = InMap.FindByKey(Category)) 341 | { 342 | Existing->bValue = State; 343 | } 344 | else 345 | { 346 | InMap.Emplace(FSubsystemBrowserConfigItem{ Category, State }); 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserStyle.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserStyle.h" 4 | 5 | #include "SubsystemBrowserFlags.h" 6 | #include "Interfaces/IPluginManager.h" 7 | #include "Styling/SlateStyle.h" 8 | #include "Styling/SlateStyleRegistry.h" 9 | 10 | #define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 11 | #define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 12 | #define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) 13 | 14 | const FName FSubsystemBrowserStyle::StyleName("SubsystemBrowserStyle"); 15 | TSharedPtr FSubsystemBrowserStyle::StyleInstance; 16 | 17 | #if UE_VERSION_OLDER_THAN(5, 0, 0) 18 | const FName FSubsystemBrowserStyle::PanelIconName(TEXT("LevelEditor.GameSettings.Small")); 19 | const FName FSubsystemBrowserStyle::FolderOpenName(TEXT("SceneOutliner.FolderOpen")); 20 | const FName FSubsystemBrowserStyle::FolderClosedName(TEXT("SceneOutliner.FolderClosed")); 21 | #else 22 | const FName FSubsystemBrowserStyle::PanelIconName(TEXT("Icons.Settings")); 23 | const FName FSubsystemBrowserStyle::FolderOpenName(TEXT("Icons.FolderOpen")); 24 | const FName FSubsystemBrowserStyle::FolderClosedName(TEXT("Icons.FolderClosed")); 25 | #endif 26 | 27 | void FSubsystemBrowserStyle::Register() 28 | { 29 | check(!StyleInstance.IsValid()); 30 | StyleInstance = MakeShared(); 31 | 32 | FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); 33 | } 34 | 35 | void FSubsystemBrowserStyle::UnRegister() 36 | { 37 | check(StyleInstance.IsValid()); 38 | 39 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); 40 | 41 | StyleInstance.Reset(); 42 | } 43 | 44 | FSubsystemBrowserStyle& FSubsystemBrowserStyle::Get() 45 | { 46 | check(StyleInstance.IsValid()); 47 | return *StyleInstance; 48 | } 49 | 50 | FSubsystemBrowserStyle::FSubsystemBrowserStyle() : FSlateStyleSet(StyleName) 51 | { 52 | const FVector2D Icon16x16(16.f, 16.f); 53 | const FVector2D Icon64x64(64.f, 64.f); 54 | 55 | TSharedPtr Plugin = IPluginManager::Get().FindPlugin(TEXT("SubsystemBrowserPlugin")); 56 | check(Plugin.IsValid()); 57 | FSlateStyleSet::SetContentRoot(FPaths::Combine(Plugin->GetBaseDir(), TEXT("Resources"))); 58 | FSlateStyleSet::SetCoreContentRoot(FPaths::EngineContentDir() / TEXT("Slate")); 59 | 60 | // register styles here 61 | // Set("SubsystemBrowser.TabIcon", new IMAGE_BRUSH("Icons/SubsystemBrowser_16x", Icon16x16)); 62 | } 63 | 64 | const ISlateStyle& FStyleHelper::Get() 65 | { 66 | #if UE_VERSION_OLDER_THAN(5,1,0) 67 | return FEditorStyle::Get(); 68 | #else 69 | return FAppStyle::Get(); 70 | #endif 71 | } 72 | 73 | const FSlateBrush* FStyleHelper::GetBrush(const FName& InName) 74 | { 75 | #if UE_VERSION_OLDER_THAN(5,1,0) 76 | return FEditorStyle::GetBrush(InName); 77 | #else 78 | return FAppStyle::GetBrush(InName); 79 | #endif 80 | } 81 | 82 | FSlateFontInfo FStyleHelper::GetFontStyle(const FName& InName) 83 | { 84 | #if UE_VERSION_OLDER_THAN(5,1,0) 85 | return FEditorStyle::GetFontStyle(InName); 86 | #else 87 | return FAppStyle::GetFontStyle(InName); 88 | #endif 89 | } 90 | 91 | FSlateIcon FStyleHelper::GetSlateIcon(const FName& InIcon) 92 | { 93 | #if UE_VERSION_OLDER_THAN(5,1,0) 94 | return FSlateIcon( FEditorStyle::GetStyleSetName(), InIcon ); 95 | #else 96 | return FSlateIcon( FAppStyle::GetAppStyleSetName(), InIcon); 97 | #endif 98 | } 99 | 100 | FSlateColor FStyleHelper::GetSlateColor(const FName& InName) 101 | { 102 | #if UE_VERSION_OLDER_THAN(5,1,0) 103 | return FEditorStyle::GetSlateColor(InName); 104 | #else 105 | return FAppStyle::GetSlateColor(InName); 106 | #endif 107 | } 108 | 109 | #undef IMAGE_BRUSH 110 | #undef BOX_BRUSH 111 | #undef BORDER_BRUSH 112 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserStyle.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "SubsystemBrowserFlags.h" 7 | #include "Styling/SlateStyle.h" 8 | #include "Styling/SlateColor.h" 9 | #include "Styling/SlateBrush.h" 10 | #include "Textures/SlateIcon.h" 11 | 12 | #if UE_VERSION_OLDER_THAN(5,1,0) 13 | #include "EditorStyleSet.h" 14 | #else 15 | #include "Styling/AppStyle.h" 16 | #endif 17 | 18 | class SUBSYSTEMBROWSER_API FSubsystemBrowserStyle final : public FSlateStyleSet 19 | { 20 | public: 21 | static const FName StyleName; 22 | 23 | static const FName PanelIconName; 24 | static const FName FolderOpenName; 25 | static const FName FolderClosedName; 26 | 27 | /** Register style set */ 28 | static void Register(); 29 | /** Unregister style set */ 30 | static void UnRegister(); 31 | 32 | /** Access the singleton instance for this style set */ 33 | static FSubsystemBrowserStyle& Get(); 34 | 35 | FSubsystemBrowserStyle(); 36 | virtual ~FSubsystemBrowserStyle() = default; 37 | 38 | private: 39 | 40 | static TSharedPtr StyleInstance; 41 | }; 42 | 43 | /** 44 | * Internal class for style system compatibility between older and newer engines. 45 | */ 46 | struct SUBSYSTEMBROWSER_API FStyleHelper 47 | { 48 | static const ISlateStyle& Get(); 49 | static const FSlateBrush* GetBrush(const FName& InName); 50 | static FSlateFontInfo GetFontStyle(const FName& InName); 51 | static FSlateIcon GetSlateIcon(const FName& InIcon); 52 | static FSlateColor GetSlateColor(const FName& Name); 53 | 54 | template 55 | static const T& GetWidgetStyle(const FName& InName) 56 | { 57 | #if UE_VERSION_OLDER_THAN(5,1,0) 58 | return FEditorStyle::GetWidgetStyle(InName); 59 | #else 60 | return FAppStyle::GetWidgetStyle(InName); 61 | #endif 62 | } 63 | 64 | template 65 | static const T* GetWidgetStylePtr(const FName& InName) 66 | { 67 | return &GetWidgetStyle(InName); 68 | } 69 | }; 70 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/SubsystemBrowserUtils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Misc/Optional.h" 6 | #include "Misc/OutputDevice.h" 7 | #include "Widgets/Notifications/SNotificationList.h" 8 | 9 | /** 10 | * Collection of various helpers to use 11 | */ 12 | struct SUBSYSTEMBROWSER_API FSubsystemBrowserUtils 13 | { 14 | /** 15 | * Get "Smart metadata" property. 16 | * 17 | * Performs a lookup for possible function/property match, if none found uses raw string value. 18 | * 19 | * Functions should be a UFUNCTION() returning FString or FText 20 | * Properties should be a UPROPERTY() of FString or FText type 21 | * 22 | */ 23 | static TOptional GetSmartMetaValue(UObject* InObject, const FName& InName, bool bHierarchical = false, bool bWarn = false); 24 | 25 | /** 26 | * Get info about subsystem "Owner" 27 | */ 28 | static FString GetSubsystemOwnerName(UObject* InObject); 29 | 30 | /** 31 | * Finds the base directory for a given module. 32 | */ 33 | static FString GetModulePathForClass(UClass* InClass); 34 | 35 | /** 36 | * Find fully qualified class module name 37 | */ 38 | static bool GetModuleDetailsForClass(UClass* InClass, FString& OutName, bool& OutGameFlag); 39 | 40 | /** 41 | * Find plugin name that contains class 42 | */ 43 | static bool GetPluginDetailsForClass(UClass* InClass, FString& OutName, FString& OutFriendlyName); 44 | 45 | /** 46 | * Collect related source files belonging to specified class 47 | */ 48 | static void CollectSourceFiles(UClass* InClass, TArray& OutSourceFiles); 49 | 50 | struct FClassFieldStats 51 | { 52 | int32 NumProperties = 0; 53 | int32 NumEditable = 0; 54 | int32 NumVisible = 0; 55 | // number of properties with Config flag 56 | int32 NumConfig = 0; 57 | // number of properties with Config flag that are Editable 58 | int32 NumConfigWithEdit = 0; 59 | // number of CallInEditor functions 60 | int32 NumCallable = 0; 61 | }; 62 | 63 | /** 64 | * Collect property display info for tooltip 65 | */ 66 | static FClassFieldStats GetClassFieldStats(UClass* InClass); 67 | 68 | /** 69 | * Find metadata value in class 70 | */ 71 | static TOptional GetMetadataOptional(UClass* InClass, FName InKey); 72 | 73 | /** 74 | * Find metadata value in entire hierarchy of classes 75 | */ 76 | static TOptional GetMetadataHierarchical(UClass* InClass, FName InKey); 77 | 78 | /** 79 | * Put text into clipboard 80 | */ 81 | static void SetClipboardText(const FString& ClipboardText); 82 | 83 | /** 84 | * @brief 85 | * @param InText 86 | * @param InType 87 | */ 88 | static void ShowBrowserInfoMessage(FText InText, SNotificationItem::ECompletionState InType); 89 | 90 | /** 91 | * @brief Generate config export string for specified item 92 | * @param Item 93 | * @param bModifiedOnly 94 | * @return 95 | */ 96 | static FString GenerateConfigExport(const struct FSubsystemTreeSubsystemItem* Item, bool bModifiedOnly); 97 | 98 | /** 99 | * 100 | * @param Item 101 | * @param bModifiedOnly 102 | * @return 103 | */ 104 | static FString GenerateConfigExport(const UObject* Item, bool bModifiedOnly); 105 | 106 | /** 107 | * 108 | */ 109 | static bool TryUpdateDefaultConfigFile(UObject* Object); 110 | 111 | /** 112 | * Dump class flags to output 113 | * 114 | * Example: `SB.PrintClass /Script/SubsystemBrowser.SubsystemBrowserTestSubsystem` 115 | */ 116 | static void PrintClassDetails(const TArray& InArgs, UWorld* InWorld, FOutputDevice& InLog); 117 | 118 | /** 119 | * Dump property flags to output 120 | * 121 | * Example: `SB.PrintProperty /Script/SubsystemBrowser.SubsystemBrowserTestSubsystem SingleDelegate` 122 | */ 123 | static void PrintPropertyDetails(const TArray& InArgs, UWorld* InWorld, FOutputDevice& InLog); 124 | 125 | /** 126 | * Build a text description for a world 127 | */ 128 | static FText GetWorldDescription(const UWorld* World); 129 | 130 | /** 131 | * Locate class by name (engine-independent version) 132 | * @param ClassName class package path to find 133 | * @return found class instance, or null 134 | */ 135 | static UClass* TryFindClassByName(const FString& ClassName); 136 | 137 | /** 138 | * Default implementation of important subobject selection. 139 | * 140 | * This is to prevent inventing detail customization and other tricks to display subsystem subobject data 141 | * 142 | * @param InSubsystem input subsystem to request subobjects from 143 | * @param OutData collection of subobjects to display 144 | */ 145 | static void DefaultSelectSubsystemSubobjects(UObject* InSubsystem, TArray& OutData); 146 | }; 147 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemBrowserPanel.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "SlateFwd.h" 7 | #include "Widgets/SWidget.h" 8 | #include "Widgets/SCompoundWidget.h" 9 | #include "UI/SubsystemTreeWidget.h" 10 | #include "UI/SubsystemTableItem.h" 11 | #include "UI/SubsystemTableHeader.h" 12 | #include "Model/SubsystemBrowserModel.h" 13 | 14 | class SComboButton; 15 | struct FPropertyAndParent; 16 | class IDetailsView; 17 | class ITableRow; 18 | 19 | /** 20 | * Subsystem browser tab content widget 21 | */ 22 | class SSubsystemBrowserPanel : public SCompoundWidget 23 | { 24 | friend class SSubsystemsHeaderRow; 25 | public: 26 | SLATE_BEGIN_ARGS(SSubsystemBrowserPanel) 27 | :_InWorld(nullptr) 28 | {} 29 | SLATE_ARGUMENT(UWorld*, InWorld) 30 | SLATE_END_ARGS() 31 | 32 | SSubsystemBrowserPanel(); 33 | virtual ~SSubsystemBrowserPanel(); 34 | 35 | void Construct(const FArguments& InArgs); 36 | 37 | bool IsItemSelected(SubsystemTreeItemPtr Item); 38 | 39 | protected: 40 | virtual void Tick( const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime ) override; 41 | 42 | void Populate(); 43 | void EmptyTreeItems(); 44 | 45 | void RefreshView() { bNeedsRefresh = true; } 46 | void RefreshDetails() { bNeedRefreshDetails = true; } 47 | void RefreshColumns() { bNeedsRefresh = true; bNeedListRebuild = true; bNeedsColumnRefresh = true; } 48 | void RequestSort() { bSortDirty = true; } 49 | void FullRefresh(); 50 | 51 | // Search bar 52 | 53 | void TransformItemToString(const ISubsystemTreeItem& Level, TArray& OutSearchStrings) const; 54 | void SetFilterText(const FText& InFilterText); 55 | FText GetSearchBoxText() const; 56 | FText GetFilterStatusText() const; 57 | FSlateColor GetFilterStatusTextColor() const; 58 | 59 | // View options panel 60 | 61 | void BrowserSplitterFinishedResizing(); 62 | 63 | FSlateColor GetViewOptionsButtonForegroundColor() const; 64 | TSharedRef GetViewOptionsButtonContent(); 65 | void BuildColumnPickerContent(FMenuBuilder& MenuBuilder); 66 | void BuildCategoryPickerContent(FMenuBuilder& MenuBuilder); 67 | 68 | void ToggleDisplayCategory(FSubsystemTreeItemID InCategory); 69 | bool GetCategoryDisplayStatus(FSubsystemTreeItemID InCategory); 70 | 71 | // Tree view 72 | 73 | void SetupColumns(SHeaderRow& HeaderRow); 74 | TSharedRef GenerateTreeRow(SubsystemTreeItemPtr Item, const TSharedRef& OwnerTable); 75 | void GetChildrenForTree(SubsystemTreeItemPtr Item, TArray& OutChildren); 76 | void OnExpansionChanged(SubsystemTreeItemPtr Item, bool bIsItemExpanded); 77 | void OnSelectionChanged(const SubsystemTreeItemPtr Item, ESelectInfo::Type SelectInfo); 78 | void OnTreeViewMouseButtonDoubleClick(SubsystemTreeItemPtr Item); 79 | 80 | EColumnSortMode::Type GetColumnSortMode(FName ColumnId) const; 81 | void OnColumnSortModeChanged(const EColumnSortPriority::Type SortPriority, const FName& ColumnId, const EColumnSortMode::Type InSortMode); 82 | void SortItems(TArray& Items) const; 83 | 84 | void ToggleDisplayColumn(FName ColumnName); 85 | void ToggleTableColoring(); 86 | void ToggleForceHiddenPropertyVisibility(); 87 | void ToggleShouldShowOnlyGame(); 88 | void ToggleShouldShowOnlyPlugins(); 89 | void ToggleShouldShowOnlyViewable(); 90 | 91 | void ShowPluginSettingsTab() const; 92 | void ShowSubsystemSettingsTab() const; 93 | 94 | FReply RequestRefresh(); 95 | 96 | // Selection and Expansion 97 | 98 | TMap GetParentsExpansionState() const; 99 | void SetParentsExpansionState(const TMap& ExpansionInfo); 100 | void ResetParentsExpansionState(); 101 | 102 | SubsystemTreeItemPtr GetFirstSelectedItem() const; 103 | const FSubsystemTreeSubsystemItem* GetFirstSelectedSubsystem() const; 104 | FSubsystemTreeItemID GetFirstSelectedItemId() const; 105 | 106 | // World picker 107 | 108 | const FSlateBrush* GetWorldsMenuBrush() const; 109 | FText GetCurrentWorldText() const; 110 | void OnSelectWorld(TWeakObjectPtr InWorld); 111 | bool IsWorldChecked(TWeakObjectPtr InWorld); 112 | TSharedRef GetWorldsButtonContent(); 113 | 114 | void HandlePIEStart(const bool bIsSimulating); 115 | void HandlePIEEnd(const bool bIsSimulating); 116 | void HandleWorldChange(UWorld* InWorld); 117 | 118 | // Details 119 | 120 | TSharedRef CreateDetails(); 121 | void RecreateDetails(); 122 | void SetSelectedObject(SubsystemTreeItemPtr Item); 123 | void ResetSelectedObject(); 124 | 125 | static bool IsDetailsPropertyReadOnly(const FPropertyAndParent& InProperty); 126 | static bool IsDetailsPropertyVisible(const FPropertyAndParent& InProperty); 127 | 128 | // Item context menu 129 | 130 | TSharedPtr ConstructSubsystemContextMenu(); 131 | bool HasSelectedSubsystem() const; 132 | 133 | // Settings 134 | 135 | void OnSettingsChanged(FName InPropertyName); 136 | 137 | // Data tracking 138 | 139 | void OnModulesChanged(FName ModuleThatChanged, EModuleChangeReason ReasonForChange); 140 | void OnSubsystemDataChanged(TSharedRef Item); 141 | 142 | private: 143 | TSharedPtr SubsystemModel; 144 | 145 | TSharedPtr DetailsView; 146 | TSharedPtr DetailsViewBox; 147 | TSharedPtr VerticalBox; 148 | TSharedPtr VerticalBoxBorder; 149 | TSharedPtr BrowserSplitter; 150 | 151 | TSharedPtr ViewOptionsComboButton; 152 | 153 | TSharedPtr SearchBoxSubsystemFilter; 154 | TSharedPtr CategoryFilter; 155 | int32 FilteredSubsystemsCount = 0; 156 | 157 | TSharedPtr HeaderRowWidget; 158 | 159 | TArray DynamicColumnSlots; 160 | 161 | /** Root items for the tree widget */ 162 | TArray RootTreeItems; 163 | 164 | /** All items that are currently displayed in the tree widget */ 165 | TMap TreeItemMap; 166 | 167 | TSharedPtr TreeWidget; 168 | 169 | TOptional> PendingSelectionObject; 170 | 171 | bool bIsReentrant = false; 172 | bool bFullRefresh = true; 173 | bool bNeedsRefresh = true; // needs initial update 174 | bool bNeedRefreshDetails = false; 175 | bool bUpdatingSelection = false; 176 | bool bLoadedExpansionSettings = false; 177 | bool bNeedsExpansionSettingsSave = false; 178 | bool bNeedListRebuild = true; // needs initial header update to upply config 179 | bool bNeedsColumnRefresh = false; // refresh header widgets? 180 | 181 | // column to sort by 182 | FName SortByColumn; 183 | // sorting order 184 | EColumnSortMode::Type SortMode = EColumnSortMode::None; 185 | // 186 | bool bSortDirty = false; 187 | }; 188 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemDetailsCustomizations.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemDetailsCustomizations.h" 4 | 5 | #include "SubsystemBrowserModule.h" 6 | 7 | 8 | void FSubsystemBrowserModule::CustomizeDetailsView(TSharedRef DetailsView, FName Usage) 9 | { 10 | // super secret 11 | } 12 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemDetailsCustomizations.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "IDetailsView.h" 6 | #include "IDetailCustomization.h" 7 | #include "IDetailChildrenBuilder.h" 8 | #include "IPropertyTypeCustomization.h" 9 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableHeader.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "UI/SubsystemTableHeader.h" 4 | #include "UI/SubsystemBrowserPanel.h" 5 | #include "Model/SubsystemBrowserModel.h" 6 | #include "Model/SubsystemBrowserColumn.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | void SSubsystemsHeaderRow::Construct(const FArguments& InArgs, const TSharedPtr& InModel, const TSharedPtr& InBrowser) 11 | { 12 | Model = InModel; 13 | Browser = InBrowser; 14 | 15 | SHeaderRow::Construct(InArgs); 16 | 17 | RebuildColumns(); 18 | } 19 | 20 | void SSubsystemsHeaderRow::RebuildColumns() 21 | { 22 | Browser.Pin()->SetupColumns(*this); 23 | } 24 | 25 | #undef LOCTEXT_NAMESPACE 26 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableHeader.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Widgets/Views/SHeaderRow.h" 6 | 7 | class FSubsystemModel; 8 | class SSubsystemBrowserPanel; 9 | 10 | class SSubsystemsHeaderRow : public SHeaderRow 11 | { 12 | public: 13 | void Construct(const FArguments& InArgs, const TSharedPtr& InModel, const TSharedPtr& InBrowser); 14 | void RebuildColumns(); 15 | 16 | private: 17 | TSharedPtr Model; 18 | TWeakPtr Browser; 19 | }; 20 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableItem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "UI/SubsystemTableItem.h" 4 | #include "SubsystemBrowserSettings.h" 5 | #include "SubsystemBrowserStyle.h" 6 | #include "Model/SubsystemBrowserModel.h" 7 | #include "UI/SubsystemBrowserPanel.h" 8 | #include "UI/SubsystemTableItemTooltip.h" 9 | #include "SlateOptMacros.h" 10 | 11 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 12 | 13 | void SSubsystemTableItem::Construct(const FArguments& InArgs, TSharedRef OwnerTableView) 14 | { 15 | Model = InArgs._InModel; 16 | Item = InArgs._InItemModel; 17 | Browser = InArgs._InBrowser; 18 | IsItemExpanded = InArgs._IsItemExpanded; 19 | HighlightText = InArgs._HighlightText; 20 | 21 | SetToolTip(SNew(SSubsystemTableItemTooltip).SubsystemTableItem(SharedThis(this))); 22 | 23 | FSuperRowType::FArguments Args = FSuperRowType::FArguments(); 24 | Super::Construct(Args, OwnerTableView); 25 | } 26 | 27 | BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION 28 | TSharedRef SSubsystemTableItem::GenerateWidgetForColumn(const FName& ColumnID) 29 | { 30 | if (!Item.IsValid()) 31 | { 32 | return SNullWidget::NullWidget; 33 | } 34 | 35 | TSharedPtr TableRowContent = SNullWidget::NullWidget; 36 | 37 | SubsystemColumnPtr Column = Model->FindTableColumn(ColumnID); 38 | if (Column.IsValid() && Model->ShouldShowColumn(Column) ) 39 | { 40 | TableRowContent = Column->GenerateColumnWidget(Item.ToSharedRef(), SharedThis(this)); 41 | } 42 | 43 | return TableRowContent.ToSharedRef(); 44 | } 45 | END_SLATE_FUNCTION_BUILD_OPTIMIZATION 46 | 47 | #undef LOCTEXT_NAMESPACE 48 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableItem.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Widgets/Views/STableRow.h" 6 | #include "Widgets/Views/SListView.h" // NoPCH 7 | #include "Model/SubsystemBrowserDescriptor.h" 8 | 9 | class FSubsystemModel; 10 | class SSubsystemBrowserPanel; 11 | 12 | class SSubsystemTableItem : public SMultiColumnTableRow 13 | { 14 | using Super = SMultiColumnTableRow; 15 | public: 16 | SLATE_BEGIN_ARGS( SSubsystemTableItem ) 17 | : _IsItemExpanded( false ) 18 | {} 19 | /** Data for the world */ 20 | SLATE_ARGUMENT(TSharedPtr, InModel) 21 | 22 | /** Item model this widget represents */ 23 | SLATE_ARGUMENT(SubsystemTreeItemPtr, InItemModel) 24 | 25 | /** The hierarchy that this item belongs to */ 26 | SLATE_ARGUMENT(TSharedPtr, InBrowser) 27 | 28 | /** True when this item has children and is expanded */ 29 | SLATE_ATTRIBUTE(bool, IsItemExpanded) 30 | 31 | /** The string in the title to highlight */ 32 | SLATE_ATTRIBUTE(FText, HighlightText) 33 | 34 | SLATE_END_ARGS() 35 | 36 | /** Constructs this widget with InArgs */ 37 | void Construct(const FArguments& InArgs, TSharedRef OwnerTableView); 38 | 39 | virtual TSharedRef GenerateWidgetForColumn( const FName& ColumnName ) override; 40 | 41 | public: 42 | TSharedPtr Model; 43 | SubsystemTreeItemPtr Item; 44 | TSharedPtr Browser; 45 | 46 | TAttribute HighlightText; 47 | TAttribute IsItemExpanded; 48 | }; 49 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableItemTooltip.cpp: -------------------------------------------------------------------------------- 1 | #include "UI/SubsystemTableItemTooltip.h" 2 | #include "SubsystemBrowserFlags.h" 3 | #include "SubsystemBrowserModule.h" 4 | #include "SubsystemBrowserSettings.h" 5 | #include "SubsystemBrowserStyle.h" 6 | #include "Widgets/Text/STextBlock.h" 7 | 8 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 9 | 10 | void SSubsystemTableItemTooltip::Construct(const FArguments& InArgs) 11 | { 12 | SubsystemTableItem = InArgs._SubsystemTableItem; 13 | 14 | Super::Construct( 15 | SToolTip::FArguments() 16 | .TextMargin(1.0f) 17 | .BorderImage(FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.ToolTipBorder")) 18 | ); 19 | } 20 | 21 | void SSubsystemTableItemTooltip::OnOpening() 22 | { 23 | TSharedPtr TableItem = SubsystemTableItem.Pin(); 24 | if (TableItem.IsValid()) 25 | { 26 | SetContentWidget(CreateToolTipWidget(TableItem.ToSharedRef())); 27 | } 28 | } 29 | 30 | void SSubsystemTableItemTooltip::OnClosed() 31 | { 32 | #if UE_VERSION_OLDER_THAN(5,0,0) 33 | SetContentWidget(SNullWidget::NullWidget); 34 | #else 35 | ResetContentWidget(); 36 | #endif 37 | } 38 | 39 | TSharedRef SSubsystemTableItemTooltip::CreateToolTipWidget(TSharedRef TableItem) const 40 | { 41 | if (TableItem->Item.IsValid() && !TableItem->Item->IsStale()) 42 | { 43 | FSubsystemTableItemTooltipBuilder TooltipBuilder(TableItem); 44 | TableItem->Item->GenerateTooltip(TooltipBuilder); 45 | 46 | // Apply external customizations for tooltips 47 | FSubsystemBrowserModule::OnGenerateTooltip.Broadcast(TableItem->Item.ToSharedRef(), TooltipBuilder); 48 | 49 | TSharedRef OverallTooltipVBox = SNew(SVerticalBox); 50 | 51 | OverallTooltipVBox->AddSlot() 52 | .AutoHeight() 53 | .Padding(0, 0, 0, 4) 54 | [ 55 | SNew(SBorder) 56 | .Padding(6) 57 | .BorderImage(FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.ContentBorder")) 58 | [ 59 | SNew(SVerticalBox) 60 | 61 | + SVerticalBox::Slot() 62 | .AutoHeight() 63 | [ 64 | SNew(SHorizontalBox) 65 | 66 | + SHorizontalBox::Slot() 67 | .AutoWidth() 68 | .VAlign(VAlign_Center) 69 | .Padding(0, 0, 4, 0) 70 | [ 71 | SNew(STextBlock) 72 | .Text(TableItem->Item->GetDisplayName()) 73 | .Font(FStyleHelper::GetFontStyle("ContentBrowser.TileViewTooltip.NameFont")) 74 | ] 75 | ] 76 | 77 | ] 78 | ]; 79 | 80 | if (TooltipBuilder.Primary.IsValid()) 81 | { 82 | OverallTooltipVBox->AddSlot() 83 | .AutoHeight() 84 | [ 85 | SNew(SBorder) 86 | .Padding(6) 87 | .BorderImage(FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.ContentBorder")) 88 | [ 89 | TooltipBuilder.Primary.ToSharedRef() 90 | ] 91 | ]; 92 | } 93 | 94 | if (TooltipBuilder.Secondary.IsValid()) 95 | { 96 | OverallTooltipVBox->AddSlot() 97 | .AutoHeight() 98 | [ 99 | SNew(SBorder) 100 | .Padding(6) 101 | .BorderImage(FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.ContentBorder")) 102 | [ 103 | TooltipBuilder.Secondary.ToSharedRef() 104 | ] 105 | ]; 106 | } 107 | 108 | if (TooltipBuilder.UserTooltip.IsValid()) 109 | { 110 | OverallTooltipVBox->AddSlot() 111 | .AutoHeight() 112 | .Padding(0, 4, 0, 0) 113 | [ 114 | SNew(SBorder) 115 | .Padding(6) 116 | .BorderImage(FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.ContentBorder")) 117 | [ 118 | SNew(SVerticalBox) 119 | 120 | + SVerticalBox::Slot() 121 | .AutoHeight() 122 | [ 123 | SNew(SHorizontalBox) 124 | 125 | + SHorizontalBox::Slot() 126 | .AutoWidth() 127 | .VAlign(VAlign_Center) 128 | .Padding(0, 0, 4, 0) 129 | [ 130 | TooltipBuilder.UserTooltip.ToSharedRef() 131 | ] 132 | ] 133 | ] 134 | ]; 135 | } 136 | 137 | return SNew(SBorder) 138 | .Padding(6) 139 | .BorderImage( FStyleHelper::GetBrush("ContentBrowser.TileViewTooltip.NonContentBorder") ) 140 | [ 141 | OverallTooltipVBox 142 | ]; 143 | } 144 | 145 | return SNullWidget::NullWidget; 146 | } 147 | 148 | void FSubsystemTableItemTooltipBuilder::AddPrimary(const FText& Key, const FText& Value, uint32 DisplayFlags) 149 | { 150 | if (!Primary.IsValid()) 151 | { 152 | SAssignNew(Primary, SVerticalBox); 153 | } 154 | AddBox(Primary.ToSharedRef(), Key, Value, DisplayFlags); 155 | } 156 | 157 | void FSubsystemTableItemTooltipBuilder::AddSecondary(const FText& Key, const FText& Value, uint32 DisplayFlags) 158 | { 159 | if (!Secondary.IsValid()) 160 | { 161 | SAssignNew(Secondary, SVerticalBox); 162 | } 163 | AddBox(Secondary.ToSharedRef(), Key, Value, DisplayFlags); 164 | } 165 | 166 | void FSubsystemTableItemTooltipBuilder::SetUserTooltip(const FText& Value) 167 | { 168 | SAssignNew(UserTooltip, STextBlock) 169 | .Text(Value) 170 | .ColorAndOpacity(FSlateColor::UseForeground()) 171 | .WrapTextAt(400.0f); 172 | } 173 | 174 | bool FSubsystemTableItemTooltipBuilder::HasAnyData() const 175 | { 176 | return Primary.IsValid() || Secondary.IsValid() || UserTooltip.IsValid(); 177 | } 178 | 179 | bool FSubsystemTableItemTooltipBuilder::IsInAdvancedMode() const 180 | { 181 | return USubsystemBrowserSettings::Get()->ShouldShowDetailsTooltips(); // || IsKeyPressed(LCtrl/LAlt)? 182 | } 183 | 184 | void FSubsystemTableItemTooltipBuilder::AddBox(TSharedRef Target, const FText& Key, const FText& Value, uint32 DisplayFlags) 185 | { 186 | const bool bImportant = (DisplayFlags & DF_IMPORTANT) != 0; 187 | const bool bHighlightable = (DisplayFlags & DF_WITH_HIGHLIGHT) != 0; 188 | 189 | FWidgetStyle ImportantStyle; 190 | ImportantStyle.SetForegroundColor(FLinearColor(1, 0.5, 0, 1)); 191 | 192 | Target->AddSlot() 193 | .AutoHeight() 194 | .Padding(0, 1) 195 | [ 196 | SNew(SHorizontalBox) 197 | + SHorizontalBox::Slot() 198 | .AutoWidth() 199 | .Padding(0, 0, 4, 0) 200 | [ 201 | SNew(STextBlock) 202 | .Text(FText::Format(LOCTEXT("SubsystemItemTooltipFormat", "{0}:"), Key)) 203 | .ColorAndOpacity( bImportant ? ImportantStyle.GetSubduedForegroundColor() : FSlateColor::UseSubduedForeground()) 204 | ] 205 | 206 | + SHorizontalBox::Slot() 207 | .AutoWidth() 208 | [ 209 | SNew(STextBlock) 210 | .Text(Value) 211 | .ColorAndOpacity(bImportant ? ImportantStyle.GetForegroundColor() : FSlateColor::UseForeground()) 212 | .HighlightText(bHighlightable ? Item->HighlightText : FText()) 213 | .WrapTextAt(400.0f) 214 | ] 215 | 216 | ]; 217 | } 218 | 219 | #undef LOCTEXT_NAMESPACE 220 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTableItemTooltip.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "UI/SubsystemTableItem.h" 4 | #include "Widgets/SToolTip.h" 5 | #include "Widgets/Text/STextBlock.h" 6 | 7 | class SSubsystemTableItemTooltip : public SToolTip 8 | { 9 | using Super = SToolTip; 10 | public: 11 | SLATE_BEGIN_ARGS(SSubsystemTableItemTooltip) 12 | { } 13 | SLATE_ARGUMENT(TSharedPtr, SubsystemTableItem) 14 | SLATE_END_ARGS() 15 | 16 | void Construct(const FArguments& InArgs); 17 | 18 | // IToolTip interface 19 | virtual bool IsEmpty() const override 20 | { 21 | return !SubsystemTableItem.IsValid(); 22 | } 23 | 24 | virtual void OnOpening() override; 25 | 26 | virtual void OnClosed() override; 27 | 28 | /** Creates a tooltip widget for this item */ 29 | TSharedRef CreateToolTipWidget(TSharedRef TableItem) const; 30 | 31 | private: 32 | TWeakPtr SubsystemTableItem; 33 | }; 34 | 35 | /** 36 | * 37 | */ 38 | class SUBSYSTEMBROWSER_API FSubsystemTableItemTooltipBuilder 39 | { 40 | friend SSubsystemTableItemTooltip; 41 | 42 | TSharedRef Item; 43 | TSharedPtr Primary; 44 | TSharedPtr Secondary; 45 | TSharedPtr UserTooltip; 46 | public: 47 | enum 48 | { 49 | DF_NONE = 0, 50 | DF_IMPORTANT = 1 << 0, 51 | DF_WITH_HIGHLIGHT = 1 << 1, 52 | }; 53 | 54 | explicit FSubsystemTableItemTooltipBuilder(TSharedRef Item) : Item(Item) {} 55 | 56 | void AddPrimary(const FText& Key, const FText& Value, uint32 DisplayFlags = DF_NONE); 57 | void EmptyPrimary() { Primary.Reset(); } 58 | 59 | void AddSecondary(const FText& Key, const FText& Value, uint32 DisplayFlags = DF_NONE); 60 | void EmptySecondary() { Secondary.Reset(); } 61 | 62 | void SetUserTooltip(const FText& Value); 63 | void EmptyUserTooltip() { UserTooltip.Reset(); } 64 | 65 | bool HasAnyData() const; 66 | bool IsInAdvancedMode() const; 67 | private: 68 | void AddBox(TSharedRef Target, const FText& Key, const FText& Value, uint32 DisplayFlags); 69 | }; 70 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTreeWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "UI/SubsystemTreeWidget.h" 4 | #include "Model/SubsystemBrowserModel.h" 5 | #include "UI/SubsystemBrowserPanel.h" 6 | 7 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 8 | 9 | void SSubsystemsTreeWidget::Construct(const FArguments& InArgs, const TSharedPtr& InModel, const TSharedPtr& InBrowser) 10 | { 11 | STreeView::Construct(InArgs); 12 | 13 | Model = InModel; 14 | Browser = InBrowser; 15 | } 16 | 17 | #undef LOCTEXT_NAMESPACE 18 | -------------------------------------------------------------------------------- /Source/SubsystemBrowser/UI/SubsystemTreeWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Widgets/Views/STreeView.h" 6 | #include "Model/SubsystemBrowserDescriptor.h" 7 | 8 | class FSubsystemModel; 9 | class SSubsystemBrowserPanel; 10 | 11 | class SSubsystemsTreeWidget : public STreeView 12 | { 13 | public: 14 | void Construct(const FArguments& InArgs, const TSharedPtr& InModel, const TSharedPtr& InBrowser); 15 | 16 | private: 17 | TSharedPtr Model; 18 | TWeakPtr Browser; 19 | }; 20 | -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTestSubsystem.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserTestSubsystem.h" 4 | 5 | #include "ISettingsModule.h" 6 | #include "Modules/ModuleManager.h" 7 | #include "Misc/EngineVersionComparison.h" 8 | #include "Blueprint/UserWidget.h" 9 | #include "UObject/Package.h" 10 | 11 | USubsystemBrowserTestSubsystem::USubsystemBrowserTestSubsystem() 12 | { 13 | IAObject = CreateDefaultSubobject("InteractionManager"); 14 | CAObject = CreateDefaultSubobject("ChunkManager"); 15 | } 16 | 17 | bool USubsystemBrowserTestSubsystem::ShouldCreateSubsystem(UObject* Outer) const 18 | { 19 | #ifdef WITH_SB_HOST_PROJECT 20 | return Super::ShouldCreateSubsystem(Outer); 21 | #else 22 | return GetClass() != USubsystemBrowserTestSubsystem::StaticClass() && Super::ShouldCreateSubsystem(Outer); 23 | #endif 24 | } 25 | 26 | void USubsystemBrowserTestSubsystem::Initialize(FSubsystemCollectionBase& Collection) 27 | { 28 | Super::Initialize(Collection); 29 | } 30 | 31 | void USubsystemBrowserTestSubsystem::Deinitialize() 32 | { 33 | Super::Deinitialize(); 34 | } 35 | 36 | FString USubsystemBrowserTestSubsystem::GetSBOwnerName() const 37 | { 38 | return TEXT("Hello from ") + GetNameSafe(GetOuter()); 39 | } 40 | 41 | TArray USubsystemBrowserTestSubsystem::GetImportantSubobjectsToDisplay() const 42 | { 43 | return TArray({ IAObject, CAObject }); 44 | } 45 | 46 | void USubsystemBrowserTestSubsystem::EditorFunction() 47 | { 48 | ++EditorFunctionCallCounter; 49 | } 50 | 51 | void USubsystemBrowserTestSubsystem::EditorFunctionReset() 52 | { 53 | EditorFunctionCallCounter = 0; 54 | } 55 | 56 | void USubsystemBrowserTestSubsystem::FillArrays() 57 | { 58 | for (uint32 Idx = 0; Idx < ArrayIntegersFillValue; ++Idx) 59 | { 60 | ArrayIntegers.Add(FMath::RandHelper(100000)); 61 | } 62 | } 63 | 64 | void USubsystemBrowserTestSubsystem::EmptyArrays() 65 | { 66 | ArrayIntegers.Empty(); 67 | } 68 | 69 | void USubsystemBrowserTestSubsystem::FillArrayObjs() 70 | { 71 | for (uint32 Idx = 0; Idx < ArrayObjectsFillValue; ++Idx) 72 | { 73 | auto Object = NewObject(GetTransientPackage(), NAME_None, RF_Transient, nullptr); 74 | Object->Obz = ChainObjects; 75 | ChainObjects = Object; 76 | 77 | ArrayObjects.Add(Object); 78 | } 79 | } 80 | 81 | void USubsystemBrowserTestSubsystem::EmptyArrayObjs() 82 | { 83 | ChainObjects = nullptr; 84 | } 85 | 86 | void USubsystemBrowserTestSubsystem::FillArrayWidgets() 87 | { 88 | for (uint32 Idx = 0; Idx < ArrayWidgetsFillValue; ++Idx) 89 | { 90 | auto Widget = CreateWidget(GetWorld()); 91 | ArrayWidgets.Add(Widget); 92 | } 93 | } 94 | 95 | void USubsystemBrowserTestSubsystem::EmptyArrayWidgets() 96 | { 97 | ArrayWidgets.Empty(); 98 | } 99 | -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTestSubsystem.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "CoreFwd.h" 6 | #include "GameFramework/Actor.h" 7 | #include "Engine/DataAsset.h" 8 | #include "Engine/World.h" 9 | #include "Subsystems/WorldSubsystem.h" 10 | #include "Blueprint/UserWidget.h" 11 | #include "Misc/EngineVersionComparison.h" 12 | #include "SubsystemBrowserTestSubsystem.generated.h" 13 | 14 | class USBDemoObject; 15 | 16 | UENUM() 17 | enum class ESBDemoEnum : uint8 18 | { 19 | Alpha, 20 | Beta, 21 | Gamma, 22 | Delta, 23 | Epsilon 24 | }; 25 | 26 | USTRUCT() 27 | struct SUBSYSTEMBROWSERTESTS_API FSBDemoStruct 28 | { 29 | GENERATED_BODY() 30 | 31 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 32 | int32 Foo = 0; 33 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 34 | int32 Bar = 0; 35 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 36 | ESBDemoEnum Baz = ESBDemoEnum::Alpha; 37 | }; 38 | 39 | UCLASS(EditInlineNew) 40 | class SUBSYSTEMBROWSERTESTS_API USBDemoObject : public UObject 41 | { 42 | GENERATED_BODY() 43 | public: 44 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 45 | int32 Foo; 46 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 47 | int32 Bar; 48 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 49 | ESBDemoEnum Baz; 50 | UPROPERTY(EditAnywhere, Category="SubsystemBrowserTest") 51 | FSBDemoStruct Structz; 52 | UPROPERTY(EditAnywhere, Instanced, Category="SubsystemBrowserTest") 53 | USBDemoObject* Obz = nullptr; 54 | }; 55 | 56 | UCLASS() 57 | class USBDemoChunkAssistantManager : public UObject 58 | { 59 | GENERATED_BODY() 60 | public: 61 | UPROPERTY(EditAnywhere, Category="Manager") 62 | TArray ChunkNames; 63 | 64 | UFUNCTION(CallInEditor, Category="Manager") 65 | void Selfdestruct() 66 | { 67 | #if !UE_VERSION_OLDER_THAN(5,0,0) 68 | MarkAsGarbage(); 69 | #endif 70 | } 71 | }; 72 | 73 | UCLASS() 74 | class USBDemoInteractionAssistantObject : public UObject 75 | { 76 | GENERATED_BODY() 77 | public: 78 | UPROPERTY(VisibleAnywhere, Category="Manager") 79 | TWeakObjectPtr LastUsedObject; 80 | }; 81 | 82 | UCLASS() 83 | class USBDemoWidget : public UUserWidget 84 | { 85 | GENERATED_BODY() 86 | public: 87 | UPROPERTY(VisibleAnywhere, meta=(BindWidgetOptional), Category="SubsystemBrowserTest") 88 | class UButton* SampleButton; 89 | UPROPERTY(VisibleAnywhere, meta=(BindWidgetOptional), Category="SubsystemBrowserTest") 90 | class UTextBlock* SampleTextBlock; 91 | }; 92 | 93 | DECLARE_DYNAMIC_DELEGATE(FSBTestDynamicDelegate); 94 | DECLARE_DYNAMIC_MULTICAST_DELEGATE(FSBTestDynamicMCDelegate); 95 | 96 | /** 97 | * 98 | */ 99 | UCLASS(Abstract, Config=Test, DefaultConfig, 100 | meta=(SBTooltip="SB Tooltip Text", SBColor="(R=255,G=128,B=0)", SBOwnerName="GetSBOwnerName", SBGetSubobjects="GetImportantSubobjectsToDisplay")) 101 | class SUBSYSTEMBROWSERTESTS_API USubsystemBrowserTestSubsystem : public UWorldSubsystem 102 | { 103 | GENERATED_BODY() 104 | public: 105 | USubsystemBrowserTestSubsystem(); 106 | 107 | virtual bool ShouldCreateSubsystem(UObject* Outer) const override; 108 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 109 | virtual void Deinitialize() override; 110 | 111 | UFUNCTION() 112 | FString GetSBOwnerName() const; 113 | 114 | UFUNCTION() 115 | TArray GetImportantSubobjectsToDisplay() const; 116 | 117 | UPROPERTY() 118 | USBDemoInteractionAssistantObject* IAObject = nullptr; 119 | UPROPERTY() 120 | USBDemoChunkAssistantManager* CAObject = nullptr; 121 | 122 | UPROPERTY(BlueprintReadWrite, Category="SubsystemBrowserTest") 123 | int32 HiddenBlueprintOnlyProperty = 0; 124 | UPROPERTY(Config) 125 | int32 HiddenConfigProperty = 0; 126 | UPROPERTY() 127 | int32 HiddenProperty = 0; 128 | 129 | UPROPERTY(EditAnywhere, Category=Edit) 130 | int32 EditAnywhereProperty = 0; 131 | UPROPERTY(EditDefaultsOnly, Category=Edit) 132 | int32 EditDefaultsOnlyProperty = 0; 133 | UPROPERTY(EditInstanceOnly, Category=Edit) 134 | int32 EditInstanceOnlyProperty = 0; 135 | 136 | UPROPERTY(VisibleAnywhere, Category=Visible) 137 | int32 VisibleAnywhereProperty = 0; 138 | UPROPERTY(VisibleDefaultsOnly, Category=Visible) 139 | int32 VisibleDefaultsOnlyProperty = 0; 140 | UPROPERTY(VisibleInstanceOnly, Category=Visible) 141 | int32 VisibleInstanceOnlyProperty = 0; 142 | 143 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=General) 144 | TSubclassOf GeneralClassProperty; 145 | UPROPERTY(EditAnywhere, Category=General) 146 | TSoftObjectPtr GeneralAssetProperty; 147 | UPROPERTY(EditAnywhere, Instanced, Category=General) 148 | USBDemoObject* GeneralInstancedProperty = nullptr; 149 | UPROPERTY(EditAnywhere, Category=General) 150 | int32 GeneralIntProperty = 0; 151 | UPROPERTY(EditAnywhere, Category=General) 152 | FSBDemoStruct GeneralStructProperty; 153 | UPROPERTY(EditAnywhere, Category=General) 154 | ESBDemoEnum GeneralEnumProperty = ESBDemoEnum::Alpha; 155 | UPROPERTY(EditAnywhere, Category=General) 156 | FGuid GeneralGuidProperty; 157 | UPROPERTY(EditAnywhere, Category=General, meta=(ForceInlineRow)) 158 | TMap GeneralMapProperty; 159 | 160 | UPROPERTY(Config, EditAnywhere, Category=Config) 161 | int32 ConfigIntProperty = 0; 162 | UPROPERTY(Config, EditAnywhere, Category=Config) 163 | FString ConfigStringProperty; 164 | UPROPERTY(Config, EditAnywhere, Category=Config) 165 | TSoftClassPtr ConfigClassProperty; 166 | UPROPERTY(Config, EditAnywhere, Category=Config) 167 | TSoftObjectPtr ConfigAssetProperty; 168 | UPROPERTY(Config, EditAnywhere, Category=Config) 169 | TArray ConfigArrayOfStructsProperty; 170 | UPROPERTY(Config, EditAnywhere, Category=Config) 171 | TArray ConfigArrayOfEnumsProperty; 172 | 173 | UFUNCTION(CallInEditor, Category=Tools) 174 | void EditorFunction(); 175 | UFUNCTION(CallInEditor, Category=Tools) 176 | void EditorFunctionReset(); 177 | UPROPERTY(VisibleAnywhere, Category=Tools) 178 | int32 EditorFunctionCallCounter = 0; 179 | 180 | UPROPERTY(VisibleAnywhere, Category=Delegates) 181 | FSBTestDynamicDelegate SingleDelegate; 182 | UPROPERTY(VisibleAnywhere, Category=Delegates) 183 | FSBTestDynamicMCDelegate MulticastDelegate; 184 | UPROPERTY(VisibleAnywhere, BlueprintAssignable, Category=Delegates) 185 | FSBTestDynamicMCDelegate MulticastAssignableDelegate; 186 | 187 | UPROPERTY(EditAnywhere, Category=ArrayInt) 188 | uint32 ArrayIntegersFillValue = 100; 189 | UPROPERTY(VisibleAnywhere, Category=ArrayInt) 190 | TArray ArrayIntegers; 191 | 192 | UFUNCTION(CallInEditor, Category=ArrayInt) 193 | void FillArrays(); 194 | UFUNCTION(CallInEditor, Category=ArrayInt) 195 | void EmptyArrays(); 196 | 197 | UPROPERTY(EditAnywhere, Category=ArrayObj) 198 | uint32 ArrayObjectsFillValue = 1; 199 | UPROPERTY(VisibleAnywhere, Category=ArrayObj) 200 | USBDemoObject* ChainObjects = nullptr; 201 | UPROPERTY(VisibleAnywhere, Category=ArrayObj) 202 | TArray ArrayObjects; 203 | 204 | UFUNCTION(CallInEditor, Category=ArrayObj) 205 | void FillArrayObjs(); 206 | UFUNCTION(CallInEditor, Category=ArrayObj) 207 | void EmptyArrayObjs(); 208 | 209 | UPROPERTY(EditAnywhere, Category=ArrayWidget) 210 | uint32 ArrayWidgetsFillValue = 1; 211 | UPROPERTY(VisibleAnywhere, Category=ArrayWidget) 212 | TArray ArrayWidgets; 213 | 214 | UFUNCTION(CallInEditor, Category=ArrayWidget) 215 | void FillArrayWidgets(); 216 | UFUNCTION(CallInEditor, Category=ArrayWidget) 217 | void EmptyArrayWidgets(); 218 | }; 219 | 220 | 221 | -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTests.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SubsystemBrowserTests : ModuleRules 6 | { 7 | public bool bStrictIncludesCheck = false; 8 | 9 | public SubsystemBrowserTests(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; 12 | 13 | // This is to emulate engine installation and verify includes during development 14 | // Gives effect similar to BuildPlugin with -StrictIncludes 15 | if (bStrictIncludesCheck) 16 | { 17 | bUseUnity = false; 18 | PCHUsage = PCHUsageMode.NoPCHs; 19 | // Enable additional checks used for Engine modules 20 | bTreatAsEngineModule = true; 21 | } 22 | 23 | // This is to use non-Public/Private folder system 24 | PublicIncludePaths.Add(ModuleDirectory); 25 | 26 | PublicDependencyModuleNames.AddRange(new string[] 27 | { 28 | "Core" 29 | }); 30 | 31 | PrivateDependencyModuleNames.AddRange(new string[] 32 | { 33 | "CoreUObject", 34 | "Engine", 35 | "Slate", 36 | "SlateCore", 37 | "UMG", 38 | "SubsystemBrowser" 39 | }); 40 | } 41 | } -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTests.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserUtils.h" 4 | #include "SubsystemBrowserTestSubsystem.h" 5 | #include "Misc/AutomationTest.h" 6 | 7 | #ifdef WITH_SB_TESTS 8 | 9 | IMPLEMENT_SIMPLE_AUTOMATION_TEST(FPropertyCountTest, "SubsystemBrowser.Util.PropertyCounter", 10 | EAutomationTestFlags::EditorContext | 11 | EAutomationTestFlags::ProductFilter); 12 | 13 | bool FPropertyCountTest::RunTest(const FString& Parameters) 14 | { 15 | UClass* SBClass = USubsystemBrowserTestSubsystem::StaticClass(); 16 | 17 | auto PropertyCounts = FSubsystemBrowserUtils::GetClassFieldStats(SBClass); 18 | TestEqual("PropertyCounts.NumProperties", PropertyCounts.NumProperties, 27); 19 | TestEqual("PropertyCounts.NumEditable", PropertyCounts.NumEditable, 24); 20 | TestEqual("PropertyCounts.NumVisible", PropertyCounts.NumVisible, 2); 21 | TestEqual("PropertyCounts.NumConfig", PropertyCounts.NumConfig, 7); 22 | TestEqual("PropertyCounts.NumCallable", PropertyCounts.NumCallable, 2); 23 | 24 | return true; 25 | } 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTestsModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemBrowserTestsModule.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FSubsystemBrowserTestsModule" 6 | 7 | void FSubsystemBrowserTestsModule::StartupModule() 8 | { 9 | 10 | } 11 | 12 | void FSubsystemBrowserTestsModule::ShutdownModule() 13 | { 14 | 15 | } 16 | 17 | #undef LOCTEXT_NAMESPACE 18 | 19 | IMPLEMENT_MODULE(FDefaultModuleImpl, SubsystemBrowserTests) -------------------------------------------------------------------------------- /Source/SubsystemBrowserTests/SubsystemBrowserTestsModule.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | #include "Modules/ModuleManager.h" 7 | 8 | class FSubsystemBrowserTestsModule : public IModuleInterface 9 | { 10 | public: 11 | virtual void StartupModule() override; 12 | virtual void ShutdownModule() override; 13 | 14 | virtual bool SupportsDynamicReloading() override 15 | { 16 | return false; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/SubsystemSettingsEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SubsystemSettingsEditor : ModuleRules 6 | { 7 | public bool bStrictIncludesCheck = false; 8 | 9 | public SubsystemSettingsEditor(ReadOnlyTargetRules Target) : base(Target) 10 | { 11 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 12 | 13 | // This is to emulate engine installation and verify includes during development 14 | // Gives effect similar to BuildPlugin with -StrictIncludes 15 | if (bStrictIncludesCheck) 16 | { 17 | bUseUnity = false; 18 | PCHUsage = PCHUsageMode.NoPCHs; 19 | // Enable additional checks used for Engine modules 20 | bTreatAsEngineModule = true; 21 | } 22 | 23 | // This is to use non-Public/Private folder system 24 | PublicIncludePaths.Add(ModuleDirectory); 25 | 26 | // These are dependencies always needed if planning on expanding plugin 27 | PublicDependencyModuleNames.AddRange(new string[] 28 | { 29 | "Core", 30 | "CoreUObject", 31 | "Engine" 32 | }); 33 | 34 | // These are dependencies that nobody should know of 35 | PrivateDependencyModuleNames.AddRange(new string [] 36 | { 37 | "Slate", 38 | "SlateCore", 39 | "UnrealEd", 40 | "EditorStyle", 41 | "WorkspaceMenuStructure", 42 | "SubsystemBrowser", 43 | "ToolMenus", 44 | "SettingsEditor", 45 | "Projects" 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/SubsystemSettingsEditorModule.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemSettingsEditorModule.h" 4 | #include "SubsystemBrowserFlags.h" 5 | #include "SubsystemBrowserSettings.h" 6 | #include "SubsystemBrowserStyle.h" 7 | #include "ToolMenus.h" 8 | 9 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 10 | 11 | IMPLEMENT_MODULE(FSubsystemSettingsEditorModule, SubsystemSettingsEditor); 12 | 13 | DEFINE_LOG_CATEGORY(LogSubsystemSettingsEditor); 14 | 15 | void FSubsystemSettingsEditorModule::StartupModule() 16 | { 17 | if (GIsEditor 18 | && !IsRunningCommandlet() 19 | && USubsystemBrowserSettings::Get()->ShouldUseSubsystemSettings()) 20 | { 21 | bEnabled = true; 22 | 23 | SettingsManager.Register(); 24 | 25 | UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FSubsystemSettingsEditorModule::RegisterMenus)); 26 | } 27 | } 28 | 29 | void FSubsystemSettingsEditorModule::ShutdownModule() 30 | { 31 | if (GIsEditor && bEnabled) 32 | { 33 | SettingsManager.Unregister(); 34 | 35 | bEnabled = false; 36 | } 37 | } 38 | 39 | void FSubsystemSettingsEditorModule::RegisterMenus() 40 | { 41 | struct Local 42 | { 43 | static void OpenSettings() 44 | { 45 | ISettingsModule& Module = FModuleManager::GetModuleChecked(TEXT("Settings")); 46 | Module.ShowViewer(TEXT("Subsystem"), TEXT(""), TEXT("")); 47 | } 48 | }; 49 | 50 | UToolMenus* const ToolMenus = UToolMenus::Get(); 51 | 52 | { // Edit -> Configuration menu 53 | UToolMenu* Menu = ToolMenus->ExtendMenu(TEXT("MainFrame.MainMenu.Edit")); 54 | FToolMenuSection& Section = Menu->FindOrAddSection(TEXT("Configuration")); 55 | 56 | Section.AddMenuEntry( 57 | TEXT("SubsystemSettings"), 58 | LOCTEXT("SubsystemSettingsMenuLabel", "Subsystem Settings"), 59 | LOCTEXT("SubsystemSettingsMenuToolTip", "Change subsystem settings"), 60 | FStyleHelper::GetSlateIcon(FSubsystemBrowserStyle::PanelIconName), 61 | FUIAction(FExecuteAction::CreateStatic(&Local::OpenSettings)) 62 | ); 63 | } 64 | 65 | #if !UE_VERSION_OLDER_THAN(5,0,0) 66 | { // Project Quick Settings on right side in UE 5 67 | UToolMenu* Menu = ToolMenus->ExtendMenu(TEXT("LevelEditor.LevelEditorToolBar.LevelToolbarQuickSettings")); 68 | FToolMenuSection& Section = Menu->FindOrAddSection(TEXT("ProjectSettingsSection")); 69 | 70 | Section.AddMenuEntry( 71 | TEXT("SubsystemSettings"), 72 | LOCTEXT("SubsystemSettingsMenuLabel", "Subsystem Settings"), 73 | LOCTEXT("SubsystemSettingsMenuToolTip", "Change subsystem settings"), 74 | FStyleHelper::GetSlateIcon(FSubsystemBrowserStyle::PanelIconName), 75 | FUIAction(FExecuteAction::CreateStatic(&Local::OpenSettings)) 76 | ); 77 | } 78 | #endif 79 | } 80 | 81 | 82 | #undef LOCTEXT_NAMESPACE 83 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/SubsystemSettingsEditorModule.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleInterface.h" 6 | #include "SubsystemSettingsManager.h" 7 | 8 | class FSubsystemSettingsEditorModule : public IModuleInterface 9 | { 10 | public: 11 | virtual void StartupModule() override; 12 | virtual void ShutdownModule() override; 13 | private: 14 | 15 | /** register settings editor menus */ 16 | void RegisterMenus(); 17 | 18 | 19 | // 20 | bool bEnabled = false; 21 | 22 | // Settings manager 23 | FSubsystemSettingsManager SettingsManager; 24 | }; 25 | 26 | DECLARE_LOG_CATEGORY_EXTERN(LogSubsystemSettingsEditor, Log, All); 27 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/SubsystemSettingsManager.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemSettingsManager.h" 4 | 5 | #include "SubsystemBrowserModule.h" 6 | #include "SubsystemBrowserSettings.h" 7 | #include "SubsystemBrowserStyle.h" 8 | #include "ISettingsModule.h" 9 | #include "ISettingsSection.h" 10 | #include "ISettingsContainer.h" 11 | #include "ISettingsCategory.h" 12 | #include "ISettingsEditorModule.h" 13 | #include "ISettingsEditorModel.h" 14 | #include "IDetailsView.h" 15 | #include "WorkspaceMenuStructure.h" 16 | #include "WorkspaceMenuStructureModule.h" 17 | #include "Widgets/Docking/SDockTab.h" 18 | #include "Framework/Docking/TabManager.h" 19 | #include "Modules/ModuleManager.h" 20 | #include "UObject/UObjectIterator.h" 21 | #include "UI/SubsystemBrowserPanel.h" 22 | #include "UI/SubsystemSettingsWidget.h" 23 | #include "SubsystemSettingsEditorModule.h" 24 | 25 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 26 | 27 | const FName FSubsystemSettingsUserMeta::MD_SBHidden(TEXT("SBHidden")); 28 | const FName FSubsystemSettingsUserMeta::MD_SBSection(TEXT("SBSettingsSection")); 29 | const FName FSubsystemSettingsUserMeta::MD_SBSectionDesc(TEXT("SBSettingsSectionDesc")); 30 | 31 | const FName FSubsystemSettingsManager::SubsystemSettingsTabName = TEXT("SubsystemSettings"); 32 | 33 | class SBTrackableDockTab : public SDockTab 34 | { 35 | public: 36 | void Construct(const FArguments& InArgs, FSubsystemSettingsManager* InManager) 37 | { 38 | InManager->TrackedSettingsWidget = SharedThis(this); 39 | SDockTab::Construct(InArgs); 40 | } 41 | }; 42 | 43 | template 44 | TSharedPtr RecursiveFindWidget(const TSharedRef& InWidget, const FName& InName) 45 | { 46 | if (InWidget->GetType() == InName) 47 | return StaticCastSharedRef(InWidget); 48 | 49 | for(int32 Idx = 0, Num = InWidget->GetChildren()->Num(); Idx < Num; ++Idx) 50 | { 51 | auto Inner = RecursiveFindWidget(InWidget->GetChildren()->GetChildAt(Idx), InName); 52 | if (Inner.IsValid()) 53 | return Inner; 54 | } 55 | return nullptr; 56 | } 57 | 58 | void FSubsystemSettingsManager::Register() 59 | { 60 | USubsystemBrowserSettings* SettingsObject = USubsystemBrowserSettings::Get(); 61 | SettingsObject->OnSettingChanged().AddRaw(this, &FSubsystemSettingsManager::HandleSettingsChanged); 62 | 63 | // Create Subsystem Settings viewer and viewer tab spawner 64 | ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked("Settings"); 65 | SettingsModule.RegisterViewer(TEXT("Subsystem"), *this); 66 | 67 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SubsystemSettingsTabName, FOnSpawnTab::CreateRaw(this, &FSubsystemSettingsManager::HandleSpawnSettingsTab)) 68 | .SetDisplayName(LOCTEXT("SubsystemBrowserSettingsTabTitle", "Subsystem Settings")) 69 | .SetTooltipText(LOCTEXT("SubsystemBrowserSettingsTabTooltip", "Open the Subsystem Settings tab.")) 70 | .SetMenuType(ETabSpawnerMenuType::Hidden) // Hide for now, got menu extenders to access it plus browser menu 71 | #if UE_VERSION_OLDER_THAN(5, 0, 0) 72 | .SetGroup(WorkspaceMenu::GetMenuStructure().GetDeveloperToolsMiscCategory()) 73 | #else 74 | .SetGroup(WorkspaceMenu::GetMenuStructure().GetToolsCategory()) 75 | #endif 76 | .SetIcon(FStyleHelper::GetSlateIcon(FSubsystemBrowserStyle::PanelIconName)); 77 | 78 | // 79 | FModuleManager::Get().OnModulesChanged().AddRaw(this, &FSubsystemSettingsManager::HandleModulesChanges); 80 | } 81 | 82 | void FSubsystemSettingsManager::Unregister() 83 | { 84 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr(TEXT("Settings"))) 85 | { 86 | SettingsModule->UnregisterViewer(TEXT("Subsystem")); 87 | } 88 | 89 | FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(SubsystemSettingsTabName); 90 | 91 | FModuleManager::Get().OnModulesChanged().RemoveAll(this); 92 | } 93 | 94 | void FSubsystemSettingsManager::ShowSettings(const FName& CategoryName, const FName& SectionName) 95 | { 96 | #if UE_VERSION_OLDER_THAN(4, 26, 0) 97 | FGlobalTabmanager::Get()->InvokeTab(FSubsystemBrowserModule::SubsystemSettingsTabName); 98 | #else 99 | FGlobalTabmanager::Get()->TryInvokeTab(SubsystemSettingsTabName); 100 | #endif 101 | 102 | ISettingsEditorModelPtr SettingsEditorModel = SettingsEditorModelPtr.Pin(); 103 | if (SettingsEditorModel.IsValid()) 104 | { 105 | ISettingsCategoryPtr Category = SettingsEditorModel->GetSettingsContainer()->GetCategory(CategoryName); 106 | if (Category.IsValid()) 107 | { 108 | ISettingsSectionPtr Section = Category->GetSection(SectionName); 109 | 110 | SettingsEditorModel->SelectSection(Section); 111 | } 112 | } 113 | } 114 | 115 | TSharedRef FSubsystemSettingsManager::HandleSpawnSettingsTab(const FSpawnTabArgs& Args) 116 | { 117 | UpdateDiscoveredSubsystems(true); 118 | 119 | ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked(TEXT("Settings")); 120 | 121 | TSharedRef SettingsEditor = SNullWidget::NullWidget; 122 | 123 | ISettingsContainerPtr SettingsContainer = SettingsModule.GetContainer(TEXT("Subsystem")); 124 | if (SettingsContainer.IsValid()) 125 | { 126 | ISettingsEditorModule& SettingsEditorModule = FModuleManager::GetModuleChecked(TEXT("SettingsEditor")); 127 | ISettingsEditorModelRef SettingsEditorModel = SettingsEditorModule.CreateModel(SettingsContainer.ToSharedRef()); 128 | 129 | SettingsEditor = SettingsEditorModule.CreateEditor(SettingsEditorModel); 130 | 131 | SettingsEditorPtr = SettingsEditor; 132 | SettingsEditorModelPtr = SettingsEditorModel; 133 | 134 | if (USubsystemBrowserSettings::Get()->ShouldUseCustomPropertyFilter()) 135 | { 136 | TSharedPtr InnerDetailsView = RecursiveFindWidget(SettingsEditor, TEXT("SDetailsView")); 137 | if (InnerDetailsView.IsValid()) 138 | { 139 | InnerDetailsView->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateStatic(&SSubsystemSettingsWidget::IsDetailsPropertyVisible)); 140 | InnerDetailsView->ForceRefresh(); 141 | } 142 | } 143 | } 144 | 145 | return SNew(SBTrackableDockTab, this) 146 | .TabRole(ETabRole::NomadTab) 147 | [ 148 | SettingsEditor 149 | ]; 150 | } 151 | 152 | void FSubsystemSettingsManager::HandleCategoriesChanged() 153 | { 154 | bNeedsRediscover = true; 155 | UpdateDiscoveredSubsystems(); 156 | } 157 | 158 | void FSubsystemSettingsManager::HandleModulesChanges(FName Name, EModuleChangeReason ModuleChangeReason) 159 | { 160 | bNeedsRediscover = true; 161 | UpdateDiscoveredSubsystems(); 162 | } 163 | 164 | void FSubsystemSettingsManager::UpdateDiscoveredSubsystems(bool bForce) 165 | { 166 | ISettingsModule& SettingsModule = FModuleManager::GetModuleChecked(TEXT("Settings")); 167 | 168 | if ((TrackedSettingsWidget.IsValid() || bForce) && bNeedsRediscover) 169 | { 170 | bNeedsRediscover = false; 171 | 172 | UnregisterDiscoveredSubsystems(SettingsModule); 173 | RegisterDiscoveredSubsystems(SettingsModule); 174 | } 175 | } 176 | 177 | void FSubsystemSettingsManager::RegisterDiscoveredSubsystems(ISettingsModule& SettingsModule) 178 | { 179 | TArray AllKnownSubsystems; 180 | 181 | const TArray& RegisteredCategories = FSubsystemBrowserModule::Get().GetCategories(); 182 | for (const SubsystemCategoryPtr& Ptr : RegisteredCategories) 183 | { 184 | if (!Ptr->IsVisibleInSettings()) 185 | continue; 186 | 187 | TArray ObjectArray; 188 | GetObjectsOfClass(Ptr->GetSubsystemClass(), ObjectArray, true, RF_NoFlags); 189 | 190 | for (UObject* Subsystem : ObjectArray) 191 | { 192 | UClass* SSClass = Subsystem->GetClass(); 193 | 194 | if (!Subsystem->HasAnyFlags(RF_ClassDefaultObject)) 195 | continue; 196 | if (!SSClass->HasAnyClassFlags(CLASS_Config)) 197 | continue; 198 | if (SSClass->HasAnyClassFlags(CLASS_Deprecated|CLASS_Abstract)) 199 | continue; 200 | if (SSClass->FindMetaData(FSubsystemSettingsUserMeta::MD_SBHidden) != nullptr) 201 | continue; 202 | if (!AllKnownSubsystems.Contains(Subsystem)) 203 | { 204 | FSubsystemBrowserUtils::FClassFieldStats ClassFieldStats = FSubsystemBrowserUtils::GetClassFieldStats(SSClass); 205 | // ignore entries with Class-level config specifier but no actual Config properties 206 | if (!ClassFieldStats.NumConfig) 207 | continue; 208 | 209 | // custom mode allows showing props without Edit specifier, if there's none - ignore object 210 | bool bUseCustom = USubsystemBrowserSettings::Get()->ShouldUseCustomSettingsWidget(); 211 | if (!bUseCustom && !ClassFieldStats.NumConfigWithEdit) 212 | continue; 213 | 214 | UE_LOG(LogSubsystemSettingsEditor, Verbose, TEXT("Discovered object %s [%s] (editable=%d, total=%d, customui=%d)"), 215 | *GetNameSafe(Subsystem), *GetNameSafe(SSClass), ClassFieldStats.NumConfigWithEdit, ClassFieldStats.NumConfig, (int32)bUseCustom); 216 | 217 | RegisterSubsystemSettings(SettingsModule, Ptr->GetSettingsName(), Subsystem, bUseCustom); 218 | 219 | AllKnownSubsystems.Add(Subsystem); 220 | } 221 | } 222 | } 223 | } 224 | 225 | void FSubsystemSettingsManager::RegisterSubsystemSettings(ISettingsModule& SettingsModule, FName Category, UObject* Subsystem, bool bCustomUI) 226 | { 227 | UClass* const Class = Subsystem->GetClass(); 228 | 229 | FDiscoveredSubsystemInfo Registered; 230 | Registered.ContainerName = TEXT("Subsystem"); 231 | Registered.CategoryName = Category; 232 | Registered.SectionName = Class->GetFName(); 233 | 234 | TOptional OptionalSection = FSubsystemBrowserUtils::GetMetadataOptional(Class, FSubsystemSettingsUserMeta::MD_SBSection); 235 | FText DisplayName = OptionalSection.IsSet() ? FText::FromString(OptionalSection.GetValue()) : Class->GetDisplayNameText(); 236 | 237 | TOptional OptionalSectionDesc = FSubsystemBrowserUtils::GetMetadataOptional(Class, FSubsystemSettingsUserMeta::MD_SBSectionDesc); 238 | FText DisplayTooltip = OptionalSectionDesc.IsSet() ? FText::FromString(OptionalSectionDesc.GetValue()) : Class->GetToolTipText(); 239 | 240 | if (bCustomUI) 241 | { 242 | // custom widget for engine subsystems that did not expose any properties for editing 243 | Registered.EditorWidget = SNew(SSubsystemSettingsWidget, Subsystem) 244 | .CategoryName(Registered.CategoryName) 245 | .SectionName(Registered.SectionName) 246 | .SectionDisplayName(DisplayName) 247 | .SectionTooltipText(DisplayTooltip); 248 | 249 | SettingsModule.RegisterSettings(Registered.ContainerName, Registered.CategoryName, Registered.SectionName, 250 | DisplayName, 251 | DisplayTooltip, 252 | Registered.EditorWidget.ToSharedRef() 253 | ); 254 | } 255 | else 256 | { 257 | SettingsModule.RegisterSettings(Registered.ContainerName, Registered.CategoryName, Registered.SectionName, 258 | DisplayName, 259 | DisplayTooltip, 260 | Subsystem 261 | ); 262 | } 263 | 264 | DiscoveredSettings.Add(Registered); 265 | } 266 | 267 | void FSubsystemSettingsManager::UnregisterDiscoveredSubsystems(ISettingsModule& SettingsModule) 268 | { 269 | for (const FDiscoveredSubsystemInfo& Settings : DiscoveredSettings) 270 | { 271 | SettingsModule.UnregisterSettings(Settings.ContainerName, Settings.CategoryName, Settings.SectionName); 272 | } 273 | 274 | DiscoveredSettings.Reset(); 275 | } 276 | 277 | void FSubsystemSettingsManager::HandleSettingsChanged(FName InPropertyName) 278 | { 279 | if (FProperty* Property = USubsystemBrowserSettings::StaticClass()->FindPropertyByName(InPropertyName)) 280 | { 281 | if (Property->HasMetaData(FSubsystemBrowserConfigMeta::MD_ConfigAffectsSettings)) 282 | { 283 | bNeedsRediscover = true; 284 | UpdateDiscoveredSubsystems(); 285 | } 286 | } 287 | } 288 | 289 | 290 | #undef LOCTEXT_NAMESPACE 291 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/SubsystemSettingsManager.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "ISettingsModule.h" 6 | #include "ISettingsViewer.h" 7 | #include "Layout/Visibility.h" 8 | #include "Modules/ModuleManager.h" 9 | 10 | class SWidget; 11 | class SDockTab; 12 | class FSpawnTabArgs; 13 | class SSubsystemSettingsWidget; 14 | class ISettingsEditorModel; 15 | class ISettingsSection; 16 | 17 | struct FSubsystemSettingsUserMeta 18 | { 19 | // Subsystem Settings - Hide type from display 20 | static const FName MD_SBHidden; 21 | // Subsystem Settings - Section name override (default is Class::GetDisplayNameText) 22 | static const FName MD_SBSection; 23 | // Subsystem Settings - Section description override (default is Class::GetTooltipText) 24 | static const FName MD_SBSectionDesc; 25 | }; 26 | 27 | /** Holds auto-discovered subsystems with settings */ 28 | struct FDiscoveredSubsystemInfo 29 | { 30 | FName ContainerName; 31 | FName CategoryName; 32 | FName SectionName; 33 | 34 | TSharedPtr EditorWidget; 35 | }; 36 | 37 | /** 38 | * Class managing all Subsystem Browser settings: primary plugin and discovered subsystems 39 | */ 40 | class FSubsystemSettingsManager : public ISettingsViewer 41 | { 42 | static const FName SubsystemSettingsTabName; 43 | public: 44 | 45 | /** 46 | * 47 | */ 48 | void Register(); 49 | 50 | /** 51 | * 52 | */ 53 | void Unregister(); 54 | 55 | /** 56 | * Show a panel with subsystem settings 57 | */ 58 | virtual void ShowSettings(const FName& CategoryName, const FName& SectionName) override; 59 | 60 | /** Handles creating the subsystem settings tab. */ 61 | TSharedRef HandleSpawnSettingsTab(const FSpawnTabArgs& Args); 62 | 63 | /** */ 64 | void HandleCategoriesChanged(); 65 | /** Handle dynamic modules load/unload */ 66 | void HandleModulesChanges(FName Name, EModuleChangeReason ModuleChangeReason); 67 | /** */ 68 | void HandleSettingsChanged(FName Name); 69 | 70 | void UpdateDiscoveredSubsystems(bool bForce = false); 71 | 72 | void RegisterDiscoveredSubsystems(ISettingsModule& SettingsModule); 73 | void RegisterSubsystemSettings(ISettingsModule& SettingsModule, FName Category, UObject* Subsystem, bool bCustomUI); 74 | 75 | void UnregisterDiscoveredSubsystems(ISettingsModule& SettingsModule); 76 | 77 | TWeakPtr GetSettingsEditorModel() const { return SettingsEditorModelPtr; } 78 | TWeakPtr GetSettingsEditorWidget() const { return SettingsEditorPtr; } 79 | 80 | private: 81 | 82 | // Settings model 83 | TWeakPtr SettingsEditorModelPtr; 84 | // Settings editor 85 | TWeakPtr SettingsEditorPtr; 86 | 87 | // 88 | friend class SBTrackableDockTab; 89 | TWeakPtr TrackedSettingsWidget; 90 | 91 | // Tracked list of discovered settings 92 | TArray DiscoveredSettings; 93 | 94 | // Flag to indicate settings need to be rediscovered upon next panel opening 95 | bool bNeedsRediscover = true; 96 | }; 97 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/UI/SubsystemSettingsWidget.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #include "SubsystemSettingsWidget.h" 4 | #include "IDetailsView.h" 5 | #include "ISettingsSection.h" 6 | #include "PropertyEditorModule.h" 7 | #include "SubsystemBrowserModule.h" 8 | #include "SubsystemBrowserSettings.h" 9 | #include "SubsystemBrowserStyle.h" 10 | #include "Misc/MessageDialog.h" 11 | #include "Modules/ModuleManager.h" 12 | #include "Widgets/Images/SImage.h" 13 | #include "Widgets/Layout/SBox.h" 14 | #include "Widgets/Text/STextBlock.h" 15 | #include "Widgets/Input/SButton.h" 16 | 17 | #define LOCTEXT_NAMESPACE "SubsystemBrowser" 18 | 19 | void SSubsystemSettingsWidget::Construct(const FArguments& InArgs, UObject* InObject) 20 | { 21 | TargetObject = InObject; 22 | 23 | const FText SectionTitleText = FText::Format(INVTEXT("{0} - {1}"), 24 | FText::FromName(InArgs._CategoryName.Get()), 25 | InArgs._SectionDisplayName.Get() 26 | ); 27 | 28 | // initialize settings view 29 | FDetailsViewArgs DetailsViewArgs; 30 | { 31 | DetailsViewArgs.ViewIdentifier = FName(*FString::Printf(TEXT("SBSettingsViewer.%s"), *GetNameSafe(InObject))); 32 | DetailsViewArgs.bAllowSearch = true; 33 | DetailsViewArgs.bHideSelectionTip = true; 34 | DetailsViewArgs.bLockable = false; 35 | DetailsViewArgs.bSearchInitialKeyFocus = true; 36 | DetailsViewArgs.bUpdatesFromSelection = false; 37 | DetailsViewArgs.NotifyHook = this; 38 | DetailsViewArgs.bShowOptions = true; 39 | DetailsViewArgs.bShowModifiedPropertiesOption = false; 40 | DetailsViewArgs.bShowAnimatedPropertiesOption = false; 41 | DetailsViewArgs.bShowDifferingPropertiesOption = false; 42 | DetailsViewArgs.bShowKeyablePropertiesOption = false; 43 | DetailsViewArgs.bShowPropertyMatrixButton = false; 44 | DetailsViewArgs.bAllowMultipleTopLevelObjects = true; 45 | DetailsViewArgs.bCustomNameAreaLocation = true; 46 | DetailsViewArgs.bCustomFilterAreaLocation = true; 47 | DetailsViewArgs.NameAreaSettings = FDetailsViewArgs::HideNameArea; 48 | // enforce display of all properties and filter them later via delegate 49 | DetailsViewArgs.bForceHiddenPropertyVisibility = true; 50 | } 51 | 52 | { 53 | TSharedRef View = FModuleManager::GetModuleChecked(TEXT("PropertyEditor")).CreateDetailView(DetailsViewArgs); 54 | if (USubsystemBrowserSettings::Get()->ShouldUseCustomPropertyFilter()) 55 | { 56 | View->SetIsPropertyVisibleDelegate(FIsPropertyVisible::CreateStatic(&SSubsystemSettingsWidget::IsDetailsPropertyVisible)); 57 | } 58 | View->SetObject(InObject); 59 | 60 | FSubsystemBrowserModule::CustomizeDetailsView(View, TEXT("SubsystemSettingsPanel")); 61 | 62 | SettingsView = View; 63 | } 64 | 65 | ChildSlot 66 | .Padding(0, 8, 16, 5) 67 | [ 68 | SNew(SVerticalBox) 69 | + SVerticalBox::Slot() 70 | .AutoHeight() 71 | [ 72 | SNew(SHorizontalBox) 73 | + SHorizontalBox::Slot() 74 | .FillWidth(1.0f) 75 | [ 76 | SNew(SVerticalBox) 77 | + SVerticalBox::Slot() 78 | .AutoHeight() 79 | [ 80 | SNew(SHorizontalBox) 81 | + SHorizontalBox::Slot() 82 | .VAlign(VAlign_Center) 83 | .Padding(2, 2, 2, 2) 84 | .AutoWidth() 85 | [ 86 | SNew(SBox).HeightOverride(16).WidthOverride(16) //tbd 87 | ] 88 | + SHorizontalBox::Slot() 89 | [ 90 | SNew(STextBlock) 91 | .Font(FStyleHelper::GetFontStyle("SettingsEditor.CatgoryAndSectionFont")) 92 | .Text(SectionTitleText) 93 | ] 94 | ] 95 | + SVerticalBox::Slot() 96 | .AutoHeight() 97 | .Padding(16, 8, 0, 0) 98 | [ 99 | SNew(STextBlock) 100 | .ColorAndOpacity(FSlateColor::UseSubduedForeground()) 101 | .Text(InArgs._SectionTooltipText) 102 | ] 103 | ] 104 | + SHorizontalBox::Slot() 105 | .AutoWidth() 106 | .HAlign(HAlign_Right) 107 | .VAlign(VAlign_Bottom) 108 | .Padding(16, 0, 0, 0) 109 | [ 110 | SNew(SHorizontalBox) 111 | +SHorizontalBox::Slot() 112 | [ 113 | SNew(SButton) 114 | .Visibility(this, &SSubsystemSettingsWidget::HandleSetAsDefaultButtonVisibility) 115 | .IsEnabled(this, &SSubsystemSettingsWidget::HandleSetAsDefaultButtonEnabled) 116 | .OnClicked(this, &SSubsystemSettingsWidget::HandleSetAsDefaultButtonClicked) 117 | .Text(LOCTEXT("SaveDefaultsButtonText", "Set as Default")) 118 | .ToolTipText(LOCTEXT("SaveDefaultsButtonTooltip", "Save the values below as the new default settings")) 119 | ] 120 | + SHorizontalBox::Slot() 121 | .AutoWidth() 122 | .Padding(8, 0, 0, 0) 123 | [ 124 | SNew(SButton) 125 | .OnClicked(this, &SSubsystemSettingsWidget::HandleExportButtonClicked) 126 | .Text(LOCTEXT("ExportButtonText", "Export to Clipboard")) 127 | .ToolTipText(LOCTEXT("ExportButtonTooltip", "Export these settings to a clipboard")) 128 | ] 129 | ] 130 | ] 131 | + SVerticalBox::Slot() 132 | .AutoHeight() 133 | .Padding(0, 8, 0, 0) 134 | [ 135 | // file area 136 | SNew(SHorizontalBox) 137 | + SHorizontalBox::Slot() 138 | .AutoWidth() 139 | .Padding(8, 0, 0, 0) 140 | .VAlign(VAlign_Center) 141 | [ 142 | SNew(SBox).HeightOverride(16).WidthOverride(16) //tbd 143 | ] 144 | + SHorizontalBox::Slot() 145 | .Padding(8, 0, 0, 0) 146 | .VAlign(VAlign_Center) 147 | .HAlign(HAlign_Left) 148 | .FillWidth(1) 149 | [ 150 | SNew(STextBlock) 151 | .Text(this, &SSubsystemSettingsWidget::GetSettingsStorageLocationText) 152 | ] 153 | ] 154 | + SVerticalBox::Slot() 155 | .AutoHeight() 156 | .Padding(0, 8, 0, 16) 157 | [ 158 | // warning area 159 | SNew(SHorizontalBox) 160 | + SHorizontalBox::Slot() 161 | .Padding(8, 0, 0, 0) 162 | .AutoWidth() 163 | .VAlign(VAlign_Center) 164 | [ 165 | SNew(SBox) 166 | .HeightOverride(16) 167 | .WidthOverride(16) 168 | [ 169 | SNew(SImage) 170 | .Image(FStyleHelper::GetBrush("SettingsEditor.WarningIcon")) 171 | ] 172 | ] 173 | + SHorizontalBox::Slot() 174 | .Padding(8, 0, 0, 0) 175 | .VAlign(VAlign_Center) 176 | .HAlign(HAlign_Left) 177 | .FillWidth(1) 178 | [ 179 | SNew(STextBlock) 180 | .Text(LOCTEXT("SubsystemMissingEditableConfigs", "This class has no publicly editable config properties, functionality is limited.")) 181 | ] 182 | ] 183 | + SVerticalBox::Slot() 184 | .FillHeight(1.0f) 185 | [ 186 | // settings area 187 | SettingsView.ToSharedRef() 188 | ] 189 | ]; 190 | } 191 | 192 | SSubsystemSettingsWidget::~SSubsystemSettingsWidget() 193 | { 194 | } 195 | 196 | FText SSubsystemSettingsWidget::GetSettingsStorageLocationText() const 197 | { 198 | FString ShortFile = FPaths::GetCleanFilename(GetTargetObjectConfigFilePath()); 199 | if (ShortFile == TEXT("Engine")) 200 | ShortFile = TEXT("Engine.ini"); // visual fix 201 | return FText::Format(INVTEXT("These settings are saved in {0}."), FText::FromString(ShortFile)); 202 | } 203 | 204 | bool SSubsystemSettingsWidget::IsDetailsPropertyVisible(const FPropertyAndParent& InProperty) 205 | { 206 | if (InProperty.Property.HasAnyPropertyFlags(CPF_Config)) 207 | { 208 | return true; 209 | } 210 | 211 | for (const FProperty* ParentProperty : InProperty.ParentProperties) 212 | { 213 | if (ParentProperty->HasAnyPropertyFlags(CPF_Config)) 214 | { 215 | return true; 216 | } 217 | } 218 | 219 | return false; 220 | } 221 | 222 | void SSubsystemSettingsWidget::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FEditPropertyChain* PropertyThatChanged) 223 | { 224 | if (PropertyChangedEvent.ChangeType == EPropertyChangeType::Interactive) 225 | return; 226 | 227 | if (PropertyChangedEvent.GetNumObjectsBeingEdited() > 0) 228 | { 229 | UObject* ObjectBeingEdited = const_cast(PropertyChangedEvent.GetObjectBeingEdited(0)); 230 | 231 | { 232 | // Determine if the Property is an Array or Array Element 233 | bool bIsArrayOrArrayElement = PropertyThatChanged->GetActiveMemberNode()->GetValue()->IsA(FArrayProperty::StaticClass()) 234 | || PropertyThatChanged->GetActiveMemberNode()->GetValue()->ArrayDim > 1 235 | || PropertyChangedEvent.Property->GetOwner(); 236 | 237 | bool bIsSetOrSetElement = PropertyThatChanged->GetActiveMemberNode()->GetValue()->IsA(FSetProperty::StaticClass()) 238 | || PropertyChangedEvent.Property->GetOwner(); 239 | 240 | bool bIsMapOrMapElement = PropertyThatChanged->GetActiveMemberNode()->GetValue()->IsA(FMapProperty::StaticClass()) 241 | || PropertyChangedEvent.Property->GetOwner(); 242 | 243 | if (ObjectBeingEdited->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig) && !bIsArrayOrArrayElement && !bIsSetOrSetElement && !bIsMapOrMapElement) 244 | { 245 | ObjectBeingEdited->UpdateSinglePropertyInConfigFile(PropertyThatChanged->GetActiveMemberNode()->GetValue(), ObjectBeingEdited->GetDefaultConfigFilename()); 246 | } 247 | else if (TargetObject == ObjectBeingEdited) 248 | { 249 | SaveSettings(); 250 | } 251 | else if (ObjectBeingEdited->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig)) 252 | { 253 | #if UE_VERSION_OLDER_THAN(5,0,0) 254 | ObjectBeingEdited->UpdateDefaultConfigFile(); 255 | #else 256 | ObjectBeingEdited->TryUpdateDefaultConfigFile(); 257 | #endif 258 | } 259 | } 260 | } 261 | } 262 | 263 | FString SSubsystemSettingsWidget::GetTargetObjectConfigFilePath() const 264 | { 265 | if (TargetObject.IsValid()) 266 | { 267 | if (TargetObject->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig)) 268 | { 269 | return GetDefaultConfigFilePath(); 270 | } 271 | if (TargetObject->GetClass()->HasAnyClassFlags(CLASS_Config)) 272 | { 273 | return TargetObject->GetClass()->GetConfigName(); 274 | } 275 | } 276 | 277 | return FString(); 278 | } 279 | 280 | FString SSubsystemSettingsWidget::GetDefaultConfigFilePath() const 281 | { 282 | FString RelativeConfigFilePath = TargetObject->GetDefaultConfigFilename(); 283 | return FPaths::ConvertRelativePathToFull(RelativeConfigFilePath); 284 | } 285 | 286 | EVisibility SSubsystemSettingsWidget::HandleSetAsDefaultButtonVisibility() const 287 | { 288 | bool bCanSaveDefaults = TargetObject.IsValid() 289 | && TargetObject->GetClass()->HasAnyClassFlags(CLASS_Config) 290 | && !TargetObject->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig | CLASS_GlobalUserConfig | CLASS_ProjectUserConfig); 291 | 292 | return bCanSaveDefaults ? EVisibility::Visible : EVisibility::Collapsed; 293 | } 294 | 295 | FReply SSubsystemSettingsWidget::HandleSetAsDefaultButtonClicked() 296 | { 297 | if (TargetObject.IsValid()) 298 | { 299 | if(FMessageDialog::Open(EAppMsgType::YesNo, LOCTEXT("SaveAsDefaultUserConfirm", "Are you sure you want to update the default settings?")) != EAppReturnType::Yes) 300 | { 301 | return FReply::Handled(); 302 | } 303 | 304 | #if UE_VERSION_OLDER_THAN(5, 0, 0) 305 | TargetObject->UpdateDefaultConfigFile(); 306 | TargetObject->ReloadConfig(nullptr, nullptr, UE4::LCPF_PropagateToInstances); 307 | #else 308 | TargetObject->TryUpdateDefaultConfigFile(); 309 | TargetObject->ReloadConfig(nullptr, nullptr, UE::LCPF_PropagateToInstances); 310 | #endif 311 | 312 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SaveAsDefaultsSucceededMessage", "The default configuration file for these settings was updated successfully."), SNotificationItem::CS_Success); 313 | } 314 | 315 | return FReply::Handled(); 316 | } 317 | 318 | bool SSubsystemSettingsWidget::HandleSetAsDefaultButtonEnabled() const 319 | { 320 | bool bCanSaveDefaults = TargetObject.IsValid() 321 | && TargetObject->GetClass()->HasAnyClassFlags(CLASS_Config) 322 | && !TargetObject->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig | CLASS_GlobalUserConfig | CLASS_ProjectUserConfig); 323 | 324 | return bCanSaveDefaults; 325 | } 326 | 327 | FReply SSubsystemSettingsWidget::HandleExportButtonClicked() const 328 | { 329 | FString ClipboardText = FSubsystemBrowserUtils::GenerateConfigExport(TargetObject.Get(), false); 330 | FSubsystemBrowserUtils::SetClipboardText(ClipboardText); 331 | FSubsystemBrowserUtils::ShowBrowserInfoMessage(LOCTEXT("SubsystemBrowserClipboardCopy_Success", "Copied to clipboard"), SNotificationItem::CS_Success); 332 | 333 | return FReply::Handled(); 334 | } 335 | 336 | void SSubsystemSettingsWidget::SaveSettings() 337 | { 338 | if (TargetObject.IsValid()) 339 | { 340 | if (TargetObject->GetClass()->HasAnyClassFlags(CLASS_DefaultConfig)) 341 | { 342 | #if UE_VERSION_OLDER_THAN(5, 0, 0) 343 | TargetObject->UpdateDefaultConfigFile(); 344 | #else 345 | TargetObject->TryUpdateDefaultConfigFile(); 346 | #endif 347 | } 348 | else if (TargetObject->GetClass()->HasAnyClassFlags(CLASS_GlobalUserConfig)) 349 | { 350 | TargetObject->UpdateGlobalUserConfigFile(); 351 | } 352 | else if (TargetObject->GetClass()->HasAnyClassFlags(CLASS_ProjectUserConfig)) 353 | { 354 | TargetObject->UpdateProjectUserConfigFile(); 355 | } 356 | else 357 | { 358 | TargetObject->SaveConfig(); 359 | } 360 | } 361 | } 362 | 363 | #undef LOCTEXT_NAMESPACE 364 | -------------------------------------------------------------------------------- /Source/SubsystemSettingsEditor/UI/SubsystemSettingsWidget.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022, Aquanox. 2 | 3 | #pragma once 4 | 5 | #include "PropertyEditorDelegates.h" 6 | #include "Widgets/DeclarativeSyntaxSupport.h" 7 | #include "Widgets/SCompoundWidget.h" 8 | #include "Misc/NotifyHook.h" 9 | 10 | class IDetailsView; 11 | 12 | /** 13 | * Custom settings widget for private types 14 | */ 15 | class SSubsystemSettingsWidget : public SCompoundWidget, public FNotifyHook 16 | { 17 | public: 18 | SLATE_BEGIN_ARGS(SSubsystemSettingsWidget) 19 | { } 20 | SLATE_ATTRIBUTE(FName, CategoryName) 21 | SLATE_ATTRIBUTE(FName, SectionName) 22 | SLATE_ATTRIBUTE(FText, SectionDisplayName) 23 | SLATE_ATTRIBUTE(FText, SectionTooltipText) 24 | SLATE_END_ARGS() 25 | 26 | 27 | void Construct(const FArguments& InArgs, UObject* InObject); 28 | virtual ~SSubsystemSettingsWidget() override; 29 | 30 | FText GetSettingsStorageLocationText() const; 31 | FString GetTargetObjectConfigFilePath() const; 32 | FString GetDefaultConfigFilePath() const; 33 | 34 | /* details view handlers */ 35 | static bool IsDetailsPropertyVisible(const FPropertyAndParent& InProperty); 36 | virtual void NotifyPostChange( const FPropertyChangedEvent& PropertyChangedEvent, class FEditPropertyChain* PropertyThatChanged ) override; 37 | 38 | /* set as default button handlers */ 39 | EVisibility HandleSetAsDefaultButtonVisibility() const; 40 | FReply HandleSetAsDefaultButtonClicked(); 41 | bool HandleSetAsDefaultButtonEnabled() const; 42 | 43 | /* */ 44 | FReply HandleExportButtonClicked() const; 45 | 46 | /* */ 47 | void SaveSettings(); 48 | 49 | private: 50 | TWeakObjectPtr TargetObject; 51 | 52 | TSharedPtr SettingsView; 53 | }; 54 | -------------------------------------------------------------------------------- /SubsystemBrowserPlugin.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 1, 4 | "VersionName": "1.4.0", 5 | "FriendlyName": "Subsystem Browser", 6 | "Description": "Plugin that adds Subsystem Browser panel to Editor", 7 | "Category": "Editor", 8 | "CreatedBy": "Aquanox", 9 | "CreatedByURL": "https://github.com/aquanox/SubsystemBrowserPlugin", 10 | "DocsURL": "", 11 | "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/f1ba87b348774d338e75d25ddbc46b98", 12 | "SupportURL": "https://github.com/aquanox/SubsystemBrowserPlugin/issues", 13 | "EngineVersion": "", 14 | "CanContainContent": false, 15 | "Installed": false, 16 | "IsBetaVersion": false, 17 | "IsExperimentalVersion": false, 18 | "EnabledByDefault": true, 19 | "Modules": [ 20 | { 21 | "Name": "SubsystemBrowser", 22 | "Type": "Editor", 23 | "LoadingPhase": "Default", 24 | "WhitelistPlatforms": [ 25 | "Win64", 26 | "Mac", 27 | "Linux" 28 | ], 29 | "WhitelistTargets": [ 30 | "Editor" 31 | ] 32 | }, 33 | { 34 | "Name": "SubsystemSettingsEditor", 35 | "Type": "Editor", 36 | "LoadingPhase": "Default", 37 | "WhitelistPlatforms": [ 38 | "Win64", 39 | "Mac", 40 | "Linux" 41 | ], 42 | "WhitelistTargets": [ 43 | "Editor" 44 | ] 45 | }, 46 | { 47 | "Name": "SubsystemBrowserTests", 48 | "Type": "Editor", 49 | "LoadingPhase": "Default", 50 | "WhitelistPlatforms": [ 51 | "Win64", 52 | "Mac", 53 | "Linux" 54 | ], 55 | "WhitelistTargets": [ 56 | "Editor" 57 | ] 58 | } 59 | ] 60 | } 61 | --------------------------------------------------------------------------------