diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp new file mode 100644 index 0000000..33897e0 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.cpp @@ -0,0 +1,114 @@ +#include "UIPanel_Base.h" + +#include "ZMMOThemeSubsystem.h" +#include "Engine/GameInstance.h" +#include "Components/Border.h" +#include "Components/NamedSlot.h" +#include "Styling/SlateBrush.h" + +namespace +{ + const FUIStyle& ResolvePanelStyle(const UUserWidget* Widget, const FUIStyle& Fallback) + { + if (Widget) + { + if (const UGameInstance* GI = Widget->GetGameInstance()) + { + if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + return Theme->GetActiveUIStyle(); + } + } + } + return Fallback; // design-time: defaults Aurora Arcana + } +} + +void UUIPanel_Base::RefreshUIStyle() +{ + if (!Background) + { + return; + } + const FUIStyle Fallback; + const FUIStyle& AS = ResolvePanelStyle(this, Fallback); + const FUIStylePanel& P = AS.Panel; + + FLinearColor Fill; + FLinearColor Outline = P.BorderColor; + switch (PanelType) + { + case EUIPanelType::Secondary: Fill = P.BgRaised; break; + case EUIPanelType::Tertiary: Fill = P.BgSunken; break; + case EUIPanelType::Card: Fill = P.CardSelectedBg; Outline = P.CardSelectedBorder; break; + case EUIPanelType::Primary: + case EUIPanelType::None: + default: Fill = P.Bg; break; + } + + FSlateBrush Brush = Background->Background; + Brush.TintColor = FSlateColor(Fill); + if (bRoundedBackground) + { + const float R = bUseSmallPadding ? P.CornerRadiusSmall : P.CornerRadius; + Brush.DrawAs = ESlateBrushDrawType::RoundedBox; + Brush.OutlineSettings = FSlateBrushOutlineSettings( + FVector4(R, R, R, R), FSlateColor(Outline), P.BorderWidth); + Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius; + } + else + { + Brush.DrawAs = ESlateBrushDrawType::Box; + Brush.OutlineSettings = FSlateBrushOutlineSettings( + FVector4(0, 0, 0, 0), FSlateColor(Outline), P.BorderWidth); + } + Background->SetBrush(Brush); + Background->SetPadding(bUseSmallPadding ? P.PaddingSmall : P.Padding); + + BP_ApplyPanelStyle(P); +} + +void UUIPanel_Base::NativePreConstruct() +{ + Super::NativePreConstruct(); + RefreshUIStyle(); +} + +void UUIPanel_Base::NativeConstruct() +{ + Super::NativeConstruct(); + + if (!bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.AddDynamic(this, &UUIPanel_Base::HandleThemeChanged); + bThemeBound = true; + } + } + } + RefreshUIStyle(); +} + +void UUIPanel_Base::NativeDestruct() +{ + if (bThemeBound) + { + if (const UGameInstance* GI = GetGameInstance()) + { + if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem()) + { + Theme->OnThemeChanged.RemoveDynamic(this, &UUIPanel_Base::HandleThemeChanged); + } + } + bThemeBound = false; + } + Super::NativeDestruct(); +} + +void UUIPanel_Base::HandleThemeChanged(FName /*NewThemeId*/) +{ + RefreshUIStyle(); +} diff --git a/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h new file mode 100644 index 0000000..8acab60 --- /dev/null +++ b/Source/ZMMO/Game/UI/Widgets/UIPanel_Base.h @@ -0,0 +1,65 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CommonUserWidget.h" +#include "UI/UIStyleTypes.h" +#include "UI/UIStyleTokens.h" +#include "UIPanel_Base.generated.h" + +class UBorder; +class UNamedSlot; + +/** + * Painel base do ZMMO — mesmo padrão do UUIButton_Base, mas container. + * Fundação CommonUI (UCommonUserWidget). Camada Abstract; o WBP concreto + * (UI_Panel_Master) herda DIRETO desta classe C++ (UMG não encadeia árvores + * de WBP — ver ARQUITETURA.md §3.3). + * + * Visual data-driven por FUIStyle.Panel (UZMMOThemeSubsystem). Designers + * injetam o conteúdo no NamedSlot "Content". + */ +UCLASS(Abstract, Blueprintable) +class ZMMO_API UUIPanel_Base : public UCommonUserWidget +{ + GENERATED_BODY() + +public: + /** Tipo do painel — escolhe Bg/BgRaised/BgSunken/Card de FUIStylePanel. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") + EUIPanelType PanelType = EUIPanelType::Primary; + + /** Arredonda o Background (RoundedBox) com a borda do tema. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") + bool bRoundedBackground = true; + + /** Usa o padding "pequeno" do tema (PaddingSmall) em vez de Padding. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel") + bool bUseSmallPadding = false; + + /** Re-resolve os tokens do tema ativo e reaplica no Background. */ + UFUNCTION(BlueprintCallable, Category = "UI Style") + void RefreshUIStyle(); + +protected: + virtual void NativePreConstruct() override; + virtual void NativeConstruct() override; + virtual void NativeDestruct() override; + + /** Hook opcional: o WBP pode estender/sobrescrever a aplicação visual. */ + UFUNCTION(BlueprintImplementableEvent, Category = "UI Style", + meta = (DisplayName = "Apply Panel Style")) + void BP_ApplyPanelStyle(const FUIStylePanel& Panel); + + /** Widgets visuais opcionais. */ + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Background; + + UPROPERTY(meta = (BindWidgetOptional)) + TObjectPtr Content; + +private: + UFUNCTION() + void HandleThemeChanged(FName NewThemeId); + + bool bThemeBound = false; +};