diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 6a775b4..0040379 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -676,6 +676,18 @@ void UHyperTwistCoachDashboardWidget::EnsureDefaultDashboardBuilt() TEXT("CoachLastAttempt"), 2 ); + LastAttemptTimingTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachLastAttemptTiming"), + 2 + ); + LastAttemptPhaseTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( + WidgetTree, + RootLayout, + TEXT("CoachLastAttemptPhase"), + 2 + ); CurrentCaseHeaderTextBlock = HyperTwistCoachDashboardWidgetInternal::AddTextRow( WidgetTree, RootLayout, @@ -1296,6 +1308,26 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() const FString VerificationNotePreview = CachedRepositoryRoundTripVerification.VerificationNotes.Num() > 0 ? CachedRepositoryRoundTripVerification.VerificationNotes[0] : TEXT("none"); + const FHyperTwistTrainingAttempt* LastAttempt = CachedRunState.Attempts.Num() > 0 + ? &CachedRunState.Attempts.Last() + : nullptr; + FString LastAttemptPhaseSummary = TEXT("none"); + if (LastAttempt != nullptr && LastAttempt->TimingBreakdown.DerivedPhaseTimings.Num() > 0) + { + TArray PhaseParts; + PhaseParts.Reserve(LastAttempt->TimingBreakdown.DerivedPhaseTimings.Num()); + for (const FHyperTwistPhaseTiming& PhaseTiming : LastAttempt->TimingBreakdown.DerivedPhaseTimings) + { + PhaseParts.Add(FString::Printf( + TEXT("%s %d-%d (%d ms)"), + PhaseTiming.PhaseKind.IsEmpty() ? TEXT("phase") : *PhaseTiming.PhaseKind, + PhaseTiming.StartedAtMs, + PhaseTiming.EndedAtMs, + PhaseTiming.DurationMs + )); + } + LastAttemptPhaseSummary = FString::Join(PhaseParts, TEXT(" | ")); + } if (HeadlineTextBlock != nullptr) { @@ -1368,6 +1400,44 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() )); } } + if (LastAttemptTimingTextBlock != nullptr) + { + if (LastAttempt != nullptr) + { + LastAttemptTimingTextBlock->SetText(FText::FromString(FString::Printf( + TEXT("Last timing: result %s | inspection %d ms | raw solve %d ms | final %d ms | penalty %s | split captures %d | did-not-finish: %s"), + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(LastAttempt->Result), + LastAttempt->TimingBreakdown.InspectionElapsedMs, + LastAttempt->TimingBreakdown.RawSolveTimeMs, + LastAttempt->TimingBreakdown.FinalTimeMs, + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(LastAttempt->TimingBreakdown.AppliedPenalty), + LastAttempt->TimingBreakdown.SplitCaptures.Num(), + LastAttempt->TimingBreakdown.bDidNotFinish ? TEXT("yes") : TEXT("no") + ))); + } + else + { + LastAttemptTimingTextBlock->SetText(FText::FromString( + TEXT("Last timing: unavailable until the active run records an attempt.") + )); + } + } + if (LastAttemptPhaseTextBlock != nullptr) + { + if (LastAttempt != nullptr) + { + LastAttemptPhaseTextBlock->SetText(FText::FromString(FString::Printf( + TEXT("Last derived phases: %s"), + *LastAttemptPhaseSummary + ))); + } + else + { + LastAttemptPhaseTextBlock->SetText(FText::FromString( + TEXT("Last derived phases: none yet.") + )); + } + } if (CurrentCaseHeaderTextBlock != nullptr) { CurrentCaseHeaderTextBlock->SetText(FText::FromString(TEXT("[Current Case]"))); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 1456cec..af3986c 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -136,6 +136,12 @@ protected: UPROPERTY(Transient) TObjectPtr LastAttemptTextBlock = nullptr; + UPROPERTY(Transient) + TObjectPtr LastAttemptTimingTextBlock = nullptr; + + UPROPERTY(Transient) + TObjectPtr LastAttemptPhaseTextBlock = nullptr; + UPROPERTY(Transient) TObjectPtr CurrentCaseHeaderTextBlock = nullptr;