// Copyright Epic Games, Inc. All Rights Reserved. #include "SFoliagePalette.h" #include "AssetRegistry/ARFilter.h" #include "AssetRegistry/AssetData.h" #include "AssetSelection.h" #include "AssetThumbnail.h" #include "Containers/UnrealString.h" #include "ContentBrowserDelegates.h" #include "ContentBrowserModule.h" #include "CoreGlobals.h" #include "CoreTypes.h" #include "DetailsViewArgs.h" #include "Editor.h" #include "Editor/EditorEngine.h" #include "Engine/Blueprint.h" #include "Engine/StaticMesh.h" #include "InstancedFoliageActor.h" #include "FoliageEdMode.h" #include "FoliagePaletteCommands.h" #include "FoliagePaletteItem.h" #include "FoliageType.h" #include "FoliageTypePaintingCustomization.h" #include "FoliageType_Actor.h" #include "FoliageType_InstancedStaticMesh.h" #include "FoliageType_InstancedStaticMeshPaintingCustomization.h" #include "Framework/Application/SlateApplication.h" #include "Framework/Commands/UIAction.h" #include "Framework/Commands/UICommandInfo.h" #include "Framework/Commands/UICommandList.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Framework/SlateDelegates.h" #include "Framework/Text/TextLayout.h" #include "Framework/Views/ITypedTableView.h" #include "HAL/PlatformMisc.h" #include "IContentBrowserSingleton.h" #include "IDetailsView.h" #include "Internationalization/Internationalization.h" #include "Layout/Children.h" #include "Layout/Margin.h" #include "Math/Color.h" #include "Math/Vector2D.h" #include "Misc/Attribute.h" #include "Misc/FeedbackContext.h" #include "Misc/MessageDialog.h" #include "Modules/ModuleManager.h" #include "PropertyCustomizationHelpers.h" #include "PropertyEditorDelegates.h" #include "PropertyEditorModule.h" #include "SPositiveActionButton.h" #include "ScopedTransaction.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Styling/ISlateStyle.h" #include "Styling/SlateColor.h" #include "Templates/Casts.h" #include "Templates/Function.h" #include "Textures/SlateIcon.h" #include "Types/SlateStructs.h" #include "Types/WidgetActiveTimerDelegate.h" #include "UObject/Class.h" #include "UObject/Package.h" #include "UObject/TopLevelAssetPath.h" #include "UObject/UnrealNames.h" #include "UObject/UObjectIterator.h" #include "Widgets/Images/SImage.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Input/SSearchBox.h" #include "Widgets/Input/SSlider.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SScaleBox.h" #include "Widgets/Layout/SScrollBorder.h" #include "Widgets/Layout/SSplitter.h" #include "Widgets/Layout/SWidgetSwitcher.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SOverlay.h" #include "Widgets/SWidget.h" #include "Widgets/Text/SRichTextBlock.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Views/ITableRow.h" #include "Widgets/Views/SListView.h" #include "Input/DragAndDrop.h" #include "DragAndDrop/ExternalContentDragDropOp.h" class UObject; struct FGeometry; struct FKeyEvent; struct FSlateBrush; #define LOCTEXT_NAMESPACE "FoliageEd_Mode" //////////////////////////////////////////////// // SFoliageDragDropHandler //////////////////////////////////////////////// /** Drag-drop zone for adding foliage types to the palette */ class SFoliageDragDropHandler : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SFoliageDragDropHandler) {} SLATE_DEFAULT_SLOT(FArguments, Content) SLATE_EVENT(FOnDrop, OnDrop) SLATE_END_ARGS() void Construct(const FArguments& InArgs) { bIsDragOn = false; OnDropDelegate = InArgs._OnDrop; this->ChildSlot [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush("WhiteBrush")) .BorderBackgroundColor(this, &SFoliageDragDropHandler::GetBackgroundColor) .Padding(FMargin(30)) [ InArgs._Content.Widget ] ]; } FReply OnDrop(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override { bIsDragOn = false; if (OnDropDelegate.IsBound()) { return OnDropDelegate.Execute(MyGeometry, DragDropEvent); } return FReply::Handled(); } virtual void OnDragEnter(const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent) override { bIsDragOn = true; } virtual void OnDragLeave(const FDragDropEvent& DragDropEvent) override { bIsDragOn = false; } private: FSlateColor GetBackgroundColor() const { return bIsDragOn ? FLinearColor(1.0f, 0.6f, 0.1f, 0.9f) : FLinearColor(0.1f, 0.1f, 0.1f, 0.9f); } private: FOnDrop OnDropDelegate; bool bIsDragOn; }; //////////////////////////////////////////////// // SUneditableFoliageTypeWarning //////////////////////////////////////////////// class SUneditableFoliageTypeWarning : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SUneditableFoliageTypeWarning) : _WarningText() , _OnHyperlinkClicked() {} /** The rich text to show in the warning */ SLATE_ATTRIBUTE(FText, WarningText) /** Called when the hyperlink in the rich text is clicked */ SLATE_EVENT(FSlateHyperlinkRun::FOnClick, OnHyperlinkClicked) SLATE_END_ARGS() /** Constructs the widget */ void Construct(const FArguments& InArgs) { ChildSlot [ SNew(SBorder) .BorderImage(FAppStyle::Get().GetBrush("ToolPanel.GroupBorder")) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .Padding(2) [ SNew(SImage) .Image(FAppStyle::Get().GetBrush("Icons.Warning")) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .Padding(2) [ SNew(SRichTextBlock) .DecoratorStyleSet(&FAppStyle::Get()) .Justification(ETextJustify::Left) .TextStyle(FAppStyle::Get(), "DetailsView.BPMessageTextStyle") .Text(InArgs._WarningText) .AutoWrapText(true) + SRichTextBlock::HyperlinkDecorator(TEXT("HyperlinkDecorator"), InArgs._OnHyperlinkClicked) ] ] ]; } }; //////////////////////////////////////////////// // SFoliagePalette //////////////////////////////////////////////// void SFoliagePalette::Construct(const FArguments& InArgs) { bItemsNeedRebuild = false; bIsUneditableFoliageTypeSelected = false; bIsRebuildTimerRegistered = false; bIsRefreshTimerRegistered = false; FoliageEditMode = InArgs._FoliageEdMode; FoliageEditMode->OnToolChanged.AddSP(this, &SFoliagePalette::HandleOnToolChanged); FEditorDelegates::OnExternalContentResolved.AddSP(this, &SFoliagePalette::OnExternalContentResolved); FFoliagePaletteCommands::Register(); UICommandList = MakeShareable(new FUICommandList); BindCommands(); // Size of the thumbnail pool should be large enough to show a reasonable amount of foliage assets on screen at once, // otherwise some thumbnail images will appear duplicated. ThumbnailPool = MakeShared(64); TypeFilter = MakeShareable(new FoliageTypeTextFilter( FoliageTypeTextFilter::FItemToStringArray::CreateSP(this, &SFoliagePalette::GetPaletteItemFilterString))); FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); FDetailsViewArgs Args; Args.NameAreaSettings = FDetailsViewArgs::HideNameArea; Args.bAllowSearch = false; Args.bHideSelectionTip = true; DetailsWidget = PropertyModule.CreateDetailView(Args); DetailsWidget->SetVisibility(FoliageEditMode->UISettings.GetShowPaletteItemDetails() ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed); DetailsWidget->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateSP(this, &SFoliagePalette::GetIsPropertyEditingEnabled)); // We want to use our own customization for UFoliageType DetailsWidget->RegisterInstancedCustomPropertyLayout(UFoliageType_InstancedStaticMesh::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFoliageType_InstancedStaticMeshPaintingCustomization::MakeInstance, FoliageEditMode) ); DetailsWidget->RegisterInstancedCustomPropertyLayout(UFoliageType::StaticClass(), FOnGetDetailCustomizationInstance::CreateStatic(&FFoliageTypePaintingCustomization::MakeInstance, FoliageEditMode) ); const FText BlankText = FText::GetEmpty(); ChildSlot [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .HAlign(HAlign_Fill) .Padding(FMargin(2.f, 8.f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) .AutoWidth() [ // +Add Foliage Type button SAssignNew(AddFoliageTypeCombo, SPositiveActionButton) .Icon(FAppStyle::Get().GetBrush("Icons.Plus")) .Text(LOCTEXT("AddFoliageTypeButtonLabel", "Foliage")) .OnGetMenuContent(this, &SFoliagePalette::GetAddFoliageTypePicker) ] + SHorizontalBox::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Center) .Padding(6.f, 0.f) [ SAssignNew(SearchBoxPtr, SSearchBox) .HintText(LOCTEXT("SearchFoliagePaletteHint", "Search Foliage")) .OnTextChanged(this, &SFoliagePalette::OnSearchTextChanged) ] // Show Details + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() [ SNew(SCheckBox) .ToolTipText(this, &SFoliagePalette::GetShowHideDetailsTooltipText) .Style(FAppStyle::Get(), "ToggleButtonCheckBox") .IsChecked_Lambda([this]() -> ECheckBoxState { return FoliageEditMode->UISettings.GetShowPaletteItemDetails() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; }) .OnCheckStateChanged(this, &SFoliagePalette::OnShowHideDetailsClicked) .Content() [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::GetBrush("LevelEditor.Tabs.Details")) ] ] // View Options + SHorizontalBox::Slot() .HAlign(HAlign_Right) .AutoWidth() [ SNew( SComboButton ) .ComboButtonStyle( FAppStyle::Get(), "SimpleComboButton" ) // Use the tool bar item style for this button .OnGetMenuContent(this, &SFoliagePalette::GetViewOptionsMenuContent) .HasDownArrow(false) .ButtonContent() [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image( FAppStyle::Get().GetBrush("Icons.Settings") ) ] ] ] + SVerticalBox::Slot() .AutoHeight() .MaxHeight(230.0f) [ SNew(SOverlay) + SOverlay::Slot() [ SNew(SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .Padding(FMargin(6.f, 3.f)) [ SNew(SBox) .Visibility(this, &SFoliagePalette::GetDropFoliageHintVisibility) .Padding(FMargin(15, 0)) .MinDesiredHeight(30.f) [ SNew(SScaleBox) .Stretch(EStretch::ScaleToFit) [ SNew(STextBlock) .Text(LOCTEXT("Foliage_DropStatic", "+ Drop Foliage Here")) .ToolTipText(LOCTEXT("Foliage_DropStatic_ToolTip", "Drag and drop foliage types or static meshes from the Content Browser to add them to the palette")) ] ] ] + SVerticalBox::Slot() [ CreatePaletteViews() ] + SVerticalBox::Slot() .Padding(FMargin(0.f)) .VAlign(VAlign_Bottom) .AutoHeight() [ SNew(SHorizontalBox) // Selected type name area + SHorizontalBox::Slot() .Padding(FMargin(3.f)) .VAlign(VAlign_Bottom) //.AutoWidth() [ SNew(STextBlock) .Text(this, &SFoliagePalette::GetDetailsNameAreaText) ] ] ] // Foliage Mesh Drop Zone + SOverlay::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SFoliageDragDropHandler) .Visibility(this, &SFoliagePalette::GetFoliageDropTargetVisibility) .OnDrop(this, &SFoliagePalette::HandleFoliageDropped) [ SNew(SScaleBox) .Stretch(EStretch::ScaleToFit) [ SNew(STextBlock) .Text(LOCTEXT("Foliage_AddFoliageMesh", "+ Foliage Type")) .ShadowOffset(FVector2D(1.f, 1.f)) ] ] ] ] // Details + SVerticalBox::Slot() [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(0, 2) .AutoHeight() [ SNew(SUneditableFoliageTypeWarning) .WarningText(LOCTEXT("CannotEditBlueprintFoliageTypeWarning", "Blueprint foliage types must be edited in the Blueprint")) .OnHyperlinkClicked(this, &SFoliagePalette::OnEditFoliageTypeBlueprintHyperlinkClicked) .Visibility(this, &SFoliagePalette::GetUneditableFoliageTypeWarningVisibility) ] + SVerticalBox::Slot() [ DetailsWidget.ToSharedRef() ] ] ]; UpdatePalette(true); } SFoliagePalette::~SFoliagePalette() { } FReply SFoliagePalette::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (UICommandList->ProcessCommandBindings(InKeyEvent)) { return FReply::Handled(); } return SCompoundWidget::OnKeyDown(MyGeometry, InKeyEvent); } void SFoliagePalette::UpdatePalette(bool bRebuildItems) { bItemsNeedRebuild |= bRebuildItems; if (!bIsRebuildTimerRegistered) { bIsRebuildTimerRegistered = true; RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SFoliagePalette::UpdatePaletteItems)); // widget view needs to be refreshed immediately since it's possibly invalid and can't participate in layouting RefreshActivePaletteViewWidget(); } } void SFoliagePalette::RefreshPalette() { // Do not register the refresh timer if we're pending a rebuild; rebuild should cause the palette to refresh if (!bIsRefreshTimerRegistered && !bIsRebuildTimerRegistered) { bIsRefreshTimerRegistered = true; RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SFoliagePalette::RefreshPaletteItems)); } } void SFoliagePalette::UpdateThumbnailForType(UFoliageType* FoliageType) { // Recreate the palette item for the given foliage type for (auto& Item : PaletteItems) { if (Item->GetFoliageType() == FoliageType) { const bool bItemIsSelected = GetActiveViewWidget()->IsItemSelected(Item); Item = MakeShared(Item->GetTypeUIInfo(), SharedThis(this), ThumbnailPool, FoliageEditMode); if (bItemIsSelected) { GetActiveViewWidget()->SetItemSelection(Item, true); } // If a local foliage type changed its mesh, we need to rebuild the palette to ensure a consistent order const bool bRebuild = !Item->IsBlueprint() && !Item->IsAsset(); UpdatePalette(bRebuild); break; } } } bool SFoliagePalette::AnySelectedTileHovered() const { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { TSharedPtr Tile = TileViewWidget->WidgetFromItem(PaletteItem); if (Tile.IsValid() && Tile->AsWidget()->IsHovered()) { return true; } } return false; } void SFoliagePalette::ActivateAllSelectedTypes(bool bActivate) const { // Apply the new check state to all of the selected types for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { PaletteItem->SetTypeActiveInPalette(bActivate); } } void SFoliagePalette::BindCommands() { const FFoliagePaletteCommands& Commands = FFoliagePaletteCommands::Get(); // Context menu commands UICommandList->MapAction( Commands.ActivateFoliageType, FExecuteAction::CreateSP(this, &SFoliagePalette::OnActivateFoliageTypes), FCanExecuteAction(), FIsActionChecked(), FIsActionButtonVisible::CreateSP(this, &SFoliagePalette::OnCanActivateFoliageTypes)); UICommandList->MapAction( Commands.DeactivateFoliageType, FExecuteAction::CreateSP(this, &SFoliagePalette::OnDeactivateFoliageTypes), FCanExecuteAction(), FIsActionChecked(), FIsActionButtonVisible::CreateSP(this, &SFoliagePalette::OnCanDeactivateFoliageTypes)); UICommandList->MapAction( Commands.RemoveFoliageType, FExecuteAction::CreateSP(this, &SFoliagePalette::OnRemoveFoliageType)); UICommandList->MapAction( Commands.ShowFoliageTypeInCB, FExecuteAction::CreateSP(this, &SFoliagePalette::OnShowFoliageTypeInCB)); UICommandList->MapAction( Commands.SelectAllInstances, FExecuteAction::CreateSP(this, &SFoliagePalette::OnSelectAllInstances), FCanExecuteAction::CreateSP(this, &SFoliagePalette::CanSelectInstances)); UICommandList->MapAction( Commands.DeselectAllInstances, FExecuteAction::CreateSP(this, &SFoliagePalette::OnDeselectAllInstances), FCanExecuteAction::CreateSP(this, &SFoliagePalette::CanSelectInstances)); UICommandList->MapAction( Commands.SelectInvalidInstances, FExecuteAction::CreateSP(this, &SFoliagePalette::OnSelectInvalidInstances), FCanExecuteAction::CreateSP(this, &SFoliagePalette::CanSelectInstances)); } void SFoliagePalette::RefreshActivePaletteViewWidget() { if (FoliageEditMode->UISettings.GetActivePaletteViewMode() == EFoliagePaletteViewMode::Thumbnail) { TileViewWidget->RequestListRefresh(); } else { TreeViewWidget->RequestTreeRefresh(); } } UFoliageType* SFoliagePalette::AddFoliageType(const FAssetData& AssetData, bool bPlaceholderAsset) { if (AddFoliageTypeCombo.IsValid()) { AddFoliageTypeCombo->SetIsMenuOpen(false, false); } GWarn->BeginSlowTask(LOCTEXT("AddFoliageType_LoadPackage", "Loading Foliage Type"), true, false); UObject* Asset = AssetData.GetAsset(); GWarn->EndSlowTask(); return FoliageEditMode->AddFoliageAsset(Asset, bPlaceholderAsset); } TSharedRef SFoliagePalette::CreatePaletteViews() { const FText BlankText = FText::GetEmpty(); // Tile View Widget SAssignNew(TileViewWidget, SFoliageTypeTileView) .ListItemsSource(&FilteredItems) .SelectionMode(ESelectionMode::Multi) .OnGenerateTile(this, &SFoliagePalette::GenerateTile) .OnContextMenuOpening(this, &SFoliagePalette::ConstructFoliageTypeContextMenu) .OnSelectionChanged(this, &SFoliagePalette::OnSelectionChanged) .ItemHeight(this, &SFoliagePalette::GetScaledThumbnailSize) .ItemWidth(this, &SFoliagePalette::GetScaledThumbnailSize) .ItemAlignment(EListItemAlignment::LeftAligned) .ClearSelectionOnClick(true) .OnMouseButtonDoubleClick(this, &SFoliagePalette::OnItemDoubleClicked); // Tree View Widget SAssignNew(TreeViewWidget, SFoliageTypeTreeView) .TreeItemsSource(&FilteredItems) .SelectionMode(ESelectionMode::Multi) .OnGenerateRow(this, &SFoliagePalette::TreeViewGenerateRow) .OnGetChildren(this, &SFoliagePalette::TreeViewGetChildren) .OnContextMenuOpening(this, &SFoliagePalette::ConstructFoliageTypeContextMenu) .OnSelectionChanged(this, &SFoliagePalette::OnSelectionChanged) .OnMouseButtonDoubleClick(this, &SFoliagePalette::OnItemDoubleClicked) .HeaderRow ( // Toggle Active SAssignNew(TreeViewHeaderRow, SHeaderRow) + SHeaderRow::Column(FoliagePaletteTreeColumns::ColumnID_ToggleActive) [ SNew(SCheckBox) .IsChecked(this, &SFoliagePalette::GetState_AllMeshes) .OnCheckStateChanged(this, &SFoliagePalette::OnCheckStateChanged_AllMeshes) ] .DefaultLabel(BlankText) .HeaderContentPadding(FMargin(0, 1, 0, 1)) .HAlignHeader(HAlign_Center) .HAlignCell(HAlign_Center) .FixedWidth(24) // Type + SHeaderRow::Column(FoliagePaletteTreeColumns::ColumnID_Type) .HeaderContentPadding(FMargin(10, 1, 0, 1)) .SortMode(this, &SFoliagePalette::GetMeshColumnSortMode) .OnSort(this, &SFoliagePalette::OnMeshesColumnSortModeChanged) .DefaultLabel(this, &SFoliagePalette::GetMeshesHeaderText) .FillWidth(5.f) // Instance Count + SHeaderRow::Column(FoliagePaletteTreeColumns::ColumnID_InstanceCount) .HeaderContentPadding(FMargin(10, 1, 0, 1)) .DefaultLabel(LOCTEXT("InstanceCount", "Count")) .DefaultTooltip(this, &SFoliagePalette::GetTotalInstanceCountTooltipText) .FillWidth(2.f) // Save Asset + SHeaderRow::Column(FoliagePaletteTreeColumns::ColumnID_Save) .FixedWidth(24.0f) .DefaultLabel(BlankText) ); // View Mode Switcher SAssignNew(WidgetSwitcher, SWidgetSwitcher); // Thumbnail View WidgetSwitcher->AddSlot(EFoliagePaletteViewMode::Thumbnail) [ SNew(SScrollBorder, TileViewWidget.ToSharedRef()) .Content() [ TileViewWidget.ToSharedRef() ] ]; // Tree View WidgetSwitcher->AddSlot(EFoliagePaletteViewMode::Tree) [ SNew(SScrollBorder, TreeViewWidget.ToSharedRef()) .Style(&FAppStyle::Get().GetWidgetStyle("FoliageEditMode.TreeView.ScrollBorder")) .Content() [ TreeViewWidget.ToSharedRef() ] ]; WidgetSwitcher->SetActiveWidgetIndex(FoliageEditMode->UISettings.GetActivePaletteViewMode()); return WidgetSwitcher.ToSharedRef(); } void SFoliagePalette::GetPaletteItemFilterString(FFoliagePaletteItemModelPtr PaletteItemModel, TArray& OutArray) const { OutArray.Add(PaletteItemModel->GetDisplayFName().ToString()); } void SFoliagePalette::OnSearchTextChanged(const FText& InFilterText) { TypeFilter->SetRawFilterText(InFilterText); SearchBoxPtr->SetError(TypeFilter->GetFilterErrorText()); UpdatePalette(); } void SFoliagePalette::AddFoliageTypePicker(const FAssetData& AssetData) { AddFoliageType(AssetData); } TSharedRef SFoliagePalette::GetAddFoliageTypePicker() { TArray ClassFilters; FoliageEditMode->GetFoliageTypeFilters(ClassFilters); return PropertyCustomizationHelpers::MakeAssetPickerWithMenu(FAssetData(), false, ClassFilters, PropertyCustomizationHelpers::GetNewAssetFactoriesForClasses(ClassFilters), FOnShouldFilterAsset(), FOnAssetSelected::CreateSP(this, &SFoliagePalette::AddFoliageTypePicker), FSimpleDelegate()); } void SFoliagePalette::HandleOnToolChanged() { RefreshDetailsWidget(); } void SFoliagePalette::SetViewMode(EFoliagePaletteViewMode::Type NewViewMode) { EFoliagePaletteViewMode::Type ActiveViewMode = FoliageEditMode->UISettings.GetActivePaletteViewMode(); if (ActiveViewMode != NewViewMode) { switch (NewViewMode) { case EFoliagePaletteViewMode::Thumbnail: // Set the tile selection to be the current tree selections TileViewWidget->ClearSelection(); for (auto& TypeInfo : TreeViewWidget->GetSelectedItems()) { TileViewWidget->SetItemSelection(TypeInfo, true); } break; case EFoliagePaletteViewMode::Tree: // Set the tree selection to be the current tile selection TreeViewWidget->ClearSelection(); for (auto& TypeInfo : TileViewWidget->GetSelectedItems()) { TreeViewWidget->SetItemSelection(TypeInfo, true); } break; } FoliageEditMode->UISettings.SetActivePaletteViewMode(NewViewMode); WidgetSwitcher->SetActiveWidgetIndex(NewViewMode); RefreshActivePaletteViewWidget(); } } bool SFoliagePalette::IsActiveViewMode(EFoliagePaletteViewMode::Type ViewMode) const { return FoliageEditMode->UISettings.GetActivePaletteViewMode() == ViewMode; } void SFoliagePalette::ToggleShowTooltips() { const bool bCurrentlyShowingTooltips = FoliageEditMode->UISettings.GetShowPaletteItemTooltips(); FoliageEditMode->UISettings.SetShowPaletteItemTooltips(!bCurrentlyShowingTooltips); } bool SFoliagePalette::ShouldShowTooltips() const { return FoliageEditMode->UISettings.GetShowPaletteItemTooltips(); } FText SFoliagePalette::GetSearchText() const { return TypeFilter->GetRawFilterText(); } void SFoliagePalette::OnSelectionChanged(FFoliagePaletteItemModelPtr Item, ESelectInfo::Type SelectInfo) { RefreshDetailsWidget(); bIsUneditableFoliageTypeSelected = false; for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { // Currently entries from blueprint classes cannot be edited in the palette // as changes do not propagate to the BP class and changes to the BP class stomp any changes made to the instance in the palette item if (PaletteItem->IsBlueprint()) { bIsUneditableFoliageTypeSelected = true; break; } } } void SFoliagePalette::OnItemDoubleClicked(FFoliagePaletteItemModelPtr Item) const { Item->SetTypeActiveInPalette(!Item->IsActive()); } TSharedRef SFoliagePalette::GetViewOptionsMenuContent() { const FFoliagePaletteCommands& Commands = FFoliagePaletteCommands::Get(); FMenuBuilder MenuBuilder(true, UICommandList); MenuBuilder.BeginSection("FoliagePaletteViewMode", LOCTEXT("ViewModeHeading", "Palette View Mode")); { MenuBuilder.AddMenuEntry( LOCTEXT("ThumbnailView", "Thumbnails"), LOCTEXT("ThumbnailView_ToolTip", "Display thumbnails for each foliage type in the palette."), FSlateIcon(), FUIAction( FExecuteAction::CreateSP(this, &SFoliagePalette::SetViewMode, EFoliagePaletteViewMode::Thumbnail), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SFoliagePalette::IsActiveViewMode, EFoliagePaletteViewMode::Thumbnail) ), NAME_None, EUserInterfaceActionType::RadioButton ); MenuBuilder.AddMenuEntry( LOCTEXT("ListView", "List"), LOCTEXT("ListView_ToolTip", "Display foliage types in the palette as a list."), FSlateIcon(), FUIAction( FExecuteAction::CreateSP(this, &SFoliagePalette::SetViewMode, EFoliagePaletteViewMode::Tree), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SFoliagePalette::IsActiveViewMode, EFoliagePaletteViewMode::Tree) ), NAME_None, EUserInterfaceActionType::RadioButton ); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("FoliagePaletteViewOptions", LOCTEXT("ViewOptionsHeading", "View Options")); { MenuBuilder.AddMenuEntry( LOCTEXT("ShowTooltips", "Show Tooltips"), LOCTEXT("ShowTooltips_ToolTip", "Whether to show tooltips when hovering over foliage types in the palette."), FSlateIcon(), FUIAction( FExecuteAction::CreateSP(this, &SFoliagePalette::ToggleShowTooltips), FCanExecuteAction(), FIsActionChecked::CreateSP(this, &SFoliagePalette::ShouldShowTooltips) ), NAME_None, EUserInterfaceActionType::ToggleButton ); MenuBuilder.AddWidget( SNew(SSlider) .ToolTipText( LOCTEXT("ThumbnailScaleToolTip", "Adjust the size of thumbnails.") ) .Value( this, &SFoliagePalette::GetThumbnailScale) .OnValueChanged( this, &SFoliagePalette::SetThumbnailScale) .IsEnabled( this, &SFoliagePalette::GetThumbnailScaleSliderEnabled) .OnMouseCaptureEnd(this, &SFoliagePalette::RefreshActivePaletteViewWidget), LOCTEXT("ThumbnailScaleLabel", "Scale"), /*bNoIndent=*/true ); //@todo: Hide inactive types } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } TSharedPtr> SFoliagePalette::GetActiveViewWidget() const { const EFoliagePaletteViewMode::Type ActiveViewMode = FoliageEditMode->UISettings.GetActivePaletteViewMode(); if (ActiveViewMode == EFoliagePaletteViewMode::Thumbnail) { return TileViewWidget; } else if (ActiveViewMode == EFoliagePaletteViewMode::Tree) { return TreeViewWidget; } return nullptr; } EVisibility SFoliagePalette::GetDropFoliageHintVisibility() const { return FoliageEditMode->GetFoliageMeshList().Num() == 0 ? EVisibility::Visible : EVisibility::Collapsed; } EVisibility SFoliagePalette::GetFoliageDropTargetVisibility() const { if (FSlateApplication::Get().IsDragDropping()) { TArray ClassFilters; FoliageEditMode->GetFoliageTypeFilters(ClassFilters); // Support StaticMesh on drag ClassFilters.Add(UStaticMesh::StaticClass()); TArray DraggedAssets = AssetUtil::ExtractAssetDataFromDrag(FSlateApplication::Get().GetDragDroppingContent()); for (const FAssetData& AssetData : DraggedAssets) { if (AssetData.IsValid()) { for (const UClass* Filter : ClassFilters) { if (AssetData.IsInstanceOf(Filter)) { return EVisibility::Visible; } } } } } return EVisibility::Hidden; } FReply SFoliagePalette::HandleFoliageDropped(const FGeometry& DropZoneGeometry, const FDragDropEvent& DragDropEvent) { TArray DroppedAssetData = AssetUtil::ExtractAssetDataFromDrag(DragDropEvent); if (DroppedAssetData.Num() > 0) { const bool bIsExternalContent = DragDropEvent.GetOperation().IsValid() && DragDropEvent.GetOperation()->IsOfType(); // Treat the entire drop as a transaction (in case multiples types are being added) const FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "FoliageMode_DragDropTypesTransaction", "Drag-drop Foliage")); TArray> AddedFoliageTypes; for (auto& AssetData : DroppedAssetData) { if (UFoliageType* FoliageType = AddFoliageType(AssetData, bIsExternalContent)) { AddedFoliageTypes.Add(FoliageType); } } if (AddedFoliageTypes.Num() > 0 && bIsExternalContent) { TSharedPtr DragDropOp = StaticCastSharedPtr(DragDropEvent.GetOperation()); ExternalContentFoliageTypes.Add(DragDropOp->GetGuid(), AddedFoliageTypes); } } return FReply::Handled(); } void SFoliagePalette::OnExternalContentResolved(const FGuid& Identifier, const FAssetData& PlaceHolderAsset, const FAssetData& ResolvedAsset) { if (TArray>* FoliageTypes = ExternalContentFoliageTypes.Find(Identifier)) { TArray ModifiedFoliageTypes; for (TWeakObjectPtr FoliageTypePtr : *FoliageTypes) { if (UFoliageType_InstancedStaticMesh* FoliageType = Cast(FoliageTypePtr.Get())) { FAssetData AssetData(FoliageType->GetStaticMesh()); if (AssetData == PlaceHolderAsset) { if (UStaticMesh* ResolvedMesh = Cast(ResolvedAsset.GetAsset())) { FoliageType->Modify(); FoliageType->SetStaticMesh(ResolvedMesh); ModifiedFoliageTypes.Add(FoliageType); } } } } ExternalContentFoliageTypes.Remove(Identifier); if (ModifiedFoliageTypes.Num() > 0) { for (TObjectIterator It(RF_ClassDefaultObject, /** bIncludeDerivedClasses */ true, /** InternalExcludeFlags */ EInternalObjectFlags::Garbage); It; ++It) { if (It->GetWorld() != nullptr) { for (UFoliageType* ModifiedFoliageType : ModifiedFoliageTypes) { It->NotifyFoliageTypeChanged(ModifiedFoliageType, true); } } } } } } // CONTEXT MENU TSharedPtr SFoliagePalette::ConstructFoliageTypeContextMenu() { const FFoliagePaletteCommands& Commands = FFoliagePaletteCommands::Get(); FMenuBuilder MenuBuilder(true, UICommandList); auto SelectedItems = GetActiveViewWidget()->GetSelectedItems(); if (SelectedItems.Num() > 0) { bool bSelectionContainsActorFoliage = SelectedItems.IndexOfByPredicate([](const FFoliagePaletteItemModelPtr& PaletteItem) { return Cast(PaletteItem->GetFoliageType()) != nullptr; }) != INDEX_NONE; const bool bShowSaveAsOption = SelectedItems.Num() == 1 && !SelectedItems[0]->IsAsset() && !SelectedItems[0]->IsBlueprint(); if (bShowSaveAsOption) { MenuBuilder.BeginSection("StaticMeshFoliageTypeOptions", LOCTEXT("StaticMeshFoliageTypeOptionsHeader", "Static Mesh")); { MenuBuilder.AddMenuEntry( LOCTEXT("SaveAsFoliageType", "Save As Foliage Type..."), LOCTEXT("SaveAsFoliageType_ToolTip", "Creates a Foliage Type asset with these settings that can be reused in other levels."), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Level.SaveIcon16x"), FUIAction( FExecuteAction::CreateSP(this, &SFoliagePalette::OnSaveSelected) ), NAME_None ); } MenuBuilder.EndSection(); } MenuBuilder.BeginSection("FoliageTypeOptions", LOCTEXT("FoliageTypeOptionsHeader", "Foliage Type")); { if (!bShowSaveAsOption) { MenuBuilder.AddMenuEntry( LOCTEXT("SaveSelectedFoliageTypes", "Save"), LOCTEXT("SaveSelectedFoliageTypes_ToolTip", "Saves any changes to the selected foliage type asset(s)."), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Level.SaveIcon16x"), FUIAction( FExecuteAction::CreateSP(this, &SFoliagePalette::OnSaveSelected), FCanExecuteAction::CreateSP(this, &SFoliagePalette::OnCanSaveAnySelectedAssets), FIsActionChecked(), FIsActionButtonVisible::CreateSP(this, &SFoliagePalette::GetIsPropertyEditingEnabled) ), NAME_None ); } MenuBuilder.AddMenuEntry(Commands.ActivateFoliageType); MenuBuilder.AddMenuEntry(Commands.DeactivateFoliageType); MenuBuilder.AddMenuEntry(Commands.RemoveFoliageType); MenuBuilder.AddSubMenu( LOCTEXT("ReplaceFoliageType", "Replace With..."), LOCTEXT("ReplaceFoliageType_ToolTip", "Replaces selected foliage type with another foliage type asset"), FNewMenuDelegate::CreateSP(this, &SFoliagePalette::FillReplaceFoliageTypeSubmenu)); if (bSelectionContainsActorFoliage) { MenuBuilder.AddSubMenu( LOCTEXT("IncludeNonFoliageActors", "Include Non Foliage Actors..."), LOCTEXT("IncludeNonFoliageActors_ToolTip", "Include non foliage type actors with matching blueprint into foliage"), FNewMenuDelegate::CreateLambda([&](FMenuBuilder& SubMenuBuilder) { SubMenuBuilder.AddMenuEntry(LOCTEXT("IncludeNonFoliageActors_CurrentLevel", "Current Level"), FText(), FSlateIcon(), FExecuteAction::CreateSP(this, &SFoliagePalette::OnIncludeNonFoliageActors, true)); SubMenuBuilder.AddMenuEntry(LOCTEXT("IncludeNonFoliageActors_AllLevels", "All Levels"), FText(), FSlateIcon(), FExecuteAction::CreateSP(this, &SFoliagePalette::OnIncludeNonFoliageActors, false)); })); MenuBuilder.AddSubMenu( LOCTEXT("ExcludeFoliageActors", "Exclude Actors..."), LOCTEXT("ExcludeFoliageActors_ToolTip", "Exclude actors with foliage type from foliage"), FNewMenuDelegate::CreateLambda([&](FMenuBuilder& SubMenuBuilder) { SubMenuBuilder.AddMenuEntry(LOCTEXT("ExcludeFoliageActors_CurrentLevel", "Current Level"), FText(), FSlateIcon(), FExecuteAction::CreateSP(this, &SFoliagePalette::OnExcludeFoliageActors, true)); SubMenuBuilder.AddMenuEntry(LOCTEXT("ExcludeFoliageActors_AllLevels", "All Levels"), FText(), FSlateIcon(), FExecuteAction::CreateSP(this, &SFoliagePalette::OnExcludeFoliageActors, false)); })); } MenuBuilder.AddMenuEntry(Commands.ShowFoliageTypeInCB); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("InstanceSelectionOptions", LOCTEXT("InstanceSelectionOptionsHeader", "Selection")); { MenuBuilder.AddMenuEntry(Commands.SelectAllInstances); MenuBuilder.AddMenuEntry(Commands.DeselectAllInstances); MenuBuilder.AddMenuEntry(Commands.SelectInvalidInstances); } MenuBuilder.EndSection(); } return MenuBuilder.MakeWidget(); } void SFoliagePalette::OnIncludeNonFoliageActors(bool bOnlyCurrentLevel) { ExecuteOnSelectedItemFoliageTypes([&, bOnlyCurrentLevel](const TArray& FoliageTypes) { FoliageEditMode->IncludeNonFoliageActors(FoliageTypes, bOnlyCurrentLevel); }); } void SFoliagePalette::OnExcludeFoliageActors(bool bOnlyCurrentLevel) { ExecuteOnSelectedItemFoliageTypes([&, bOnlyCurrentLevel](const TArray& FoliageTypes) { FoliageEditMode->ExcludeFoliageActors(FoliageTypes, bOnlyCurrentLevel); }); } void SFoliagePalette::OnSaveSelected() { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { UFoliageType* FoliageType = PaletteItem->GetFoliageType(); if (!FoliageType->IsAsset() || FoliageType->GetOutermost()->IsDirty()) { UFoliageType* SavedFoliageType = FoliageEditMode->SaveFoliageTypeObject(FoliageType); if (SavedFoliageType) { FoliageType = SavedFoliageType; } } } } bool SFoliagePalette::OnCanSaveAnySelectedAssets() const { // We can save if at least one of the selected items is a dirty asset for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { UFoliageType* FoliageType = PaletteItem->GetFoliageType(); if (FoliageType->IsAsset() && FoliageType->GetOutermost()->IsDirty()) { return true; } } return false; } bool SFoliagePalette::AreAnyNonAssetTypesSelected() const { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { if (!PaletteItem->GetFoliageType()->IsAsset()) { // At least one selected type isn't an asset return true; } } return false; } void SFoliagePalette::OnActivateFoliageTypes() { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { PaletteItem->SetTypeActiveInPalette(true); } } bool SFoliagePalette::OnCanActivateFoliageTypes() const { // At least one selected item must be inactive for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { if (!PaletteItem->IsActive()) { return true; } } return false; } void SFoliagePalette::OnDeactivateFoliageTypes() { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { PaletteItem->SetTypeActiveInPalette(false); } } bool SFoliagePalette::OnCanDeactivateFoliageTypes() const { // At least one selected item must be active for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { if (PaletteItem->IsActive()) { return true; } } return false; } void SFoliagePalette::FillReplaceFoliageTypeSubmenu(FMenuBuilder& MenuBuilder) { FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked(TEXT("ContentBrowser")); FAssetPickerConfig AssetPickerConfig; AssetPickerConfig.Filter.ClassPaths.Add(UFoliageType::StaticClass()->GetClassPathName()); AssetPickerConfig.Filter.bRecursiveClasses = true; AssetPickerConfig.OnAssetSelected = FOnAssetSelected::CreateSP(this, &SFoliagePalette::OnReplaceFoliageTypeSelected); AssetPickerConfig.InitialAssetViewType = EAssetViewType::List; AssetPickerConfig.bAllowNullSelection = false; TSharedRef MenuContent = SNew(SBox) .WidthOverride(384.f) .HeightOverride(500.f) [ ContentBrowserModule.Get().CreateAssetPicker(AssetPickerConfig) ]; MenuBuilder.AddWidget(MenuContent, FText::GetEmpty(), true); } void SFoliagePalette::OnReplaceFoliageTypeSelected(const FAssetData& AssetData) { FSlateApplication::Get().DismissAllMenus(); UFoliageType* NewFoliageType = Cast(AssetData.GetAsset()); if (GetActiveViewWidget()->GetSelectedItems().Num() && NewFoliageType) { for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { UFoliageType* OldFoliageType = PaletteItem->GetFoliageType(); if (OldFoliageType != NewFoliageType) { FoliageEditMode->ReplaceSettingsObject(OldFoliageType, NewFoliageType); } } } } void SFoliagePalette::OnRemoveFoliageType() { int32 NumInstances = 0; TArray FoliageTypeList; for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { NumInstances += PaletteItem->GetTypeUIInfo()->InstanceCountTotal; FoliageTypeList.Add(PaletteItem->GetFoliageType()); } bool bProceed = true; if (NumInstances > 0) { FText Message = FText::Format(NSLOCTEXT("UnrealEd", "FoliageMode_DeleteMesh", "Are you sure you want to remove {0} instances?"), FText::AsNumber(NumInstances)); bProceed = (FMessageDialog::Open(EAppMsgType::YesNo, Message) == EAppReturnType::Yes); } if (bProceed) { FoliageEditMode->RemoveFoliageType(FoliageTypeList.GetData(), FoliageTypeList.Num()); } } void SFoliagePalette::OnShowFoliageTypeInCB() { TArray SelectedAssets; for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { UFoliageType* FoliageType = PaletteItem->GetFoliageType(); if (FoliageType->IsAsset()) { SelectedAssets.Add(FoliageType); } else if (UBlueprint* FoliageTypeBP = Cast(FoliageType->GetClass()->ClassGeneratedBy)) { SelectedAssets.Add(FoliageTypeBP); } else { SelectedAssets.Add(FoliageType->GetSource()); } } if (SelectedAssets.Num()) { GEditor->SyncBrowserToObjects(SelectedAssets); } } void SFoliagePalette::ReflectSelectionInPalette() { TArray SelectedFoliageTypes; FoliageEditMode->GetSelectedInstanceFoliageTypes(SelectedFoliageTypes); SelectFoliageTypesInPalette(SelectedFoliageTypes); } void SFoliagePalette::SelectFoliageTypesInPalette(const TArray& FoliageTypes) { TArray SelectedItems; SelectedItems.Reserve(FoliageTypes.Num()); for (FFoliagePaletteItemModelPtr& PaletteItem : FilteredItems) { if (FoliageTypes.Contains(PaletteItem->GetFoliageType())) { SelectedItems.Add(PaletteItem); } } GetActiveViewWidget()->ClearSelection(); GetActiveViewWidget()->SetItemSelection(SelectedItems, true); } void SFoliagePalette::ExecuteOnSelectedItemFoliageTypes(TFunctionRef&)> ExecuteFunc) { TArray SelectedItems = GetActiveViewWidget()->GetSelectedItems(); TArray FoliageTypes; FoliageTypes.Reserve(SelectedItems.Num()); for (FFoliagePaletteItemModelPtr& PaletteItem : SelectedItems) { FoliageTypes.Add(PaletteItem->GetFoliageType()); } ExecuteFunc(FoliageTypes); } void SFoliagePalette::OnSelectAllInstances() { ExecuteOnSelectedItemFoliageTypes([&](const TArray& FoliageTypes) { FoliageEditMode->SelectInstances(FoliageTypes, true); }); } void SFoliagePalette::OnDeselectAllInstances() { ExecuteOnSelectedItemFoliageTypes([&](const TArray& FoliageTypes) { FoliageEditMode->SelectInstances(FoliageTypes, false); }); } void SFoliagePalette::OnSelectInvalidInstances() { ExecuteOnSelectedItemFoliageTypes([&](const TArray& FoliageTypes) { FoliageEditMode->SelectInvalidInstances(FoliageTypes); }); } bool SFoliagePalette::CanSelectInstances() const { return FoliageEditMode->UISettings.GetSelectToolSelected() || FoliageEditMode->UISettings.GetLassoSelectToolSelected(); } // THUMBNAIL VIEW TSharedRef SFoliagePalette::GenerateTile(FFoliagePaletteItemModelPtr Item, const TSharedRef& OwnerTable) { return SNew(SFoliagePaletteItemTile, OwnerTable, Item); // Refresh the palette to ensure that thumbnails are correct RefreshPalette(); } float SFoliagePalette::GetScaledThumbnailSize() const { const FInt32Interval& SizeRange = FoliagePaletteConstants::ThumbnailSizeRange; return SizeRange.Min + SizeRange.Size() * FoliageEditMode->UISettings.GetPaletteThumbnailScale(); } float SFoliagePalette::GetThumbnailScale() const { return FoliageEditMode->UISettings.GetPaletteThumbnailScale(); } void SFoliagePalette::SetThumbnailScale(float InScale) { FoliageEditMode->UISettings.SetPaletteThumbnailScale(InScale); } bool SFoliagePalette::GetThumbnailScaleSliderEnabled() const { return FoliageEditMode->UISettings.GetActivePaletteViewMode() == EFoliagePaletteViewMode::Thumbnail; } // TREE VIEW TSharedRef SFoliagePalette::TreeViewGenerateRow(FFoliagePaletteItemModelPtr Item, const TSharedRef& OwnerTable) { return SNew(SFoliagePaletteItemRow, OwnerTable, Item); } void SFoliagePalette::TreeViewGetChildren(FFoliagePaletteItemModelPtr Item, TArray& OutChildren) { //OutChildren = Item->GetChildren(); } ECheckBoxState SFoliagePalette::GetState_AllMeshes() const { bool bHasChecked = false; bool bHasUnchecked = false; for (const FFoliagePaletteItemModelPtr& PaletteItem : FilteredItems) { if (PaletteItem->IsActive()) { bHasChecked = true; } else { bHasUnchecked = true; } if (bHasChecked && bHasUnchecked) { return ECheckBoxState::Undetermined; } } return bHasChecked ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } void SFoliagePalette::OnCheckStateChanged_AllMeshes(ECheckBoxState InState) { const bool bActivate = InState == ECheckBoxState::Checked; for (FFoliagePaletteItemModelPtr& PaletteItem : FilteredItems) { PaletteItem->SetTypeActiveInPalette(bActivate); } } FText SFoliagePalette::GetMeshesHeaderText() const { int32 NumMeshes = FoliageEditMode->GetFoliageMeshList().Num(); return FText::Format(LOCTEXT("FoliageMeshCount", "Meshes ({0})"), FText::AsNumber(NumMeshes)); } EColumnSortMode::Type SFoliagePalette::GetMeshColumnSortMode() const { return FoliageEditMode->GetFoliageMeshListSortMode(); } void SFoliagePalette::OnMeshesColumnSortModeChanged(EColumnSortPriority::Type InPriority, const FName& InColumnName, EColumnSortMode::Type InSortMode) { FoliageEditMode->OnFoliageMeshListSortModeChanged(InSortMode); } FText SFoliagePalette::GetTotalInstanceCountTooltipText() const { // Probably should cache these values, // but we call this only occasionally when tooltip is active int32 InstanceCountTotal = 0; int32 InstanceCountCurrentLevel = 0; FoliageEditMode->CalcTotalInstanceCount(InstanceCountTotal, InstanceCountCurrentLevel); return FText::Format(LOCTEXT("FoliageTotalInstanceCount", "Current Level: {0} Total: {1}"), FText::AsNumber(InstanceCountCurrentLevel), FText::AsNumber(InstanceCountTotal)); } // DETAILS VIEW void SFoliagePalette::RefreshDetailsWidget() { TArray SelectedFoliageTypes; for (const auto& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { SelectedFoliageTypes.Add(PaletteItem->GetFoliageType()); } const bool bForceRefresh = true; DetailsWidget->SetObjects(SelectedFoliageTypes, bForceRefresh); } bool SFoliagePalette::GetIsPropertyEditingEnabled() const { return !bIsUneditableFoliageTypeSelected; } FText SFoliagePalette::GetDetailsNameAreaText() const { FText OutText; auto SelectedItems = GetActiveViewWidget()->GetSelectedItems(); if (SelectedItems.Num() == 1) { OutText = FText::FromName(SelectedItems[0]->GetDisplayFName()); } else if (SelectedItems.Num() > 1) { OutText = FText::Format(LOCTEXT("DetailsNameAreaText_Multiple", "{0} Types Selected"), FText::AsNumber(SelectedItems.Num())); } return OutText; } FText SFoliagePalette::GetShowHideDetailsTooltipText() const { const bool bDetailsCurrentlyVisible = DetailsWidget->GetVisibility() != EVisibility::Collapsed; return bDetailsCurrentlyVisible ? LOCTEXT("HideDetails_Tooltip", "Hide details for the selected foliage types.") : LOCTEXT("ShowDetails_Tooltip", "Show details for the selected foliage types."); } const FSlateBrush* SFoliagePalette::GetShowHideDetailsImage() const { const bool bDetailsCurrentlyVisible = DetailsWidget->GetVisibility() != EVisibility::Collapsed; return FAppStyle::Get().GetBrush(bDetailsCurrentlyVisible ? "Symbols.DoubleDownArrow" : "Symbols.DoubleUpArrow"); } void SFoliagePalette::OnShowHideDetailsClicked(const ECheckBoxState InCheckedState) const { const bool bShouldShowDetails = InCheckedState == ECheckBoxState::Checked; DetailsWidget->SetVisibility(bShouldShowDetails ? EVisibility::SelfHitTestInvisible : EVisibility::Collapsed ); FoliageEditMode->UISettings.SetShowPaletteItemDetails(bShouldShowDetails); } EVisibility SFoliagePalette::GetUneditableFoliageTypeWarningVisibility() const { return bIsUneditableFoliageTypeSelected ? EVisibility::Visible : EVisibility::Collapsed; } void SFoliagePalette::OnEditFoliageTypeBlueprintHyperlinkClicked(const FSlateHyperlinkRun::FMetadata& Metadata) { UBlueprint* Blueprint = nullptr; // Get the first selected foliage type blueprint for (FFoliagePaletteItemModelPtr& PaletteItem : GetActiveViewWidget()->GetSelectedItems()) { Blueprint = Cast(PaletteItem->GetFoliageType()->GetClass()->ClassGeneratedBy); if (Blueprint != nullptr) { break; } } if (Blueprint) { // Open the blueprint GEditor->EditObject(Blueprint); } } EActiveTimerReturnType SFoliagePalette::UpdatePaletteItems(double InCurrentTime, float InDeltaTime) { if (bItemsNeedRebuild) { bItemsNeedRebuild = false; // Cache the currently selected items auto ActiveViewWidget = GetActiveViewWidget(); TArray PreviouslySelectedItems = ActiveViewWidget->GetSelectedItems(); ActiveViewWidget->ClearSelection(); // Rebuild the list of palette items const auto& AllTypesList = FoliageEditMode->GetFoliageMeshList(); PaletteItems.Empty(AllTypesList.Num()); for (const FFoliageMeshUIInfoPtr& TypeInfo : AllTypesList) { PaletteItems.Add(MakeShareable(new FFoliagePaletteItemModel(TypeInfo, SharedThis(this), ThumbnailPool, FoliageEditMode))); } // Restore the selection for (auto& PrevSelectedItem : PreviouslySelectedItems) { // Select any replacements for previously selected foliage types for (auto& Item : PaletteItems) { if (Item->GetDisplayFName() == PrevSelectedItem->GetDisplayFName()) { ActiveViewWidget->SetItemSelection(Item, true); break; } } } } // Update the filtered items FilteredItems.Empty(); for (auto& Item : PaletteItems) { if (TypeFilter->PassesFilter(Item)) { FilteredItems.Add(Item); } } // Refresh the appropriate view RefreshActivePaletteViewWidget(); bIsRebuildTimerRegistered = false; return EActiveTimerReturnType::Stop; } EActiveTimerReturnType SFoliagePalette::RefreshPaletteItems(double InCurrentTime, float InDeltaTime) { // Do not refresh the palette if we're waiting on a rebuild if (!bItemsNeedRebuild) { RefreshActivePaletteViewWidget(); } bIsRefreshTimerRegistered = false; return EActiveTimerReturnType::Stop; } #undef LOCTEXT_NAMESPACE