// 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 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> ObjectsBeingCustomized; DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized); if (ObjectsBeingCustomized.Num() > 0) { ModelAsset = Cast(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(); }) ] ]; }