From c7becb57ea100766c02c8b1fc1a85a232b56a39b Mon Sep 17 00:00:00 2001 From: Emmanuel AYME Date: Fri, 6 Mar 2026 01:44:33 +0100 Subject: [PATCH] Add Smart HUD UI scaling based on anchors. Add widget recursive dump. Add debug logs. --- libs/UEngine/UETools.cpp | 155 +++++++++++++++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 13 deletions(-) diff --git a/libs/UEngine/UETools.cpp b/libs/UEngine/UETools.cpp index 1888b09..31d08c2 100644 --- a/libs/UEngine/UETools.cpp +++ b/libs/UEngine/UETools.cpp @@ -1,6 +1,8 @@ -#include "UETools.hpp" +#include "UETools.hpp" #include "Engine_classes.hpp" +std::shared_ptr g_logger; + SDK::APawn* GetPawnFromWorld(SDK::UWorld* world) { if (!world) world = SDK::UWorld::GetWorld(); if (!world) return nullptr; @@ -17,6 +19,7 @@ SDK::APawn* GetPawnFromWorld(SDK::UWorld* world) { } void ReactivateDevConsole(std::shared_ptr logger) { + g_logger = logger; std::thread([logger]() { auto start = std::chrono::high_resolution_clock::now(); // Measure the time to renable console SDK::UEngine* Engine = nullptr; @@ -62,14 +65,99 @@ void ReactivateDevConsole(std::shared_ptr logger) { }).detach(); } // --- HUD & UI methods --- -static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, float Right, int CurrentDepth, int MaxDepth) { +void ApplyOffsetsSmart_Internal( SDK::UWidget* Widget, float Left, float Right, int Depth = 0, int MaxDepth = 1) { + if (!Widget) return; + + // Apply if CanvasSlot + if (Widget->Slot && Widget->Slot->IsA(SDK::UCanvasPanelSlot::StaticClass())) { + if (Depth <= MaxDepth) { + auto* Slot = static_cast(Widget->Slot); + + SDK::FMargin Offsets = Slot->GetOffsets(); + SDK::FAnchors Anchors = Slot->GetAnchors(); + + float MinX = Anchors.Minimum.X; + float MaxX = Anchors.Maximum.X; + #ifdef MY_VERBOSE_LOGS + if (g_logger) g_logger->debug("CanvasPanelSlot: {} {} - Slot: {} {} - Offets: {} {} {} {} - Anchors: {} {} {} {}", + Widget->GetName(), Widget->Class->GetName(), + Slot->GetName(), Slot->Class->GetName(), + Offsets.Left, Offsets.Top, Offsets.Right, Offsets.Bottom, + Anchors.Minimum.X, Anchors.Minimum.Y, Anchors.Maximum.X, Anchors.Maximum.Y); + #endif + if (MinX == 0.f && MaxX == 1.f) return; // Ignore stretch + if (MinX == 0.5f && MaxX == 0.5f) return; // Ignore centered + + if (MinX == 0.f && MaxX == 0.f) // Modify only pure left + Offsets.Left = Left; + else if (MinX == 1.f && MaxX == 1.f) // Modify only pure right + Offsets.Left = -Right; + + Slot->SetOffsets(Offsets); + } + } + + // Stop to max depth + if (Depth >= MaxDepth) return; + + // Browse the children + if (Widget->IsA(SDK::UPanelWidget::StaticClass())) { + auto* Panel = static_cast(Widget); + + for (int i = 0; i < Panel->GetChildrenCount(); ++i) + ApplyOffsetsSmart_Internal(Panel->GetChildAt(i), Left, Right, Depth + 1, MaxDepth); + } + + if (Widget->IsA(SDK::UUserWidget::StaticClass())) { + auto* UW = static_cast(Widget); + + if (UW->WidgetTree && UW->WidgetTree->RootWidget) + ApplyOffsetsSmart_Internal(UW->WidgetTree->RootWidget, Left, Right, Depth + 1, MaxDepth); + } +} + +void ApplyOffsetsSmart(SDK::UWidget* Widget, float Left, float Right, int MaxDepth) { +#ifdef MY_VERBOSE_LOGS + if (g_logger) g_logger->debug("========================================================="); + if (g_logger) g_logger->debug("Widget offset applying: {} {}", + Widget->GetName(), Widget->Class->GetName()); + if (g_logger) g_logger->debug("========================================================="); +#endif + ApplyOffsetsSmart_Internal(Widget, Left, Right, 0, MaxDepth); +} + +static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, float Right, + const std::vector& ExcludeObjects, const std::vector& ExcludeClass, + int CurrentDepth, int MaxDepth) { if (!Widget || CurrentDepth > MaxDepth) return; // Apply offsets according to Slot type + for (const auto& ExcludeObject : ExcludeObjects) { + if (ExcludeObject == Widget) return; + } + + std::string widgetName; + std::string className; + + widgetName = Widget->GetName(); + if (Widget->Class) className = Widget->Class->GetName(); + for (const auto& pattern : ExcludeClass) { + if (widgetName.find(pattern) != std::string::npos || className.find(pattern) != std::string::npos) + return; + } + if (Widget->Slot) { if (Widget->Slot->IsA(SDK::UCanvasPanelSlot::StaticClass())) { auto* Slot = static_cast(Widget->Slot); SDK::FMargin Offsets = Slot->GetOffsets(); + #ifdef MY_VERBOSE_LOGS + SDK::FAnchors anchors = Slot->GetAnchors(); + if (g_logger) g_logger->debug("CanvasPanelSlot: {} {} - Slot: {} {} - Offets: {} {} {} {} - Anchors: {} {} {} {}", \ + Widget->GetName(), Widget->Class->GetName(), \ + Slot->GetName(), Slot->Class->GetName(), \ + Offsets.Left, Offsets.Top, Offsets.Right, Offsets.Bottom, \ + anchors.Minimum.X, anchors.Minimum.Y, anchors.Maximum.X, anchors.Maximum.Y); + #endif Offsets.Left = Left; Offsets.Right = Right; Slot->SetOffsets(Offsets); @@ -77,6 +165,12 @@ static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, flo else if (Widget->Slot->IsA(SDK::UVerticalBoxSlot::StaticClass())) { auto* Slot = static_cast(Widget->Slot); SDK::FMargin Margin = Slot->Padding; + #ifdef MY_VERBOSE_LOGS + if (g_logger) g_logger->debug("VerticalBox Slot: {} {} - Slot: {} {} - Margins: {} {} {} {}", \ + Widget->GetName(), Widget->Class->GetName(), \ + Slot->GetName(), Slot->Class->GetName(), \ + Margin.Left, Margin.Top, Margin.Right, Margin.Bottom); + #endif Margin.Left = Left; Margin.Right = Right; Slot->SetPadding(Margin); @@ -87,23 +181,24 @@ static void ApplyOffsetsRecursive_Internal(SDK::UWidget* Widget, float Left, flo auto* Panel = static_cast(Widget); int childrenCount = Panel->GetChildrenCount(); for (int i = 0; i < childrenCount; ++i) { - ApplyOffsetsRecursive_Internal(Panel->GetChildAt(i), Left, Right, CurrentDepth + 1, MaxDepth); + ApplyOffsetsRecursive_Internal(Panel->GetChildAt(i), Left, Right, ExcludeObjects, ExcludeClass, CurrentDepth + 1, MaxDepth); } } // Go deeper if the widget is an UserWidget if (Widget->IsA(SDK::UUserWidget::StaticClass())) { auto* ChildWidget = static_cast(Widget); if (ChildWidget->WidgetTree && ChildWidget->WidgetTree->RootWidget) { - ApplyOffsetsRecursive_Internal(ChildWidget->WidgetTree->RootWidget, Left, Right, CurrentDepth + 1, MaxDepth); + ApplyOffsetsRecursive_Internal(ChildWidget->WidgetTree->RootWidget, Left, Right, ExcludeObjects, ExcludeClass, CurrentDepth + 1, MaxDepth); } } } -void ApplyOffsetsRecursive(SDK::UWidget* Widget, float Left, float Right, int MaxDepth) { - ApplyOffsetsRecursive_Internal(Widget, Left, Right, 0, MaxDepth); +void ApplyOffsetsRecursive(SDK::UWidget* Widget, float Left, float Right, + const std::vector& ExcludeObjects, const std::vector& ExcludeClass, int MaxDepth) { + ApplyOffsetsRecursive_Internal(Widget, Left, Right, ExcludeObjects, ExcludeClass, 0, MaxDepth); } -static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float Offset, SDK::EHorizontalAlignment alignment, +static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float left, float right, SDK::EHorizontalAlignment alignment, const std::vector& ExcludeNames, int CurrentDepth, int MaxDepth) { if (!Widget || CurrentDepth > MaxDepth) return; @@ -121,10 +216,9 @@ static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float Off SDK::FMargin Padding = Slot->Padding; if (Slot->HorizontalAlignment == alignment) { - Padding.Left = Offset; - Padding.Right = Offset; + Padding.Left = left; + Padding.Right = right; } - Slot->SetPadding(Padding); }; @@ -135,14 +229,14 @@ static void ApplyOverlayOffsetRecursive_Internal(SDK::UWidget* Widget, float Off SDK::UPanelWidget* Panel = (SDK::UPanelWidget*)Widget; int childrenCount = Panel->GetChildrenCount(); for (int i = 0; i < childrenCount; ++i) { - ApplyOverlayOffsetRecursive_Internal(Panel->GetChildAt(i), Offset, alignment, ExcludeNames, CurrentDepth + 1, MaxDepth); + ApplyOverlayOffsetRecursive_Internal(Panel->GetChildAt(i), left, right, alignment, ExcludeNames, CurrentDepth + 1, MaxDepth); } } } -void ApplyOverlayOffsetRecursive(SDK::UWidget* Widget, float Offset, SDK::EHorizontalAlignment alignment, +void ApplyOverlayOffsetRecursive(SDK::UWidget* Widget, float left, float right, SDK::EHorizontalAlignment alignment, const std::vector& ExcludeNames, int MaxDepth) { - ApplyOverlayOffsetRecursive_Internal(Widget, Offset, alignment, ExcludeNames, 0, MaxDepth); + ApplyOverlayOffsetRecursive_Internal(Widget, left, right, alignment, ExcludeNames, 0, MaxDepth); } void FindAndApplyCanvasRecursive(SDK::UWidget* widget, float offset, int MaxDepth, int currentDepth) { @@ -244,4 +338,39 @@ void DumpUIAnalysis(std::shared_ptr logger) { void ClearWidgetTracking() { g_WidgetTracker.clear(); +} + +void DumpWidgetRecursive(SDK::UWidget* Widget, int Depth, int MaxDepth) { + if (!Widget || Depth > MaxDepth) return; + + std::string indent(Depth * 2, ' '); + std::string className = Widget->Class ? Widget->Class->GetName() : "UnknownClass"; + std::string widgetName = Widget->GetName(); + if (g_logger) + g_logger->debug("{}Widget: {} [{}]", indent, widgetName, className); + + // Slot info si disponible + if (Widget->Slot) { + std::string slotClass = Widget->Slot->Class ? Widget->Slot->Class->GetName() : "UnknownSlot"; + if (g_logger) + g_logger->debug("{} Slot class: {}", indent, slotClass); + } + + // Descendre dans les enfants si c'est un panel et qu'on est encore sous MaxDepth + if (Depth < MaxDepth) { + if (Widget->IsA(SDK::UPanelWidget::StaticClass())) { + auto* panel = static_cast(Widget); + int count = panel->GetChildrenCount(); + for (int i = 0; i < count; ++i) { + DumpWidgetRecursive(panel->GetChildAt(i), Depth + 1, MaxDepth); + } + } + + if (Widget->IsA(SDK::UUserWidget::StaticClass())) { + auto* userWidget = static_cast(Widget); + if (userWidget->WidgetTree && userWidget->WidgetTree->RootWidget) { + DumpWidgetRecursive(userWidget->WidgetTree->RootWidget, Depth + 1, MaxDepth); + } + } + } } \ No newline at end of file