BaseLevel selection popup with thumbnail grid
- Add Thumbnail field to FPS_Editor_BaseLevelEntry (UTexture2D) - Replace BaseLevel combo box with button that opens a selection popup - Popup displays base levels in a 3-column grid with thumbnails (2:1 ratio) - Validate/Cancel buttons to confirm or dismiss selection - Gray placeholder shown for entries without thumbnail Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -54,7 +54,7 @@ struct FPS_Editor_BaseLevelEntry
|
|||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
/** Display name shown in the editor combo box and used for scene filtering. */
|
/** Display name shown in the editor UI and used for scene filtering. */
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||||
FString DisplayName;
|
FString DisplayName;
|
||||||
|
|
||||||
@@ -64,6 +64,10 @@ struct FPS_Editor_BaseLevelEntry
|
|||||||
*/
|
*/
|
||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||||
FString MapPath;
|
FString MapPath;
|
||||||
|
|
||||||
|
/** Thumbnail image shown in the BaseLevel selection popup. */
|
||||||
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "PS Editor")
|
||||||
|
TObjectPtr<UTexture2D> Thumbnail;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -266,6 +266,69 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::RebuildWidget()
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
// BaseLevel selection popup
|
||||||
|
+ SOverlay::Slot()
|
||||||
|
.HAlign(HAlign_Center)
|
||||||
|
.VAlign(VAlign_Center)
|
||||||
|
[
|
||||||
|
SAssignNew(BaseLevelPopup, SBorder)
|
||||||
|
.Visibility(EVisibility::Collapsed)
|
||||||
|
.BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush"))
|
||||||
|
.BorderBackgroundColor(FLinearColor(0.08f, 0.08f, 0.08f, 0.97f))
|
||||||
|
.Padding(FMargin(24.0f, 16.0f))
|
||||||
|
[
|
||||||
|
SNew(SVerticalBox)
|
||||||
|
+ SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 0.0f, 0.0f, 12.0f)
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(TEXT("Select Base Level")))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 14))
|
||||||
|
.ColorAndOpacity(FLinearColor::White)
|
||||||
|
]
|
||||||
|
+ SVerticalBox::Slot().FillHeight(1.0f).MaxHeight(400.0f)
|
||||||
|
[
|
||||||
|
SNew(SScrollBox)
|
||||||
|
.ConsumeMouseWheel(EConsumeMouseWheel::Always)
|
||||||
|
+ SScrollBox::Slot()
|
||||||
|
[
|
||||||
|
SAssignNew(BaseLevelGridContainer, SVerticalBox)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
+ SVerticalBox::Slot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 12.0f, 0.0f, 0.0f)
|
||||||
|
[
|
||||||
|
SNew(SHorizontalBox)
|
||||||
|
+ SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f)
|
||||||
|
[
|
||||||
|
SNew(SButton)
|
||||||
|
.OnClicked_Lambda([this]() -> FReply
|
||||||
|
{
|
||||||
|
ConfirmBaseLevelSelection();
|
||||||
|
return FReply::Handled();
|
||||||
|
})
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(TEXT("Validate")))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 11))
|
||||||
|
.ColorAndOpacity(FLinearColor(0.2f, 0.9f, 0.2f))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
+ SHorizontalBox::Slot().AutoWidth().Padding(8.0f, 0.0f)
|
||||||
|
[
|
||||||
|
SNew(SButton)
|
||||||
|
.OnClicked_Lambda([this]() -> FReply
|
||||||
|
{
|
||||||
|
CancelBaseLevelPopup();
|
||||||
|
return FReply::Handled();
|
||||||
|
})
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(TEXT("Cancel")))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 11))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
// Close confirmation overlay
|
// Close confirmation overlay
|
||||||
+ SOverlay::Slot()
|
+ SOverlay::Slot()
|
||||||
.HAlign(HAlign_Center)
|
.HAlign(HAlign_Center)
|
||||||
@@ -868,8 +931,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
{
|
{
|
||||||
if (*Opt == SceneLevelName)
|
if (*Opt == SceneLevelName)
|
||||||
{
|
{
|
||||||
if (BaseLevelCombo.IsValid()) BaseLevelCombo->SetSelectedItem(Opt);
|
if (BaseLevelButtonText.IsValid()) BaseLevelButtonText->SetText(FText::FromString(SceneLevelName));
|
||||||
if (BaseLevelComboText.IsValid()) BaseLevelComboText->SetText(FText::FromString(SceneLevelName));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -896,7 +958,7 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
// BaseLevel combo box
|
// BaseLevel selection button
|
||||||
+ SVerticalBox::Slot()
|
+ SVerticalBox::Slot()
|
||||||
.AutoHeight()
|
.AutoHeight()
|
||||||
.Padding(0.0f, 4.0f, 0.0f, 2.0f)
|
.Padding(0.0f, 4.0f, 0.0f, 2.0f)
|
||||||
@@ -911,54 +973,14 @@ TSharedRef<SWidget> UPS_Editor_MainWidget::BuildSceneButtons()
|
|||||||
]
|
]
|
||||||
+ SHorizontalBox::Slot().FillWidth(1.0f).VAlign(VAlign_Center)
|
+ SHorizontalBox::Slot().FillWidth(1.0f).VAlign(VAlign_Center)
|
||||||
[
|
[
|
||||||
SAssignNew(BaseLevelCombo, SComboBox<TSharedPtr<FString>>)
|
SNew(SButton)
|
||||||
.OptionsSource(&BaseLevelOptions)
|
.OnClicked_Lambda([this]() -> FReply
|
||||||
.Method(EPopupMethod::UseCurrentWindow)
|
|
||||||
.OnSelectionChanged_Lambda([this](TSharedPtr<FString> Selected, ESelectInfo::Type SelectInfo)
|
|
||||||
{
|
{
|
||||||
if (!Selected.IsValid()) return;
|
OpenBaseLevelPopup();
|
||||||
// Ignore programmatic selection (e.g. from NativeConstruct init)
|
return FReply::Handled();
|
||||||
if (SelectInfo == ESelectInfo::Direct) return;
|
|
||||||
|
|
||||||
APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer());
|
|
||||||
if (!EditorPC) return;
|
|
||||||
|
|
||||||
// Clear current scenario before switching BaseLevel
|
|
||||||
if (UPS_Editor_SceneSerializer* Ser = EditorPC->GetSceneSerializer())
|
|
||||||
{
|
|
||||||
if (UPS_Editor_SpawnManager* SM = EditorPC->GetSpawnManager())
|
|
||||||
{
|
|
||||||
Ser->ClearScene(SM);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (SaveNameField.IsValid())
|
|
||||||
{
|
|
||||||
SaveNameField->SetText(FText::GetEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
FString SublevelPath = *Selected;
|
|
||||||
if (FString* Found = BaseLevelPathMap.Find(*Selected))
|
|
||||||
{
|
|
||||||
SublevelPath = *Found;
|
|
||||||
}
|
|
||||||
EditorPC->LoadBaseLevelAsSublevel(*Selected, SublevelPath);
|
|
||||||
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
|
||||||
|
|
||||||
if (BaseLevelComboText.IsValid())
|
|
||||||
{
|
|
||||||
BaseLevelComboText->SetText(FText::FromString(*Selected));
|
|
||||||
}
|
|
||||||
PopulateSceneList();
|
|
||||||
LastOutlinerActorCount = -1; // Force outliner refresh
|
|
||||||
})
|
|
||||||
.OnGenerateWidget_Lambda([](TSharedPtr<FString> Item) -> TSharedRef<SWidget>
|
|
||||||
{
|
|
||||||
return SNew(STextBlock)
|
|
||||||
.Text(FText::FromString(Item.IsValid() ? *Item : TEXT("")))
|
|
||||||
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 9));
|
|
||||||
})
|
})
|
||||||
[
|
[
|
||||||
SAssignNew(BaseLevelComboText, STextBlock)
|
SAssignNew(BaseLevelButtonText, STextBlock)
|
||||||
.Text(FText::FromString(TEXT("Select...")))
|
.Text(FText::FromString(TEXT("Select...")))
|
||||||
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 9))
|
.Font(FCoreStyle::GetDefaultFontStyle("Bold", 9))
|
||||||
.ColorAndOpacity(FLinearColor(0.4f, 0.8f, 0.4f))
|
.ColorAndOpacity(FLinearColor(0.4f, 0.8f, 0.4f))
|
||||||
@@ -1022,7 +1044,7 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
|||||||
Super::NativeConstruct();
|
Super::NativeConstruct();
|
||||||
PopulateSpawnCatalog();
|
PopulateSpawnCatalog();
|
||||||
|
|
||||||
// Populate BaseLevel combo from MasterCatalog
|
// Populate BaseLevel options from MasterCatalog
|
||||||
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
if (APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer()))
|
||||||
{
|
{
|
||||||
BaseLevelOptions.Add(MakeShared<FString>(TEXT("None")));
|
BaseLevelOptions.Add(MakeShared<FString>(TEXT("None")));
|
||||||
@@ -1035,22 +1057,18 @@ void UPS_Editor_MainWidget::NativeConstruct()
|
|||||||
{
|
{
|
||||||
BaseLevelPathMap.Add(Entry.DisplayName, Entry.MapPath);
|
BaseLevelPathMap.Add(Entry.DisplayName, Entry.MapPath);
|
||||||
}
|
}
|
||||||
|
if (Entry.Thumbnail)
|
||||||
|
{
|
||||||
|
BaseLevelThumbnails.Add(Entry.DisplayName, Entry.Thumbnail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a BaseLevel was set (e.g. from subsystem), select it in the combo
|
// If a BaseLevel was set (e.g. from subsystem), update the button text
|
||||||
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
||||||
if (!CurrentLevelFilter.IsEmpty())
|
if (!CurrentLevelFilter.IsEmpty() && BaseLevelButtonText.IsValid())
|
||||||
{
|
{
|
||||||
for (const TSharedPtr<FString>& Opt : BaseLevelOptions)
|
BaseLevelButtonText->SetText(FText::FromString(CurrentLevelFilter));
|
||||||
{
|
|
||||||
if (*Opt == CurrentLevelFilter)
|
|
||||||
{
|
|
||||||
if (BaseLevelCombo.IsValid()) BaseLevelCombo->SetSelectedItem(Opt);
|
|
||||||
if (BaseLevelComboText.IsValid()) BaseLevelComboText->SetText(FText::FromString(CurrentLevelFilter));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore scenario name in save field (from subsystem LastEdited)
|
// Restore scenario name in save field (from subsystem LastEdited)
|
||||||
@@ -1134,14 +1152,9 @@ void UPS_Editor_MainWidget::PopulateSceneList()
|
|||||||
EditorPC->LoadBaseLevelAsSublevel(SceneLevelName, SceneMapPath);
|
EditorPC->LoadBaseLevelAsSublevel(SceneLevelName, SceneMapPath);
|
||||||
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
||||||
|
|
||||||
for (const TSharedPtr<FString>& Opt : BaseLevelOptions)
|
if (BaseLevelButtonText.IsValid())
|
||||||
{
|
{
|
||||||
if (*Opt == SceneLevelName)
|
BaseLevelButtonText->SetText(FText::FromString(SceneLevelName));
|
||||||
{
|
|
||||||
if (BaseLevelCombo.IsValid()) BaseLevelCombo->SetSelectedItem(Opt);
|
|
||||||
if (BaseLevelComboText.IsValid()) BaseLevelComboText->SetText(FText::FromString(SceneLevelName));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2305,6 +2318,177 @@ void UPS_Editor_MainWidget::ShowLoadingScreen(bool bShow)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UPS_Editor_MainWidget::OpenBaseLevelPopup()
|
||||||
|
{
|
||||||
|
if (!BaseLevelGridContainer.IsValid() || !BaseLevelPopup.IsValid()) return;
|
||||||
|
|
||||||
|
BaseLevelGridContainer->ClearChildren();
|
||||||
|
PendingBaseLevelSelection.Empty();
|
||||||
|
|
||||||
|
const float CardWidth = 160.0f;
|
||||||
|
const float CardHeight = 80.0f; // 2:1 ratio
|
||||||
|
const int32 Columns = 3;
|
||||||
|
TSharedPtr<SHorizontalBox> CurrentRow;
|
||||||
|
int32 ColIndex = 0;
|
||||||
|
|
||||||
|
for (const TSharedPtr<FString>& Option : BaseLevelOptions)
|
||||||
|
{
|
||||||
|
if (!Option.IsValid()) continue;
|
||||||
|
const FString Name = *Option;
|
||||||
|
|
||||||
|
// Start new row if needed
|
||||||
|
if (ColIndex % Columns == 0)
|
||||||
|
{
|
||||||
|
CurrentRow = SNew(SHorizontalBox);
|
||||||
|
BaseLevelGridContainer->AddSlot().AutoHeight().Padding(0.0f, 4.0f)
|
||||||
|
[
|
||||||
|
CurrentRow.ToSharedRef()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SVerticalBox> Card = SNew(SVerticalBox);
|
||||||
|
|
||||||
|
// Thumbnail
|
||||||
|
TObjectPtr<UTexture2D>* FoundThumb = BaseLevelThumbnails.Find(Name);
|
||||||
|
if (FoundThumb && *FoundThumb)
|
||||||
|
{
|
||||||
|
TSharedPtr<FSlateBrush> Brush = MakeShared<FSlateBrush>();
|
||||||
|
Brush->SetResourceObject(*FoundThumb);
|
||||||
|
Brush->ImageSize = FVector2D(CardWidth, CardHeight);
|
||||||
|
Brush->DrawAs = ESlateBrushDrawType::Image;
|
||||||
|
Brush->Tiling = ESlateBrushTileType::NoTile;
|
||||||
|
CachedBrushes.Add(Brush);
|
||||||
|
|
||||||
|
TWeakPtr<FSlateBrush> WeakBrush = Brush;
|
||||||
|
Card->AddSlot().AutoHeight().HAlign(HAlign_Center)
|
||||||
|
[
|
||||||
|
SNew(SImage)
|
||||||
|
.Image_Lambda([WeakBrush]() -> const FSlateBrush*
|
||||||
|
{
|
||||||
|
return WeakBrush.IsValid() ? WeakBrush.Pin().Get() : nullptr;
|
||||||
|
})
|
||||||
|
.DesiredSizeOverride(FVector2D(CardWidth, CardHeight))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Card->AddSlot().AutoHeight().HAlign(HAlign_Center)
|
||||||
|
[
|
||||||
|
SNew(SBorder)
|
||||||
|
.BorderImage(FCoreStyle::Get().GetBrush("WhiteBrush"))
|
||||||
|
.BorderBackgroundColor(FLinearColor(0.15f, 0.15f, 0.15f))
|
||||||
|
[
|
||||||
|
SNew(SBox)
|
||||||
|
.WidthOverride(CardWidth)
|
||||||
|
.HeightOverride(CardHeight)
|
||||||
|
.HAlign(HAlign_Center)
|
||||||
|
.VAlign(VAlign_Center)
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(Name))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Italic", 9))
|
||||||
|
.ColorAndOpacity(FLinearColor(0.5f, 0.5f, 0.5f))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name label
|
||||||
|
Card->AddSlot().AutoHeight().HAlign(HAlign_Center).Padding(0.0f, 4.0f, 0.0f, 0.0f)
|
||||||
|
[
|
||||||
|
SNew(STextBlock)
|
||||||
|
.Text(FText::FromString(Name))
|
||||||
|
.Font(FCoreStyle::GetDefaultFontStyle("Regular", 9))
|
||||||
|
.ColorAndOpacity(FLinearColor::White)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Add card to current row
|
||||||
|
CurrentRow->AddSlot().AutoWidth().Padding(8.0f, 0.0f)
|
||||||
|
[
|
||||||
|
SNew(SBox)
|
||||||
|
.MinDesiredWidth(CardWidth + 16.0f)
|
||||||
|
[
|
||||||
|
SNew(SButton)
|
||||||
|
.OnClicked_Lambda([this, Name]() -> FReply
|
||||||
|
{
|
||||||
|
PendingBaseLevelSelection = Name;
|
||||||
|
return FReply::Handled();
|
||||||
|
})
|
||||||
|
[
|
||||||
|
Card
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
ColIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseLevelPopup->SetVisibility(EVisibility::Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPS_Editor_MainWidget::ConfirmBaseLevelSelection()
|
||||||
|
{
|
||||||
|
if (BaseLevelPopup.IsValid())
|
||||||
|
{
|
||||||
|
BaseLevelPopup->SetVisibility(EVisibility::Collapsed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PendingBaseLevelSelection.IsEmpty()) return;
|
||||||
|
|
||||||
|
APS_Editor_PlayerController* EditorPC = Cast<APS_Editor_PlayerController>(GetOwningPlayer());
|
||||||
|
if (!EditorPC) return;
|
||||||
|
|
||||||
|
// Clear current scenario before switching BaseLevel
|
||||||
|
if (UPS_Editor_SceneSerializer* Ser = EditorPC->GetSceneSerializer())
|
||||||
|
{
|
||||||
|
if (UPS_Editor_SpawnManager* SM = EditorPC->GetSpawnManager())
|
||||||
|
{
|
||||||
|
Ser->ClearScene(SM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SaveNameField.IsValid())
|
||||||
|
{
|
||||||
|
SaveNameField->SetText(FText::GetEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the selected BaseLevel
|
||||||
|
FString SublevelPath = PendingBaseLevelSelection;
|
||||||
|
if (FString* Found = BaseLevelPathMap.Find(PendingBaseLevelSelection))
|
||||||
|
{
|
||||||
|
SublevelPath = *Found;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PendingBaseLevelSelection == TEXT("None"))
|
||||||
|
{
|
||||||
|
EditorPC->LoadBaseLevelAsSublevel(TEXT("None"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EditorPC->LoadBaseLevelAsSublevel(PendingBaseLevelSelection, SublevelPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentLevelFilter = EditorPC->CurrentBaseLevel;
|
||||||
|
|
||||||
|
if (BaseLevelButtonText.IsValid())
|
||||||
|
{
|
||||||
|
BaseLevelButtonText->SetText(FText::FromString(
|
||||||
|
PendingBaseLevelSelection == TEXT("None") ? TEXT("Select...") : PendingBaseLevelSelection));
|
||||||
|
}
|
||||||
|
|
||||||
|
PopulateSceneList();
|
||||||
|
LastOutlinerActorCount = -1;
|
||||||
|
PendingBaseLevelSelection.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UPS_Editor_MainWidget::CancelBaseLevelPopup()
|
||||||
|
{
|
||||||
|
if (BaseLevelPopup.IsValid())
|
||||||
|
{
|
||||||
|
BaseLevelPopup->SetVisibility(EVisibility::Collapsed);
|
||||||
|
}
|
||||||
|
PendingBaseLevelSelection.Empty();
|
||||||
|
}
|
||||||
|
|
||||||
void UPS_Editor_MainWidget::OnCloseClicked()
|
void UPS_Editor_MainWidget::OnCloseClicked()
|
||||||
{
|
{
|
||||||
if (CloseConfirmOverlay.IsValid())
|
if (CloseConfirmOverlay.IsValid())
|
||||||
|
|||||||
@@ -77,14 +77,23 @@ private:
|
|||||||
// Scene save/load
|
// Scene save/load
|
||||||
TSharedPtr<SEditableTextBox> SaveNameField;
|
TSharedPtr<SEditableTextBox> SaveNameField;
|
||||||
TSharedPtr<SVerticalBox> SceneListContainer;
|
TSharedPtr<SVerticalBox> SceneListContainer;
|
||||||
TSharedPtr<SComboBox<TSharedPtr<FString>>> BaseLevelCombo;
|
|
||||||
TArray<TSharedPtr<FString>> BaseLevelOptions;
|
TArray<TSharedPtr<FString>> BaseLevelOptions;
|
||||||
TSharedPtr<STextBlock> BaseLevelComboText;
|
TSharedPtr<STextBlock> BaseLevelButtonText;
|
||||||
FString CurrentLevelFilter;
|
FString CurrentLevelFilter;
|
||||||
/** Maps DisplayName → full MapPath for base levels (from MasterCatalog). */
|
/** Maps DisplayName → full MapPath for base levels (from MasterCatalog). */
|
||||||
TMap<FString, FString> BaseLevelPathMap;
|
TMap<FString, FString> BaseLevelPathMap;
|
||||||
|
/** Maps DisplayName → Thumbnail texture for base levels. */
|
||||||
|
TMap<FString, TObjectPtr<UTexture2D>> BaseLevelThumbnails;
|
||||||
TArray<TSharedPtr<FSlateBrush>> CachedBrushes;
|
TArray<TSharedPtr<FSlateBrush>> CachedBrushes;
|
||||||
|
|
||||||
|
// BaseLevel selection popup
|
||||||
|
TSharedPtr<SWidget> BaseLevelPopup;
|
||||||
|
TSharedPtr<SVerticalBox> BaseLevelGridContainer;
|
||||||
|
FString PendingBaseLevelSelection;
|
||||||
|
void OpenBaseLevelPopup();
|
||||||
|
void ConfirmBaseLevelSelection();
|
||||||
|
void CancelBaseLevelPopup();
|
||||||
|
|
||||||
// Transform fields
|
// Transform fields
|
||||||
TSharedPtr<SEditableTextBox> LocX, LocY, LocZ;
|
TSharedPtr<SEditableTextBox> LocX, LocY, LocZ;
|
||||||
TSharedPtr<SEditableTextBox> RotX, RotY, RotZ;
|
TSharedPtr<SEditableTextBox> RotX, RotY, RotZ;
|
||||||
|
|||||||
Reference in New Issue
Block a user