├── Config ├── DefaultEditor.ini ├── DefaultEngine.ini └── DefaultGame.ini ├── Content └── EditorResources │ └── IconExampleEditorMode.png ├── LICENSE ├── Source ├── ToolExample.Target.cs ├── ToolExample │ ├── CustomDataType │ │ └── ExampleData.h │ ├── DetailsCustomization │ │ └── ExampleActor.h │ ├── EditorMode │ │ └── ExampleTargetPoint.h │ ├── ToolExample.Build.cs │ ├── ToolExample.cpp │ ├── ToolExample.h │ ├── ToolExampleGameModeBase.cpp │ └── ToolExampleGameModeBase.h ├── ToolExampleEditor.Target.cs └── ToolExampleEditor │ ├── CustomDataType │ ├── ExampleDataFactory.cpp │ ├── ExampleDataFactory.h │ ├── ExampleDataTypeActions.cpp │ ├── ExampleDataTypeActions.h │ ├── ReimportExampleDataFactory.cpp │ └── ReimportExampleDataFactory.h │ ├── CustomProjectSettings │ └── ExampleSettings.h │ ├── DetailsCustomization │ ├── ExampleActorDetails.cpp │ └── ExampleActorDetails.h │ ├── EditorMode │ ├── ExampleEdMode.cpp │ ├── ExampleEdMode.h │ ├── ExampleEdModeTool.cpp │ ├── ExampleEdModeTool.h │ ├── ExampleEdModeToolkit.h │ ├── SExampleEdModeWidget.cpp │ └── SExampleEdModeWidget.h │ ├── ExampleTabToolBase.h │ ├── IExampleModuleInterface.h │ ├── MenuTool │ ├── MenuTool.cpp │ └── MenuTool.h │ ├── TabTool │ ├── TabTool.cpp │ ├── TabTool.h │ ├── TabToolPanel.cpp │ └── TabToolPanel.h │ ├── ToolExampleEditor.Build.cs │ ├── ToolExampleEditor.cpp │ └── ToolExampleEditor.h └── ToolExample.uproject /Config/DefaultEditor.ini: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxjk/ToolExample/805afebda7a067b973d426597b7ff8d7e9c608b6/Config/DefaultEditor.ini -------------------------------------------------------------------------------- /Config/DefaultEngine.ini: -------------------------------------------------------------------------------- 1 | [URL] 2 | [/Script/EngineSettings.GameMapsSettings] 3 | EditorStartupMap= 4 | GameDefaultMap= 5 | GlobalDefaultGameMode="/Script/ToolExample.ToolExampleGameMode" 6 | 7 | [/Script/HardwareTargeting.HardwareTargetingSettings] 8 | TargetedHardwareClass=Desktop 9 | AppliedTargetedHardwareClass=Desktop 10 | DefaultGraphicsPerformance=Maximum 11 | AppliedDefaultGraphicsPerformance=Maximum 12 | 13 | [/Script/Engine.PhysicsSettings] 14 | DefaultGravityZ=-980.000000 15 | DefaultTerminalVelocity=4000.000000 16 | DefaultFluidFriction=0.300000 17 | SimulateScratchMemorySize=262144 18 | RagdollAggregateThreshold=4 19 | TriangleMeshTriangleMinAreaThreshold=5.000000 20 | bEnableAsyncScene=False 21 | bEnableShapeSharing=False 22 | bEnablePCM=True 23 | bEnableStabilization=False 24 | bWarnMissingLocks=True 25 | bEnable2DPhysics=False 26 | LockedAxis=Invalid 27 | DefaultDegreesOfFreedom=Full3D 28 | BounceThresholdVelocity=200.000000 29 | FrictionCombineMode=Average 30 | RestitutionCombineMode=Average 31 | MaxAngularVelocity=3600.000000 32 | MaxDepenetrationVelocity=0.000000 33 | ContactOffsetMultiplier=0.020000 34 | MinContactOffset=2.000000 35 | MaxContactOffset=8.000000 36 | bSimulateSkeletalMeshOnDedicatedServer=True 37 | DefaultShapeComplexity=CTF_UseSimpleAndComplex 38 | bDefaultHasComplexCollision=True 39 | bSuppressFaceRemapTable=False 40 | bSupportUVFromHitResults=False 41 | bDisableActiveActors=False 42 | bDisableCCD=False 43 | bEnableEnhancedDeterminism=False 44 | MaxPhysicsDeltaTime=0.033333 45 | bSubstepping=False 46 | bSubsteppingAsync=False 47 | MaxSubstepDeltaTime=0.016667 48 | MaxSubsteps=6 49 | SyncSceneSmoothingFactor=0.000000 50 | AsyncSceneSmoothingFactor=0.990000 51 | InitialAverageFrameRate=0.016667 52 | PhysXTreeRebuildRate=10 53 | 54 | 55 | -------------------------------------------------------------------------------- /Config/DefaultGame.ini: -------------------------------------------------------------------------------- 1 | [/Script/EngineSettings.GeneralProjectSettings] 2 | ProjectID=C0A7B37A40F5020C10F1B6809AE1B5E1 3 | 4 | -------------------------------------------------------------------------------- /Content/EditorResources/IconExampleEditorMode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lxjk/ToolExample/805afebda7a067b973d426597b7ff8d7e9c608b6/Content/EditorResources/IconExampleEditorMode.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Eric Zhang 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 | -------------------------------------------------------------------------------- /Source/ToolExample.Target.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class ToolExampleTarget : TargetRules 7 | { 8 | public ToolExampleTarget(TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "ToolExample" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/ToolExample/CustomDataType/ExampleData.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExampleData.generated.h" 4 | 5 | UCLASS(Blueprintable) 6 | class TOOLEXAMPLE_API UExampleData : public UObject 7 | { 8 | GENERATED_BODY() 9 | 10 | public: 11 | UPROPERTY(EditAnywhere, Category = "Properties") 12 | FString ExampleString; 13 | 14 | #if WITH_EDITORONLY_DATA 15 | UPROPERTY(Category = SourceAsset, VisibleAnywhere) 16 | FString SourceFilePath; 17 | #endif 18 | }; -------------------------------------------------------------------------------- /Source/ToolExample/DetailsCustomization/ExampleActor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExampleActor.generated.h" 4 | 5 | UCLASS() 6 | class TOOLEXAMPLE_API AExampleActor : public AActor 7 | { 8 | GENERATED_BODY() 9 | 10 | public: 11 | 12 | UPROPERTY(EditAnywhere, Category = "Options") 13 | bool bOption1 = false; 14 | 15 | UPROPERTY(EditAnywhere, Category = "Options") 16 | bool bOption2 = false; 17 | 18 | UPROPERTY(EditAnywhere, Category = "Test") 19 | int testInt = 0; 20 | 21 | }; -------------------------------------------------------------------------------- /Source/ToolExample/EditorMode/ExampleTargetPoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Engine/Targetpoint.h" 4 | #include "ExampleTargetPoint.generated.h" 5 | 6 | UCLASS() 7 | class TOOLEXAMPLE_API AExampleTargetPoint : public ATargetPoint 8 | { 9 | GENERATED_BODY() 10 | 11 | public: 12 | UPROPERTY(EditAnywhere, Category = "Points") 13 | TArray Points; 14 | }; -------------------------------------------------------------------------------- /Source/ToolExample/ToolExample.Build.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class ToolExample : ModuleRules 6 | { 7 | public ToolExample(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); 12 | 13 | PrivateDependencyModuleNames.AddRange(new string[] { }); 14 | 15 | // Uncomment if you are using Slate UI 16 | // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); 17 | 18 | // Uncomment if you are using online features 19 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 20 | 21 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/ToolExample/ToolExample.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "ToolExample.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ToolExample, "ToolExample" ); 7 | -------------------------------------------------------------------------------- /Source/ToolExample/ToolExample.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/ToolExample/ToolExampleGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | 4 | #include "ToolExampleGameModeBase.h" 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Source/ToolExample/ToolExampleGameModeBase.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/GameModeBase.h" 7 | #include "ToolExampleGameModeBase.generated.h" 8 | 9 | /** 10 | * 11 | */ 12 | UCLASS() 13 | class TOOLEXAMPLE_API AToolExampleGameModeBase : public AGameModeBase 14 | { 15 | GENERATED_BODY() 16 | 17 | 18 | 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class ToolExampleEditorTarget : TargetRules 7 | { 8 | public ToolExampleEditorTarget(TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | 12 | ExtraModuleNames.AddRange( new string[] { "ToolExample" } ); 13 | ExtraModuleNames.AddRange( new string[] { "ToolExampleEditor" }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ExampleDataFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "ExampleDataFactory.h" 3 | #include "CustomDataType/ExampleData.h" 4 | 5 | UExampleDataFactory::UExampleDataFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 6 | { 7 | Formats.Add(TEXT("xmp;Example Data")); 8 | SupportedClass = UExampleData::StaticClass(); 9 | bCreateNew = false; // turned off for import 10 | bEditAfterNew = false; // turned off for import 11 | bEditorImport = true; 12 | bText = true; 13 | } 14 | 15 | UObject* UExampleDataFactory::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) 16 | { 17 | UExampleData* NewObjectAsset = NewObject(InParent, Class, Name, Flags | RF_Transactional); 18 | return NewObjectAsset; 19 | } 20 | 21 | UObject* UExampleDataFactory::FactoryCreateText(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const TCHAR*& Buffer, const TCHAR* BufferEnd, FFeedbackContext* Warn) 22 | { 23 | FEditorDelegates::OnAssetPreImport.Broadcast(this, InClass, InParent, InName, Type); 24 | 25 | // if class type or extension doesn't match, return 26 | if (InClass != UExampleData::StaticClass() || 27 | FCString::Stricmp(Type, TEXT("xmp")) != 0) 28 | return nullptr; 29 | 30 | UExampleData* Data = CastChecked(NewObject(InParent, InName, Flags)); 31 | MakeExampleDataFromText(Data, Buffer, BufferEnd); 32 | 33 | // save the source file path 34 | Data->SourceFilePath = UAssetImportData::SanitizeImportFilename(CurrentFilename, Data->GetOutermost()); 35 | 36 | FEditorDelegates::OnAssetPostImport.Broadcast(this, Data); 37 | 38 | return Data; 39 | } 40 | 41 | bool UExampleDataFactory::FactoryCanImport(const FString& Filename) 42 | { 43 | return FPaths::GetExtension(Filename).Equals(TEXT("xmp")); 44 | } 45 | 46 | void UExampleDataFactory::MakeExampleDataFromText(class UExampleData* Data, const TCHAR*& Buffer, const TCHAR* BufferEnd) 47 | { 48 | Data->ExampleString = Buffer; 49 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ExampleDataFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "UnrealEd.h" 3 | #include "ExampleDataFactory.generated.h" 4 | 5 | UCLASS() 6 | class UExampleDataFactory : public UFactory 7 | { 8 | GENERATED_UCLASS_BODY() 9 | public: 10 | // Begin UFactory Interface 11 | virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; 12 | virtual UObject* FactoryCreateText(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const TCHAR*& Buffer, const TCHAR* BufferEnd, FFeedbackContext* Warn) override; 13 | virtual bool FactoryCanImport(const FString& Filename) override; 14 | // End UFactory Interface 15 | 16 | // helper function 17 | static void MakeExampleDataFromText(class UExampleData* Data, const TCHAR*& Buffer, const TCHAR* BufferEnd); 18 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ExampleDataTypeActions.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "ExampleDataTypeActions.h" 3 | #include "CustomDataType/ExampleData.h" 4 | 5 | 6 | FExampleDataTypeActions::FExampleDataTypeActions(EAssetTypeCategories::Type InAssetCategory) 7 | : MyAssetCategory(InAssetCategory) 8 | { 9 | } 10 | 11 | FText FExampleDataTypeActions::GetName() const 12 | { 13 | return FText::FromString("Example Data"); 14 | } 15 | 16 | FColor FExampleDataTypeActions::GetTypeColor() const 17 | { 18 | return FColor(230, 205, 165); 19 | } 20 | 21 | UClass* FExampleDataTypeActions::GetSupportedClass() const 22 | { 23 | return UExampleData::StaticClass(); 24 | } 25 | 26 | uint32 FExampleDataTypeActions::GetCategories() 27 | { 28 | return MyAssetCategory; 29 | } 30 | 31 | void FExampleDataTypeActions::GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) 32 | { 33 | auto ExampleDataImports = GetTypedWeakObjectPtrs(InObjects); 34 | 35 | MenuBuilder.AddMenuEntry( 36 | FText::FromString("Reimport"), 37 | FText::FromString("Reimports example data."), 38 | FSlateIcon(), 39 | FUIAction( 40 | FExecuteAction::CreateSP(this, &FExampleDataTypeActions::ExecuteReimport, ExampleDataImports), 41 | FCanExecuteAction() 42 | ) 43 | ); 44 | } 45 | 46 | void FExampleDataTypeActions::ExecuteReimport(TArray> Objects) 47 | { 48 | for (auto ObjIt = Objects.CreateConstIterator(); ObjIt; ++ObjIt) 49 | { 50 | auto Object = (*ObjIt).Get(); 51 | if (Object) 52 | { 53 | FReimportManager::Instance()->Reimport(Object, /*bAskForNewFileIfMissing=*/true); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ExampleDataTypeActions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "AssetTypeActions_Base.h" 4 | 5 | class UExampleData; 6 | 7 | class FExampleDataTypeActions : public FAssetTypeActions_Base 8 | { 9 | public: 10 | FExampleDataTypeActions(EAssetTypeCategories::Type InAssetCategory); 11 | 12 | // IAssetTypeActions interface 13 | virtual FText GetName() const override; 14 | virtual FColor GetTypeColor() const override; 15 | virtual UClass* GetSupportedClass() const override; 16 | virtual uint32 GetCategories() override; 17 | virtual bool HasActions(const TArray& InObjects) const override { return true; } 18 | virtual void GetActions(const TArray& InObjects, FMenuBuilder& MenuBuilder) override; 19 | // End of IAssetTypeActions interface 20 | 21 | void ExecuteReimport(TArray> Objects); 22 | 23 | private: 24 | EAssetTypeCategories::Type MyAssetCategory; 25 | }; 26 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ReimportExampleDataFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "ReimportExampleDataFactory.h" 3 | #include "ExampleDataFactory.h" 4 | #include "CustomDataType/ExampleData.h" 5 | 6 | bool UReimportExampleDataFactory::CanReimport(UObject* Obj, TArray& OutFilenames) 7 | { 8 | UExampleData* ExampleData = Cast(Obj); 9 | if (ExampleData) 10 | { 11 | OutFilenames.Add(UAssetImportData::ResolveImportFilename(ExampleData->SourceFilePath, ExampleData->GetOutermost())); 12 | return true; 13 | } 14 | return false; 15 | } 16 | 17 | void UReimportExampleDataFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) 18 | { 19 | UExampleData* ExampleData = Cast(Obj); 20 | if (ExampleData && ensure(NewReimportPaths.Num() == 1)) 21 | { 22 | ExampleData->SourceFilePath = UAssetImportData::SanitizeImportFilename(NewReimportPaths[0], ExampleData->GetOutermost()); 23 | } 24 | } 25 | 26 | EReimportResult::Type UReimportExampleDataFactory::Reimport(UObject* Obj) 27 | { 28 | UExampleData* ExampleData = Cast(Obj); 29 | if (!ExampleData) 30 | { 31 | return EReimportResult::Failed; 32 | } 33 | 34 | const FString Filename = UAssetImportData::ResolveImportFilename(ExampleData->SourceFilePath, ExampleData->GetOutermost()); 35 | if (!FPaths::GetExtension(Filename).Equals(TEXT("xmp"))) 36 | { 37 | return EReimportResult::Failed; 38 | } 39 | 40 | CurrentFilename = Filename; 41 | FString Data; 42 | if (FFileHelper::LoadFileToString(Data, *CurrentFilename)) 43 | { 44 | const TCHAR* Ptr = *Data; 45 | ExampleData->Modify(); 46 | ExampleData->MarkPackageDirty(); 47 | 48 | UExampleDataFactory::MakeExampleDataFromText(ExampleData, Ptr, Ptr + Data.Len()); 49 | 50 | // save the source file path and timestamp 51 | ExampleData->SourceFilePath = UAssetImportData::SanitizeImportFilename(CurrentFilename, ExampleData->GetOutermost()); 52 | } 53 | 54 | return EReimportResult::Succeeded; 55 | } 56 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomDataType/ReimportExampleDataFactory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ExampleDataFactory.h" 3 | #include "ReimportExampleDataFactory.generated.h" 4 | 5 | UCLASS() 6 | class UReimportExampleDataFactory : public UExampleDataFactory, public FReimportHandler 7 | { 8 | GENERATED_BODY() 9 | 10 | // Begin FReimportHandler interface 11 | virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; 12 | virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; 13 | virtual EReimportResult::Type Reimport(UObject* Obj) override; 14 | // End FReimportHandler interface 15 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/CustomProjectSettings/ExampleSettings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExampleSettings.generated.h" 4 | 5 | UCLASS(config = EditorUserSettings, defaultconfig) 6 | class UExampleSettings : public UObject 7 | { 8 | GENERATED_BODY() 9 | 10 | UPROPERTY(EditAnywhere, config, Category = Test) 11 | bool bTest = false; 12 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/DetailsCustomization/ExampleActorDetails.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ToolExampleEditor/ToolExampleEditor.h" 3 | #include "ExampleActorDetails.h" 4 | 5 | #include "DetailsCustomization/ExampleActor.h" 6 | 7 | TSharedRef FExampleActorDetails::MakeInstance() 8 | { 9 | return MakeShareable(new FExampleActorDetails); 10 | } 11 | 12 | void FExampleActorDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout) 13 | { 14 | TArray> Objects; 15 | DetailLayout.GetObjectsBeingCustomized(Objects); 16 | if (Objects.Num() != 1) 17 | { 18 | // skip customization if select more than one objects 19 | return; 20 | } 21 | AExampleActor* actor = (AExampleActor*)Objects[0].Get(); 22 | 23 | // hide original property 24 | DetailLayout.HideProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(AExampleActor, bOption1))); 25 | DetailLayout.HideProperty(DetailLayout.GetProperty(GET_MEMBER_NAME_CHECKED(AExampleActor, bOption2))); 26 | 27 | // add custom widget to "Options" category 28 | IDetailCategoryBuilder& OptionsCategory = DetailLayout.EditCategory("Options", FText::FromString(""), ECategoryPriority::Important); 29 | OptionsCategory.AddCustomRow(FText::FromString("Options")) 30 | .WholeRowContent() 31 | [ 32 | SNew(SHorizontalBox) 33 | + SHorizontalBox::Slot() 34 | .AutoWidth() 35 | .VAlign(VAlign_Center) 36 | [ 37 | SNew(SCheckBox) 38 | .Style(FEditorStyle::Get(), "RadioButton") 39 | .IsChecked(this, &FExampleActorDetails::IsModeRadioChecked, actor, 1) 40 | .OnCheckStateChanged(this, &FExampleActorDetails::OnModeRadioChanged, actor, 1) 41 | [ 42 | SNew(STextBlock).Text(FText::FromString("Option 1")) 43 | ] 44 | ] 45 | + SHorizontalBox::Slot() 46 | .AutoWidth() 47 | .Padding(10.f, 0.f, 0.f, 0.f) 48 | .VAlign(VAlign_Center) 49 | [ 50 | SNew(SCheckBox) 51 | .Style(FEditorStyle::Get(), "RadioButton") 52 | .IsChecked(this, &FExampleActorDetails::IsModeRadioChecked, actor, 2) 53 | .OnCheckStateChanged(this, &FExampleActorDetails::OnModeRadioChanged, actor, 2) 54 | [ 55 | SNew(STextBlock).Text(FText::FromString("Option 2")) 56 | ] 57 | ] 58 | ]; 59 | 60 | } 61 | 62 | ECheckBoxState FExampleActorDetails::IsModeRadioChecked(AExampleActor* actor, int optionIndex) const 63 | { 64 | bool bFlag = false; 65 | if (actor) 66 | { 67 | if (optionIndex == 1) 68 | bFlag = actor->bOption1; 69 | else if (optionIndex == 2) 70 | bFlag = actor->bOption2; 71 | } 72 | return bFlag ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; 73 | } 74 | 75 | void FExampleActorDetails::OnModeRadioChanged(ECheckBoxState CheckType, AExampleActor* actor, int optionIndex) 76 | { 77 | bool bFlag = (CheckType == ECheckBoxState::Checked); 78 | if (actor) 79 | { 80 | actor->Modify(); 81 | if (bFlag) 82 | { 83 | // clear all options first 84 | actor->bOption1 = false; 85 | actor->bOption2 = false; 86 | } 87 | if (optionIndex == 1) 88 | actor->bOption1 = bFlag; 89 | else if (optionIndex == 2) 90 | actor->bOption2 = bFlag; 91 | } 92 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/DetailsCustomization/ExampleActorDetails.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "IDetailCustomization.h" 4 | 5 | class AExampleActor; 6 | 7 | class FExampleActorDetails : public IDetailCustomization 8 | { 9 | public: 10 | /** Makes a new instance of this detail layout class for a specific detail view requesting it */ 11 | static TSharedRef MakeInstance(); 12 | 13 | /** IDetailCustomization interface */ 14 | virtual void CustomizeDetails(IDetailLayoutBuilder& DetailLayout) override; 15 | 16 | protected: 17 | 18 | // widget functions 19 | ECheckBoxState IsModeRadioChecked(AExampleActor* actor, int optionIndex) const; 20 | void OnModeRadioChanged(ECheckBoxState CheckType, AExampleActor* actor, int optionIndex); 21 | 22 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/ExampleEdMode.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "Editor/UnrealEd/Public/Toolkits/ToolkitManager.h" 3 | #include "ScopedTransaction.h" 4 | #include "ExampleEdModeToolkit.h" 5 | #include "ExampleEdMode.h" 6 | #include "EditorMode/ExampleTargetPoint.h" 7 | 8 | class ExampleEditorCommands : public TCommands 9 | { 10 | public: 11 | ExampleEditorCommands() : TCommands 12 | ( 13 | "ExampleEditor", // Context name for fast lookup 14 | FText::FromString(TEXT("Example Editor")), // context name for displaying 15 | NAME_None, // Parent 16 | FEditorStyle::GetStyleSetName() 17 | ) 18 | { 19 | } 20 | 21 | #define LOCTEXT_NAMESPACE "" 22 | virtual void RegisterCommands() override 23 | { 24 | UI_COMMAND(DeletePoint, "Delete Point", "Delete the currently selected point.", EUserInterfaceActionType::Button, FInputGesture(EKeys::Delete)); 25 | } 26 | #undef LOCTEXT_NAMESPACE 27 | 28 | public: 29 | TSharedPtr DeletePoint; 30 | }; 31 | 32 | IMPLEMENT_HIT_PROXY(HExamplePointProxy, HHitProxy); 33 | 34 | const FEditorModeID FExampleEdMode::EM_Example(TEXT("EM_Example")); 35 | 36 | FExampleEdMode::FExampleEdMode() 37 | { 38 | ExampleEditorCommands::Register(); 39 | ExampleEdModeActions = MakeShareable(new FUICommandList); 40 | } 41 | 42 | FExampleEdMode::~FExampleEdMode() 43 | { 44 | ExampleEditorCommands::Unregister(); 45 | } 46 | 47 | void FExampleEdMode::MapCommands() 48 | { 49 | const auto& Commands = ExampleEditorCommands::Get(); 50 | 51 | ExampleEdModeActions->MapAction( 52 | Commands.DeletePoint, 53 | FExecuteAction::CreateSP(this, &FExampleEdMode::RemovePoint), 54 | FCanExecuteAction::CreateSP(this, &FExampleEdMode::CanRemovePoint)); 55 | } 56 | 57 | void FExampleEdMode::Enter() 58 | { 59 | FEdMode::Enter(); 60 | 61 | if (!Toolkit.IsValid()) 62 | { 63 | Toolkit = MakeShareable(new FExampleEdModeToolkit); 64 | Toolkit->Init(Owner->GetToolkitHost()); 65 | } 66 | 67 | // reset 68 | currentSelectedTarget = nullptr; 69 | currentSelectedIndex = -1; 70 | 71 | MapCommands(); 72 | } 73 | 74 | void FExampleEdMode::Exit() 75 | { 76 | FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef()); 77 | Toolkit.Reset(); 78 | 79 | FEdMode::Exit(); 80 | } 81 | 82 | void FExampleEdMode::Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) 83 | { 84 | const FColor normalColor(200, 200, 200); 85 | const FColor selectedColor(255, 128, 0); 86 | 87 | UWorld* World = GetWorld(); 88 | for (TActorIterator It(World); It; ++It) 89 | { 90 | AExampleTargetPoint* actor = (*It); 91 | if (actor) 92 | { 93 | FVector actorLoc = actor->GetActorLocation(); 94 | for (int i = 0; i < actor->Points.Num(); ++i) 95 | { 96 | bool bSelected = (actor == currentSelectedTarget && i == currentSelectedIndex); 97 | const FColor& color = bSelected ? selectedColor : normalColor; 98 | // set hit proxy and draw 99 | PDI->SetHitProxy(new HExamplePointProxy(actor, i)); 100 | PDI->DrawPoint(actor->Points[i], color, 15.f, SDPG_Foreground); 101 | PDI->DrawLine(actor->Points[i], actorLoc, color, SDPG_Foreground); 102 | PDI->SetHitProxy(NULL); 103 | } 104 | } 105 | } 106 | 107 | FEdMode::Render(View, Viewport, PDI); 108 | } 109 | 110 | bool FExampleEdMode::HandleClick(FEditorViewportClient* InViewportClient, HHitProxy *HitProxy, const FViewportClick &Click) 111 | { 112 | bool isHandled = false; 113 | 114 | if (HitProxy) 115 | { 116 | if (HitProxy->IsA(HExamplePointProxy::StaticGetType())) 117 | { 118 | isHandled = true; 119 | HExamplePointProxy* examplePointProxy = (HExamplePointProxy*)HitProxy; 120 | AExampleTargetPoint* actor = Cast(examplePointProxy->RefObject); 121 | int32 index = examplePointProxy->Index; 122 | if (actor && index >= 0 && index < actor->Points.Num()) 123 | { 124 | SelectPoint(actor, index); 125 | } 126 | } 127 | } 128 | 129 | if (HitProxy && isHandled && Click.GetKey() == EKeys::RightMouseButton) 130 | { 131 | TSharedPtr MenuWidget = GenerateContextMenu(InViewportClient); 132 | if (MenuWidget.IsValid()) 133 | { 134 | FSlateApplication::Get().PushMenu( 135 | Owner->GetToolkitHost()->GetParentWidget(), 136 | FWidgetPath(), 137 | MenuWidget.ToSharedRef(), 138 | FSlateApplication::Get().GetCursorPos(), 139 | FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)); 140 | } 141 | } 142 | 143 | return isHandled; 144 | } 145 | 146 | bool FExampleEdMode::InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) 147 | { 148 | if (InViewportClient->GetCurrentWidgetAxis() == EAxisList::None) 149 | { 150 | return false; 151 | } 152 | 153 | if (HasValidSelection()) 154 | { 155 | if (!InDrag.IsZero()) 156 | { 157 | currentSelectedTarget->Modify(); 158 | currentSelectedTarget->Points[currentSelectedIndex] += InDrag; 159 | } 160 | return true; 161 | } 162 | 163 | return false; 164 | } 165 | 166 | bool FExampleEdMode::InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) 167 | { 168 | bool isHandled = false; 169 | 170 | if (!isHandled && Event == IE_Pressed) 171 | { 172 | isHandled = ExampleEdModeActions->ProcessCommandBindings(Key, FSlateApplication::Get().GetModifierKeys(), false); 173 | } 174 | 175 | return isHandled; 176 | } 177 | 178 | TSharedPtr FExampleEdMode::GenerateContextMenu(FEditorViewportClient* InViewportClient) const 179 | { 180 | FMenuBuilder MenuBuilder(true, NULL); 181 | 182 | MenuBuilder.PushCommandList(ExampleEdModeActions.ToSharedRef()); 183 | MenuBuilder.BeginSection("Example Section"); 184 | if (HasValidSelection()) 185 | { 186 | // add label for point index 187 | TSharedRef LabelWidget = 188 | SNew(STextBlock) 189 | .Text(FText::FromString(FString::FromInt(currentSelectedIndex))) 190 | .ColorAndOpacity(FLinearColor::Green); 191 | MenuBuilder.AddWidget(LabelWidget, FText::FromString(TEXT("Point Index: "))); 192 | MenuBuilder.AddMenuSeparator(); 193 | // add delete point entry 194 | MenuBuilder.AddMenuEntry(ExampleEditorCommands::Get().DeletePoint); 195 | } 196 | MenuBuilder.EndSection(); 197 | MenuBuilder.PopCommandList(); 198 | 199 | TSharedPtr MenuWidget = MenuBuilder.MakeWidget(); 200 | return MenuWidget; 201 | } 202 | 203 | bool FExampleEdMode::ShowModeWidgets() const 204 | { 205 | return true; 206 | } 207 | 208 | bool FExampleEdMode::ShouldDrawWidget() const 209 | { 210 | return true; 211 | } 212 | 213 | bool FExampleEdMode::UsesTransformWidget() const 214 | { 215 | return true; 216 | } 217 | 218 | FVector FExampleEdMode::GetWidgetLocation() const 219 | { 220 | if (HasValidSelection()) 221 | { 222 | return currentSelectedTarget->Points[currentSelectedIndex]; 223 | } 224 | return FEdMode::GetWidgetLocation(); 225 | } 226 | 227 | AExampleTargetPoint* GetSelectedTargetPointActor() 228 | { 229 | TArray selectedObjects; 230 | GEditor->GetSelectedActors()->GetSelectedObjects(selectedObjects); 231 | if (selectedObjects.Num() == 1) 232 | { 233 | return Cast(selectedObjects[0]); 234 | } 235 | return nullptr; 236 | } 237 | 238 | void FExampleEdMode::AddPoint() 239 | { 240 | AExampleTargetPoint* actor = GetSelectedTargetPointActor(); 241 | if (actor) 242 | { 243 | const FScopedTransaction Transaction(FText::FromString("Add Point")); 244 | 245 | // add new point, slightly in front of camera 246 | FEditorViewportClient* client = (FEditorViewportClient*)GEditor->GetActiveViewport()->GetClient(); 247 | FVector newPoint = client->GetViewLocation() + client->GetViewRotation().Vector() * 50.f; 248 | actor->Modify(); 249 | actor->Points.Add(newPoint); 250 | // auto select this new point 251 | SelectPoint(actor, actor->Points.Num() - 1); 252 | } 253 | } 254 | 255 | bool FExampleEdMode::CanAddPoint() const 256 | { 257 | return GetSelectedTargetPointActor() != nullptr; 258 | } 259 | 260 | void FExampleEdMode::RemovePoint() 261 | { 262 | if (HasValidSelection()) 263 | { 264 | const FScopedTransaction Transaction(FText::FromString("Remove Point")); 265 | 266 | currentSelectedTarget->Modify(); 267 | currentSelectedTarget->Points.RemoveAt(currentSelectedIndex); 268 | // deselect the point 269 | SelectPoint(nullptr, -1); 270 | } 271 | } 272 | 273 | bool FExampleEdMode::CanRemovePoint() const 274 | { 275 | return HasValidSelection(); 276 | } 277 | 278 | bool FExampleEdMode::HasValidSelection() const 279 | { 280 | return currentSelectedTarget.IsValid() && currentSelectedIndex >= 0 && currentSelectedIndex < currentSelectedTarget->Points.Num(); 281 | } 282 | 283 | void FExampleEdMode::SelectPoint(AExampleTargetPoint* actor, int32 index) 284 | { 285 | currentSelectedTarget = actor; 286 | currentSelectedIndex = index; 287 | 288 | // select this actor only 289 | if (currentSelectedTarget.IsValid()) 290 | { 291 | GEditor->SelectNone(true, true); 292 | GEditor->SelectActor(currentSelectedTarget.Get(), true, true); 293 | } 294 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/ExampleEdMode.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EditorModes.h" 4 | 5 | struct HExamplePointProxy : public HHitProxy 6 | { 7 | DECLARE_HIT_PROXY(); 8 | 9 | HExamplePointProxy(UObject* InRefObject, int32 InIndex) 10 | : HHitProxy(HPP_UI), RefObject(InRefObject), Index(InIndex) 11 | {} 12 | 13 | UObject* RefObject; 14 | int32 Index; 15 | }; 16 | 17 | class AExampleTargetPoint; 18 | 19 | class FExampleEdMode : public FEdMode 20 | { 21 | public: 22 | 23 | const static FEditorModeID EM_Example; 24 | 25 | // FEdMode interface 26 | virtual void Enter() override; 27 | virtual void Exit() override; 28 | virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override; 29 | //virtual void Tick(FEditorViewportClient* ViewportClient, float DeltaTime) override; 30 | virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy *HitProxy, const FViewportClick &Click) override; 31 | virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override; 32 | virtual bool InputKey(FEditorViewportClient* ViewportClient, FViewport* Viewport, FKey Key, EInputEvent Event) override; 33 | virtual bool ShowModeWidgets() const override; 34 | virtual bool ShouldDrawWidget() const override; 35 | virtual bool UsesTransformWidget() const override; 36 | virtual FVector GetWidgetLocation() const override; 37 | //virtual bool GetCustomDrawingCoordinateSystem(FMatrix& InMatrix, void* InData) override; 38 | //virtual bool GetCustomInputCoordinateSystem(FMatrix& InMatrix, void* InData) override; 39 | //virtual void ActorSelectionChangeNotify() override; 40 | //virtual void MapChangeNotify() override; 41 | //virtual void SelectionChanged() override; 42 | //virtual bool IsCompatibleWith(FEditorModeID OtherModeID) const override; 43 | // End of FEdMode interface 44 | 45 | FExampleEdMode(); 46 | ~FExampleEdMode(); 47 | 48 | void AddPoint(); 49 | bool CanAddPoint() const; 50 | void RemovePoint(); 51 | bool CanRemovePoint() const; 52 | bool HasValidSelection() const; 53 | void SelectPoint(AExampleTargetPoint* actor, int32 index); 54 | 55 | TWeakObjectPtr currentSelectedTarget; 56 | int32 currentSelectedIndex = -1; 57 | 58 | 59 | TSharedPtr ExampleEdModeActions; 60 | void MapCommands(); 61 | TSharedPtr GenerateContextMenu(FEditorViewportClient* InViewportClient) const; 62 | 63 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/ExampleEdModeTool.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "ExampleEdModeTool.h" 3 | #include "ExampleEdMode.h" 4 | 5 | #define IMAGE_BRUSH(RelativePath, ...) FSlateImageBrush(StyleSet->RootToContentDir(RelativePath, TEXT(".png")), __VA_ARGS__) 6 | 7 | TSharedPtr< FSlateStyleSet > ExampleEdModeTool::StyleSet = nullptr; 8 | 9 | void ExampleEdModeTool::OnStartupModule() 10 | { 11 | RegisterStyleSet(); 12 | RegisterEditorMode(); 13 | } 14 | 15 | void ExampleEdModeTool::OnShutdownModule() 16 | { 17 | UnregisterStyleSet(); 18 | UnregisterEditorMode(); 19 | } 20 | 21 | void ExampleEdModeTool::RegisterStyleSet() 22 | { 23 | // Const icon sizes 24 | const FVector2D Icon20x20(20.0f, 20.0f); 25 | const FVector2D Icon40x40(40.0f, 40.0f); 26 | 27 | // Only register once 28 | if (StyleSet.IsValid()) 29 | { 30 | return; 31 | } 32 | 33 | StyleSet = MakeShareable(new FSlateStyleSet("ExampleEdModeToolStyle")); 34 | StyleSet->SetContentRoot(FPaths::GameDir() / TEXT("Content/EditorResources")); 35 | StyleSet->SetCoreContentRoot(FPaths::GameDir() / TEXT("Content/EditorResources")); 36 | 37 | // Spline editor 38 | { 39 | StyleSet->Set("ExampleEdMode", new IMAGE_BRUSH(TEXT("IconExampleEditorMode"), Icon40x40)); 40 | StyleSet->Set("ExampleEdMode.Small", new IMAGE_BRUSH(TEXT("IconExampleEditorMode"), Icon20x20)); 41 | } 42 | 43 | FSlateStyleRegistry::RegisterSlateStyle(*StyleSet.Get()); 44 | } 45 | 46 | void ExampleEdModeTool::UnregisterStyleSet() 47 | { 48 | if (StyleSet.IsValid()) 49 | { 50 | FSlateStyleRegistry::UnRegisterSlateStyle(*StyleSet.Get()); 51 | ensure(StyleSet.IsUnique()); 52 | StyleSet.Reset(); 53 | } 54 | } 55 | 56 | void ExampleEdModeTool::RegisterEditorMode() 57 | { 58 | FEditorModeRegistry::Get().RegisterMode( 59 | FExampleEdMode::EM_Example, 60 | FText::FromString("Example Editor Mode"), 61 | FSlateIcon(StyleSet->GetStyleSetName(), "ExampleEdMode", "ExampleEdMode.Small"), 62 | true, 500 63 | ); 64 | } 65 | 66 | void ExampleEdModeTool::UnregisterEditorMode() 67 | { 68 | FEditorModeRegistry::Get().UnregisterMode(FExampleEdMode::EM_Example); 69 | } 70 | 71 | #undef IMAGE_BRUSH -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/ExampleEdModeTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolExampleEditor/ExampleTabToolBase.h" 4 | 5 | class ExampleEdModeTool : public FExampleTabToolBase 6 | { 7 | public: 8 | virtual void OnStartupModule() override; 9 | virtual void OnShutdownModule() override; 10 | 11 | virtual ~ExampleEdModeTool() {} 12 | private: 13 | static TSharedPtr< class FSlateStyleSet > StyleSet; 14 | 15 | void RegisterStyleSet(); 16 | void UnregisterStyleSet(); 17 | 18 | void RegisterEditorMode(); 19 | void UnregisterEditorMode(); 20 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/ExampleEdModeToolkit.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "BaseToolkit.h" 4 | #include "ExampleEdMode.h" 5 | #include "SExampleEdModeWidget.h" 6 | 7 | class FExampleEdModeToolkit: public FModeToolkit 8 | { 9 | public: 10 | 11 | FExampleEdModeToolkit() 12 | { 13 | SAssignNew(ExampleEdModeWidget, SExampleEdModeWidget); 14 | } 15 | 16 | /** IToolkit interface */ 17 | virtual FName GetToolkitFName() const override { return FName("ExampleEdMode"); } 18 | virtual FText GetBaseToolkitName() const override { return NSLOCTEXT("BuilderModeToolkit", "DisplayName", "Builder"); } 19 | virtual class FEdMode* GetEditorMode() const override { return GLevelEditorModeTools().GetActiveMode(FExampleEdMode::EM_Example); } 20 | virtual TSharedPtr GetInlineContent() const override { return ExampleEdModeWidget; } 21 | 22 | private: 23 | 24 | TSharedPtr ExampleEdModeWidget; 25 | }; 26 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/SExampleEdModeWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "ExampleEdMode.h" 3 | #include "SExampleEdModeWidget.h" 4 | 5 | void SExampleEdModeWidget::Construct(const FArguments& InArgs) 6 | { 7 | ChildSlot 8 | [ 9 | SNew(SScrollBox) 10 | + SScrollBox::Slot() 11 | .VAlign(VAlign_Top) 12 | .Padding(5.f) 13 | [ 14 | SNew(SVerticalBox) 15 | + SVerticalBox::Slot() 16 | .AutoHeight() 17 | .Padding(0.f, 5.f, 0.f, 0.f) 18 | [ 19 | SNew(STextBlock) 20 | .Text(FText::FromString(TEXT("This is a editor mode example."))) 21 | ] 22 | + SVerticalBox::Slot() 23 | .AutoHeight() 24 | .Padding(0.f, 5.f, 0.f, 0.f) 25 | [ 26 | SNew(SHorizontalBox) 27 | + SHorizontalBox::Slot() 28 | .AutoWidth() 29 | .Padding(2, 0, 0, 0) 30 | .VAlign(VAlign_Center) 31 | [ 32 | SNew(SButton) 33 | .Text(FText::FromString("Add")) 34 | .OnClicked(this, &SExampleEdModeWidget::OnAddPoint) 35 | .IsEnabled(this, &SExampleEdModeWidget::CanAddPoint) 36 | ] 37 | + SHorizontalBox::Slot() 38 | .AutoWidth() 39 | .VAlign(VAlign_Center) 40 | .Padding(0, 0, 2, 0) 41 | [ 42 | SNew(SButton) 43 | .Text(FText::FromString("Remove")) 44 | .OnClicked(this, &SExampleEdModeWidget::OnRemovePoint) 45 | .IsEnabled(this, &SExampleEdModeWidget::CanRemovePoint) 46 | ] 47 | ] 48 | ] 49 | ]; 50 | } 51 | 52 | FExampleEdMode* SExampleEdModeWidget::GetEdMode() const 53 | { 54 | return (FExampleEdMode*)GLevelEditorModeTools().GetActiveMode(FExampleEdMode::EM_Example); 55 | } 56 | 57 | FReply SExampleEdModeWidget::OnAddPoint() 58 | { 59 | GetEdMode()->AddPoint(); 60 | return FReply::Handled(); 61 | } 62 | 63 | bool SExampleEdModeWidget::CanAddPoint() const 64 | { 65 | return GetEdMode()->CanAddPoint(); 66 | } 67 | 68 | FReply SExampleEdModeWidget::OnRemovePoint() 69 | { 70 | GetEdMode()->RemovePoint(); 71 | return FReply::Handled(); 72 | } 73 | 74 | bool SExampleEdModeWidget::CanRemovePoint() const 75 | { 76 | return GetEdMode()->CanRemovePoint(); 77 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/EditorMode/SExampleEdModeWidget.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SlateApplication.h" 4 | 5 | class SExampleEdModeWidget : public SCompoundWidget 6 | { 7 | public: 8 | SLATE_BEGIN_ARGS(SExampleEdModeWidget) {} 9 | SLATE_END_ARGS(); 10 | 11 | void Construct(const FArguments& InArgs); 12 | 13 | // Util Functions 14 | class FExampleEdMode* GetEdMode() const; 15 | 16 | FReply OnAddPoint(); 17 | bool CanAddPoint() const; 18 | FReply OnRemovePoint(); 19 | bool CanRemovePoint() const; 20 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/ExampleTabToolBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolExampleEditor/ToolExampleEditor.h" 4 | #include "ToolExampleEditor/IExampleModuleInterface.h" 5 | #include "TabManager.h" 6 | #include "SDockTab.h" 7 | 8 | class FExampleTabToolBase : public IExampleModuleListenerInterface, public TSharedFromThis< FExampleTabToolBase > 9 | { 10 | 11 | public: 12 | 13 | // IPixelopusToolBase 14 | virtual void OnStartupModule() override 15 | { 16 | Initialize(); 17 | FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TabName, FOnSpawnTab::CreateRaw(this, &FExampleTabToolBase::SpawnTab)) 18 | .SetGroup(FToolExampleEditor::Get().GetMenuRoot()) 19 | .SetDisplayName(TabDisplayName) 20 | .SetTooltipText(ToolTipText); 21 | }; 22 | 23 | virtual void OnShutdownModule() override 24 | { 25 | FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TabName); 26 | }; 27 | 28 | // In this function set TabName/TabDisplayName/ToolTipText 29 | virtual void Initialize() {}; 30 | virtual TSharedRef SpawnTab(const FSpawnTabArgs& TabSpawnArgs) { return SNew(SDockTab); }; 31 | 32 | virtual void MakeMenuEntry(FMenuBuilder &menuBuilder) 33 | { 34 | FGlobalTabmanager::Get()->PopulateTabSpawnerMenu(menuBuilder, TabName); 35 | }; 36 | 37 | protected: 38 | FName TabName; 39 | FText TabDisplayName; 40 | FText ToolTipText; 41 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/IExampleModuleInterface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ModuleManager.h" 4 | 5 | 6 | class IExampleModuleListenerInterface 7 | { 8 | public: 9 | virtual void OnStartupModule() {}; 10 | virtual void OnShutdownModule() {}; 11 | }; 12 | 13 | class IExampleModuleInterface : public IModuleInterface 14 | { 15 | public: 16 | void StartupModule() override 17 | { 18 | if (!IsRunningCommandlet()) 19 | { 20 | AddModuleListeners(); 21 | for (int32 i = 0; i < ModuleListeners.Num(); ++i) 22 | { 23 | ModuleListeners[i]->OnStartupModule(); 24 | } 25 | } 26 | } 27 | 28 | void ShutdownModule() override 29 | { 30 | for (int32 i = 0; i < ModuleListeners.Num(); ++i) 31 | { 32 | ModuleListeners[i]->OnShutdownModule(); 33 | } 34 | } 35 | 36 | virtual void AddModuleListeners() {}; 37 | 38 | protected: 39 | TArray> ModuleListeners; 40 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/MenuTool/MenuTool.cpp: -------------------------------------------------------------------------------- 1 | #include "ToolExampleEditor/ToolExampleEditor.h" 2 | #include "MenuTool.h" 3 | 4 | #include "ScopedTransaction.h" 5 | 6 | #define LOCTEXT_NAMESPACE "MenuTool" 7 | 8 | class MenuToolCommands : public TCommands 9 | { 10 | public: 11 | 12 | MenuToolCommands() 13 | : TCommands( 14 | TEXT("MenuTool"), // Context name for fast lookup 15 | FText::FromString("Example Menu tool"), // Context name for displaying 16 | NAME_None, // No parent context 17 | FEditorStyle::GetStyleSetName() // Icon Style Set 18 | ) 19 | { 20 | } 21 | 22 | virtual void RegisterCommands() override 23 | { 24 | UI_COMMAND(MenuCommand1, "Menu Command 1", "Test Menu Command 1.", EUserInterfaceActionType::Button, FInputGesture()); 25 | UI_COMMAND(MenuCommand2, "Menu Command 2", "Test Menu Command 2.", EUserInterfaceActionType::Button, FInputGesture()); 26 | UI_COMMAND(MenuCommand3, "Menu Command 3", "Test Menu Command 3.", EUserInterfaceActionType::Button, FInputGesture()); 27 | 28 | } 29 | 30 | public: 31 | TSharedPtr MenuCommand1; 32 | TSharedPtr MenuCommand2; 33 | TSharedPtr MenuCommand3; 34 | }; 35 | 36 | void MenuTool::MapCommands() 37 | { 38 | const auto& Commands = MenuToolCommands::Get(); 39 | 40 | CommandList->MapAction( 41 | Commands.MenuCommand1, 42 | FExecuteAction::CreateSP(this, &MenuTool::MenuCommand1), 43 | FCanExecuteAction()); 44 | 45 | CommandList->MapAction( 46 | Commands.MenuCommand2, 47 | FExecuteAction::CreateSP(this, &MenuTool::MenuCommand2), 48 | FCanExecuteAction()); 49 | 50 | CommandList->MapAction( 51 | Commands.MenuCommand3, 52 | FExecuteAction::CreateSP(this, &MenuTool::MenuCommand3), 53 | FCanExecuteAction()); 54 | } 55 | 56 | 57 | void MenuTool::OnStartupModule() 58 | { 59 | CommandList = MakeShareable(new FUICommandList); 60 | MenuToolCommands::Register(); 61 | MapCommands(); 62 | FToolExampleEditor::Get().AddMenuExtension( 63 | FMenuExtensionDelegate::CreateRaw(this, &MenuTool::MakeMenuEntry), 64 | FName("Section_1"), 65 | CommandList); 66 | } 67 | 68 | void MenuTool::OnShutdownModule() 69 | { 70 | MenuToolCommands::Unregister(); 71 | } 72 | 73 | 74 | void MenuTool::MakeMenuEntry(FMenuBuilder &menuBuilder) 75 | { 76 | menuBuilder.AddMenuEntry(MenuToolCommands::Get().MenuCommand1); 77 | menuBuilder.AddSubMenu( 78 | FText::FromString("Sub Menu"), 79 | FText::FromString("This is example sub menu"), 80 | FNewMenuDelegate::CreateSP(this, &MenuTool::MakeSubMenu) 81 | ); 82 | 83 | // add tag 84 | TSharedRef AddTagWidget = 85 | SNew(SHorizontalBox) 86 | + SHorizontalBox::Slot() 87 | .AutoWidth() 88 | .VAlign(VAlign_Center) 89 | [ 90 | SNew(SEditableTextBox) 91 | .MinDesiredWidth(50) 92 | .Text(this, &MenuTool::GetTagToAddText) 93 | .OnTextCommitted(this, &MenuTool::OnTagToAddTextCommited) 94 | ] 95 | + SHorizontalBox::Slot() 96 | .AutoWidth() 97 | .Padding(5, 0, 0, 0) 98 | .VAlign(VAlign_Center) 99 | [ 100 | SNew(SButton) 101 | .Text(FText::FromString("Add Tag")) 102 | .OnClicked(this, &MenuTool::AddTag) 103 | ]; 104 | 105 | menuBuilder.AddWidget(AddTagWidget, FText::FromString("")); 106 | } 107 | 108 | void MenuTool::MakeSubMenu(FMenuBuilder &menuBuilder) 109 | { 110 | menuBuilder.AddMenuEntry(MenuToolCommands::Get().MenuCommand2); 111 | menuBuilder.AddMenuEntry(MenuToolCommands::Get().MenuCommand3); 112 | } 113 | 114 | void MenuTool::MenuCommand1() 115 | { 116 | UE_LOG(LogClass, Log, TEXT("clicked MenuCommand1")); 117 | } 118 | 119 | void MenuTool::MenuCommand2() 120 | { 121 | UE_LOG(LogClass, Log, TEXT("clicked MenuCommand2")); 122 | } 123 | 124 | void MenuTool::MenuCommand3() 125 | { 126 | UE_LOG(LogClass, Log, TEXT("clicked MenuCommand3")); 127 | } 128 | 129 | FReply MenuTool::AddTag() 130 | { 131 | if (!TagToAdd.IsNone()) 132 | { 133 | const FScopedTransaction Transaction(FText::FromString("Add Tag")); 134 | for (FSelectionIterator It(GEditor->GetSelectedActorIterator()); It; ++It) 135 | { 136 | AActor* Actor = static_cast(*It); 137 | if (!Actor->Tags.Contains(TagToAdd)) 138 | { 139 | Actor->Modify(); 140 | Actor->Tags.Add(TagToAdd); 141 | } 142 | } 143 | } 144 | 145 | return FReply::Handled(); 146 | } 147 | 148 | FText MenuTool::GetTagToAddText() const 149 | { 150 | return FText::FromName(TagToAdd); 151 | } 152 | 153 | void MenuTool::OnTagToAddTextCommited(const FText& InText, ETextCommit::Type CommitInfo) 154 | { 155 | FString str = InText.ToString(); 156 | TagToAdd = FName(*str.Trim()); 157 | } 158 | 159 | #undef LOCTEXT_NAMESPACE -------------------------------------------------------------------------------- /Source/ToolExampleEditor/MenuTool/MenuTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolExampleEditor/IExampleModuleInterface.h" 4 | 5 | class MenuTool : public IExampleModuleListenerInterface, public TSharedFromThis 6 | { 7 | public: 8 | virtual ~MenuTool() {} 9 | 10 | virtual void OnStartupModule() override; 11 | virtual void OnShutdownModule() override; 12 | 13 | void MakeMenuEntry(FMenuBuilder &menuBuilder); 14 | void MakeSubMenu(FMenuBuilder &menuBuilder); 15 | 16 | 17 | protected: 18 | TSharedPtr CommandList; 19 | 20 | void MapCommands(); 21 | 22 | //************************ 23 | // UI Command functions 24 | void MenuCommand1(); 25 | void MenuCommand2(); 26 | void MenuCommand3(); 27 | 28 | FName TagToAdd; 29 | 30 | FReply AddTag(); 31 | FText GetTagToAddText() const; 32 | void OnTagToAddTextCommited(const FText& InText, ETextCommit::Type CommitInfo); 33 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/TabTool/TabTool.cpp: -------------------------------------------------------------------------------- 1 | 2 | //#include "AssetRegistryModule.h" 3 | //#include "ScopedTransaction.h" 4 | //#include "SDockTab.h" 5 | //#include "SDockableTab.h" 6 | //#include "SDockTabStack.h" 7 | //#include "SlateApplication.h" 8 | #include "ToolExampleEditor/ToolExampleEditor.h" 9 | #include "TabToolPanel.h" 10 | #include "TabTool.h" 11 | 12 | void TabTool::OnStartupModule() 13 | { 14 | FExampleTabToolBase::OnStartupModule(); 15 | FToolExampleEditor::Get().AddMenuExtension(FMenuExtensionDelegate::CreateRaw(this, &TabTool::MakeMenuEntry), FName("Section_2")); 16 | } 17 | 18 | void TabTool::OnShutdownModule() 19 | { 20 | FExampleTabToolBase::OnShutdownModule(); 21 | } 22 | 23 | void TabTool::Initialize() 24 | { 25 | TabName = "TabTool"; 26 | TabDisplayName = FText::FromString("Tab Tool"); 27 | ToolTipText = FText::FromString("Tab Tool Window"); 28 | } 29 | 30 | TSharedRef TabTool::SpawnTab(const FSpawnTabArgs& TabSpawnArgs) 31 | { 32 | TSharedRef SpawnedTab = SNew(SDockTab) 33 | .TabRole(ETabRole::NomadTab) 34 | [ 35 | SNew(TabToolPanel) 36 | .Tool(SharedThis(this)) 37 | ]; 38 | 39 | return SpawnedTab; 40 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/TabTool/TabTool.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ToolExampleEditor/ExampleTabToolBase.h" 4 | 5 | class TabTool : public FExampleTabToolBase 6 | { 7 | public: 8 | virtual ~TabTool() {} 9 | virtual void OnStartupModule() override; 10 | virtual void OnShutdownModule() override; 11 | virtual void Initialize() override; 12 | virtual TSharedRef SpawnTab(const FSpawnTabArgs& TabSpawnArgs) override; 13 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/TabTool/TabToolPanel.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "ToolExampleEditor/ToolExampleEditor.h" 3 | #include "TabToolPanel.h" 4 | 5 | void TabToolPanel::Construct(const FArguments& InArgs) 6 | { 7 | tool = InArgs._Tool; 8 | 9 | if (tool.IsValid()) 10 | { 11 | // do anything you need from tool object 12 | } 13 | 14 | ChildSlot 15 | [ 16 | SNew(SScrollBox) 17 | + SScrollBox::Slot() 18 | .VAlign(VAlign_Top) 19 | .Padding(5) 20 | [ 21 | SNew(SBorder) 22 | .BorderBackgroundColor(FColor(192, 192, 192, 255)) 23 | .Padding(15.0f) 24 | [ 25 | SNew(STextBlock) 26 | .Text(FText::FromString(TEXT("This is a tab example."))) 27 | ] 28 | ] 29 | ]; 30 | } -------------------------------------------------------------------------------- /Source/ToolExampleEditor/TabTool/TabToolPanel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SDockTab.h" 4 | #include "SDockableTab.h" 5 | #include "SDockTabStack.h" 6 | #include "SlateApplication.h" 7 | #include "TabTool.h" 8 | 9 | class TabToolPanel : public SCompoundWidget 10 | { 11 | 12 | SLATE_BEGIN_ARGS(TabToolPanel) 13 | {} 14 | 15 | SLATE_ARGUMENT(TWeakPtr, Tool) 16 | SLATE_END_ARGS() 17 | 18 | void Construct(const FArguments& InArgs); 19 | 20 | protected: 21 | 22 | TWeakPtr tool; 23 | }; -------------------------------------------------------------------------------- /Source/ToolExampleEditor/ToolExampleEditor.Build.cs: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | using UnrealBuildTool; 4 | using System.IO; 5 | 6 | public class ToolExampleEditor : ModuleRules 7 | { 8 | public ToolExampleEditor(ReadOnlyTargetRules Target) : base(Target) 9 | { 10 | PublicIncludePaths.AddRange( 11 | new string[] { 12 | // ... add public include paths required here ... 13 | } 14 | ); 15 | 16 | PrivateIncludePaths.AddRange( 17 | new string[] { 18 | // ... add other private include paths required here ... 19 | } 20 | ); 21 | 22 | PublicDependencyModuleNames.AddRange( 23 | new string[] { 24 | "Core", 25 | "Engine", 26 | "CoreUObject", 27 | "InputCore", 28 | "LevelEditor", 29 | "Slate", 30 | "EditorStyle", 31 | "AssetTools", 32 | "EditorWidgets", 33 | "UnrealEd", 34 | "BlueprintGraph", 35 | "AnimGraph", 36 | "ComponentVisualizers", 37 | "ToolExample" 38 | } 39 | ); 40 | 41 | PrivateDependencyModuleNames.AddRange( 42 | new string[] 43 | { 44 | "Core", 45 | "CoreUObject", 46 | "Engine", 47 | "AppFramework", 48 | "SlateCore", 49 | "AnimGraph", 50 | "UnrealEd", 51 | "KismetWidgets", 52 | "MainFrame", 53 | "PropertyEditor", 54 | "ComponentVisualizers", 55 | "ToolExample" 56 | // ... add private dependencies that you statically link with here ... 57 | } 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/ToolExampleEditor.cpp: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #include "ToolExampleEditor.h" 4 | #include "MenuTool/MenuTool.h" 5 | #include "TabTool/TabTool.h" 6 | #include "EditorMode/ExampleEdModeTool.h" 7 | 8 | #include "DetailsCustomization/ExampleActor.h" 9 | #include "DetailsCustomization/ExampleActorDetails.h" 10 | 11 | #include "CustomDataType/ExampleDataTypeActions.h" 12 | 13 | #include "ISettingsModule.h" 14 | #include "Developer/Settings/Public/ISettingsContainer.h" 15 | #include "CustomProjectSettings/ExampleSettings.h" 16 | 17 | IMPLEMENT_GAME_MODULE(FToolExampleEditor, ToolExampleEditor) 18 | 19 | TSharedRef FToolExampleEditor::MenuRoot = FWorkspaceItem::NewGroup(FText::FromString("Menu Root")); 20 | 21 | void FToolExampleEditor::AddModuleListeners() 22 | { 23 | // add tools 24 | ModuleListeners.Add(MakeShareable(new MenuTool)); 25 | ModuleListeners.Add(MakeShareable(new TabTool)); 26 | ModuleListeners.Add(MakeShareable(new ExampleEdModeTool)); 27 | } 28 | 29 | void FToolExampleEditor::StartupModule() 30 | { 31 | if (!IsRunningCommandlet()) 32 | { 33 | FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); 34 | LevelEditorMenuExtensibilityManager = LevelEditorModule.GetMenuExtensibilityManager(); 35 | MenuExtender = MakeShareable(new FExtender); 36 | MenuExtender->AddMenuBarExtension("Window", EExtensionHook::After, NULL, FMenuBarExtensionDelegate::CreateRaw(this, &FToolExampleEditor::MakePulldownMenu)); 37 | LevelEditorMenuExtensibilityManager->AddExtender(MenuExtender); 38 | } 39 | 40 | // register custom layouts 41 | { 42 | static FName PropertyEditor("PropertyEditor"); 43 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(PropertyEditor); 44 | PropertyModule.RegisterCustomClassLayout(AExampleActor::StaticClass()->GetFName(), FOnGetDetailCustomizationInstance::CreateStatic(&FExampleActorDetails::MakeInstance)); 45 | } 46 | 47 | // register custom types: 48 | { 49 | IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); 50 | // add custom category 51 | EAssetTypeCategories::Type ExampleCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("Example")), FText::FromString("Example")); 52 | // register our custom asset with example category 53 | TSharedPtr Action = MakeShareable(new FExampleDataTypeActions(ExampleCategory)); 54 | AssetTools.RegisterAssetTypeActions(Action.ToSharedRef()); 55 | // saved it here for unregister later 56 | CreatedAssetTypeActions.Add(Action); 57 | } 58 | 59 | // register settings: 60 | { 61 | ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); 62 | if (SettingsModule) 63 | { 64 | TSharedPtr ProjectSettingsContainer = SettingsModule->GetContainer("Project"); 65 | ProjectSettingsContainer->DescribeCategory("ExampleCategory", FText::FromString("Example Category"), FText::FromString("Example settings description text here")); 66 | 67 | SettingsModule->RegisterSettings("Project", "ExampleCategory", "ExampleSettings", 68 | FText::FromString("Example Settings"), 69 | FText::FromString("Configure Example Settings"), 70 | GetMutableDefault() 71 | ); 72 | } 73 | } 74 | 75 | IExampleModuleInterface::StartupModule(); 76 | } 77 | 78 | void FToolExampleEditor::ShutdownModule() 79 | { 80 | // unregister custom layouts 81 | if (FModuleManager::Get().IsModuleLoaded("PropertyEditor")) 82 | { 83 | FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked("PropertyEditor"); 84 | PropertyModule.UnregisterCustomClassLayout(AExampleActor::StaticClass()->GetFName()); 85 | } 86 | 87 | // Unregister all the asset types that we registered 88 | if (FModuleManager::Get().IsModuleLoaded("AssetTools")) 89 | { 90 | IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); 91 | for (int32 i = 0; i < CreatedAssetTypeActions.Num(); ++i) 92 | { 93 | AssetTools.UnregisterAssetTypeActions(CreatedAssetTypeActions[i].ToSharedRef()); 94 | } 95 | } 96 | CreatedAssetTypeActions.Empty(); 97 | 98 | // unregister settings 99 | ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings"); 100 | if (SettingsModule) 101 | { 102 | SettingsModule->UnregisterSettings("Project", "ExampleCategory", "ExampleSettings"); 103 | } 104 | 105 | IExampleModuleInterface::ShutdownModule(); 106 | } 107 | 108 | 109 | void FToolExampleEditor::AddMenuExtension(const FMenuExtensionDelegate &extensionDelegate, FName extensionHook, const TSharedPtr &CommandList, EExtensionHook::Position position) 110 | { 111 | MenuExtender->AddMenuExtension(extensionHook, position, CommandList, extensionDelegate); 112 | } 113 | 114 | void FToolExampleEditor::MakePulldownMenu(FMenuBarBuilder &menuBuilder) 115 | { 116 | menuBuilder.AddPullDownMenu( 117 | FText::FromString("Example"), 118 | FText::FromString("Open the Example menu"), 119 | FNewMenuDelegate::CreateRaw(this, &FToolExampleEditor::FillPulldownMenu), 120 | "Example", 121 | FName(TEXT("ExampleMenu")) 122 | ); 123 | } 124 | 125 | void FToolExampleEditor::FillPulldownMenu(FMenuBuilder &menuBuilder) 126 | { 127 | menuBuilder.BeginSection("ExampleSection", FText::FromString("Section 1")); 128 | menuBuilder.AddMenuSeparator(FName("Section_1")); 129 | menuBuilder.EndSection(); 130 | 131 | menuBuilder.BeginSection("ExampleSection", FText::FromString("Section 2")); 132 | menuBuilder.AddMenuSeparator(FName("Section_2")); 133 | menuBuilder.EndSection(); 134 | } 135 | -------------------------------------------------------------------------------- /Source/ToolExampleEditor/ToolExampleEditor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "UnrealEd.h" 6 | //#include "Engine.h" 7 | #include "SlateBasics.h" 8 | #include "SlateExtras.h" 9 | //#include "Internationalization.h" 10 | //#include "MultiBoxExtender.h" 11 | #include "Editor/LevelEditor/Public/LevelEditor.h" 12 | #include "Editor/PropertyEditor/Public/PropertyEditing.h" 13 | #include "IAssetTypeActions.h" 14 | 15 | #include "IExampleModuleInterface.h" 16 | 17 | class FToolExampleEditor : public IExampleModuleInterface 18 | { 19 | public: 20 | /** IModuleInterface implementation */ 21 | virtual void StartupModule() override; 22 | virtual void ShutdownModule() override; 23 | 24 | virtual void AddModuleListeners() override; 25 | 26 | /** 27 | * Singleton-like access to this module's interface. This is just for convenience! 28 | * Beware of calling this during the shutdown phase, though. Your module might have been unloaded already. 29 | * 30 | * @return Returns singleton instance, loading the module on demand if needed 31 | */ 32 | static inline FToolExampleEditor& Get() 33 | { 34 | return FModuleManager::LoadModuleChecked< FToolExampleEditor >("ToolExampleEditor"); 35 | } 36 | 37 | /** 38 | * Checks to see if this module is loaded and ready. It is only valid to call Get() if IsAvailable() returns true. 39 | * 40 | * @return True if the module is loaded and ready to use 41 | */ 42 | static inline bool IsAvailable() 43 | { 44 | return FModuleManager::Get().IsModuleLoaded("ToolExampleEditor"); 45 | } 46 | 47 | void AddMenuExtension(const FMenuExtensionDelegate &extensionDelegate, FName extensionHook, const TSharedPtr &CommandList = NULL, EExtensionHook::Position position = EExtensionHook::Before); 48 | TSharedRef GetMenuRoot() { return MenuRoot; }; 49 | 50 | protected: 51 | TSharedPtr LevelEditorMenuExtensibilityManager; 52 | TSharedPtr MenuExtender; 53 | 54 | static TSharedRef MenuRoot; 55 | 56 | TArray> CreatedAssetTypeActions; 57 | 58 | void MakePulldownMenu(FMenuBarBuilder &menuBuilder); 59 | void FillPulldownMenu(FMenuBuilder &menuBuilder); 60 | }; -------------------------------------------------------------------------------- /ToolExample.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.23", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "ToolExample", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default" 11 | }, 12 | { 13 | "Name": "ToolExampleEditor", 14 | "Type": "Editor", 15 | "LoadingPhase": "PostEngineInit", 16 | "AdditionalDependencies": [ 17 | "Engine" 18 | ] 19 | } 20 | ] 21 | } --------------------------------------------------------------------------------