EM_Task/UnrealEd/Private/Toolkits/BaseToolkit.cpp
Boshuang Zhao 5144a49c9b add
2026-02-13 16:18:33 +08:00

399 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "Toolkits/BaseToolkit.h"
#include "Toolkits/ToolkitManager.h"
#include "Widgets/Docking/SDockableTab.h"
#include "Widgets/Docking/SDockTabStack.h"
#include "PropertyEditorModule.h"
#include "Modules/ModuleManager.h"
#include "IDetailsView.h"
#include "InteractiveToolManager.h"
#include "Editor.h"
#include "InteractiveTool.h"
#include "Tools/UEdMode.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#include "EdMode.h"
#define LOCTEXT_NAMESPACE "BaseToolkit"
FBaseToolkit::FBaseToolkit()
: ToolkitMode(EToolkitMode::Standalone),
ToolkitCommands(new FUICommandList())
{
WorkspaceMenuCategory = FWorkspaceItem::NewGroup(LOCTEXT("WorkspaceMenu_BaseToolkit", "Toolkit"));
}
FBaseToolkit::~FBaseToolkit()
{
// Destroy any tabs that we still have hanging out. This is just for convenience, so that the derived
// classes don't need to bother cleaning up their tabs when a world-centric editor is closed. However,
// if the order that tabs are destroyed is important, it is suggested that you clean the up yourself!
{
for (auto CurSpotIt(ToolkitTabsInSpots.CreateConstIterator()); CurSpotIt; ++CurSpotIt)
{
const auto& TabsForSpot = CurSpotIt.Value();
for (auto CurTabIt(TabsForSpot.CreateConstIterator()); CurTabIt; ++CurTabIt)
{
const auto& PinnedTab = CurTabIt->Pin();
if (PinnedTab.IsValid())
{
PinnedTab->RemoveTabFromParent();
}
}
}
}
}
bool FBaseToolkit::IsWorldCentricAssetEditor() const
{
return ToolkitMode == EToolkitMode::WorldCentric;
}
bool FBaseToolkit::IsHosted() const
{
return ToolkitHost.IsValid();
}
const TSharedRef<IToolkitHost> FBaseToolkit::GetToolkitHost() const
{
return ToolkitHost.Pin().ToSharedRef();
}
const TMap<EToolkitTabSpot::Type, TArray<TWeakPtr<SDockableTab>>>& FBaseToolkit::GetToolkitTabsInSpots() const
{
return ToolkitTabsInSpots;
}
FName FBaseToolkit::GetToolkitContextFName() const
{
return GetToolkitFName();
}
bool FBaseToolkit::ProcessCommandBindings(const FKeyEvent& InKeyEvent) const
{
if (ToolkitCommands->ProcessCommandBindings(InKeyEvent))
{
return true;
}
return false;
}
void FBaseToolkit::AddToolkitTab(const TSharedRef<SDockableTab>& TabToAdd, const EToolkitTabSpot::Type TabSpot)
{
// Figure out where to put this tab by asking the toolkit host which spot to put it in. It will give us
// back a dock tab stack that we can add the tab to!
TSharedRef<SDockTabStack> FoundTabStack = GetToolkitHost()->GetTabSpot(TabSpot);
// When a context menu is opening for this tab, ask the spawning app to fill the menu.
ensureMsgf(false, TEXT("TabToAdd->SetOnTabStackMenuOpening( FOnTabStackMenuOpening::CreateSP( GetToolkitHost(), &IToolkitHost::PopulateLayoutMenu ) );"));
// TabToAdd->SetOnTabStackMenuOpening( FOnTabStackMenuOpening::CreateSP( GetToolkitHost(), &IToolkitHost::PopulateLayoutMenu ) );
// Add the tab
FoundTabStack->AddTab(TabToAdd);
// Keep track of tabs
ToolkitTabsInSpots.FindOrAdd(TabSpot).Add(TabToAdd);
}
FString FBaseToolkit::GetTabPrefix() const
{
if (IsWorldCentricAssetEditor())
{
return GetWorldCentricTabPrefix();
}
else
{
return TEXT("");
}
}
FLinearColor FBaseToolkit::GetTabColorScale() const
{
return IsWorldCentricAssetEditor() ? GetWorldCentricTabColorScale() : FLinearColor(0, 0, 0, 0);
}
void FBaseToolkit::BringToolkitToFront()
{
if (ensure(ToolkitHost.IsValid()))
{
// Bring the host window to front
ToolkitHost.Pin()->BringToFront();
// First, figure out what the foreground tab is in each tab stack we have tabs docked inside of
TSet<SDockTabStack*> TabStacksWithOurTabsForegrounded;
{
for (auto CurSpotIt(ToolkitTabsInSpots.CreateConstIterator()); CurSpotIt; ++CurSpotIt)
{
const auto& TabsForSpot = CurSpotIt.Value();
for (auto CurTabIt(TabsForSpot.CreateConstIterator()); CurTabIt; ++CurTabIt)
{
const auto& PinnedTab = CurTabIt->Pin();
if (PinnedTab.IsValid())
{
if (PinnedTab->IsForeground())
{
const auto& TabStack = PinnedTab->GetParentDockTabStack();
if (TabStack.IsValid())
{
TabStacksWithOurTabsForegrounded.Add(TabStack.Get());
}
}
}
}
}
}
// @todo toolkit major: Also draw user's attention when clicked?
// @todo toolkit major: If any of the tabs are in their own floating windows, these should be brought to front
// Now, make sure that our tabs are foregrounded in their respective stacks!
// NOTE: We don't want to push tabs to the front that are in a stack where one of our other tabs is already front-most
for (auto CurSpotIt(ToolkitTabsInSpots.CreateConstIterator()); CurSpotIt; ++CurSpotIt)
{
const auto& TabsForSpot = CurSpotIt.Value();
for (auto CurTabIt(TabsForSpot.CreateConstIterator()); CurTabIt; ++CurTabIt)
{
const auto& PinnedTab = CurTabIt->Pin();
if (PinnedTab.IsValid())
{
const auto& TabStack = PinnedTab->GetParentDockTabStack();
if (TabStack.IsValid())
{
// Only foreground if we don't already have a tab foregrounded in this tab's stack
if (!TabStacksWithOurTabsForegrounded.Contains(TabStack.Get()))
{
PinnedTab->BringToFrontInParent();
// Take note that we've foregrounded a tab in this stack, no need to do that again
TabStacksWithOurTabsForegrounded.Add(TabStack.Get());
}
}
else
{
// Just do what we can to foreground ourselves
PinnedTab->BringToFrontInParent();
}
}
}
}
// Tell the toolkit its been brought to the fore - give it a chance to update anything it needs to
ToolkitBroughtToFront();
}
}
TSharedPtr<class SWidget> FBaseToolkit::GetInlineContent() const
{
return TSharedPtr<class SWidget>();
}
bool FBaseToolkit::IsBlueprintEditor() const
{
return false;
}
#undef LOCTEXT_NAMESPACE
void FModeToolkit::Init(const TSharedPtr<class IToolkitHost>& InitToolkitHost)
{
check(InitToolkitHost.IsValid());
ToolkitMode = EToolkitMode::WorldCentric;
ToolkitHost = InitToolkitHost;
if (UEdMode* ScriptableEditorMode = GetScriptableEditorMode())
{
ScriptableEditorMode->GetToolManager()->OnToolStarted.AddSP(this, &FModeToolkit::OnToolStarted);
ScriptableEditorMode->GetToolManager()->OnToolEnded.AddSP(this, &FModeToolkit::OnToolEnded);
}
FPropertyEditorModule& PropertyEditorModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
FDetailsViewArgs ModeDetailsViewArgs(
/*bUpdateFromSelection=*/false,
/*bLockable=*/false,
/*bAllowSearch=*/false,
FDetailsViewArgs::HideNameArea,
/*bHideSelectionTip=*/true,
/*InNotifyHook=*/nullptr,
/*InSearchInitialKeyFocus=*/false,
/*InViewIdentifier=*/NAME_None);
ModeDetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic;
ModeDetailsViewArgs.bShowOptions = false;
ModeDetailsViewArgs.bAllowMultipleTopLevelObjects = true;
ModeDetailsView = PropertyEditorModule.CreateDetailView(ModeDetailsViewArgs);
FDetailsViewArgs DetailsViewArgs(
/*bUpdateFromSelection=*/false,
/*bLockable=*/false,
/*bAllowSearch=*/false,
FDetailsViewArgs::HideNameArea,
/*bHideSelectionTip=*/true,
/*InNotifyHook=*/nullptr,
/*InSearchInitialKeyFocus=*/false,
/*InViewIdentifier=*/NAME_None);
DetailsViewArgs.DefaultsOnlyVisibility = EEditDefaultsOnlyNodeVisibility::Automatic;
DetailsViewArgs.bShowOptions = false;
DetailsViewArgs.bAllowMultipleTopLevelObjects = true;
DetailsView = PropertyEditorModule.CreateDetailView(DetailsViewArgs);
FToolkitManager::Get().RegisterNewToolkit(SharedThis(this));
}
FModeToolkit::~FModeToolkit()
{
if (UEdMode* ScriptableEditorMode = GetScriptableEditorMode())
{
if (ScriptableEditorMode->GetToolManager())
{
ScriptableEditorMode->GetToolManager()->OnToolStarted.RemoveAll(this);
ScriptableEditorMode->GetToolManager()->OnToolEnded.RemoveAll(this);
}
}
}
FName FModeToolkit::GetToolkitFName() const
{
return FName("EditorModeToolkit");
}
FText FModeToolkit::GetBaseToolkitName() const
{
return NSLOCTEXT("EditorModeToolkit", "DisplayName", "EditorMode Tool");
}
FString FModeToolkit::GetWorldCentricTabPrefix() const
{
return FString();
}
bool FModeToolkit::IsAssetEditor() const
{
return false;
}
const TArray<UObject*>* FModeToolkit::GetObjectsCurrentlyBeingEdited() const
{
return NULL;
}
FLinearColor FModeToolkit::GetWorldCentricTabColorScale() const
{
return FLinearColor();
}
bool FModeToolkit::CanStartTool(const FString& ToolTypeIdentifier)
{
UInteractiveToolManager* Manager = GetScriptableEditorMode()->GetToolManager();
return (Manager->HasActiveTool(EToolSide::Left) == false) &&
(Manager->CanActivateTool(EToolSide::Left, ToolTypeIdentifier) == true);
}
bool FModeToolkit::CanAcceptActiveTool()
{
return GetScriptableEditorMode()->GetToolManager()->CanAcceptActiveTool(EToolSide::Left);
}
bool FModeToolkit::CanCancelActiveTool()
{
return GetScriptableEditorMode()->GetToolManager()->CanCancelActiveTool(EToolSide::Left);
}
bool FModeToolkit::CanCompleteActiveTool()
{
return GetScriptableEditorMode()->GetToolManager()->HasActiveTool(EToolSide::Left) && CanCancelActiveTool() == false;
}
void FModeToolkit::OnToolStarted(UInteractiveToolManager* Manager, UInteractiveTool* Tool)
{
// Update properties panel
UInteractiveTool* CurTool = GetScriptableEditorMode()->GetToolManager()->GetActiveTool(EToolSide::Left);
if (CurTool)
{
DetailsView->SetObjects(CurTool->GetToolProperties());
}
}
void FModeToolkit::OnToolEnded(UInteractiveToolManager* Manager, UInteractiveTool* Tool)
{
DetailsView->SetObject(nullptr);
}
class FEdMode* FModeToolkit::GetEditorMode() const
{
return nullptr;
}
FText FModeToolkit::GetEditorModeDisplayName() const
{
if (FEdMode* EdMode = GetEditorMode())
{
return EdMode->GetModeInfo().Name;
}
else if (UEdMode* ScriptableMode = GetScriptableEditorMode())
{
return ScriptableMode->GetModeInfo().Name;
}
return FText::GetEmpty();
}
FSlateIcon FModeToolkit::GetEditorModeIcon() const
{
if (FEdMode* EdMode = GetEditorMode())
{
return EdMode->GetModeInfo().IconBrush;
}
else if (UEdMode* ScriptableMode = GetScriptableEditorMode())
{
return ScriptableMode->GetModeInfo().IconBrush;
}
return FSlateIcon();
}
UEdMode* FModeToolkit::GetScriptableEditorMode() const
{
return nullptr;
}
TSharedPtr<SWidget> FModeToolkit::GetInlineContent() const
{
return SNew(SVerticalBox) + SVerticalBox::Slot().AutoHeight()[ModeDetailsView.ToSharedRef()] + SVerticalBox::Slot()[DetailsView.ToSharedRef()];
}
void FModeToolkit::BuildToolPalette(FName PaletteName, class FToolBarBuilder& ToolbarBuilder)
{
if (UEdMode* EditorMode = GetScriptableEditorMode())
{
TMap<FName, TArray<TSharedPtr<FUICommandInfo>>> CommandLists = EditorMode->GetModeCommands();
TArray<TSharedPtr<FUICommandInfo>>* CurrentCommandListPtr = CommandLists.Find(PaletteName);
if (CurrentCommandListPtr)
{
TArray<TSharedPtr<FUICommandInfo>> CurrentCommandList = *CurrentCommandListPtr;
for (TSharedPtr<FUICommandInfo> Command: CurrentCommandList)
{
ToolbarBuilder.AddToolBarButton(Command);
}
}
}
}
void FModeToolkit::OnToolPaletteChanged(FName PaletteName)
{
if (UEdMode* EditorMode = GetScriptableEditorMode())
{
EditorMode->SetCurrentPaletteName(PaletteName);
}
}
void FModeToolkit::SetModeSettingsObject(UObject* InSettingsObject)
{
ModeDetailsView->SetObject(InSettingsObject);
}