// Copyright Epic Games, Inc. All Rights Reserved. #include "SBlueprintEditorToolbar.h" #include "AssetToolsModule.h" #include "BlueprintEditor.h" #include "BlueprintEditorCommands.h" #include "BlueprintEditorContext.h" #include "DiffUtils.h" #include "Containers/Array.h" #include "Containers/EnumAsByte.h" #include "Containers/UnrealString.h" #include "Delegates/Delegate.h" #include "Engine/Blueprint.h" #include "Engine/LevelScriptBlueprint.h" #include "FindInBlueprintManager.h" #include "Framework/Commands/InputChord.h" #include "Framework/Commands/UIAction.h" #include "Framework/Commands/UICommandInfo.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Framework/SlateDelegates.h" #include "GraphEditorActions.h" #include "HAL/PlatformMath.h" #include "HAL/PlatformMisc.h" #include "IAssetTools.h" #include "IAssetTypeActions.h" #include "ISourceCodeAccessModule.h" #include "ISourceCodeAccessor.h" #include "ISourceControlModule.h" #include "ISourceControlProvider.h" #include "ISourceControlRevision.h" #include "ISourceControlState.h" #include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/DebuggerCommands.h" #include "Layout/Margin.h" #include "Math/Vector2D.h" #include "Misc/AssertionMacros.h" #include "Misc/Attribute.h" #include "Misc/MessageDialog.h" #include "Misc/Paths.h" #include "Modules/ModuleManager.h" #include "SBlueprintEditorSelectedDebugObjectWidget.h" #include "SBlueprintRevisionMenu.h" #include "SourceControlHelpers.h" #include "Styling/ISlateStyle.h" #include "Templates/Casts.h" #include "ToolMenu.h" #include "ToolMenuDelegates.h" #include "ToolMenuEntry.h" #include "ToolMenuMisc.h" #include "ToolMenuSection.h" #include "ToolMenus.h" #include "UObject/Object.h" #include "UObject/ObjectMacros.h" #include "UObject/Package.h" #include "UObject/UObjectGlobals.h" #include "UObject/UObjectHash.h" #include "UObject/WeakObjectPtr.h" #include "UObject/WeakObjectPtrTemplates.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/Layout/SBorder.h" class FUICommandList; class SWidget; #define LOCTEXT_NAMESPACE "KismetToolbar" ////////////////////////////////////////////////////////////////////////// // SBlueprintModeSeparator class SBlueprintModeSeparator : public SBorder { public: SLATE_BEGIN_ARGS(SBlueprintModeSeparator) {} SLATE_END_ARGS() void Construct(const FArguments& InArg) { SBorder::Construct( SBorder::FArguments() .BorderImage(FAppStyle::GetBrush("BlueprintEditor.PipelineSeparator")) .Padding(0.0f) ); } // SWidget interface virtual FVector2D ComputeDesiredSize(float) const override { const float Height = 20.0f; const float Thickness = 16.0f; return FVector2D(Thickness, Height); } // End of SWidget interface }; ////////////////////////////////////////////////////////////////////////// // FKismet2Menu void FKismet2Menu::FillFileMenuBlueprintSection(UToolMenu* InMenu) { FToolMenuInsert InsertPosition("FileLoadAndSave", EToolMenuInsertType::After); { FToolMenuSection& Section = InMenu->AddSection("FileBlueprint", LOCTEXT("BlueprintHeading", "Blueprint")); Section.InsertPosition = InsertPosition; Section.AddMenuEntry( FBlueprintEditorCommands::Get().CompileBlueprint ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().RefreshAllNodes ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().ReparentBlueprint ); Section.AddMenuEntry(FBlueprintEditorCommands::Get().BeginBlueprintMerge); } InMenu->AddDynamicSection("FileDeveloper", FNewToolMenuDelegate::CreateLambda([InsertPosition](UToolMenu* InMenu) { // Only show the developer menu on machines with the solution (assuming they can build it) ISourceCodeAccessModule* SourceCodeAccessModule = FModuleManager::GetModulePtr("SourceCodeAccess"); if (SourceCodeAccessModule != nullptr && SourceCodeAccessModule->GetAccessor().CanAccessSourceCode()) { FToolMenuSection& Section = InMenu->AddSection("FileDeveloper"); Section.InsertPosition = InsertPosition; Section.AddSubMenu( "DeveloperMenu", LOCTEXT("DeveloperMenu", "Developer"), LOCTEXT("DeveloperMenu_ToolTip", "Open the developer menu"), FNewToolMenuDelegate::CreateStatic( &FKismet2Menu::FillDeveloperMenu ), false); } })); } void FKismet2Menu::FillDeveloperMenu(UToolMenu* InMenu) { { FToolMenuSection& Section = InMenu->AddSection("FileDeveloperCompilerSettings", LOCTEXT("CompileOptionsHeading", "Compiler Settings")); Section.AddMenuEntry( FBlueprintEditorCommands::Get().SaveIntermediateBuildProducts ); } if (FFindInBlueprintSearchManager::Get().ShouldEnableDeveloperMenuTools()) { FToolMenuSection& Section = InMenu->AddSection("FileDeveloperSearchTools", LOCTEXT("SearchToolsHeading", "Search Tools")); Section.AddMenuEntry(FBlueprintEditorCommands::Get().GenerateSearchIndex); Section.AddMenuEntry(FBlueprintEditorCommands::Get().DumpCachedIndexData); } if (false) { { FToolMenuSection& Section = InMenu->AddSection("FileDeveloperFindReferences"); Section.AddMenuEntry(FBlueprintEditorCommands::Get().FindReferencesFromClass); Section.AddMenuEntry(FBlueprintEditorCommands::Get().FindReferencesFromBlueprint); Section.AddMenuEntry(FBlueprintEditorCommands::Get().FindReferencesFromBlueprint); } } { FToolMenuSection& Section = InMenu->AddSection("SchemaDeveloperSettings", LOCTEXT("SchemaDevUtilsHeading", "Schema Utilities")); Section.AddMenuEntry(FBlueprintEditorCommands::Get().ShowActionMenuItemSignatures); } } void FKismet2Menu::FillEditMenu(UToolMenu* InMenu) { { FToolMenuSection& Section = InMenu->AddSection("EditSearch", LOCTEXT("EditMenu_SearchHeading", "Search")); Section.InsertPosition = FToolMenuInsert("EditHistory", EToolMenuInsertType::After); Section.AddMenuEntry( FBlueprintEditorCommands::Get().FindInBlueprint ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().FindInBlueprints ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().DeleteUnusedVariables ); } } void FKismet2Menu::FillViewMenu(UToolMenu* InMenu) { { FToolMenuSection& Section = InMenu->AddSection("ViewPinVisibility", LOCTEXT("ViewMenu_PinVisibilityHeading", "Pin Visibility")); Section.AddMenuEntry(FGraphEditorCommands::Get().ShowAllPins); Section.AddMenuEntry(FGraphEditorCommands::Get().HideNoConnectionNoDefaultPins); Section.AddMenuEntry(FGraphEditorCommands::Get().HideNoConnectionPins); } { FToolMenuSection& Section = InMenu->AddSection("ViewZoom", LOCTEXT("ViewMenu_ZoomHeading", "Zoom") ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().ZoomToWindow ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().ZoomToSelection ); } } void FKismet2Menu::FillDebugMenu(UToolMenu* InMenu) { { FToolMenuSection& Section = InMenu->AddSection("BlueprintDebugger", LOCTEXT("BlueprintDebugger_Heading", "Blueprint Debugger")); Section.AddMenuEntry( FBlueprintEditorCommands::Get().OpenBlueprintDebugger, /* InLabelOverride = */ LOCTEXT("BpDebuggerTitle", "Blueprint Debugger"), /* InTooltipOverride = */ LOCTEXT("BpDebuggerTooltip","Open the Blueprint Debugger."), FSlateIcon(FAppStyle::GetAppStyleSetName(), "BlueprintDebugger.TabIcon") ); } { FToolMenuSection& Section = InMenu->AddSection("DebugBreakpoints", LOCTEXT("DebugMenu_BreakpointHeading", "Breakpoints")); Section.AddMenuEntry( FBlueprintEditorCommands::Get().DisableAllBreakpoints ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().EnableAllBreakpoints ); Section.AddMenuEntry( FBlueprintEditorCommands::Get().ClearAllBreakpoints ); } { FToolMenuSection& Section = InMenu->AddSection("DebugWatches", LOCTEXT("DebugMenu_WatchHeading", "Watches")); Section.AddMenuEntry( FBlueprintEditorCommands::Get().ClearAllWatches ); } } void FKismet2Menu::SetupBlueprintEditorMenu(const FName MainMenuName) { const FName ParentMenuName("MainFrame.MainMenu"); { const FName FileMenuName = *(MainMenuName.ToString() + TEXT(".File")); if (!UToolMenus::Get()->IsMenuRegistered(FileMenuName)) { FKismet2Menu::FillFileMenuBlueprintSection(UToolMenus::Get()->RegisterMenu(FileMenuName, *(ParentMenuName.ToString() + TEXT(".File")))); } } { const FName EditMenuName = *(MainMenuName.ToString() + TEXT(".Edit")); if (!UToolMenus::Get()->IsMenuRegistered(EditMenuName)) { FKismet2Menu::FillEditMenu(UToolMenus::Get()->RegisterMenu(EditMenuName, *(ParentMenuName.ToString() + TEXT(".Edit")))); } } // Add additional blueprint editor menus { FToolMenuSection& Section = UToolMenus::Get()->ExtendMenu(MainMenuName)->FindOrAddSection(NAME_None); // View if (!Section.FindEntry("View")) { Section.AddSubMenu( "View", LOCTEXT("ViewMenu", "View"), LOCTEXT("ViewMenu_ToolTip", "Open the View menu"), FNewToolMenuDelegate::CreateStatic(&FKismet2Menu::FillViewMenu) ).InsertPosition = FToolMenuInsert("Edit", EToolMenuInsertType::After); } // Debug if (!Section.FindEntry("Debug")) { Section.AddSubMenu( "Debug", LOCTEXT("DebugMenu", "Debug"), LOCTEXT("DebugMenu_ToolTip", "Open the debug menu"), FNewToolMenuDelegate::CreateStatic(&FKismet2Menu::FillDebugMenu) ).InsertPosition = FToolMenuInsert("Edit", EToolMenuInsertType::After); } } } ////////////////////////////////////////////////////////////////////////// // FFullBlueprintEditorCommands void FFullBlueprintEditorCommands::RegisterCommands() { UI_COMMAND(Compile, "Compile", "Compile the blueprint", EUserInterfaceActionType::Button, FInputChord()); UI_COMMAND(SaveOnCompile_Never, "Never", "Sets the save-on-compile option to 'Never', meaning that your Blueprints will not be saved when they are compiled", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(SaveOnCompile_SuccessOnly, "On Success Only", "Sets the save-on-compile option to 'Success Only', meaning that your Blueprints will be saved whenever they are successfully compiled", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(SaveOnCompile_Always, "Always", "Sets the save-on-compile option to 'Always', meaning that your Blueprints will be saved whenever they are compiled (even if there were errors)", EUserInterfaceActionType::RadioButton, FInputChord()); UI_COMMAND(SwitchToScriptingMode, "Graph", "Switches to Graph Editing Mode", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(SwitchToBlueprintDefaultsMode, "Defaults", "Switches to Class Defaults Mode", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(SwitchToComponentsMode, "Components", "Switches to Components Mode", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(EditGlobalOptions, "Class Settings", "Edit Class Settings", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(EditClassDefaults, "Class Defaults", "Edit the initial values of your class.", EUserInterfaceActionType::ToggleButton, FInputChord()); UI_COMMAND(JumpToErrorNode, "Jump to Error Node", "When enabled, then the Blueprint will snap focus to nodes producing an error during compilation", EUserInterfaceActionType::ToggleButton, FInputChord()); } ////////////////////////////////////////////////////////////////////////// // Static FBlueprintEditorToolbar Helpers namespace BlueprintEditorToolbarImpl { static void GenerateCompileOptionsMenu(UToolMenu* InMenu); static void MakeSaveOnCompileSubMenu(UToolMenu* InMenu); static void MakeCompileDeveloperSubMenu(UToolMenu* InMenu); }; static void BlueprintEditorToolbarImpl::GenerateCompileOptionsMenu(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Section"); const FFullBlueprintEditorCommands& Commands = FFullBlueprintEditorCommands::Get(); // @TODO: disable the menu and change up the tooltip when all sub items are disabled Section.AddSubMenu( "SaveOnCompile", LOCTEXT("SaveOnCompileSubMenu", "Save on Compile"), LOCTEXT("SaveOnCompileSubMenu_ToolTip", "Determines how the Blueprint is saved whenever you compile it."), FNewToolMenuDelegate::CreateStatic(&BlueprintEditorToolbarImpl::MakeSaveOnCompileSubMenu)); Section.AddMenuEntry(Commands.JumpToErrorNode); // Section.AddSubMenu( // "DevCompile", // LOCTEXT("DevCompileSubMenu", "Developer"), // LOCTEXT("DevCompileSubMenu_ToolTip", "Advanced settings that aid in devlopment/debugging of the Blueprint system as a whole."), // FNewMenuDelegate::CreateStatic(&BlueprintEditorToolbarImpl::MakeCompileDeveloperSubMenu)); } static void BlueprintEditorToolbarImpl::MakeSaveOnCompileSubMenu(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Section"); const FFullBlueprintEditorCommands& Commands = FFullBlueprintEditorCommands::Get(); Section.AddMenuEntry(Commands.SaveOnCompile_Never); Section.AddMenuEntry(Commands.SaveOnCompile_SuccessOnly); Section.AddMenuEntry(Commands.SaveOnCompile_Always); } static void BlueprintEditorToolbarImpl::MakeCompileDeveloperSubMenu(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Section"); const FBlueprintEditorCommands& EditorCommands = FBlueprintEditorCommands::Get(); Section.AddMenuEntry(EditorCommands.SaveIntermediateBuildProducts); Section.AddMenuEntry(EditorCommands.ShowActionMenuItemSignatures); } ////////////////////////////////////////////////////////////////////////// // FBlueprintEditorToolbar void FBlueprintEditorToolbar::AddBlueprintGlobalOptionsToolbar(UToolMenu* InMenu, bool bRegisterViewport) { FToolMenuSection& Section = InMenu->FindOrAddSection("Settings"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); Section.AddDynamicEntry("BlueprintGlobalOptions", FNewToolMenuSectionDelegate::CreateLambda([bRegisterViewport](FToolMenuSection& InSection) { const UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->GetBlueprintObj()) { const FFullBlueprintEditorCommands& Commands = FFullBlueprintEditorCommands::Get(); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.EditGlobalOptions)); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.EditClassDefaults)); if (bRegisterViewport) { InSection.AddEntry(FToolMenuEntry::InitToolBarButton(FBlueprintEditorCommands::Get().EnableSimulation)); } } })); } void FBlueprintEditorToolbar::AddCompileToolbar(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Compile"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::Before); Section.AddDynamicEntry("CompileCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { const UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->BlueprintEditor.IsValid() && Context->GetBlueprintObj()) { TSharedPtr BlueprintEditorToolbar = Context->BlueprintEditor.Pin()->GetToolbarBuilder(); if (BlueprintEditorToolbar.IsValid()) { const FFullBlueprintEditorCommands& Commands = FFullBlueprintEditorCommands::Get(); FToolMenuEntry& CompileButton = InSection.AddEntry(FToolMenuEntry::InitToolBarButton( Commands.Compile, TAttribute(), TAttribute(BlueprintEditorToolbar.ToSharedRef(), &FBlueprintEditorToolbar::GetStatusTooltip), TAttribute(BlueprintEditorToolbar.ToSharedRef(), &FBlueprintEditorToolbar::GetStatusImage), "CompileBlueprint")); CompileButton.StyleNameOverride = "CalloutToolbar"; FToolMenuEntry& CompileOptions = InSection.AddEntry(FToolMenuEntry::InitComboButton( "CompileComboButton", FUIAction(), FNewToolMenuDelegate::CreateStatic(&BlueprintEditorToolbarImpl::GenerateCompileOptionsMenu), LOCTEXT("BlupeintCompileOptions_ToolbarTooltip", "Options to customize how Blueprints compile") )); CompileOptions.StyleNameOverride = "CalloutToolbar"; CompileOptions.ToolBarData.bSimpleComboBox = true; } } })); // We want the diff menu to be on any blueprint toolbar that also contains compile FToolMenuSection& DiffSection = InMenu->AddSection("SourceControl"); DiffSection.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); DiffSection.AddDynamicEntry("SourceControlCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { const UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->BlueprintEditor.IsValid() && Context->GetBlueprintObj()) { TSharedPtr BlueprintEditorToolbar = Context->BlueprintEditor.Pin()->GetToolbarBuilder(); if (BlueprintEditorToolbar.IsValid()) { FToolMenuEntry& DiffEntry = InSection.AddEntry(FToolMenuEntry::InitComboButton( "Diff", FUIAction(), FOnGetContent::CreateStatic(&FBlueprintEditorToolbar::MakeDiffMenu, Context), LOCTEXT("Diff", "Diff"), LOCTEXT("BlueprintEditorDiffToolTip", "Diff against previous revisions"), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "BlueprintDiff.ToolbarIcon") )); DiffEntry.StyleNameOverride = "CalloutToolbar"; } } })); } void FBlueprintEditorToolbar::AddNewToolbar(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Add"); Section.InsertPosition = FToolMenuInsert("MyBlueprint", EToolMenuInsertType::After); Section.AddDynamicEntry("AddCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->BlueprintEditor.IsValid() && Context->GetBlueprintObj()) { const FBlueprintEditorCommands& Commands = FBlueprintEditorCommands::Get(); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.AddNewVariable, TAttribute(), TAttribute(), TAttribute(), FName(TEXT("BPEAddNewVariable")))); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.AddNewFunction, TAttribute(), TAttribute(), TAttribute(), FName(TEXT("BPEAddNewFunction")))); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.AddNewMacroDeclaration, TAttribute(), TAttribute(), TAttribute(), FName(TEXT("BPEAddNewMacro")))); // Add New Animation Graph isn't supported right now. InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.AddNewEventGraph, TAttribute(), TAttribute(), TAttribute(), FName(TEXT("BPEAddNewEventGraph")))); InSection.AddEntry(FToolMenuEntry::InitToolBarButton(Commands.AddNewDelegate, TAttribute(), TAttribute(), TAttribute(), FName(TEXT("BPEAddNewDelegate")))); } })); } void FBlueprintEditorToolbar::AddScriptingToolbar(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Script"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); Section.AddDynamicEntry("ScriptCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->BlueprintEditor.IsValid() && Context->GetBlueprintObj()) { InSection.AddEntry(FToolMenuEntry::InitToolBarButton(FBlueprintEditorCommands::Get().FindInBlueprint)); InSection.AddEntry(FToolMenuEntry::InitToolBarButton( FBlueprintEditorCommands::Get().ToggleHideUnrelatedNodes, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "GraphEditor.ToggleHideUnrelatedNodes") )); InSection.AddEntry(FToolMenuEntry::InitComboButton( "HideUnrelatedNodesOptions", FUIAction(), FOnGetContent::CreateSP(Context->BlueprintEditor.Pin().Get(), &FBlueprintEditor::MakeHideUnrelatedNodesOptionsMenu), LOCTEXT("HideUnrelatedNodesOptions", "Hide Unrelated Nodes Options"), LOCTEXT("HideUnrelatedNodesOptionsMenu", "Hide Unrelated Nodes options menu"), TAttribute(), true )); } })); } void FBlueprintEditorToolbar::AddDebuggingToolbar(UToolMenu* InMenu) { FToolMenuSection& Section = InMenu->AddSection("Debugging"); Section.InsertPosition = FToolMenuInsert("Asset", EToolMenuInsertType::After); Section.AddDynamicEntry("DebuggingCommands", FNewToolMenuSectionDelegate::CreateLambda([](FToolMenuSection& InSection) { UBlueprintEditorToolMenuContext* Context = InSection.FindContext(); if (Context && Context->BlueprintEditor.IsValid() && Context->GetBlueprintObj()) { FPlayWorldCommands::BuildToolbar(InSection); if (Context->GetBlueprintObj()->BlueprintType != BPTYPE_MacroLibrary) { // Selected debug actor button InSection.AddEntry(FToolMenuEntry::InitWidget("SelectedDebugObjectWidget", SNew(SBlueprintEditorSelectedDebugObjectWidget, Context->BlueprintEditor.Pin()), FText::GetEmpty())); } } })); } FSlateIcon FBlueprintEditorToolbar::GetStatusImage() const { UBlueprint* BlueprintObj = BlueprintEditor.Pin()->GetBlueprintObj(); EBlueprintStatus Status = BlueprintObj->Status; // For macro types, always show as up-to-date, since we don't compile them if (BlueprintObj->BlueprintType == BPTYPE_MacroLibrary) { Status = BS_UpToDate; } static const FName CompileStatusBackground("Blueprint.CompileStatus.Background"); static const FName CompileStatusUnknown("Blueprint.CompileStatus.Overlay.Unknown"); static const FName CompileStatusError("Blueprint.CompileStatus.Overlay.Error"); static const FName CompileStatusGood("Blueprint.CompileStatus.Overlay.Good"); static const FName CompileStatusWarning("Blueprint.CompileStatus.Overlay.Warning"); switch (Status) { default: case BS_Unknown: case BS_Dirty: return FSlateIcon(FAppStyle::GetAppStyleSetName(), CompileStatusBackground, NAME_None, CompileStatusUnknown); case BS_Error: return FSlateIcon(FAppStyle::GetAppStyleSetName(), CompileStatusBackground, NAME_None, CompileStatusError); case BS_UpToDate: return FSlateIcon(FAppStyle::GetAppStyleSetName(), CompileStatusBackground, NAME_None, CompileStatusGood); case BS_UpToDateWithWarnings: return FSlateIcon(FAppStyle::GetAppStyleSetName(), CompileStatusBackground, NAME_None, CompileStatusWarning); } } FText FBlueprintEditorToolbar::GetStatusTooltip() const { UBlueprint* BlueprintObj = BlueprintEditor.Pin()->GetBlueprintObj(); EBlueprintStatus Status = BlueprintObj->Status; // For macro types, always show as up-to-date, since we don't compile them if (BlueprintObj->BlueprintType == BPTYPE_MacroLibrary) { Status = BS_UpToDate; } switch (Status) { default: case BS_Unknown: return LOCTEXT("Recompile_Status", "Unknown status; should recompile"); case BS_Dirty: return LOCTEXT("Dirty_Status", "Dirty; needs to be recompiled"); case BS_Error: return LOCTEXT("CompileError_Status", "There was an error during compilation, see the log for details"); case BS_UpToDate: return LOCTEXT("GoodToGo_Status", "Good to go"); case BS_UpToDateWithWarnings: return LOCTEXT("GoodToGoWarning_Status", "There was a warning during compilation, see the log for details"); } } /** Delegate called to diff a specific revision with the current */ static void OnDiffRevisionPicked(FRevisionInfo const& RevisionInfo, TWeakObjectPtr BlueprintObj) { if (BlueprintObj.IsValid()) { bool const bIsLevelScriptBlueprint = FBlueprintEditorUtils::IsLevelScriptBlueprint(BlueprintObj.Get()); FString const Filename = SourceControlHelpers::PackageFilename(bIsLevelScriptBlueprint ? BlueprintObj.Get()->GetOuter()->GetPathName() : BlueprintObj.Get()->GetPathName()); ISourceControlProvider& SourceControlProvider = ISourceControlModule::Get().GetProvider(); // Get the SCC state FSourceControlStatePtr SourceControlState = SourceControlProvider.GetState(Filename, EStateCacheUsage::Use); if (SourceControlState.IsValid()) { for (int32 HistoryIndex = 0; HistoryIndex < SourceControlState->GetHistorySize(); HistoryIndex++) { TSharedPtr Revision = SourceControlState->GetHistoryItem(HistoryIndex); check(Revision.IsValid()); if (Revision->GetRevision() == RevisionInfo.Revision) { // Get the revision of this package from source control if (UPackage* PreviousTempPkg = DiffUtils::LoadPackageForDiff(Revision)) { UObject* PreviousAsset = nullptr; // If its a levelscript blueprint, find the previous levelscript blueprint in the map if (bIsLevelScriptBlueprint) { TArray ObjectsInOuter; GetObjectsWithOuter(PreviousTempPkg, ObjectsInOuter); // Look for the level script blueprint for this package for (int32 Index = 0; Index < ObjectsInOuter.Num(); Index++) { UObject* Obj = ObjectsInOuter[Index]; if (ULevelScriptBlueprint* ObjAsBlueprint = Cast(Obj)) { PreviousAsset = ObjAsBlueprint; break; } } if (!PreviousAsset) { UE_LOG(LogSourceControl, Warning, TEXT("Revision %s of %s doesn't have a LevelScriptBlueprint"), *Revision->GetRevision(), *Revision->GetFilename()); } } // otherwise its a normal Blueprint else { FString PreviousAssetName = FPaths::GetBaseFilename(Filename, true); PreviousAsset = FindObject(PreviousTempPkg, *PreviousAssetName); } if (PreviousAsset != nullptr) { FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked(TEXT("AssetTools")); FRevisionInfo OldRevision = { Revision->GetRevision(), Revision->GetCheckInIdentifier(), Revision->GetDate() }; FRevisionInfo CurrentRevision = { TEXT(""), Revision->GetCheckInIdentifier(), Revision->GetDate() }; AssetToolsModule.Get().DiffAssets(PreviousAsset, BlueprintObj.Get(), OldRevision, CurrentRevision); } } else { FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("SourceControl.HistoryWindow", "UnableToLoadAssets", "Unable to load assets to diff. Content may no longer be supported?")); } break; } } } } } TSharedRef FBlueprintEditorToolbar::MakeDiffMenu(const UBlueprintEditorToolMenuContext* InContext) { if (ISourceControlModule::Get().IsEnabled() && ISourceControlModule::Get().GetProvider().IsAvailable()) { UBlueprint* BlueprintObj = InContext ? InContext->GetBlueprintObj() : nullptr; if (BlueprintObj) { TWeakObjectPtr BlueprintPtr = BlueprintObj; // Add our async SCC task widget return SNew(SBlueprintRevisionMenu, BlueprintObj) .OnRevisionSelected_Static(&OnDiffRevisionPicked, BlueprintPtr); } else { // if BlueprintObj is null then this means that multiple blueprints are selected FMenuBuilder MenuBuilder(true, NULL); MenuBuilder.AddMenuEntry(LOCTEXT("NoRevisionsForMultipleBlueprints", "Multiple blueprints selected"), FText(), FSlateIcon(), FUIAction()); return MenuBuilder.MakeWidget(); } } FMenuBuilder MenuBuilder(true, NULL); MenuBuilder.AddMenuEntry(LOCTEXT("SourceControlDisabled", "Revision control is disabled"), FText(), FSlateIcon(), FUIAction()); return MenuBuilder.MakeWidget(); } #undef LOCTEXT_NAMESPACE