Adds Custom DataAsset UI + Added Request buttons for RaceDatas/TeamList/Rankings request to ApiStatus Tab + Addes Tracked Requests For Rankings + Added Utils Module For Blueprint Utilities Functions
This commit is contained in:
@ -2,28 +2,37 @@
|
||||
|
||||
public class DTFluxAssetsEditor : ModuleRules
|
||||
{
|
||||
public DTFluxAssetsEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
public DTFluxAssetsEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
}
|
||||
);
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"AssetTools",
|
||||
"SlateCore",
|
||||
"UnrealEd",
|
||||
"DTFluxCore",
|
||||
}
|
||||
);
|
||||
}
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"SlateCore",
|
||||
"Slate",
|
||||
"AssetTools",
|
||||
"UnrealEd",
|
||||
"DTFluxCore",
|
||||
"ToolMenus",
|
||||
"EditorWidgets",
|
||||
"EditorStyle",
|
||||
"PropertyEditor",
|
||||
"SharedSettingsWidgets",
|
||||
"PropertyEditor",
|
||||
"DesktopWidgets",
|
||||
"ApplicationCore",
|
||||
"InputCore"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -3,28 +3,60 @@
|
||||
#include "DTFluxAssetModelTypeActions.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "DTFluxModelAssetDetailCustomization.h"
|
||||
#include "Assets/DTFluxModelAsset.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FDTFluxAssetsEditorModule"
|
||||
|
||||
|
||||
DTFLUXASSETSEDITOR_API DEFINE_LOG_CATEGORY(logDTFluxAssetEditor)
|
||||
|
||||
void FDTFluxAssetsEditorModule::StartupModule()
|
||||
{
|
||||
IAssetTools& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
EAssetTypeCategories::Type Category = AssetToolsModule.RegisterAdvancedAssetCategory("DTFlux", INVTEXT("DTFlux"));
|
||||
DTFluxAssetModelActions = MakeShareable(new FDTFluxAssetModelTypeActions(Category));
|
||||
DTFluxAssetModelActions = MakeShareable(new FDTFluxAssetModelTypeActions(Category));
|
||||
AssetToolsModule.RegisterAssetTypeActions(DTFluxAssetModelActions.ToSharedRef());
|
||||
RegisterCustomizations();
|
||||
}
|
||||
|
||||
void FDTFluxAssetsEditorModule::ShutdownModule()
|
||||
{
|
||||
if(DTFluxAssetModelActions.IsValid() && FModuleManager::Get().IsModuleLoaded("AssetTools"))
|
||||
if (DTFluxAssetModelActions.IsValid() && FModuleManager::Get().IsModuleLoaded("AssetTools"))
|
||||
{
|
||||
IAssetTools& AssetToolsModule = FModuleManager::GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
AssetToolsModule.UnregisterAssetTypeActions(DTFluxAssetModelActions.ToSharedRef());
|
||||
}
|
||||
UnregisterCustomizations();
|
||||
}
|
||||
|
||||
|
||||
void FDTFluxAssetsEditorModule::RegisterCustomizations()
|
||||
{
|
||||
// Enregistrer la customization pour DTFluxModelAsset
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
|
||||
PropertyModule.RegisterCustomClassLayout(
|
||||
UDTFluxModelAsset::StaticClass()->GetFName(),
|
||||
FOnGetDetailCustomizationInstance::CreateStatic(&FDTFluxModelAssetCustomization::MakeInstance)
|
||||
);
|
||||
|
||||
PropertyModule.NotifyCustomizationModuleChanged();
|
||||
}
|
||||
|
||||
void FDTFluxAssetsEditorModule::UnregisterCustomizations()
|
||||
{
|
||||
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
|
||||
{
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>(
|
||||
"PropertyEditor");
|
||||
|
||||
PropertyModule.UnregisterCustomClassLayout(UDTFluxModelAsset::StaticClass()->GetFName());
|
||||
|
||||
PropertyModule.NotifyCustomizationModuleChanged();
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FDTFluxAssetsEditorModule, DTFluxAssetsEditor)
|
||||
|
||||
IMPLEMENT_MODULE(FDTFluxAssetsEditorModule, DTFluxAssetsEditor)
|
||||
|
||||
@ -0,0 +1,217 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "DTFluxModelAssetDetailCustomization.h"
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Assets/DTFluxModelAsset.h"
|
||||
|
||||
|
||||
TSharedRef<IDetailCustomization> FDTFluxModelAssetCustomization::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FDTFluxModelAssetCustomization);
|
||||
}
|
||||
|
||||
EActiveTimerReturnType SDTFluxAssetModelDetailsWidget::ForceInitialLayout(double InCurrentTime, float InDeltaTime)
|
||||
{
|
||||
// Forcer la mise à jour des TreeViews
|
||||
if (ContestTreeView.IsValid())
|
||||
{
|
||||
ContestTreeView->RequestTreeRefresh();
|
||||
}
|
||||
|
||||
if (ParticipantTreeView.IsValid())
|
||||
{
|
||||
ParticipantTreeView->RequestTreeRefresh();
|
||||
}
|
||||
|
||||
// Forcer l'invalidation du layout
|
||||
Invalidate(EInvalidateWidget::Layout);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Forced initial layout refresh"));
|
||||
|
||||
// Arrêter le timer (exécution unique)
|
||||
return EActiveTimerReturnType::Stop;
|
||||
}
|
||||
|
||||
void FDTFluxModelAssetCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
// Edit object
|
||||
TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
|
||||
DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
|
||||
|
||||
if (ObjectsBeingCustomized.Num() > 0)
|
||||
{
|
||||
ModelAsset = Cast<UDTFluxModelAsset>(ObjectsBeingCustomized[0].Get());
|
||||
}
|
||||
|
||||
if (!ModelAsset.IsValid())
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("No valid DTFluxModelAsset found"));
|
||||
return;
|
||||
}
|
||||
|
||||
// ===== Hiding Categories/Props =====
|
||||
DetailBuilder.HideCategory("General");
|
||||
DetailBuilder.HideCategory("Default");
|
||||
DetailBuilder.HideCategory("Transform");
|
||||
DetailBuilder.HideCategory("Rendering");
|
||||
DetailBuilder.HideCategory("Input");
|
||||
DetailBuilder.HideCategory("Actor");
|
||||
DetailBuilder.HideCategory("Advanced");
|
||||
// Hide individual Props
|
||||
DetailBuilder.HideProperty("EventName");
|
||||
DetailBuilder.HideProperty("Persons");
|
||||
DetailBuilder.HideProperty("Participants");
|
||||
DetailBuilder.HideProperty("Contests");
|
||||
DetailBuilder.HideProperty("ContestRankings");
|
||||
DetailBuilder.HideProperty("StageRankings");
|
||||
DetailBuilder.HideProperty("SplitRankings");
|
||||
|
||||
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
||||
"DTFlux Model Explorer",
|
||||
FText::FromString("DTFlux Model Explorer"),
|
||||
ECategoryPriority::Important
|
||||
);
|
||||
|
||||
// Créer le widget hiérarchique
|
||||
DetailsWidget = SNew(SDTFluxAssetModelDetailsWidget)
|
||||
.ModelAsset(ModelAsset.Get());
|
||||
|
||||
MainCategory.AddCustomRow(FText::FromString("Data Explorer"))
|
||||
.WholeRowContent()
|
||||
[
|
||||
SNew(SBox)
|
||||
.MinDesiredHeight(800.0f)
|
||||
[
|
||||
DetailsWidget.ToSharedRef()
|
||||
]
|
||||
];
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("DTFluxModelAsset custom-only interface applied"));
|
||||
}
|
||||
|
||||
|
||||
void FDTFluxModelAssetCustomization::CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
// ===== WIDGET PRINCIPAL =====
|
||||
IDetailCategoryBuilder& MainCategory = DetailBuilder.EditCategory(
|
||||
"DTFlux Model Explorer",
|
||||
FText::FromString("DTFlux Model Explorer"),
|
||||
ECategoryPriority::Important
|
||||
);
|
||||
|
||||
DetailsWidget = SNew(SDTFluxAssetModelDetailsWidget)
|
||||
.ModelAsset(ModelAsset.Get());
|
||||
|
||||
MainCategory.AddCustomRow(FText::FromString("Data Explorer"))
|
||||
.WholeRowContent()
|
||||
[
|
||||
SNew(SBox)
|
||||
.MinDesiredHeight(650.0f)
|
||||
|
||||
[
|
||||
DetailsWidget.ToSharedRef()
|
||||
]
|
||||
];
|
||||
IDetailCategoryBuilder& QuickActionsCategory = DetailBuilder.EditCategory(
|
||||
"Quick Actions",
|
||||
FText::FromString("Quick Actions"),
|
||||
ECategoryPriority::Default
|
||||
);
|
||||
|
||||
QuickActionsCategory.AddCustomRow(FText::FromString("Raw Data Access"))
|
||||
.NameContent()
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("Raw Data Access"))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
]
|
||||
.ValueContent()
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0, 0, 5, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "Button")
|
||||
.Text(FText::FromString("Edit Raw Properties"))
|
||||
.ToolTipText(FText::FromString("Temporarily show standard properties for advanced editing"))
|
||||
.OnClicked_Lambda([this, &DetailBuilder]() -> FReply
|
||||
{
|
||||
// Forcer le rafraîchissement du DetailsPanel pour montrer les propriétés standard
|
||||
DetailBuilder.ForceRefreshDetails();
|
||||
UE_LOG(LogTemp, Warning,
|
||||
TEXT(
|
||||
"Tip: To edit raw data, right-click the asset and choose 'Edit' or use the Content Browser"
|
||||
));
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0, 0, 5, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "Button")
|
||||
.Text(FText::FromString("Log All Data"))
|
||||
.ToolTipText(FText::FromString("Print all data to Output Log"))
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
if (ModelAsset.IsValid())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("=== DTFLUX MODEL DUMP ==="));
|
||||
UE_LOG(LogTemp, Warning, TEXT("Event: %s"), *ModelAsset->EventName);
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("--- CONTESTS (%d) ---"), ModelAsset->Contests.Num());
|
||||
for (const auto& Contest : ModelAsset->Contests)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Contest '%s' (ID: %d) - %d stages, %d participants"),
|
||||
*Contest.Key, Contest.Value.ContestId,
|
||||
Contest.Value.Stages.Num(), Contest.Value.ParticipantsBib.Num());
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("--- PARTICIPANTS (%d) ---"), ModelAsset->Participants.Num());
|
||||
for (const auto& Participant : ModelAsset->Participants)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("Bib %d: %s (%s) - %d teammates"),
|
||||
Participant.Value.Bib, *Participant.Value.Team,
|
||||
*Participant.Value.Category, Participant.Value.GetTeammate().Num());
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("--- PERSONS (%d) ---"), ModelAsset->Persons.Num());
|
||||
for (int32 i = 0; i < ModelAsset->Persons.Num(); ++i)
|
||||
{
|
||||
const auto& Person = ModelAsset->Persons[i];
|
||||
UE_LOG(LogTemp, Warning, TEXT("Person %d: %s %s (%s)"),
|
||||
i, *Person.FirstName, *Person.LastName, *Person.Gender);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("======================="));
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "PrimaryButton")
|
||||
.Text(FText::FromString("Refresh"))
|
||||
.ToolTipText(FText::FromString("Refresh the hierarchy view"))
|
||||
.OnClicked_Lambda([this]() -> FReply
|
||||
{
|
||||
if (DetailsWidget.IsValid())
|
||||
{
|
||||
DetailsWidget->RefreshData();
|
||||
}
|
||||
return FReply::Handled();
|
||||
})
|
||||
]
|
||||
];
|
||||
}
|
||||
@ -0,0 +1,645 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
#include "Widget/DTFluxAssetModelDetailsWidget.h"
|
||||
|
||||
#include "Widgets/Layout/SBorder.h"
|
||||
#include "Widgets/Layout/SBox.h"
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
#include "Widgets/Views/STableRow.h"
|
||||
#include "Widgets/Views/SHeaderRow.h"
|
||||
|
||||
void SHierarchicalTreeItemRow::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView)
|
||||
{
|
||||
Item = InArgs._Item;
|
||||
ParentWidget = InArgs._ParentWidget;
|
||||
|
||||
SMultiColumnTableRow<TSharedPtr<FHierarchicalTreeItem>>::Construct(
|
||||
SMultiColumnTableRow<TSharedPtr<FHierarchicalTreeItem>>::FArguments(),
|
||||
InOwnerTableView
|
||||
);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> SHierarchicalTreeItemRow::GenerateWidgetForColumn(const FName& ColumnName)
|
||||
{
|
||||
if (!Item.IsValid())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid item for column %s"), *ColumnName.ToString());
|
||||
return SNew(STextBlock).Text(FText::FromString("Invalid Item"));
|
||||
}
|
||||
|
||||
if (!ParentWidget)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Invalid ParentWidget for column %s"),
|
||||
*ColumnName.ToString());
|
||||
return SNew(STextBlock).Text(FText::FromString("Invalid Parent"));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, VeryVerbose, TEXT("GenerateWidgetForColumn: %s for item %s"),
|
||||
*ColumnName.ToString(), *Item->Name);
|
||||
|
||||
if (ColumnName == "Name")
|
||||
{
|
||||
return SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.VAlign(VAlign_Center)
|
||||
.Padding(0, 0, 5, 0)
|
||||
[
|
||||
SNew(SImage)
|
||||
.Image(ParentWidget->GetItemIcon(Item->Type))
|
||||
.ColorAndOpacity(ParentWidget->GetItemTypeColor(Item->Type))
|
||||
]
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
.VAlign(VAlign_Center)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString(Item->Name))
|
||||
.ColorAndOpacity(ParentWidget->GetItemTypeColor(Item->Type))
|
||||
.Font_Lambda([this]() -> FSlateFontInfo
|
||||
{
|
||||
return Item->Type == FHierarchicalTreeItem::EItemType::Contest
|
||||
? FAppStyle::GetFontStyle("HeadingText")
|
||||
: FAppStyle::GetFontStyle("NormalText");
|
||||
})
|
||||
];
|
||||
}
|
||||
else if (ColumnName == "ID")
|
||||
{
|
||||
return SNew(STextBlock)
|
||||
.Text(FText::FromString(Item->ID))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
.Justification(ETextJustify::Center);
|
||||
}
|
||||
else if (ColumnName == "Details")
|
||||
{
|
||||
return SNew(STextBlock)
|
||||
.Text(FText::FromString(Item->Details))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
||||
}
|
||||
else if (ColumnName == "Status")
|
||||
{
|
||||
return SNew(STextBlock)
|
||||
.Text(FText::FromString(Item->Status))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
||||
}
|
||||
else if (ColumnName == "Extra")
|
||||
{
|
||||
return SNew(STextBlock)
|
||||
.Text(FText::FromString(Item->Extra))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
.OverflowPolicy(ETextOverflowPolicy::Ellipsis);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Warning, TEXT("GenerateWidgetForColumn: Unknown column %s"), *ColumnName.ToString());
|
||||
return SNew(STextBlock).Text(FText::FromString(FString::Printf(TEXT("Unknown: %s"), *ColumnName.ToString())));
|
||||
}
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::Construct(const FArguments& InArgs)
|
||||
{
|
||||
ModelAsset = InArgs._ModelAsset;
|
||||
|
||||
ChildSlot
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
|
||||
// === SECTION STATISTIQUES ===
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
.Padding(10)
|
||||
[
|
||||
SAssignNew(StatsText, STextBlock)
|
||||
.Text(this, &SDTFluxAssetModelDetailsWidget::GetStatsText)
|
||||
.Font(FAppStyle::GetFontStyle("HeadingText"))
|
||||
.Justification(ETextJustify::Center)
|
||||
]
|
||||
]
|
||||
|
||||
// === SECTION BOUTONS DE NAVIGATION ===
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
[
|
||||
SNew(SHorizontalBox)
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0, 0, 5, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
||||
.Text(FText::FromString("Expand All Contests"))
|
||||
.OnClicked(this, &SDTFluxAssetModelDetailsWidget::OnExpandAllClicked)
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
.Padding(0, 0, 10, 0)
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
|
||||
.Text(FText::FromString("Collapse All Contests"))
|
||||
.OnClicked(this, &SDTFluxAssetModelDetailsWidget::OnCollapseAllClicked)
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.FillWidth(1.0f)
|
||||
[
|
||||
SNew(SSpacer)
|
||||
]
|
||||
|
||||
+ SHorizontalBox::Slot()
|
||||
.AutoWidth()
|
||||
[
|
||||
SNew(SButton)
|
||||
.ButtonStyle(FAppStyle::Get(), "PrimaryButton")
|
||||
.Text(FText::FromString("Refresh"))
|
||||
.OnClicked(this, &SDTFluxAssetModelDetailsWidget::OnRefreshClicked)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
#pragma region ListView
|
||||
+ SVerticalBox::Slot()
|
||||
.FillHeight(1.0f)
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder"))
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
.Padding(0)
|
||||
[
|
||||
SNew(SBox)
|
||||
[
|
||||
#pragma region ScrollBox
|
||||
SNew(SScrollBox)
|
||||
.Orientation(Orient_Vertical)
|
||||
.ScrollBarVisibility(EVisibility::Visible)
|
||||
.ConsumeMouseWheel(EConsumeMouseWheel::WhenScrollingPossible)
|
||||
.ScrollBarAlwaysVisible(true) // Force la scrollbar à être toujours visible
|
||||
|
||||
#pragma region ListView.Contest
|
||||
+ SScrollBox::Slot()
|
||||
.Padding(0, 0, 0, 10)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
.Padding(10, 5)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("CONTESTS HIERARCHY"))
|
||||
.Font(FAppStyle::GetFontStyle("HeadingText"))
|
||||
.ColorAndOpacity(FLinearColor(0.2f, 0.6f, 1.0f))
|
||||
]
|
||||
]
|
||||
|
||||
// TreeView Contests
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SBox)
|
||||
[
|
||||
SAssignNew(ContestTreeView, STreeView<FHierarchicalTreeItemPtr>)
|
||||
.TreeItemsSource(&RootItems)
|
||||
.OnGenerateRow(this, &SDTFluxAssetModelDetailsWidget::OnGenerateRowForTree)
|
||||
.OnGetChildren(this, &SDTFluxAssetModelDetailsWidget::OnGetChildrenForTree)
|
||||
.OnSelectionChanged(
|
||||
this, &SDTFluxAssetModelDetailsWidget::OnTreeSelectionChanged)
|
||||
.OnSetExpansionRecursive(
|
||||
this, &SDTFluxAssetModelDetailsWidget::OnSetExpansionRecursive)
|
||||
.SelectionMode(ESelectionMode::Single)
|
||||
.ConsumeMouseWheel(EConsumeMouseWheel::WhenScrollingPossible)
|
||||
.HeaderRow
|
||||
(
|
||||
SNew(SHeaderRow)
|
||||
.CanSelectGeneratedColumn(true)
|
||||
|
||||
+ SHeaderRow::Column("Name")
|
||||
.DefaultLabel(FText::FromString("Contest / Stage / Split"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
|
||||
+ SHeaderRow::Column("ID")
|
||||
.DefaultLabel(FText::FromString("ID"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
.FillWidth(.02f)
|
||||
|
||||
|
||||
+ SHeaderRow::Column("Details")
|
||||
.DefaultLabel(FText::FromString("Details"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
.FillWidth(.3f)
|
||||
|
||||
+ SHeaderRow::Column("Status")
|
||||
.DefaultLabel(FText::FromString("Status / Time"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
.FillWidth(.1f)
|
||||
|
||||
|
||||
+ SHeaderRow::Column("Extra")
|
||||
.DefaultLabel(FText::FromString("Extra Info"))
|
||||
.FillWidth(.2f)
|
||||
|
||||
.SortMode(EColumnSortMode::None)
|
||||
)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ListView.Participant
|
||||
+ SScrollBox::Slot()
|
||||
.Padding(0, 10, 0, 0)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
[
|
||||
SNew(SVerticalBox)
|
||||
|
||||
// Header "Participants"
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
.Padding(10, 5)
|
||||
[
|
||||
SNew(STextBlock)
|
||||
.Text(FText::FromString("PARTICIPANTS LIST"))
|
||||
.Font(FAppStyle::GetFontStyle("HeadingText"))
|
||||
.ColorAndOpacity(FLinearColor(0.2f, 0.8f, 0.8f))
|
||||
]
|
||||
]
|
||||
|
||||
// TreeView Participants
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
[
|
||||
SNew(SBox)
|
||||
[
|
||||
SAssignNew(ParticipantTreeView, STreeView<FHierarchicalTreeItemPtr>)
|
||||
.TreeItemsSource(&ParticipantItems)
|
||||
.OnGenerateRow(this, &SDTFluxAssetModelDetailsWidget::OnGenerateRowForTree)
|
||||
.OnGetChildren(this, &SDTFluxAssetModelDetailsWidget::OnGetChildrenForTree)
|
||||
.OnSelectionChanged(
|
||||
this, &SDTFluxAssetModelDetailsWidget::OnTreeSelectionChanged)
|
||||
.OnSetExpansionRecursive(
|
||||
this, &SDTFluxAssetModelDetailsWidget::OnSetExpansionRecursive)
|
||||
.SelectionMode(ESelectionMode::Single)
|
||||
.ConsumeMouseWheel(EConsumeMouseWheel::WhenScrollingPossible)
|
||||
.HeaderRow
|
||||
(
|
||||
SNew(SHeaderRow)
|
||||
.CanSelectGeneratedColumn(true)
|
||||
|
||||
+ SHeaderRow::Column("Name")
|
||||
.DefaultLabel(FText::FromString("Participant"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
|
||||
+ SHeaderRow::Column("ID")
|
||||
.DefaultLabel(FText::FromString("Bib"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
.FillWidth(.08f)
|
||||
|
||||
+ SHeaderRow::Column("Details")
|
||||
.DefaultLabel(FText::FromString("Category & Teammates"))
|
||||
.SortMode(EColumnSortMode::None)
|
||||
.FillWidth(.2f)
|
||||
|
||||
+ SHeaderRow::Column("Status")
|
||||
.DefaultLabel(FText::FromString("Status"))
|
||||
.FillWidth(.1f)
|
||||
.SortMode(EColumnSortMode::None)
|
||||
|
||||
+ SHeaderRow::Column("Extra")
|
||||
.DefaultLabel(FText::FromString("Club"))
|
||||
.FillWidth(.2f)
|
||||
.SortMode(EColumnSortMode::None)
|
||||
)
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
#pragma endregion
|
||||
]
|
||||
#pragma endregion
|
||||
]
|
||||
#pragma endregion
|
||||
]
|
||||
]
|
||||
|
||||
#pragma region DetailView.Participant
|
||||
+ SVerticalBox::Slot()
|
||||
.AutoHeight()
|
||||
.Padding(5)
|
||||
[
|
||||
SNew(SBorder)
|
||||
.BorderImage(FAppStyle::GetBrush("DetailsView.CategoryTop"))
|
||||
.Padding(10)
|
||||
[
|
||||
SAssignNew(SelectionText, STextBlock)
|
||||
.Text(FText::FromString("Select an item to see details. Use expand/collapse arrows in the tree."))
|
||||
.Font(FAppStyle::GetFontStyle("NormalText"))
|
||||
.AutoWrapText(true)
|
||||
]
|
||||
]
|
||||
#pragma endregion
|
||||
]
|
||||
|
||||
];
|
||||
|
||||
RefreshData();
|
||||
|
||||
RegisterActiveTimer(
|
||||
0.1f, FWidgetActiveTimerDelegate::CreateSP(this, &SDTFluxAssetModelDetailsWidget::ForceInitialLayout));
|
||||
}
|
||||
|
||||
// ===== CONSTRUCTION DE LA HIÉRARCHIE =====
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::BuildContestHierarchy()
|
||||
{
|
||||
RootItems.Empty();
|
||||
|
||||
if (!ModelAsset)
|
||||
return;
|
||||
|
||||
// Construire la hiérarchie Contest → Stages → Splits
|
||||
for (const auto& ContestPair : ModelAsset->Contests)
|
||||
{
|
||||
const FString& ContestName = ContestPair.Key;
|
||||
const FDTFluxContest& Contest = ContestPair.Value;
|
||||
|
||||
// Créer l'élément Contest racine
|
||||
auto ContestItem = FHierarchicalTreeItem::CreateContest(ContestName, Contest);
|
||||
|
||||
// Ajouter les Stages comme enfants
|
||||
for (const FDTFluxStage& Stage : Contest.Stages)
|
||||
{
|
||||
auto StageItem = FHierarchicalTreeItem::CreateStage(Stage, Contest.ContestId);
|
||||
ContestItem->AddChild(StageItem);
|
||||
}
|
||||
|
||||
// Ajouter les Splits comme enfants directs du Contest
|
||||
for (const FDTFluxSplit& Split : Contest.Splits)
|
||||
{
|
||||
auto SplitItem = FHierarchicalTreeItem::CreateSplit(Split, Contest.ContestId);
|
||||
ContestItem->AddChild(SplitItem);
|
||||
}
|
||||
|
||||
RootItems.Add(ContestItem);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Built contest hierarchy with %d root contests"), RootItems.Num());
|
||||
}
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::BuildParticipantList()
|
||||
{
|
||||
ParticipantItems.Empty();
|
||||
|
||||
if (!ModelAsset)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("BuildParticipantList: ModelAsset is null!"));
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: ModelAsset has %d participants"), ModelAsset->Participants.Num());
|
||||
|
||||
// Créer la liste des participants (pas de hiérarchie pour les participants)
|
||||
for (const auto& ParticipantPair : ModelAsset->Participants)
|
||||
{
|
||||
const FDTFluxParticipant& Participant = ParticipantPair.Value;
|
||||
auto ParticipantItem = FHierarchicalTreeItem::CreateParticipant(Participant);
|
||||
ParticipantItems.Add(ParticipantItem);
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Added participant %s (Bib: %d)"),
|
||||
*ParticipantItem->Name, ParticipantItem->Bib);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("BuildParticipantList: Built participant list with %d participants"),
|
||||
ParticipantItems.Num());
|
||||
}
|
||||
|
||||
// ===== CALLBACKS TREEVIEW =====
|
||||
|
||||
TSharedRef<ITableRow> SDTFluxAssetModelDetailsWidget::OnGenerateRowForTree(
|
||||
FHierarchicalTreeItemPtr Item, const TSharedRef<STableViewBase>& OwnerTable)
|
||||
{
|
||||
if (!Item.IsValid())
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("OnGenerateRowForTree: Invalid item!"));
|
||||
return SNew(STableRow<FHierarchicalTreeItemPtr>, OwnerTable);
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("OnGenerateRowForTree: Generating row for %s (Type: %d)"),
|
||||
*Item->Name, (int32)Item->Type);
|
||||
|
||||
return SNew(SHierarchicalTreeItemRow, OwnerTable)
|
||||
.Item(Item)
|
||||
.ParentWidget(this);
|
||||
}
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::OnGetChildrenForTree(FHierarchicalTreeItemPtr Item,
|
||||
TArray<FHierarchicalTreeItemPtr>& OutChildren)
|
||||
{
|
||||
if (Item.IsValid())
|
||||
{
|
||||
OutChildren = Item->Children;
|
||||
}
|
||||
}
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::OnTreeSelectionChanged(FHierarchicalTreeItemPtr SelectedItem,
|
||||
ESelectInfo::Type SelectInfo)
|
||||
{
|
||||
if (SelectionText.IsValid())
|
||||
{
|
||||
if (SelectedItem.IsValid())
|
||||
{
|
||||
FString TypeString;
|
||||
switch (SelectedItem->Type)
|
||||
{
|
||||
case FHierarchicalTreeItem::EItemType::Contest:
|
||||
TypeString = "Contest";
|
||||
break;
|
||||
case FHierarchicalTreeItem::EItemType::Stage:
|
||||
TypeString = "Stage";
|
||||
break;
|
||||
case FHierarchicalTreeItem::EItemType::Split:
|
||||
TypeString = "Split";
|
||||
break;
|
||||
case FHierarchicalTreeItem::EItemType::Participant:
|
||||
TypeString = "Participant";
|
||||
break;
|
||||
}
|
||||
FString SelectionInfo = FString::Printf(
|
||||
TEXT("📋 Selected: %s (%s)\n🔢 ID: %s\n📄 Details: %s\n📊 Status: %s\n➕ Extra: %s\n🌟 Children: %d"),
|
||||
*SelectedItem->Name,
|
||||
*TypeString,
|
||||
*SelectedItem->ID,
|
||||
*SelectedItem->Details,
|
||||
*SelectedItem->Status,
|
||||
*SelectedItem->Extra,
|
||||
SelectedItem->Children.Num());
|
||||
SelectionText->SetText(FText::FromString(SelectionInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectionText->SetText(FText::FromString("Select an item to see details"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::OnSetExpansionRecursive(FHierarchicalTreeItemPtr Item, bool bIsExpanded)
|
||||
{
|
||||
if (Item.IsValid() && ContestTreeView.IsValid())
|
||||
{
|
||||
ContestTreeView->SetItemExpansion(Item, bIsExpanded);
|
||||
|
||||
// Expansion récursive des enfants
|
||||
for (auto Child : Item->Children)
|
||||
{
|
||||
OnSetExpansionRecursive(Child, bIsExpanded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CALLBACKS DES BOUTONS =====
|
||||
|
||||
FReply SDTFluxAssetModelDetailsWidget::OnExpandAllClicked()
|
||||
{
|
||||
if (ContestTreeView.IsValid())
|
||||
{
|
||||
for (auto& RootItem : RootItems)
|
||||
{
|
||||
OnSetExpansionRecursive(RootItem, true);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Expanded all contests"));
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SDTFluxAssetModelDetailsWidget::OnCollapseAllClicked()
|
||||
{
|
||||
if (ContestTreeView.IsValid())
|
||||
{
|
||||
for (auto& RootItem : RootItems)
|
||||
{
|
||||
OnSetExpansionRecursive(RootItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("Collapsed all contests"));
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply SDTFluxAssetModelDetailsWidget::OnRefreshClicked()
|
||||
{
|
||||
RefreshData();
|
||||
UE_LOG(LogTemp, Log, TEXT("Data refreshed"));
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
// ===== REFRESHDATA =====
|
||||
|
||||
void SDTFluxAssetModelDetailsWidget::RefreshData()
|
||||
{
|
||||
if (!ModelAsset)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("ModelAsset is null!"));
|
||||
return;
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("RefreshData: Starting refresh for ModelAsset %s"), *ModelAsset->GetName());
|
||||
|
||||
// Nettoyer les données existantes
|
||||
RootItems.Empty();
|
||||
ParticipantItems.Empty();
|
||||
|
||||
// Construire la hiérarchie
|
||||
BuildContestHierarchy();
|
||||
BuildParticipantList();
|
||||
|
||||
// Refresh les vues
|
||||
if (ContestTreeView.IsValid())
|
||||
{
|
||||
ContestTreeView->RequestTreeRefresh();
|
||||
}
|
||||
|
||||
if (ParticipantTreeView.IsValid())
|
||||
{
|
||||
ParticipantTreeView->RequestTreeRefresh();
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Log, TEXT("RefreshData: Completed successfully - %d contests, %d participants"), RootItems.Num(),
|
||||
ParticipantItems.Num());
|
||||
}
|
||||
|
||||
// ===== MÉTHODES UTILITAIRES =====
|
||||
|
||||
FSlateColor SDTFluxAssetModelDetailsWidget::GetItemTypeColor(FHierarchicalTreeItem::EItemType Type) const
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case FHierarchicalTreeItem::EItemType::Contest:
|
||||
return FSlateColor(FLinearColor(0.2f, 0.6f, 1.0f));
|
||||
case FHierarchicalTreeItem::EItemType::Stage:
|
||||
return FSlateColor(FLinearColor(0.2f, 0.8f, 0.2f));
|
||||
case FHierarchicalTreeItem::EItemType::Split:
|
||||
return FSlateColor(FLinearColor(1.0f, 0.8f, 0.2f));
|
||||
case FHierarchicalTreeItem::EItemType::Participant:
|
||||
return FSlateColor(FLinearColor(0.2f, 0.8f, 0.8f));
|
||||
default:
|
||||
return FSlateColor(FLinearColor::White);
|
||||
}
|
||||
}
|
||||
|
||||
const FSlateBrush* SDTFluxAssetModelDetailsWidget::GetItemIcon(FHierarchicalTreeItem::EItemType Type) const
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case FHierarchicalTreeItem::EItemType::Contest:
|
||||
return FAppStyle::GetBrush("TreeArrow_Collapsed");
|
||||
case FHierarchicalTreeItem::EItemType::Stage:
|
||||
case FHierarchicalTreeItem::EItemType::Split:
|
||||
return FAppStyle::GetBrush("TreeArrow_Expanded");
|
||||
case FHierarchicalTreeItem::EItemType::Participant:
|
||||
return FAppStyle::GetBrush("Icons.User");
|
||||
default:
|
||||
return FAppStyle::GetBrush("Icons.Help");
|
||||
}
|
||||
}
|
||||
|
||||
FText SDTFluxAssetModelDetailsWidget::GetStatsText() const
|
||||
{
|
||||
if (!ModelAsset)
|
||||
return FText::FromString("No data");
|
||||
|
||||
return FText::FromString(FString::Printf(
|
||||
TEXT("Contests: [%d] Participants: [%d] Persons: [%d]"),
|
||||
ModelAsset->Contests.Num(),
|
||||
ModelAsset->Participants.Num(),
|
||||
ModelAsset->Persons.Num()
|
||||
));
|
||||
}
|
||||
@ -21,9 +21,11 @@ DTFLUXASSETSEDITOR_API DECLARE_LOG_CATEGORY_EXTERN(logDTFluxAssetEditor, Log, Al
|
||||
class FDTFluxAssetsEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
TSharedPtr<FDTFluxAssetModelTypeActions> DTFluxAssetModelActions;
|
||||
void RegisterCustomizations();
|
||||
void UnregisterCustomizations();
|
||||
};
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "IDetailCustomization.h"
|
||||
#include "Widget/DTFluxAssetModelDetailsWidget.h"
|
||||
|
||||
class FDTFluxModelAssetCustomization : public IDetailCustomization
|
||||
{
|
||||
public:
|
||||
// IDetailCustomization interface
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||
|
||||
void CustomizeDetailsWithRawDataAccess(IDetailLayoutBuilder& DetailBuilder);
|
||||
|
||||
// Crée une instance de cette customization
|
||||
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||
|
||||
private:
|
||||
// Handle vers l'objet en cours d'édition
|
||||
TWeakObjectPtr<UDTFluxModelAsset> ModelAsset;
|
||||
|
||||
|
||||
// Widget personnalisé
|
||||
TSharedPtr<SDTFluxAssetModelDetailsWidget> DetailsWidget;
|
||||
};
|
||||
@ -0,0 +1,191 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Types/Struct/DTFluxRaceDataStructs.h"
|
||||
#include "Widgets/Views/STreeView.h"
|
||||
#include "Widgets/Views/SHeaderRow.h"
|
||||
#include "Assets/DTFluxModelAsset.h"
|
||||
|
||||
|
||||
struct FHierarchicalTreeItem;
|
||||
class SDTFluxAssetModelDetailsWidget;
|
||||
|
||||
// ✅ SOUS-CLASSE DE SMultiColumnTableRow
|
||||
class SHierarchicalTreeItemRow : public SMultiColumnTableRow<TSharedPtr<FHierarchicalTreeItem>>
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SHierarchicalTreeItemRow)
|
||||
{
|
||||
}
|
||||
|
||||
SLATE_ARGUMENT(TSharedPtr<FHierarchicalTreeItem>, Item)
|
||||
SLATE_ARGUMENT(SDTFluxAssetModelDetailsWidget*, ParentWidget)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView);
|
||||
|
||||
protected:
|
||||
virtual TSharedRef<SWidget> GenerateWidgetForColumn(const FName& ColumnName) override;
|
||||
|
||||
private:
|
||||
TSharedPtr<FHierarchicalTreeItem> Item;
|
||||
SDTFluxAssetModelDetailsWidget* ParentWidget;
|
||||
};
|
||||
|
||||
// ✅ STRUCTURE SIMPLE POUR TREEVIEW
|
||||
struct FHierarchicalTreeItem
|
||||
{
|
||||
enum class EItemType
|
||||
{
|
||||
Contest,
|
||||
Stage,
|
||||
Split,
|
||||
Participant
|
||||
};
|
||||
|
||||
EItemType Type;
|
||||
FString Name;
|
||||
FString ID;
|
||||
FString Details;
|
||||
FString Status;
|
||||
FString Extra;
|
||||
|
||||
// Données pour retrouver l'élément original
|
||||
int32 ContestId = -1;
|
||||
int32 StageId = -1;
|
||||
int32 SplitId = -1;
|
||||
int32 Bib = -1;
|
||||
|
||||
// Enfants pour la hiérarchie
|
||||
TArray<TSharedPtr<FHierarchicalTreeItem>> Children;
|
||||
|
||||
FHierarchicalTreeItem(EItemType InType, const FString& InName)
|
||||
: Type(InType), Name(InName)
|
||||
{
|
||||
}
|
||||
|
||||
void AddChild(TSharedPtr<FHierarchicalTreeItem> Child)
|
||||
{
|
||||
if (Child.IsValid())
|
||||
{
|
||||
Children.Add(Child);
|
||||
}
|
||||
}
|
||||
|
||||
// Factory methods pour créer les éléments
|
||||
static TSharedPtr<FHierarchicalTreeItem> CreateContest(const FString& ContestName, const FDTFluxContest& Contest)
|
||||
{
|
||||
TSharedPtr<FHierarchicalTreeItem> Item = MakeShareable(
|
||||
new FHierarchicalTreeItem(EItemType::Contest, ContestName));
|
||||
Item->ContestId = Contest.ContestId;
|
||||
Item->ID = FString::Printf(TEXT("%d"), Contest.ContestId);
|
||||
Item->Details = FString::Printf(
|
||||
TEXT("%d stages, %d participants"), Contest.Stages.Num(), Contest.ParticipantsBib.Num());
|
||||
Item->Status = Contest.Date.ToString(TEXT("%Y-%m-%d"));
|
||||
Item->Extra = Contest.IsFinished() ? TEXT("Finished") : TEXT("Active");
|
||||
return Item;
|
||||
}
|
||||
|
||||
static TSharedPtr<FHierarchicalTreeItem> CreateStage(const FDTFluxStage& Stage, int32 InContestId)
|
||||
{
|
||||
FString StageName = Stage.Name.IsEmpty() ? FString::Printf(TEXT("Stage %d"), Stage.StageId) : Stage.Name;
|
||||
TSharedPtr<FHierarchicalTreeItem> Item = MakeShareable(new FHierarchicalTreeItem(EItemType::Stage, StageName));
|
||||
Item->ContestId = InContestId;
|
||||
Item->StageId = Stage.StageId;
|
||||
Item->ID = FString::Printf(TEXT("%d"), Stage.StageId);
|
||||
Item->Details = FString::Printf(TEXT("Start: %s"), *Stage.StartTime.ToString(TEXT("%H:%M")));
|
||||
Item->Status = FString::Printf(TEXT("End: %s"), *Stage.EndTime.ToString(TEXT("%H:%M")));
|
||||
Item->Extra = FString::Printf(TEXT("Cutoff: %s"), *Stage.CutOff.ToString(TEXT("%H:%M")));
|
||||
return Item;
|
||||
}
|
||||
|
||||
static TSharedPtr<FHierarchicalTreeItem> CreateSplit(const FDTFluxSplit& Split, int32 InContestId)
|
||||
{
|
||||
FString SplitName = Split.Name.IsEmpty() ? FString::Printf(TEXT("Split %d"), Split.SplitId) : Split.Name;
|
||||
TSharedPtr<FHierarchicalTreeItem> Item = MakeShareable(new FHierarchicalTreeItem(EItemType::Split, SplitName));
|
||||
Item->ContestId = InContestId;
|
||||
Item->SplitId = Split.SplitId;
|
||||
Item->ID = FString::Printf(TEXT("%d"), Split.SplitId);
|
||||
Item->Details = FString::Printf(TEXT("%d rankings"), Split.SplitRankings.Num());
|
||||
Item->Status = TEXT("-");
|
||||
Item->Extra = TEXT("-");
|
||||
return Item;
|
||||
}
|
||||
|
||||
static TSharedPtr<FHierarchicalTreeItem> CreateParticipant(const FDTFluxParticipant& Participant,
|
||||
UDTFluxModelAsset* InModelAsset = nullptr)
|
||||
{
|
||||
FString ParticipantName = Participant.GetConcatFormattedName();
|
||||
TSharedPtr<FHierarchicalTreeItem> Item = MakeShareable(
|
||||
new FHierarchicalTreeItem(EItemType::Participant, ParticipantName));
|
||||
Item->Bib = Participant.Bib;
|
||||
Item->ContestId = Participant.ContestId;
|
||||
Item->ID = FString::Printf(TEXT("%d"), Participant.Bib);
|
||||
Item->Details = FString::Printf(TEXT("%s - %d teammates"), *Participant.Category,
|
||||
Participant.GetTeammate().Num());
|
||||
FString Status = UEnum::GetValueAsString(Participant.Status);
|
||||
TArray<FString> StatusArray;
|
||||
Status.ParseIntoArray(StatusArray, TEXT("::"));
|
||||
Item->Status = StatusArray.Last();
|
||||
Item->Extra = Participant.Club;
|
||||
return Item;
|
||||
}
|
||||
};
|
||||
|
||||
typedef TSharedPtr<FHierarchicalTreeItem> FHierarchicalTreeItemPtr;
|
||||
|
||||
/**
|
||||
* Widget avec STreeView simple et efficace
|
||||
*/
|
||||
class DTFLUXASSETSEDITOR_API SDTFluxAssetModelDetailsWidget : public SCompoundWidget
|
||||
{
|
||||
public:
|
||||
SLATE_BEGIN_ARGS(SDTFluxAssetModelDetailsWidget)
|
||||
{
|
||||
}
|
||||
|
||||
SLATE_ARGUMENT(UDTFluxModelAsset*, ModelAsset)
|
||||
SLATE_END_ARGS()
|
||||
|
||||
void Construct(const FArguments& InArgs);
|
||||
void RefreshData();
|
||||
|
||||
// Méthodes publiques pour la sous-classe Row
|
||||
FSlateColor GetItemTypeColor(FHierarchicalTreeItem::EItemType Type) const;
|
||||
const FSlateBrush* GetItemIcon(FHierarchicalTreeItem::EItemType Type) const;
|
||||
|
||||
private:
|
||||
// Données
|
||||
UDTFluxModelAsset* ModelAsset = nullptr;
|
||||
TArray<FHierarchicalTreeItemPtr> RootItems; // Contests racines avec hiérarchie
|
||||
TArray<FHierarchicalTreeItemPtr> ParticipantItems; // Participants séparés
|
||||
|
||||
// Widgets - TreeView simple
|
||||
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ContestTreeView;
|
||||
TSharedPtr<STreeView<FHierarchicalTreeItemPtr>> ParticipantTreeView;
|
||||
TSharedPtr<STextBlock> StatsText;
|
||||
TSharedPtr<STextBlock> SelectionText;
|
||||
|
||||
// Méthodes de construction
|
||||
void BuildContestHierarchy();
|
||||
void BuildParticipantList();
|
||||
|
||||
// Callbacks TreeView
|
||||
TSharedRef<ITableRow> OnGenerateRowForTree(FHierarchicalTreeItemPtr Item,
|
||||
const TSharedRef<STableViewBase>& OwnerTable);
|
||||
void OnGetChildrenForTree(FHierarchicalTreeItemPtr Item, TArray<FHierarchicalTreeItemPtr>& OutChildren);
|
||||
void OnTreeSelectionChanged(FHierarchicalTreeItemPtr SelectedItem, ESelectInfo::Type SelectInfo);
|
||||
void OnSetExpansionRecursive(FHierarchicalTreeItemPtr Item, bool bIsExpanded);
|
||||
|
||||
// Callbacks des boutons
|
||||
FReply OnRefreshClicked();
|
||||
FReply OnExpandAllClicked();
|
||||
FReply OnCollapseAllClicked();
|
||||
|
||||
EActiveTimerReturnType ForceInitialLayout(double InCurrentTime, float InDeltaTime);
|
||||
|
||||
// Utilitaires
|
||||
FText GetStatsText() const;
|
||||
};
|
||||
Reference in New Issue
Block a user