// Copyright Epic Games, Inc. All Rights Reserved. #include "ClothPaintingModule.h" #include "ClothPaintToolCommands.h" #include "ClothPainterCommands.h" #include "ClothingPaintEditMode.h" #include "EditorModeManager.h" #include "Delegates/Delegate.h" #include "EditorModeRegistry.h" #include "Framework/Commands/UIAction.h" #include "Framework/Commands/UICommandList.h" #include "Framework/Docking/TabManager.h" #include "Framework/MultiBox/MultiBoxExtender.h" #include "HAL/Platform.h" #include "HAL/PlatformCrt.h" #include "ISkeletalMeshEditor.h" #include "ISkeletalMeshEditorModule.h" #include "Internationalization/Internationalization.h" #include "Misc/Attribute.h" #include "Modules/ModuleManager.h" #include "SClothPaintTab.h" #include "SkeletalMeshToolMenuContext.h" #include "Styling/AppStyle.h" #include "Styling/ISlateStyle.h" #include "Templates/Casts.h" #include "Textures/SlateIcon.h" #include "ToolMenu.h" #include "ToolMenuDelegates.h" #include "ToolMenuEntry.h" #include "ToolMenuOwner.h" #include "ToolMenuSection.h" #include "ToolMenus.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/Docking/SDockTab.h" #include "WorkflowOrientedApp/ApplicationMode.h" #include "WorkflowOrientedApp/WorkflowTabFactory.h" class FAssetEditorToolkit; class SWidget; #define LOCTEXT_NAMESPACE "ClothPaintingModule" const FName PaintModeID = "ClothPaintMode"; IMPLEMENT_MODULE(FClothPaintingModule, ClothPainter); DECLARE_DELEGATE_OneParam(FOnToggleClothPaintMode, bool); struct FClothPaintTabSummoner : public FWorkflowTabFactory { public: /** Tab ID name */ static const FName TabName; FClothPaintTabSummoner(TSharedPtr InHostingApp) : FWorkflowTabFactory(TabName, InHostingApp) { TabLabel = LOCTEXT("ClothPaintTabLabel", "Clothing"); TabIcon = FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.SkeletalMesh"); } virtual TSharedRef CreateTabBody(const FWorkflowTabSpawnInfo& Info) const override { return SNew(SClothPaintTab).InHostingApp(HostingApp); } virtual FText GetTabToolTipText(const FWorkflowTabSpawnInfo& Info) const override { return LOCTEXT("ClothPaintTabToolTip", "Tab for Painting Cloth properties"); } static TSharedPtr CreateFactory(TSharedPtr InAssetEditor) { return MakeShareable(new FClothPaintTabSummoner(InAssetEditor)); } protected: }; const FName FClothPaintTabSummoner::TabName = TEXT("ClothPainting"); void FClothPaintingModule::StartupModule() { SetupMode(); // Register any commands for the cloth painter ClothPaintToolCommands::RegisterClothPaintToolCommands(); FClothPainterCommands::Register(); UToolMenus::RegisterStartupCallback(FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FClothPaintingModule::RegisterMenus)); if(!SkelMeshEditorExtenderHandle.IsValid()) { ISkeletalMeshEditorModule& SkelMeshEditorModule = FModuleManager::Get().LoadModuleChecked("SkeletalMeshEditor"); TArray& Extenders = SkelMeshEditorModule.GetAllSkeletalMeshEditorToolbarExtenders(); Extenders.Add(ISkeletalMeshEditorModule::FSkeletalMeshEditorToolbarExtender::CreateRaw(this, &FClothPaintingModule::ExtendSkelMeshEditorToolbar)); SkelMeshEditorExtenderHandle = Extenders.Last().GetHandle(); } } void FClothPaintingModule::SetupMode() { // Add application mode extender Extender = FWorkflowApplicationModeExtender::CreateRaw(this, &FClothPaintingModule::ExtendApplicationMode); FWorkflowCentricApplication::GetModeExtenderList().Add(Extender); FEditorModeRegistry::Get().RegisterMode(PaintModeID, LOCTEXT("ClothPaintEditMode", "Cloth Painting"), FSlateIcon(), false); } TSharedRef FClothPaintingModule::ExtendApplicationMode(const FName ModeName, TSharedRef InMode) { // For skeleton and animation editor modes add our custom tab factory to it if (ModeName == TEXT("SkeletalMeshEditorMode")) { InMode->AddTabFactory(FCreateWorkflowTabFactory::CreateStatic(&FClothPaintTabSummoner::CreateFactory)); RegisteredApplicationModes.Add(InMode); } return InMode; } TSharedRef FClothPaintingModule::ExtendSkelMeshEditorToolbar(const TSharedRef InCommandList, TSharedRef InSkeletalMeshEditor) { // Add toolbar extender TSharedPtr ToolbarExtender = MakeShareable(new FExtender); TWeakPtr Ptr(InSkeletalMeshEditor); InCommandList->MapAction(FClothPainterCommands::Get().TogglePaintMode, FExecuteAction::CreateRaw(this, &FClothPaintingModule::OnToggleMode, Ptr), FCanExecuteAction(), FIsActionChecked::CreateRaw(this, &FClothPaintingModule::IsPaintModeActive, Ptr) ); return ToolbarExtender.ToSharedRef(); } void FClothPaintingModule::RegisterMenus() { FToolMenuOwnerScoped OwnerScoped(this); UToolMenu* Toolbar = UToolMenus::Get()->ExtendMenu("AssetEditor.SkeletalMeshEditor.ToolBar"); { FToolMenuSection& Section = Toolbar->FindOrAddSection("SkeletalMesh"); Section.AddDynamicEntry("TogglePaintMode", FNewToolMenuSectionDelegate::CreateLambda([this](FToolMenuSection& InSection) { USkeletalMeshToolMenuContext* Context = InSection.FindContext(); if (Context && Context->SkeletalMeshEditor.IsValid()) { InSection.AddEntry(FToolMenuEntry::InitToolBarButton( FClothPainterCommands::Get().TogglePaintMode, TAttribute::Create(TAttribute::FGetter::CreateRaw(this, &FClothPaintingModule::GetPaintToolsButtonText, Context->SkeletalMeshEditor)), TAttribute::Create(TAttribute::FGetter::CreateRaw(this, &FClothPaintingModule::GetPaintToolsButtonToolTip, Context->SkeletalMeshEditor)), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "MeshPaint.Brush") )); } })); } } FText FClothPaintingModule::GetPaintToolsButtonText(TWeakPtr InSkeletalMeshEditor) const { if(IsPaintModeActive(InSkeletalMeshEditor)) { return LOCTEXT("ToggleButton_Deactivate", "Deactivate Cloth Paint"); } return LOCTEXT("ToggleButton_Activate", "Activate Cloth Paint"); } FText FClothPaintingModule::GetPaintToolsButtonToolTip(TWeakPtr InSkeletalMeshEditor) const { if(IsPaintModeActive(InSkeletalMeshEditor)) { return LOCTEXT("ToggleButton_Deactivate_ToolTip", "Deactivate the cloth paint tool, and go back to the current selection mode."); } return LOCTEXT("ToggleButton_Activate_ToolTip", "Activate the cloth paint tool, and open the Clothing window to allow selection of the clothing assets and of their paint targets."); } bool FClothPaintingModule::IsPaintModeActive(TWeakPtr InSkeletalMeshEditor) const { if (!GetActiveClothTab(InSkeletalMeshEditor, false /* don't invoke tab*/).IsValid()) { // haven't initialized UI yet return false; } TSharedPtr SkeletalMeshEditor = InSkeletalMeshEditor.Pin(); return SkeletalMeshEditor.IsValid() && SkeletalMeshEditor->GetEditorModeManager().IsModeActive(PaintModeID); } void FClothPaintingModule::OnToggleMode(TWeakPtr InSkeletalMeshEditor) const { ISkeletalMeshEditor* SkeletalMeshEditor = InSkeletalMeshEditor.Pin().Get(); FEditorModeTools& ModeManager = SkeletalMeshEditor->GetEditorModeManager(); if (!IsPaintModeActive(InSkeletalMeshEditor)) { ModeManager.ActivateMode(PaintModeID, true); FClothingPaintEditMode* PaintMode = (FClothingPaintEditMode*)SkeletalMeshEditor->GetEditorModeManager().GetActiveMode(PaintModeID); if (PaintMode) { PaintMode->SetPersonaToolKit(SkeletalMeshEditor->GetPersonaToolkit()); PaintMode->SetupClothPaintTab(GetActiveClothTab(InSkeletalMeshEditor)); } } else { ModeManager.DeactivateMode(PaintModeID); } } TSharedPtr FClothPaintingModule::GetActiveClothTab(TWeakPtr InSkeletalMeshEditor, bool bInvoke /*= true*/) const { TSharedPtr TabManager = InSkeletalMeshEditor.Pin()->GetTabManager(); if(bInvoke) { TabManager->TryInvokeTab(FTabId(FClothPaintTabSummoner::TabName)); } // If we can't summon this tab we will have spawned a placeholder which we can not cast as done below, // there is no valid active clothing tab so return nullptr if(TabManager->HasTabSpawner(FClothPaintTabSummoner::TabName)) { TSharedPtr Tab = TabManager->FindExistingLiveTab(FTabId(FClothPaintTabSummoner::TabName)); if(Tab.IsValid()) { TSharedPtr TabContent = Tab->GetContent(); TSharedPtr ClothingTab = StaticCastSharedPtr(TabContent); return ClothingTab; } } return nullptr; } void FClothPaintingModule::ShutdownModule() { ShutdownMode(); UToolMenus::UnRegisterStartupCallback(this); UToolMenus::UnregisterOwner(this); // Remove skel mesh editor extenders ISkeletalMeshEditorModule* SkelMeshEditorModule = FModuleManager::GetModulePtr("SkeletalMeshEditor"); if (SkelMeshEditorModule) { TArray& Extenders = SkelMeshEditorModule->GetAllSkeletalMeshEditorToolbarExtenders(); Extenders.RemoveAll([this](const ISkeletalMeshEditorModule::FSkeletalMeshEditorToolbarExtender& InDelegate) {return InDelegate.GetHandle() == SkelMeshEditorExtenderHandle; }); } } void FClothPaintingModule::ShutdownMode() { // Remove extender delegate FWorkflowCentricApplication::GetModeExtenderList().RemoveAll([this](FWorkflowApplicationModeExtender& StoredExtender) { return StoredExtender.GetHandle() == Extender.GetHandle(); }); // During shutdown clean up all factories from any modes which are still active/alive for(TWeakPtr WeakMode : RegisteredApplicationModes) { if(WeakMode.IsValid()) { TSharedPtr Mode = WeakMode.Pin(); Mode->RemoveTabFactory(FClothPaintTabSummoner::TabName); } } RegisteredApplicationModes.Empty(); FEditorModeRegistry::Get().UnregisterMode(PaintModeID); } #undef LOCTEXT_NAMESPACE // "AnimationModifiersModule"