Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- https://bit.ly/freePS2020
- // Copyright Epic Games, Inc. All Rights Reserved.
- #include "SWorldHierarchyImpl.h"
- #include "SLevelsTreeWidget.h"
- #include "SWorldHierarchyItem.h"
- #include "SWorldHierarchy.h"
- #include "WorldBrowserModule.h"
- #include "Modules/ModuleManager.h"
- #include "Framework/Application/SlateApplication.h"
- #include "Framework/MultiBox/MultiBoxBuilder.h"
- #include "ToolMenus.h"
- #include "Widgets/Layout/SSeparator.h"
- #include "Widgets/Images/SImage.h"
- #include "Widgets/Input/SComboButton.h"
- #include "Widgets/Input/SSearchBox.h"
- #include "Widgets/Input/SButton.h"
- #include "WorldTreeItemTypes.h"
- #include "LevelFolders.h"
- #include "ScopedTransaction.h"
- #include "Editor.h"
- #define LOCTEXT_NAMESPACE "WorldBrowser"
- DEFINE_LOG_CATEGORY_STATIC(LogWorldHierarchy, Log, All);
- SWorldHierarchyImpl::SWorldHierarchyImpl()
- : bUpdatingSelection(false)
- , bIsReentrant(false)
- , bFullRefresh(true)
- , bNeedsRefresh(true)
- , bRebuildFolders(false)
- , bSortDirty(false)
- , bFoldersOnlyMode(false)
- {
- }
- SWorldHierarchyImpl::~SWorldHierarchyImpl()
- {
- GEditor->UnregisterForUndo(this);
- WorldModel->SelectionChanged.RemoveAll(this);
- WorldModel->HierarchyChanged.RemoveAll(this);
- WorldModel->CollectionChanged.RemoveAll(this);
- WorldModel->PreLevelsUnloaded.RemoveAll(this);
- if (SearchBoxLevelFilter.IsValid())
- {
- WorldModel->RemoveFilter(SearchBoxLevelFilter.ToSharedRef());
- }
- if (FLevelFolders::IsAvailable())
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- LevelFolders.OnFolderCreate.RemoveAll(this);
- LevelFolders.OnFolderMove.RemoveAll(this);
- LevelFolders.OnFolderDelete.RemoveAll(this);
- }
- FEditorDelegates::PostSaveWorld.RemoveAll(this);
- }
- void SWorldHierarchyImpl::Construct(const FArguments& InArgs)
- {
- WorldModel = InArgs._InWorldModel;
- check(WorldModel.IsValid());
- WorldModel->SelectionChanged.AddSP(this, &SWorldHierarchyImpl::OnUpdateSelection);
- WorldModel->HierarchyChanged.AddSP(this, &SWorldHierarchyImpl::RebuildFoldersAndFullRefresh);
- WorldModel->CollectionChanged.AddSP(this, &SWorldHierarchyImpl::RebuildFoldersAndFullRefresh);
- WorldModel->PreLevelsUnloaded.AddSP(this, &SWorldHierarchyImpl::OnBroadcastLevelsUnloaded);
- bFoldersOnlyMode = InArgs._ShowFoldersOnly;
- ExcludedFolders = InArgs._InExcludedFolders;
- OnItemPicked = InArgs._OnItemPickedDelegate;
- if (!bFoldersOnlyMode)
- {
- SearchBoxLevelFilter = MakeShareable(new LevelTextFilter(
- LevelTextFilter::FItemToStringArray::CreateSP(this, &SWorldHierarchyImpl::TransformLevelToString)
- ));
- }
- SearchBoxHierarchyFilter = MakeShareable(new HierarchyFilter(
- HierarchyFilter::FItemToStringArray::CreateSP(this, &SWorldHierarchyImpl::TransformItemToString)
- ));
- // Might be overkill to have both filters call full refresh on change, but this should just request a full refresh
- // twice instead of actually performing the refresh itself.
- if (SearchBoxLevelFilter.IsValid())
- {
- SearchBoxLevelFilter->OnChanged().AddSP(this, &SWorldHierarchyImpl::FullRefresh);
- }
- SearchBoxHierarchyFilter->OnChanged().AddSP(this, &SWorldHierarchyImpl::FullRefresh);
- HeaderRowWidget =
- SNew( SHeaderRow )
- .Visibility(EVisibility::Collapsed)
- /** Level visibility column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_Visibility)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth(24.0f)
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Visibility", "Visibility"))
- ]
- /** LevelName label column */
- + SHeaderRow::Column( HierarchyColumns::ColumnID_LevelLabel )
- .FillWidth( 0.45f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(LOCTEXT("Column_LevelNameLabel", "Level"))
- ]
- /** Lighting Scenario column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_LightingScenario)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth( 18.0f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Lighting Scenario", "Lighting Scenario"))
- ]
- /** Level lock column */
- +SHeaderRow::Column(HierarchyColumns::ColumnID_Lock)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth( 24.0f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Lock", "Lock"))
- ]
- /** Level kismet column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_Kismet)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth( 24.0f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Blueprint", "Open the level blueprint for this Level"))
- ]
- /** Level SCC status column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_SCCStatus)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth( 24.0f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "SCCStatus", "Status in Source Control"))
- ]
- /** Level save column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_Save)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth( 24.0f )
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Save", "Save this Level"))
- ]
- /** Level color column */
- + SHeaderRow::Column(HierarchyColumns::ColumnID_Color)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- .FixedWidth(24.0f)
- .HeaderContent()
- [
- SNew(STextBlock)
- .ToolTipText(NSLOCTEXT("WorldBrowser", "Color", "Color used for visualization of Level"))
- ];
- FOnContextMenuOpening ContextMenuEvent;
- if (!bFoldersOnlyMode)
- {
- ContextMenuEvent = FOnContextMenuOpening::CreateSP(this, &SWorldHierarchyImpl::ConstructLevelContextMenu);
- }
- TSharedRef<SWidget> CreateNewFolderButton = SNullWidget::NullWidget;
- if (!bFoldersOnlyMode)
- {
- CreateNewFolderButton = SNew(SButton)
- .ButtonStyle(FEditorStyle::Get(), "HoverHintOnly")
- .ToolTipText(LOCTEXT("CreateFolderTooltip", "Create a new folder containing the current selection"))
- .OnClicked(this, &SWorldHierarchyImpl::OnCreateFolderClicked)
- .Visibility(WorldModel->HasFolderSupport() ? EVisibility::Visible : EVisibility::Collapsed)
- [
- SNew(SImage)
- .Image(FEditorStyle::GetBrush("WorldBrowser.NewFolderIcon"))
- ];
- }
- ChildSlot
- [
- SNew(SVerticalBox)
- // Hierarchy Toolbar
- +SVerticalBox::Slot()
- .AutoHeight()
- [
- SNew(SHorizontalBox)
- // Filter box
- + SHorizontalBox::Slot()
- .FillWidth(1.0f)
- [
- SNew(SSearchBox)
- .ToolTipText(LOCTEXT("FilterSearchToolTip", "Type here to search Levels"))
- .HintText(LOCTEXT("FilterSearchHint", "Search Levels"))
- .OnTextChanged(this, &SWorldHierarchyImpl::SetFilterText)
- ]
- // Create New Folder icon
- + SHorizontalBox::Slot()
- .AutoWidth()
- .Padding(4.0f, 0.0f, 0.0f, 0.0f)
- [
- CreateNewFolderButton
- ]
- ]
- // Empty Label
- +SVerticalBox::Slot()
- .HAlign(HAlign_Center)
- [
- SNew(STextBlock)
- .Visibility(this, &SWorldHierarchyImpl::GetEmptyLabelVisibility)
- .Text(LOCTEXT("EmptyLabel", "Empty"))
- .ColorAndOpacity(FLinearColor(0.4f, 1.0f, 0.4f))
- ]
- // Hierarchy
- +SVerticalBox::Slot()
- .FillHeight(1.f)
- [
- SAssignNew(TreeWidget, SLevelsTreeWidget, WorldModel, SharedThis(this))
- .TreeItemsSource(&RootTreeItems)
- .SelectionMode(ESelectionMode::Multi)
- .OnGenerateRow(this, &SWorldHierarchyImpl::GenerateTreeRow)
- .OnGetChildren(this, &SWorldHierarchyImpl::GetChildrenForTree)
- .OnSelectionChanged(this, &SWorldHierarchyImpl::OnSelectionChanged)
- .OnExpansionChanged(this, &SWorldHierarchyImpl::OnExpansionChanged)
- .OnMouseButtonDoubleClick(this, &SWorldHierarchyImpl::OnTreeViewMouseButtonDoubleClick)
- .OnContextMenuOpening(ContextMenuEvent)
- .OnItemScrolledIntoView(this, &SWorldHierarchyImpl::OnTreeItemScrolledIntoView)
- .HeaderRow(HeaderRowWidget.ToSharedRef())
- ]
- // Separator
- +SVerticalBox::Slot()
- .AutoHeight()
- .Padding(0, 0, 0, 1)
- [
- SNew(SSeparator)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- ]
- // View options
- +SVerticalBox::Slot()
- .AutoHeight()
- [
- SNew(SHorizontalBox)
- .Visibility(bFoldersOnlyMode ? EVisibility::Collapsed : EVisibility::Visible)
- // Asset count
- +SHorizontalBox::Slot()
- .FillWidth(1.f)
- .VAlign(VAlign_Center)
- .Padding(8, 0)
- [
- SNew( STextBlock )
- .Text( this, &SWorldHierarchyImpl::GetFilterStatusText )
- .ColorAndOpacity( this, &SWorldHierarchyImpl::GetFilterStatusTextColor )
- ]
- // View mode combo button
- +SHorizontalBox::Slot()
- .AutoWidth()
- [
- SAssignNew( ViewOptionsComboButton, SComboButton )
- .ContentPadding(0)
- .ForegroundColor( this, &SWorldHierarchyImpl::GetViewButtonForegroundColor )
- .ButtonStyle( FEditorStyle::Get(), "ToggleButton" ) // Use the tool bar item style for this button
- .OnGetMenuContent( this, &SWorldHierarchyImpl::GetViewButtonContent )
- .ButtonContent()
- [
- SNew(SHorizontalBox)
- +SHorizontalBox::Slot()
- .AutoWidth()
- .VAlign(VAlign_Center)
- [
- SNew(SImage).Image( FEditorStyle::GetBrush("GenericViewButton") )
- ]
- +SHorizontalBox::Slot()
- .AutoWidth()
- .Padding(2, 0, 0, 0)
- .VAlign(VAlign_Center)
- [
- SNew(STextBlock).Text( LOCTEXT("ViewButton", "View Options") )
- ]
- ]
- ]
- ]
- ];
- if (FLevelFolders::IsAvailable())
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- LevelFolders.OnFolderCreate.AddSP(this, &SWorldHierarchyImpl::OnBroadcastFolderCreate);
- LevelFolders.OnFolderMove.AddSP(this, &SWorldHierarchyImpl::OnBroadcastFolderMove);
- LevelFolders.OnFolderDelete.AddSP(this, &SWorldHierarchyImpl::OnBroadcastFolderDelete);
- if (!bFoldersOnlyMode)
- {
- FEditorDelegates::PostSaveWorld.AddSP(this, &SWorldHierarchyImpl::OnWorldSaved);
- }
- }
- if (SearchBoxLevelFilter.IsValid())
- {
- WorldModel->AddFilter(SearchBoxLevelFilter.ToSharedRef());
- }
- OnUpdateSelection();
- GEditor->RegisterForUndo(this);
- }
- void SWorldHierarchyImpl::Tick( const FGeometry& AllotedGeometry, const double InCurrentTime, const float InDeltaTime )
- {
- SCompoundWidget::Tick(AllotedGeometry, InCurrentTime, InDeltaTime);
- if (bNeedsRefresh)
- {
- if (!bIsReentrant)
- {
- Populate();
- }
- }
- if (bSortDirty)
- {
- SortItems(RootTreeItems);
- for (const auto& Pair : TreeItemMap)
- {
- Pair.Value->Flags.bChildrenRequiresSort = true;
- }
- bSortDirty = false;
- }
- }
- void SWorldHierarchyImpl::OnWorldSaved(uint32 SaveFlags, UWorld* World, bool bSuccess)
- {
- if (FLevelFolders::IsAvailable())
- {
- for (TSharedPtr<FLevelModel> RootLevel : WorldModel->GetRootLevelList())
- {
- FLevelFolders::Get().SaveLevel(RootLevel.ToSharedRef());
- }
- }
- }
- void SWorldHierarchyImpl::RefreshView()
- {
- bNeedsRefresh = true;
- }
- TSharedRef<ITableRow> SWorldHierarchyImpl::GenerateTreeRow(WorldHierarchy::FWorldTreeItemPtr Item, const TSharedRef<STableViewBase>& OwnerTable)
- {
- check(Item.IsValid());
- return SNew(SWorldHierarchyItem, OwnerTable)
- .InWorldModel(WorldModel)
- .InHierarchy(SharedThis(this))
- .InItemModel(Item)
- .IsItemExpanded(Item->Flags.bExpanded)
- .HighlightText(this, &SWorldHierarchyImpl::GetSearchBoxText)
- .FoldersOnlyMode(bFoldersOnlyMode)
- ;
- }
- void SWorldHierarchyImpl::GetChildrenForTree(WorldHierarchy::FWorldTreeItemPtr Item, TArray<WorldHierarchy::FWorldTreeItemPtr>& OutChildren)
- {
- OutChildren = Item->GetChildren();
- if (Item->Flags.bChildrenRequiresSort)
- {
- if (OutChildren.Num() > 0)
- {
- SortItems(OutChildren);
- // Empty out the children and repopulate them in the correct order
- Item->RemoveAllChildren();
- for (WorldHierarchy::FWorldTreeItemPtr Child : OutChildren)
- {
- Item->AddChild(Child.ToSharedRef());
- }
- }
- Item->Flags.bChildrenRequiresSort = false;
- }
- }
- bool SWorldHierarchyImpl::PassesFilter(const WorldHierarchy::IWorldTreeItem& Item)
- {
- bool bPassesFilter = true;
- WorldHierarchy::FFolderTreeItem* Folder = Item.GetAsFolderTreeItem();
- if (bFoldersOnlyMode && Folder == nullptr)
- {
- // Level items should fail to pass the filter if we only want to display folders
- bPassesFilter = false;
- }
- else
- {
- bPassesFilter = SearchBoxHierarchyFilter->PassesFilter(Item);
- }
- if (bPassesFilter && ExcludedFolders.Num() > 0)
- {
- if (Folder != nullptr)
- {
- FName CheckPath = Folder->GetFullPath();
- // Folders should not be shown if it or its parent have been excluded
- while (!CheckPath.IsNone())
- {
- if (ExcludedFolders.Contains(CheckPath))
- {
- bPassesFilter = false;
- break;
- }
- CheckPath = WorldHierarchy::GetParentPath(CheckPath);
- }
- }
- }
- return bPassesFilter;
- }
- TSharedPtr<SWidget> SWorldHierarchyImpl::ConstructLevelContextMenu() const
- {
- TSharedRef<SWidget> MenuWidget = SNullWidget::NullWidget;
- if (!WorldModel->IsReadOnly())
- {
- UToolMenus* ToolMenus = UToolMenus::Get();
- static const FName MenuName = "WorldBrowser.WorldHierarchy.LevelContextMenu";
- if (!ToolMenus->IsMenuRegistered(MenuName))
- {
- ToolMenus->RegisterMenu(MenuName);
- }
- FToolMenuContext Context(WorldModel->GetCommandList(), TSharedPtr<FExtender>());
- UToolMenu* Menu = ToolMenus->GenerateMenu(MenuName, Context);
- TArray<WorldHierarchy::FWorldTreeItemPtr> SelectedItems = GetSelectedTreeItems();
- if (SelectedItems.Num() == 1)
- {
- // If exactly one item is selected, allow it to generate its own context menu
- SelectedItems[0]->GenerateContextMenu(Menu, *this);
- }
- else if (SelectedItems.Num() == 0)
- {
- // If no items are selected, allow the first root level item to create a context menu
- RootTreeItems[0]->GenerateContextMenu(Menu, *this);
- }
- Menu->AddDynamicSection("HierarchyDynamicSection", FNewToolMenuDelegateLegacy::CreateLambda([=](FMenuBuilder& MenuBuilder, UToolMenu* Menu)
- {
- WorldModel->BuildHierarchyMenu(MenuBuilder);
- }));
- // Generate the "Move To" and "Select" submenus based on the current selection
- if (WorldModel->HasFolderSupport())
- {
- bool bOnlyFoldersSelected = SelectedItems.Num() > 0;
- bool bAllSelectedItemsCanMove = SelectedItems.Num() > 0;
- for (WorldHierarchy::FWorldTreeItemPtr Item : SelectedItems)
- {
- bOnlyFoldersSelected &= Item->GetAsFolderTreeItem() != nullptr;
- bAllSelectedItemsCanMove &= Item->CanChangeParents();
- if (!bOnlyFoldersSelected && !bAllSelectedItemsCanMove)
- {
- // Neither submenu can be built, kill the check
- break;
- }
- }
- if (bAllSelectedItemsCanMove && FLevelFolders::IsAvailable())
- {
- FToolMenuSection& Section = Menu->AddSection("Section");
- Section.AddSubMenu(
- "MoveSelectionTo",
- LOCTEXT("MoveSelectionTo", "Move To"),
- LOCTEXT("MoveSelectionTo_Tooltip", "Move selection to another folder"),
- FNewMenuDelegate::CreateSP(const_cast<SWorldHierarchyImpl*>(this), &SWorldHierarchyImpl::FillFoldersSubmenu)
- );
- }
- if (bOnlyFoldersSelected)
- {
- FToolMenuSection& Section = Menu->AddSection("Section");
- Section.AddSubMenu(
- "SelectSubmenu",
- LOCTEXT("SelectSubmenu", "Select"),
- LOCTEXT("SelectSubmenu_Tooltip", "Select child items of the current selection"),
- FNewMenuDelegate::CreateSP(const_cast<SWorldHierarchyImpl*>(this), &SWorldHierarchyImpl::FillSelectionSubmenu)
- );
- }
- }
- MenuWidget = ToolMenus->GenerateWidget(Menu);
- }
- return MenuWidget;
- }
- void SWorldHierarchyImpl::FillFoldersSubmenu(FMenuBuilder& MenuBuilder)
- {
- TArray<WorldHierarchy::FWorldTreeItemPtr> SelectedItems = GetSelectedTreeItems();
- check(SelectedItems.Num() > 0);
- // Assume that the root item of the first selected item is the root for all of them
- TSharedPtr<FLevelModel> RootItem = SelectedItems[0]->GetRootItem();
- FName RootPath = NAME_None;
- MenuBuilder.AddMenuEntry(
- LOCTEXT("CreateNewFolder", "Create New Folder"),
- LOCTEXT("CreateNewFolder_Tooltip", "Move the selection to a new folder"),
- FSlateIcon(FEditorStyle::GetStyleSetName(), "WorldBrowser.NewFolderIcon"),
- FExecuteAction::CreateSP(this, &SWorldHierarchyImpl::CreateFolder, RootItem, RootPath)
- );
- AddMoveToFolderOutliner(MenuBuilder, SelectedItems, RootItem.ToSharedRef());
- }
- void SWorldHierarchyImpl::AddMoveToFolderOutliner(FMenuBuilder& MenuBuilder, const TArray<WorldHierarchy::FWorldTreeItemPtr>& SelectedItems, TSharedRef<FLevelModel> RootItem)
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- if (LevelFolders.GetFolderProperties(RootItem).Num() > 0)
- {
- TSet<FName> ExcludedFolderPaths;
- // Exclude selected folders
- for (WorldHierarchy::FWorldTreeItemPtr Item : SelectedItems)
- {
- if (WorldHierarchy::FFolderTreeItem* Folder = Item->GetAsFolderTreeItem())
- {
- ExcludedFolderPaths.Add(Folder->GetFullPath());
- }
- }
- // Copy the world model to ensure that any delegates fired for the mini hierarchy doesn't affect the main hierarchy
- FWorldBrowserModule& WorldBrowserModule = FModuleManager::LoadModuleChecked<FWorldBrowserModule>("WorldBrowser");
- TSharedPtr<FLevelCollectionModel> WorldModelCopy = WorldBrowserModule.SharedWorldModel(WorldModel->GetWorld());
- TSharedRef<SWidget> MiniHierarchy =
- SNew(SVerticalBox)
- + SVerticalBox::Slot()
- .MaxHeight(400.0f)
- [
- SNew(SWorldHierarchyImpl)
- .InWorldModel(WorldModelCopy)
- .ShowFoldersOnly(true)
- .InExcludedFolders(ExcludedFolderPaths)
- .OnItemPickedDelegate(FOnWorldHierarchyItemPicked::CreateSP(this, &SWorldHierarchyImpl::MoveSelectionTo))
- ];
- MenuBuilder.BeginSection(FName(), LOCTEXT("ExistingFolders", "Existing:"));
- MenuBuilder.AddWidget(MiniHierarchy, FText::GetEmpty(), false);
- MenuBuilder.EndSection();
- }
- }
- void SWorldHierarchyImpl::MoveSelectionTo(WorldHierarchy::FWorldTreeItemRef Item)
- {
- FSlateApplication::Get().DismissAllMenus();
- TSharedPtr<FLevelModel> RootLevel = Item->GetRootItem();
- FName Path = NAME_None;
- if (WorldHierarchy::FFolderTreeItem* Folder = Item->GetAsFolderTreeItem())
- {
- Path = Folder->GetFullPath();
- }
- MoveItemsTo(RootLevel, Path);
- RefreshView();
- }
- void SWorldHierarchyImpl::FillSelectionSubmenu(FMenuBuilder& MenuBuilder)
- {
- const bool bSelectAllDescendants = true;
- MenuBuilder.AddMenuEntry(
- LOCTEXT("SelectImmediateChildren", "Immediate Children"),
- LOCTEXT("SelectImmediateChildren_Tooltip", "Select all immediate children of the selected folders"),
- FSlateIcon(),
- FExecuteAction::CreateSP(this, &SWorldHierarchyImpl::SelectFolderDescendants, !bSelectAllDescendants)
- );
- MenuBuilder.AddMenuEntry(
- LOCTEXT("SelectAllDescendants", "All Descendants"),
- LOCTEXT("SelectAllDescendants_Tooltip", "Selects all descendants of the selected folders"),
- FSlateIcon(),
- FExecuteAction::CreateSP(this, &SWorldHierarchyImpl::SelectFolderDescendants, bSelectAllDescendants)
- );
- }
- void SWorldHierarchyImpl::SelectFolderDescendants(bool bSelectAllDescendants)
- {
- TArray<WorldHierarchy::FWorldTreeItemPtr> OldSelection = GetSelectedTreeItems();
- FLevelModelList SelectedLevels;
- TreeWidget->ClearSelection();
- for (WorldHierarchy::FWorldTreeItemPtr Item : OldSelection)
- {
- for (WorldHierarchy::FWorldTreeItemPtr Child : Item->GetChildren())
- {
- if (bSelectAllDescendants)
- {
- SelectedLevels.Append(Child->GetLevelModels());
- }
- else
- {
- SelectedLevels.Append(Child->GetModel());
- }
- }
- }
- if (SelectedLevels.Num() > 0)
- {
- WorldModel->SetSelectedLevels(SelectedLevels);
- }
- }
- void SWorldHierarchyImpl::MoveDroppedItems(const TArray<WorldHierarchy::FWorldTreeItemPtr>& DraggedItems, FName FolderPath)
- {
- if (DraggedItems.Num() > 0)
- {
- // Ensure that the dragged items are selected in the tree
- TreeWidget->ClearSelection();
- for (WorldHierarchy::FWorldTreeItemPtr Item : DraggedItems)
- {
- TreeWidget->SetItemSelection(Item, true);
- }
- // Assume that the root of the first is the root of all the items
- const FScopedTransaction Transaction(LOCTEXT("ItemsMoved", "Move World Hierarchy Items"));
- MoveItemsTo(DraggedItems[0]->GetRootItem(), FolderPath);
- RefreshView();
- }
- }
- void SWorldHierarchyImpl::AddDroppedLevelsToFolder(const TArray<FAssetData>& WorldAssetList, FName FolderPath)
- {
- if (WorldAssetList.Num() > 0)
- {
- // Populate the set of existing levels in the world
- TSet<FName> ExistingLevels;
- for (TSharedPtr<FLevelModel> Level : WorldModel->GetAllLevels())
- {
- ExistingLevels.Add(Level->GetLongPackageName());
- }
- WorldModel->AddExistingLevelsFromAssetData(WorldAssetList);
- // Set the folder path of any newly added levels
- for (TSharedPtr<FLevelModel> Level : WorldModel->GetAllLevels())
- {
- if (!ExistingLevels.Contains(Level->GetLongPackageName()))
- {
- Level->SetFolderPath(FolderPath);
- }
- }
- RefreshView();
- }
- }
- void SWorldHierarchyImpl::OnTreeItemScrolledIntoView( WorldHierarchy::FWorldTreeItemPtr Item, const TSharedPtr<ITableRow>& Widget )
- {
- if (Item == ItemPendingRename)
- {
- ItemPendingRename = nullptr;
- Item->RenameRequestEvent.ExecuteIfBound();
- }
- }
- void SWorldHierarchyImpl::OnExpansionChanged(WorldHierarchy::FWorldTreeItemPtr Item, bool bIsItemExpanded)
- {
- Item->SetExpansion(bIsItemExpanded);
- WorldHierarchy::FFolderTreeItem* Folder = Item->GetAsFolderTreeItem();
- if (FLevelFolders::IsAvailable() && Folder != nullptr)
- {
- if (FLevelFolderProps* Props = FLevelFolders::Get().GetFolderProperties(Item->GetRootItem().ToSharedRef(), Folder->GetFullPath()))
- {
- Props->bExpanded = Item->Flags.bExpanded;
- }
- }
- RefreshView();
- }
- void SWorldHierarchyImpl::OnSelectionChanged(const WorldHierarchy::FWorldTreeItemPtr Item, ESelectInfo::Type SelectInfo)
- {
- if (bUpdatingSelection)
- {
- return;
- }
- bUpdatingSelection = true;
- TArray<WorldHierarchy::FWorldTreeItemPtr> SelectedItems = GetSelectedTreeItems();
- FLevelModelList SelectedLevels;
- for (const WorldHierarchy::FWorldTreeItemPtr& TreeItem : SelectedItems)
- {
- // Folder items should return all child models, but anything else should only return the model for that item
- if (TreeItem->GetAsFolderTreeItem() != nullptr)
- {
- SelectedLevels.Append(TreeItem->GetLevelModels());
- }
- else
- {
- SelectedLevels.Append(TreeItem->GetModel());
- }
- }
- if (!bFoldersOnlyMode)
- {
- WorldModel->SetSelectedLevels(SelectedLevels);
- }
- bUpdatingSelection = false;
- if (TreeWidget->GetNumItemsSelected() > 0)
- {
- OnItemPicked.ExecuteIfBound(GetSelectedTreeItems()[0].ToSharedRef());
- }
- }
- void SWorldHierarchyImpl::OnUpdateSelection()
- {
- if (bUpdatingSelection)
- {
- return;
- }
- bUpdatingSelection = true;
- ItemsSelectedAfterRefresh.Empty();
- const FLevelModelList& SelectedItems = WorldModel->GetSelectedLevels();
- TreeWidget->ClearSelection();
- // To get the list of items that should be displayed as selected we need to find the level tree items belonging to the selected level models.
- if (SelectedItems.Num() > 0)
- {
- for (auto It = TreeItemMap.CreateConstIterator(); It; ++It)
- {
- WorldHierarchy::FWorldTreeItemPtr TreeItemPtr = It->Value;
- if (TreeItemPtr.IsValid())
- {
- for (auto SelectedItemIt = SelectedItems.CreateConstIterator(); SelectedItemIt; ++SelectedItemIt)
- {
- if (TreeItemPtr->HasModel(*SelectedItemIt))
- {
- ItemsSelectedAfterRefresh.Add(It->Key);
- break;
- }
- }
- }
- }
- }
- RefreshView();
- bUpdatingSelection = false;
- }
- void SWorldHierarchyImpl::OnTreeViewMouseButtonDoubleClick(WorldHierarchy::FWorldTreeItemPtr Item)
- {
- if (Item->CanBeCurrent())
- {
- Item->MakeCurrent();
- }
- else
- {
- Item->SetExpansion(!Item->Flags.bExpanded);
- TreeWidget->SetItemExpansion(Item, Item->Flags.bExpanded);
- }
- }
- FReply SWorldHierarchyImpl::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
- {
- if (WorldModel->GetCommandList()->ProcessCommandBindings(InKeyEvent))
- {
- return FReply::Handled();
- }
- else if ( InKeyEvent.GetKey() == EKeys::F2 )
- {
- // If a single folder is selected, F2 should attempt to rename it
- if (TreeWidget->GetNumItemsSelected() == 1)
- {
- WorldHierarchy::FWorldTreeItemPtr ItemToRename = GetSelectedTreeItems()[0];
- if (ItemToRename->GetAsFolderTreeItem() != nullptr)
- {
- ItemPendingRename = ItemToRename;
- ScrollItemIntoView(ItemToRename);
- return FReply::Handled();
- }
- }
- }
- else if ( InKeyEvent.GetKey() == EKeys::Platform_Delete )
- {
- // Delete was pressed, but no levels were unloaded. Any selected folders should be removed transactionally
- const bool bTransactional = true;
- DeleteFolders(GetSelectedTreeItems(), bTransactional);
- }
- // F5 (Refresh) should be handled by the world model
- return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent);
- }
- void SWorldHierarchyImpl::OnBroadcastLevelsUnloaded()
- {
- // We deleted levels from the hierarchy, so do not record the folder delete transaction either
- const bool bTransactional = false;
- DeleteFolders(GetSelectedTreeItems(), bTransactional);
- }
- void SWorldHierarchyImpl::InitiateRename(WorldHierarchy::FWorldTreeItemRef InItem)
- {
- // Only folders items are valid for rename in this view
- if (InItem->GetAsFolderTreeItem() != nullptr)
- {
- ItemPendingRename = InItem;
- ScrollItemIntoView(InItem);
- }
- }
- void SWorldHierarchyImpl::EmptyTreeItems()
- {
- for (auto& Pair : TreeItemMap)
- {
- Pair.Value->RemoveAllChildren();
- }
- PendingOperations.Empty();
- TreeItemMap.Reset();
- PendingTreeItemMap.Reset();
- RootTreeItems.Empty();
- NewItemActions.Empty();
- ItemPendingRename.Reset();
- }
- void SWorldHierarchyImpl::RepopulateEntireTree()
- {
- EmptyTreeItems();
- for (const TSharedPtr<FLevelModel>& Level : WorldModel->GetFilteredLevels())
- {
- if (Level.IsValid())
- {
- ConstructItemFor<WorldHierarchy::FLevelModelTreeItem>(Level.ToSharedRef());
- }
- }
- if (FLevelFolders::IsAvailable() && WorldModel->HasFolderSupport())
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- // Add any folders which might match the search terms for each root level
- for (TSharedPtr<FLevelModel> RootLevel : WorldModel->GetRootLevelList())
- {
- for (const auto& Pair : LevelFolders.GetFolderProperties(RootLevel.ToSharedRef()))
- {
- if (!TreeItemMap.Contains(Pair.Key))
- {
- ConstructItemFor<WorldHierarchy::FFolderTreeItem>(Pair.Key);
- }
- }
- }
- }
- }
- TMap<WorldHierarchy::FWorldTreeItemID, bool> SWorldHierarchyImpl::GetParentsExpansionState() const
- {
- TMap<WorldHierarchy::FWorldTreeItemID, bool> ExpansionStates;
- for (const auto& Pair : TreeItemMap)
- {
- if (Pair.Value->GetChildren().Num() > 0)
- {
- ExpansionStates.Add(Pair.Key, Pair.Value->Flags.bExpanded);
- }
- }
- return ExpansionStates;
- }
- void SWorldHierarchyImpl::SetParentsExpansionState(const TMap<WorldHierarchy::FWorldTreeItemID, bool>& ExpansionInfo)
- {
- for (const auto& Pair : TreeItemMap)
- {
- auto& Item = Pair.Value;
- if (Item->GetChildren().Num() > 0)
- {
- const bool* bExpandedPtr = ExpansionInfo.Find(Pair.Key);
- bool bExpanded = bExpandedPtr != nullptr ? *bExpandedPtr : Item->Flags.bExpanded;
- TreeWidget->SetItemExpansion(Item, bExpanded);
- }
- }
- }
- void SWorldHierarchyImpl::OnBroadcastFolderCreate(TSharedPtr<FLevelModel> LevelModel, FName NewPath)
- {
- if (!TreeItemMap.Contains(NewPath))
- {
- ConstructItemFor<WorldHierarchy::FFolderTreeItem>(NewPath);
- }
- }
- void SWorldHierarchyImpl::OnBroadcastFolderDelete(TSharedPtr<FLevelModel> LevelModel, FName Path)
- {
- WorldHierarchy::FWorldTreeItemPtr* Folder = TreeItemMap.Find(Path);
- if (Folder != nullptr)
- {
- PendingOperations.Emplace(WorldHierarchy::FPendingWorldTreeOperation::Removed, Folder->ToSharedRef());
- RefreshView();
- }
- }
- void SWorldHierarchyImpl::OnBroadcastFolderMove(TSharedPtr<FLevelModel> LevelModel, FName OldPath, FName NewPath)
- {
- WorldHierarchy::FWorldTreeItemPtr Folder = TreeItemMap.FindRef(OldPath);
- if (Folder.IsValid())
- {
- // Remove the item with the old ID
- TreeItemMap.Remove(Folder->GetID());
- // Get all items that were moved
- TArray<WorldHierarchy::FWorldTreeItemPtr> AllSelectedItems = GetSelectedTreeItems();
- // Change the path, and place it back in the tree with the new ID
- {
- WorldHierarchy::FFolderTreeItem* FolderItem = Folder->GetAsFolderTreeItem();
- FolderItem->SetNewPath(NewPath);
- }
- for (WorldHierarchy::FWorldTreeItemPtr Child : Folder->GetChildren())
- {
- // Any level model children that were not explicitly moved will need to be moved here to remain in
- // sync with their parent folders
- if (!AllSelectedItems.Contains(Child) && Child->GetAsLevelModelTreeItem() != nullptr)
- {
- Child->SetParentPath(NewPath);
- }
- }
- TreeItemMap.Add(Folder->GetID(), Folder);
- PendingOperations.Emplace(WorldHierarchy::FPendingWorldTreeOperation::Moved, Folder.ToSharedRef());
- RefreshView();
- }
- }
- void SWorldHierarchyImpl::FullRefresh()
- {
- bFullRefresh = true;
- RefreshView();
- }
- void SWorldHierarchyImpl::RebuildFoldersAndFullRefresh()
- {
- bRebuildFolders = true;
- FullRefresh();
- }
- void SWorldHierarchyImpl::RequestSort()
- {
- bSortDirty = true;
- }
- void SWorldHierarchyImpl::Populate()
- {
- TGuardValue<bool> ReentrantGuard(bIsReentrant, true);
- bool bMadeSignificantChanges = false;
- const TMap<WorldHierarchy::FWorldTreeItemID, bool> ExpansionStateInfo = GetParentsExpansionState();
- if (bRebuildFolders)
- {
- if (FLevelFolders::IsAvailable())
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- for (TSharedPtr<FLevelModel> LevelModel : WorldModel->GetRootLevelList())
- {
- LevelFolders.RebuildFolderList(LevelModel.ToSharedRef());
- }
- }
- bRebuildFolders = false;
- }
- if (bFullRefresh)
- {
- RepopulateEntireTree();
- bFullRefresh = false;
- bMadeSignificantChanges = true;
- }
- if (PendingOperations.Num() > 0)
- {
- const int32 End = FMath::Min(PendingOperations.Num(), MaxPendingOperations);
- for (int32 Index = 0; Index < End; ++Index)
- {
- const WorldHierarchy::FPendingWorldTreeOperation& PendingOp = PendingOperations[Index];
- switch (PendingOp.Operation)
- {
- case WorldHierarchy::FPendingWorldTreeOperation::Added:
- bMadeSignificantChanges = AddItemToTree(PendingOp.Item);
- break;
- case WorldHierarchy::FPendingWorldTreeOperation::Moved:
- bMadeSignificantChanges = true;
- OnItemMoved(PendingOp.Item);
- break;
- case WorldHierarchy::FPendingWorldTreeOperation::Removed:
- bMadeSignificantChanges = true;
- RemoveItemFromTree(PendingOp.Item);
- break;
- default:
- check(false);
- break;
- }
- }
- PendingOperations.RemoveAt(0, End);
- }
- SetParentsExpansionState(ExpansionStateInfo);
- if (ItemsSelectedAfterRefresh.Num() > 0)
- {
- bool bScrolledIntoView = false;
- for (const WorldHierarchy::FWorldTreeItemID& ID : ItemsSelectedAfterRefresh)
- {
- if (TreeItemMap.Contains(ID))
- {
- WorldHierarchy::FWorldTreeItemPtr Item = TreeItemMap[ID];
- TreeWidget->SetItemSelection(Item, true);
- if (!bScrolledIntoView)
- {
- bScrolledIntoView = true;
- TreeWidget->RequestScrollIntoView(Item);
- }
- }
- }
- ItemsSelectedAfterRefresh.Empty();
- }
- if (bMadeSignificantChanges)
- {
- RequestSort();
- }
- TreeWidget->RequestTreeRefresh();
- if (PendingOperations.Num() == 0)
- {
- NewItemActions.Empty();
- bNeedsRefresh = false;
- }
- }
- bool SWorldHierarchyImpl::AddItemToTree(WorldHierarchy::FWorldTreeItemRef InItem)
- {
- const WorldHierarchy::FWorldTreeItemID ItemID = InItem->GetID();
- bool bItemAdded = false;
- PendingTreeItemMap.Remove(ItemID);
- if (!TreeItemMap.Find(ItemID))
- {
- // Not currently in the tree, check if the item passes the current filter
- bool bFilteredOut = !PassesFilter(*InItem);
- InItem->Flags.bFilteredOut = bFilteredOut;
- if (!bFilteredOut)
- {
- AddUnfilteredItemToTree(InItem);
- bItemAdded = true;
- if (WorldHierarchy::ENewItemAction* ActionPtr = NewItemActions.Find(ItemID))
- {
- WorldHierarchy::ENewItemAction Actions = *ActionPtr;
- if ((Actions & WorldHierarchy::ENewItemAction::Select) != WorldHierarchy::ENewItemAction::None)
- {
- TreeWidget->ClearSelection();
- TreeWidget->SetItemSelection(InItem, true);
- }
- if ((Actions & WorldHierarchy::ENewItemAction::Rename) != WorldHierarchy::ENewItemAction::None)
- {
- ItemPendingRename = InItem;
- }
- WorldHierarchy::ENewItemAction ScrollIntoView = WorldHierarchy::ENewItemAction::ScrollIntoView | WorldHierarchy::ENewItemAction::Rename;
- if ((Actions & ScrollIntoView) != WorldHierarchy::ENewItemAction::None)
- {
- ScrollItemIntoView(InItem);
- }
- }
- }
- }
- return bItemAdded;
- }
- void SWorldHierarchyImpl::AddUnfilteredItemToTree(WorldHierarchy::FWorldTreeItemRef InItem)
- {
- WorldHierarchy::FWorldTreeItemPtr Parent = EnsureParentForItem(InItem);
- const WorldHierarchy::FWorldTreeItemID ItemID = InItem->GetID();
- if (TreeItemMap.Contains(ItemID))
- {
- UE_LOG(LogWorldHierarchy, Error, TEXT("(%d | %s) already exists in the World Hierarchy. Dumping map..."), GetTypeHash(ItemID), *InItem->GetDisplayString());
- for (const auto& Entry : TreeItemMap)
- {
- UE_LOG(LogWorldHierarchy, Log, TEXT("(%d | %s)"), GetTypeHash(Entry.Key), *Entry.Value->GetDisplayString());
- }
- // Treat this as a fatal error
- check(false);
- }
- TreeItemMap.Add(ItemID, InItem);
- if (Parent.IsValid())
- {
- Parent->AddChild(InItem);
- }
- else
- {
- RootTreeItems.Add(InItem);
- }
- if (FLevelFolders::IsAvailable())
- {
- WorldHierarchy::FFolderTreeItem* Folder = InItem->GetAsFolderTreeItem();
- if (Folder != nullptr)
- {
- if (const FLevelFolderProps* Props = FLevelFolders::Get().GetFolderProperties(InItem->GetRootItem().ToSharedRef(), Folder->GetFullPath()))
- {
- InItem->SetExpansion(Props->bExpanded);
- }
- }
- }
- }
- void SWorldHierarchyImpl::RemoveItemFromTree(WorldHierarchy::FWorldTreeItemRef InItem)
- {
- if (TreeItemMap.Contains(InItem->GetID()))
- {
- WorldHierarchy::FWorldTreeItemPtr Parent = InItem->GetParent();
- if (Parent.IsValid())
- {
- Parent->RemoveChild(InItem);
- OnChildRemovedFromParent(Parent.ToSharedRef());
- }
- else
- {
- RootTreeItems.Remove(InItem);
- }
- TreeItemMap.Remove(InItem->GetID());
- }
- }
- void SWorldHierarchyImpl::OnItemMoved(WorldHierarchy::FWorldTreeItemRef InItem)
- {
- // If the item no longer matches the filter, remove it from the tree
- if (!InItem->Flags.bFilteredOut && !PassesFilter(*InItem))
- {
- RemoveItemFromTree(InItem);
- }
- else
- {
- WorldHierarchy::FWorldTreeItemPtr Parent = InItem->GetParent();
- if (Parent.IsValid())
- {
- Parent->RemoveChild(InItem);
- OnChildRemovedFromParent(Parent.ToSharedRef());
- }
- else
- {
- RootTreeItems.Remove(InItem);
- }
- Parent = EnsureParentForItem(InItem);
- if (Parent.IsValid())
- {
- Parent->AddChild(InItem);
- Parent->SetExpansion(true);
- TreeWidget->SetItemExpansion(Parent, true);
- }
- else
- {
- RootTreeItems.Add(InItem);
- }
- }
- }
- void SWorldHierarchyImpl::ScrollItemIntoView(WorldHierarchy::FWorldTreeItemPtr Item)
- {
- WorldHierarchy::FWorldTreeItemPtr Parent = Item->GetParent();
- while (Parent.IsValid())
- {
- TreeWidget->SetItemExpansion(Parent, true);
- Parent = Parent->GetParent();
- }
- TreeWidget->RequestScrollIntoView(Item);
- }
- void SWorldHierarchyImpl::OnChildRemovedFromParent(WorldHierarchy::FWorldTreeItemRef InParent)
- {
- if (InParent->Flags.bFilteredOut && InParent->GetChildren().Num() == 0)
- {
- // Parent does not match the search terms nor does it have any children that matches the search terms
- RemoveItemFromTree(InParent);
- }
- }
- WorldHierarchy::FWorldTreeItemPtr SWorldHierarchyImpl::EnsureParentForItem(WorldHierarchy::FWorldTreeItemRef Item)
- {
- WorldHierarchy::FWorldTreeItemID ParentID = Item->GetParentID();
- WorldHierarchy::FWorldTreeItemPtr ParentPtr;
- if (TreeItemMap.Contains(ParentID))
- {
- ParentPtr = TreeItemMap[ParentID];
- }
- else
- {
- ParentPtr = Item->CreateParent();
- if (ParentPtr.IsValid())
- {
- AddUnfilteredItemToTree(ParentPtr.ToSharedRef());
- }
- }
- return ParentPtr;
- }
- bool SWorldHierarchyImpl::IsTreeItemExpanded(WorldHierarchy::FWorldTreeItemPtr Item) const
- {
- return Item->Flags.bExpanded;
- }
- void SWorldHierarchyImpl::SortItems(TArray<WorldHierarchy::FWorldTreeItemPtr>& Items)
- {
- if (Items.Num() > 1)
- {
- Items.Sort([](WorldHierarchy::FWorldTreeItemPtr Item1, WorldHierarchy::FWorldTreeItemPtr Item2) {
- const int32 Priority1 = Item1->GetSortPriority();
- const int32 Priority2 = Item2->GetSortPriority();
- if (Priority1 == Priority2)
- {
- return Item1->GetDisplayString() < Item2->GetDisplayString();
- }
- return Priority1 > Priority2;
- });
- }
- }
- void SWorldHierarchyImpl::TransformLevelToString(const FLevelModel* Level, TArray<FString>& OutSearchStrings) const
- {
- if (Level != nullptr && Level->HasValidPackage())
- {
- OutSearchStrings.Add(FPackageName::GetShortName(Level->GetLongPackageName()));
- }
- }
- void SWorldHierarchyImpl::TransformItemToString(const WorldHierarchy::IWorldTreeItem& Item, TArray<FString>& OutSearchStrings) const
- {
- OutSearchStrings.Add(Item.GetDisplayString());
- }
- void SWorldHierarchyImpl::SetFilterText(const FText& InFilterText)
- {
- // Ensure that the level and hierarchy filters remain in sync
- if (SearchBoxLevelFilter.IsValid())
- {
- SearchBoxLevelFilter->SetRawFilterText(InFilterText);
- }
- SearchBoxHierarchyFilter->SetRawFilterText(InFilterText);
- }
- FText SWorldHierarchyImpl::GetSearchBoxText() const
- {
- return SearchBoxHierarchyFilter->GetRawFilterText();
- }
- FText SWorldHierarchyImpl::GetFilterStatusText() const
- {
- const int32 SelectedLevelsCount = WorldModel->GetSelectedLevels().Num();
- const int32 TotalLevelsCount = WorldModel->GetAllLevels().Num();
- const int32 FilteredLevelsCount = WorldModel->GetFilteredLevels().Num();
- if (!WorldModel->IsFilterActive())
- {
- if (SelectedLevelsCount == 0)
- {
- return FText::Format(LOCTEXT("ShowingAllLevelsFmt", "{0} levels"), FText::AsNumber(TotalLevelsCount));
- }
- else
- {
- return FText::Format(LOCTEXT("ShowingAllLevelsSelectedFmt", "{0} levels ({1} selected)"), FText::AsNumber(TotalLevelsCount), FText::AsNumber(SelectedLevelsCount));
- }
- }
- else if (WorldModel->IsFilterActive() && FilteredLevelsCount == 0)
- {
- return FText::Format(LOCTEXT("ShowingNoLevelsFmt", "No matching levels ({0} total)"), FText::AsNumber(TotalLevelsCount));
- }
- else if (SelectedLevelsCount != 0)
- {
- return FText::Format(LOCTEXT("ShowingOnlySomeLevelsSelectedFmt", "Showing {0} of {1} levels ({2} selected)"), FText::AsNumber(FilteredLevelsCount), FText::AsNumber(TotalLevelsCount), FText::AsNumber(SelectedLevelsCount));
- }
- else
- {
- return FText::Format(LOCTEXT("ShowingOnlySomeLevelsFmt", "Showing {0} of {1} levels"), FText::AsNumber(FilteredLevelsCount), FText::AsNumber(TotalLevelsCount));
- }
- }
- FReply SWorldHierarchyImpl::OnCreateFolderClicked()
- {
- // Assume that the folder will be created for the first persistent level
- TSharedPtr<FLevelModel> PersistentLevel = WorldModel->GetRootLevelList()[0];
- CreateFolder(PersistentLevel);
- return FReply::Handled();
- }
- EVisibility SWorldHierarchyImpl::GetEmptyLabelVisibility() const
- {
- return ( !bFoldersOnlyMode || RootTreeItems.Num() > 0 ) ? EVisibility::Collapsed : EVisibility::Visible;
- }
- void SWorldHierarchyImpl::CreateFolder(TSharedPtr<FLevelModel> InModel, FName ParentPath /* = NAME_None */)
- {
- if (FLevelFolders::IsAvailable())
- {
- TSharedPtr<FLevelModel> PersistentLevelModel = InModel;
- if (!InModel.IsValid())
- {
- // We're not making this for any specific level...assume it's the first persistent level in the world
- PersistentLevelModel = WorldModel->GetRootLevelList()[0];
- }
- const FScopedTransaction Transaction(LOCTEXT("UndoAction_CreateFolder", "Create Folder"));
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- FName NewFolderName = ParentPath;
- // Get the folder name for the selected level items
- if (NewFolderName.IsNone())
- {
- // Attempt to find the most relevant shared folder for all selected items
- TArray<WorldHierarchy::FWorldTreeItemPtr> SelectedItems = GetSelectedTreeItems();
- TSet<FName> SharedAncestorPaths = SelectedItems.Num() > 0 ? SelectedItems[0]->GetAncestorPaths() : TSet<FName>();
- for (int32 Index = 1; Index < SelectedItems.Num(); ++Index)
- {
- SharedAncestorPaths = SharedAncestorPaths.Intersect(SelectedItems[Index]->GetAncestorPaths());
- if (SharedAncestorPaths.Num() == 0)
- {
- // No common ancestor path found, put them at the root
- break;
- }
- }
- // Find the longest name in the shared ancestor paths, because that's the most local "root" folder
- for (FName Ancestor : SharedAncestorPaths)
- {
- if (Ancestor.ToString().Len() > NewFolderName.ToString().Len())
- {
- NewFolderName = Ancestor;
- }
- }
- }
- NewFolderName = LevelFolders.GetDefaultFolderName(PersistentLevelModel.ToSharedRef(), NewFolderName);
- MoveItemsTo(PersistentLevelModel, NewFolderName);
- }
- }
- void SWorldHierarchyImpl::MoveItemsTo(TSharedPtr<FLevelModel> InModel, FName InPath)
- {
- if (FLevelFolders::IsAvailable())
- {
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- // Get the selected folders first before any items move
- TArray<WorldHierarchy::FWorldTreeItemPtr> PreviouslySelectedItems = GetSelectedTreeItems();
- TArray<WorldHierarchy::FFolderTreeItem*> SelectedFolders;
- for (WorldHierarchy::FWorldTreeItemPtr Item : PreviouslySelectedItems)
- {
- if (WorldHierarchy::FFolderTreeItem* Folder = Item->GetAsFolderTreeItem())
- {
- SelectedFolders.Add(Folder);
- }
- }
- // Move the levels first
- LevelFolders.CreateFolderContainingSelectedLevels(WorldModel.ToSharedRef(), InModel.ToSharedRef(), InPath);
- // Ensure that any moved levels will have their hierarchy items updated
- for (TSharedPtr<FLevelModel> SelectedLevel : WorldModel->GetSelectedLevels())
- {
- WorldHierarchy::FWorldTreeItemID LevelID(SelectedLevel->GetLevelObject());
- if (TreeItemMap.Contains(LevelID))
- {
- PendingOperations.Emplace(WorldHierarchy::FPendingWorldTreeOperation::Moved, TreeItemMap[LevelID].ToSharedRef());
- }
- }
- // Move any of the previously selected folders
- for (WorldHierarchy::FFolderTreeItem* Folder : SelectedFolders)
- {
- FName OldPath = Folder->GetFullPath();
- FName NewPath = FName(*(InPath.ToString() / Folder->GetLeafName().ToString()));
- LevelFolders.RenameFolder(Folder->GetRootItem().ToSharedRef(), OldPath, NewPath);
- }
- NewItemActions.Add(InPath, WorldHierarchy::ENewItemAction::Select | WorldHierarchy::ENewItemAction::Rename);
- }
- }
- void SWorldHierarchyImpl::DeleteFolders(TArray<WorldHierarchy::FWorldTreeItemPtr> SelectedItems, bool bTransactional/* = true*/)
- {
- TArray<WorldHierarchy::FWorldTreeItemPtr> FolderItems;
- TSet<FName> DeletedPaths;
- for (WorldHierarchy::FWorldTreeItemPtr Item : SelectedItems)
- {
- // Only take folder items
- if (WorldHierarchy::FFolderTreeItem* Folder = Item->GetAsFolderTreeItem())
- {
- FolderItems.Add(Item);
- DeletedPaths.Add(Folder->GetFullPath());
- }
- }
- FScopedTransaction Transaction(LOCTEXT("DeleteFolderTransaction", "Delete Folder"));
- FLevelFolders& LevelFolders = FLevelFolders::Get();
- // Folders are deleted one at a time
- for (WorldHierarchy::FWorldTreeItemPtr Item : FolderItems)
- {
- TSharedRef<FLevelModel> LevelModel = Item->GetRootItem().ToSharedRef();
- // First, move the folder's children up to the ancestor that will not be deleted
- FName ItemPath = Item->GetAsFolderTreeItem()->GetFullPath();
- FName ParentPath = ItemPath;
- do
- {
- ParentPath = WorldHierarchy::GetParentPath(ParentPath);
- } while (DeletedPaths.Contains(ParentPath) && !ParentPath.IsNone());
- TArray<WorldHierarchy::FWorldTreeItemPtr> Children = Item->GetChildren();
- for (WorldHierarchy::FWorldTreeItemPtr Child : Children)
- {
- if (!SelectedItems.Contains(Child))
- {
- if (WorldHierarchy::FFolderTreeItem* ChildFolder = Child->GetAsFolderTreeItem())
- {
- FName NewChildPath = ChildFolder->GetLeafName();
- if (!ParentPath.IsNone())
- {
- NewChildPath = FName(*(ParentPath.ToString() / NewChildPath.ToString()));
- }
- LevelFolders.RenameFolder(LevelModel, ChildFolder->GetFullPath(), NewChildPath);
- }
- else
- {
- Child->SetParentPath(ParentPath);
- OnItemMoved(Child.ToSharedRef());
- }
- }
- }
- // Then delete the folder
- LevelFolders.DeleteFolder(LevelModel, ItemPath);
- }
- if (!bTransactional || FolderItems.Num() == 0)
- {
- Transaction.Cancel();
- }
- }
- FSlateColor SWorldHierarchyImpl::GetFilterStatusTextColor() const
- {
- if (!WorldModel->IsFilterActive())
- {
- // White = no text filter
- return FLinearColor(1.0f, 1.0f, 1.0f);
- }
- else if (WorldModel->GetFilteredLevels().Num() == 0)
- {
- // Red = no matching actors
- return FLinearColor(1.0f, 0.4f, 0.4f);
- }
- else
- {
- // Green = found at least one match!
- return FLinearColor(0.4f, 1.0f, 0.4f);
- }
- }
- TSharedRef<SWidget> SWorldHierarchyImpl::GetViewButtonContent()
- {
- FMenuBuilder MenuBuilder(true, NULL);
- MenuBuilder.BeginSection("SubLevelsViewMenu", LOCTEXT("ShowHeading", "Show"));
- {
- MenuBuilder.AddMenuEntry(LOCTEXT("ToggleDisplayPaths", "Display Paths"),
- LOCTEXT("ToggleDisplayPaths_Tooltip", "If enabled, displays the path for each level"),
- FSlateIcon(),
- FUIAction(
- FExecuteAction::CreateSP(this, &SWorldHierarchyImpl::ToggleDisplayPaths_Executed),
- FCanExecuteAction(),
- FIsActionChecked::CreateSP(this, &SWorldHierarchyImpl::GetDisplayPathsState)),
- NAME_None,
- EUserInterfaceActionType::ToggleButton
- );
- MenuBuilder.AddMenuEntry(LOCTEXT("ToggleDisplayActorsCount", "Display Actors Count"),
- LOCTEXT("ToggleDisplayActorsCount_Tooltip", "If enabled, displays actors count for each level"),
- FSlateIcon(),
- FUIAction(
- FExecuteAction::CreateSP(this, &SWorldHierarchyImpl::ToggleDisplayActorsCount_Executed),
- FCanExecuteAction(),
- FIsActionChecked::CreateSP(this, &SWorldHierarchyImpl::GetDisplayActorsCountState)),
- NAME_None,
- EUserInterfaceActionType::ToggleButton
- );
- }
- MenuBuilder.EndSection();
- return MenuBuilder.MakeWidget();
- }
- FSlateColor SWorldHierarchyImpl::GetViewButtonForegroundColor() const
- {
- static const FName InvertedForegroundName("InvertedForeground");
- static const FName DefaultForegroundName("DefaultForeground");
- return ViewOptionsComboButton->IsHovered() ? FEditorStyle::GetSlateColor(InvertedForegroundName) : FEditorStyle::GetSlateColor(DefaultForegroundName);
- }
- bool SWorldHierarchyImpl::GetDisplayPathsState() const
- {
- return WorldModel->GetDisplayPathsState();
- }
- void SWorldHierarchyImpl::ToggleDisplayActorsCount_Executed()
- {
- WorldModel->SetDisplayActorsCountState(!WorldModel->GetDisplayActorsCountState());
- }
- bool SWorldHierarchyImpl::GetDisplayActorsCountState() const
- {
- return WorldModel->GetDisplayActorsCountState();
- }
- void SWorldHierarchyImpl::PostUndo(bool bSuccess)
- {
- if (!bIsReentrant)
- {
- FullRefresh();
- }
- }
- #undef LOCTEXT_NAMESPACE
Add Comment
Please, Sign In to add comment