refactor(ui/base): panel data-driven 3-brush + button via FButtonStyle

UIPanel_Base: substitui PanelType/Padding por bUseTheme + 3 chaves
(Background/Outline/OutlineEffectKey) lidas de FUIStylePanel. Modo manual
(bUseTheme=false) expoe 3 FSlateBrush diretos via FUIPanelBrushSet pra
override por instancia. Adiciona BindWidgetOptional OutlineEffect (3o
Border sobre Outline pra glow/embossing). Defaults NoDrawType (regra
Hyper: brush vazio nao pinta branco).

UIButton_Base: remove auto-config (Variants enum, Number/Icon/Size/
InputAction/Timer). Vira shell minimal com BindWidgetOptional + helpers
GetActiveUIStyle/GetActiveButtonStyle pra leitura de DT_UI_Styles.

UIStyleTokens: FUIStylePanel ganha PanelOutlineEffect map. FUIPanelBrushSet
vira 3 FSlateBrush diretos. FUIStyleButton refatora pra 7 sub-maps por
variante FName (ButtonStyle=FButtonStyle do Slate, ButtonStroke, ButtonFont,
Text/Icon colors). Tudo com defaults NoDrawType.

UIStyleTypes: adiciona enum EUIPanelTexture::Rectangle_Square_Gradient_01.

Content: UI_Panel_Master e UI_Button_Master_New consomem a API nova;
DT_UI_Styles atualizado com rows/keys novas. UIServerCard_Base ajustado
pro consumer da API nova.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-26 00:12:58 -03:00
parent 9390ed9da9
commit 037aed30ce
10 changed files with 175 additions and 946 deletions

Binary file not shown.

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "Fonts/SlateFontInfo.h"
#include "Styling/SlateBrush.h"
#include "Styling/SlateTypes.h"
#include "Layout/Margin.h"
#include "Templates/SubclassOf.h"
#include "Engine/Texture2D.h"
@@ -195,117 +196,13 @@ struct ZMMO_API FUITextStyle
};
// =====================================================================
// FUIStyleButton — padrão multi-map (Hyper-style). 3 sub-mapas com a MESMA
// chave (nome da variante: "Primary", "Secondary", "Danger", "Ghost", ou
// customizada). UUIButton_Base.Variant é FName e busca em cada sub-mapa.
// O struct FUIStyleButtonVariant (composto, FLAT) permanece com a forma
// antiga — assim o hook BP_ApplyUIStyle dos WBP_Master continua funcional.
// FUIStyleButton — padrão Hyper "Buttons": 7 sub-mapas independentes
// indexados pela MESMA chave FName de variante (autor define livremente:
// "Default", "Square", "Topbar", "Home_Main_Menu", ...). UUIButton_Base.Variant
// é FName e busca o pedaço necessário em cada sub-mapa. ButtonStyle traz
// FButtonStyle do Slate completo (Normal/Hovered/Pressed/Disabled como
// FSlateBrush, Foregrounds, Padding, Sounds).
// =====================================================================
USTRUCT(BlueprintType)
struct ZMMO_API FUIStyleButtonBackground
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgNormal = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgHover = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgPressed = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f);
/** Textura opcional como background (Hyper-style). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TObjectPtr<UTexture2D> BackgroundTexture = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FMargin BackgroundMargin = FMargin(8.f);
};
USTRUCT(BlueprintType)
struct ZMMO_API FUIStyleButtonStroke
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BorderNormal = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BorderHover = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0"))
float BorderWidth = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button", meta = (ClampMin = "0"))
float CornerRadius = 8.f;
};
USTRUCT(BlueprintType)
struct ZMMO_API FUIStyleButtonFont
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor TextColor = FLinearColor::White;
/** CommonTextStyle define fonte/tamanho/peso; TextColor sobrescreve a cor. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TSubclassOf<UCommonTextStyle> TextStyle;
};
/**
* View FLAT composta da variante de botão (Background + Stroke + Font).
* Mantida com a forma antiga (BgNormal/BgHover/.../TextStyle no topo) para
* NÃO QUEBRAR o hook BP_ApplyUIStyle nos WBP_Master existentes. É construída
* pelo UUIButton_Base::ResolveVariant a partir dos 3 sub-mapas do tema.
*/
USTRUCT(BlueprintType)
struct ZMMO_API FUIStyleButtonVariant
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgNormal = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgHover = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgPressed = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BgDisabled = FLinearColor(0.f, 0.f, 0.f, 0.42f);
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BorderNormal = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor BorderHover = FLinearColor::Transparent;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FLinearColor TextColor = FLinearColor::White;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TSubclassOf<UCommonTextStyle> TextStyle;
// Campos extras vindos das sub-categorias (background texture + stroke):
UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button")
TObjectPtr<UTexture2D> BackgroundTexture = nullptr;
UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button")
FMargin BackgroundMargin = FMargin(8.f);
UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button")
float BorderWidth = 1.f;
UPROPERTY(BlueprintReadOnly, Category = "UI Style|Button")
float CornerRadius = 8.f;
};
USTRUCT(BlueprintType)
struct ZMMO_API FUIStyleButton
{
@@ -313,163 +210,103 @@ struct ZMMO_API FUIStyleButton
FUIStyleButton()
{
// ---- Backgrounds (Primary/Secondary/Danger/Ghost) ----
FUIStyleButtonBackground PriBg;
PriBg.BgNormal = FLinearColor(FColor(201, 167, 90)); // gold
PriBg.BgHover = FLinearColor(FColor(231, 200, 115)); // gold-hi
PriBg.BgPressed = FLinearColor(FColor(201, 167, 90));
PriBg.BgDisabled = FLinearColor(FColor(201, 167, 90, 107));
Backgrounds.Add(TEXT("Primary"), PriBg);
// Defaults "no draw" pra não pintar branco quando o brush estiver vazio
// (regra Hyper — ver feedback_slatebrush_empty_white). Autor preenche
// no DT_UI_Styles.
FButtonStyle DefaultStyle;
DefaultStyle.Normal.DrawAs = ESlateBrushDrawType::NoDrawType;
DefaultStyle.Hovered.DrawAs = ESlateBrushDrawType::NoDrawType;
DefaultStyle.Pressed.DrawAs = ESlateBrushDrawType::NoDrawType;
DefaultStyle.Disabled.DrawAs = ESlateBrushDrawType::NoDrawType;
ButtonStyle.Add(TEXT("Default"), DefaultStyle);
FUIStyleButtonBackground SecBg;
SecBg.BgNormal = FLinearColor(FColor(17, 24, 42)); // panel-2
SecBg.BgHover = FLinearColor(FColor(22, 32, 58));
SecBg.BgPressed = FLinearColor(FColor(17, 24, 42));
Backgrounds.Add(TEXT("Secondary"), SecBg);
FSlateBrush DefaultStroke;
DefaultStroke.DrawAs = ESlateBrushDrawType::NoDrawType;
ButtonStroke.Add(TEXT("Default"), DefaultStroke);
FUIStyleButtonBackground DngBg;
DngBg.BgNormal = FLinearColor(FColor(217, 92, 92, 36));
DngBg.BgHover = FLinearColor(FColor(217, 92, 92, 66));
DngBg.BgPressed = FLinearColor(FColor(217, 92, 92, 36));
Backgrounds.Add(TEXT("Danger"), DngBg);
ButtonFont.Add(TEXT("Default"), FSlateFontInfo());
FUIStyleButtonBackground GhoBg;
GhoBg.BgNormal = FLinearColor::Transparent;
GhoBg.BgHover = FLinearColor(FColor(22, 32, 58, 128));
GhoBg.BgPressed = FLinearColor::Transparent;
Backgrounds.Add(TEXT("Ghost"), GhoBg);
ButtonTextColorRegular.Add(TEXT("Default"), FLinearColor::White);
ButtonTextColorHovered.Add(TEXT("Default"), FLinearColor::White);
ButtonTextColorSelected.Add(TEXT("Default"), FLinearColor::White);
// ---- Strokes ----
FUIStyleButtonStroke PriS;
PriS.BorderNormal = FLinearColor(FColor(201, 167, 90));
PriS.BorderHover = FLinearColor(FColor(231, 200, 115));
Strokes.Add(TEXT("Primary"), PriS);
FUIStyleButtonStroke SecS;
SecS.BorderNormal = FLinearColor(FColor(58, 75, 120));
SecS.BorderHover = FLinearColor(FColor(78, 163, 255));
Strokes.Add(TEXT("Secondary"), SecS);
FUIStyleButtonStroke DngS;
DngS.BorderNormal = FLinearColor(FColor(217, 92, 92));
DngS.BorderHover = FLinearColor(FColor(217, 92, 92));
Strokes.Add(TEXT("Danger"), DngS);
FUIStyleButtonStroke GhoS;
GhoS.BorderNormal = FLinearColor(FColor(58, 75, 120, 140));
GhoS.BorderHover = FLinearColor(FColor(78, 163, 255));
Strokes.Add(TEXT("Ghost"), GhoS);
// ---- Fonts ----
FUIStyleButtonFont PriF; PriF.TextColor = FLinearColor(FColor(10, 10, 18));
Fonts.Add(TEXT("Primary"), PriF);
FUIStyleButtonFont SecF; SecF.TextColor = FLinearColor(FColor(244, 240, 230));
Fonts.Add(TEXT("Secondary"), SecF);
FUIStyleButtonFont DngF; DngF.TextColor = FLinearColor(FColor(240, 182, 182));
Fonts.Add(TEXT("Danger"), DngF);
FUIStyleButtonFont GhoF; GhoF.TextColor = FLinearColor(FColor(174, 182, 200));
Fonts.Add(TEXT("Ghost"), GhoF);
ButtonIconColor.Add(TEXT("Default"), FLinearColor::White);
}
/** Cores/textura de fundo por variante (chave FName). */
/** FButtonStyle do Slate por variante: Normal/Hovered/Pressed/Disabled
* como FSlateBrush completo + Foregrounds + Padding + Sounds. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TMap<FName, FUIStyleButtonBackground> Backgrounds;
TMap<FName, FButtonStyle> ButtonStyle;
/** Borda (cor, espessura, raio) por variante. */
/** Stroke/contorno como brush independente (texturizável). Hoje o
* UUIButton_Base não consome este campo automaticamente — quem precisa
* de stroke decora o FSlateBrush dentro de ButtonStyle.Normal via
* OutlineSettings. Reservado pra evolução com 2 Borders empilhados. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TMap<FName, FUIStyleButtonStroke> Strokes;
TMap<FName, FSlateBrush> ButtonStroke;
/** Tipografia (cor de texto, CommonTextStyle) por variante. */
/** Fonte do texto por variante. */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TMap<FName, FUIStyleButtonFont> Fonts;
// Globais comuns às variantes:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
FMargin Padding = FMargin(22.f, 12.f);
TMap<FName, FSlateFontInfo> ButtonFont;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
float MinHeight = 48.f;
TMap<FName, FLinearColor> ButtonTextColorRegular;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
float HoverScale = 1.02f;
TMap<FName, FLinearColor> ButtonTextColorHovered;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
float PressedScale = 0.98f;
TMap<FName, FLinearColor> ButtonTextColorSelected;
/** Tamanho por silhueta (Square/Rectangle/Topbar/Header). */
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Button")
TMap<EUIButtonShape, FVector2D> ShapeSizes;
TMap<FName, FLinearColor> ButtonIconColor;
};
/**
* Espelha 1:1 o Struct_UI_Style_Panels do Hyper: só os dois mapas de brush
* (fundo + contorno) indexados por EUIPanelTexture. É o que o UI_Panel_Master
* do Hyper expõe editável na instância (Details). EditAnywhere/ReadWrite para
* o designer ajustar brush por widget; o tema (DT) só faz seed/fallback.
* Override manual por instância (modo bUseTheme = false). 3 brushes diretos
* pros 3 Borders do painel — designer edita o brush bruto no Details.
* Default = NoDrawType pra não pintar branco quando vazio (regra Hyper).
*/
USTRUCT(BlueprintType)
struct ZMMO_API FUIPanelBrushSet
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture")
TMap<EUIPanelTexture, FSlateBrush> PanelBackground;
FUIPanelBrushSet()
{
PanelBackground.DrawAs = ESlateBrushDrawType::NoDrawType;
PanelOutline.DrawAs = ESlateBrushDrawType::NoDrawType;
PanelOutlineEffect.DrawAs = ESlateBrushDrawType::NoDrawType;
}
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Texture")
TMap<EUIPanelTexture, FSlateBrush> PanelOutline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush")
FSlateBrush PanelBackground;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush")
FSlateBrush PanelOutline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel|Brush")
FSlateBrush PanelOutlineEffect;
};
/**
* Espelha 1:1 o Struct_UI_Style_Panels do Hyper, agora com 3 brushes por
* EUIPanelTexture: fundo, contorno e efeito sobre o contorno. Tudo
* data-driven via brush no DT_UI_Styles.
*/
USTRUCT(BlueprintType)
struct ZMMO_API FUIStylePanel
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor Bg = FLinearColor(FColor(12, 18, 34, 235)); // Panel
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor BgRaised = FLinearColor(FColor(17, 24, 42)); // Panel2
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor BgSunken = FLinearColor(FColor(14, 20, 36)); // Panel3
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor BorderColor = FLinearColor(FColor(58, 75, 120));
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor BorderSoftColor = FLinearColor(FColor(58, 75, 120, 140));
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
float CornerRadius = 12.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
float CornerRadiusSmall = 10.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
float BorderWidth = 1.f;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FMargin Padding = FMargin(30.f);
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FMargin PaddingSmall = FMargin(20.f);
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor CardSelectedBorder = FLinearColor(FColor(201, 167, 90)); // Accent
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel")
FLinearColor CardSelectedBg = FLinearColor(FColor(24, 32, 58));
/**
* Modo texturizado (espelha Struct_UI_Style_Panels do Hyper): brush por
* EUIPanelTexture. PanelBackground = fundo; PanelOutline = contorno.
* Se vazio p/ a chave, o painel usa o modo cor (RoundedBox) acima.
*/
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture")
TMap<EUIPanelTexture, FSlateBrush> PanelBackground;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture")
TMap<EUIPanelTexture, FSlateBrush> PanelOutline;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "UI Style|Panel|Texture")
TMap<EUIPanelTexture, FSlateBrush> PanelOutlineEffect;
};
USTRUCT(BlueprintType)

View File

@@ -66,6 +66,7 @@ enum class EUIPanelTexture : uint8
Rectangle_Vertical_03 UMETA(DisplayName = "Rectangle Vertical 03"),
Rectangle_Vertical_04 UMETA(DisplayName = "Rectangle Vertical 04"),
Rectangle_Square_01 UMETA(DisplayName = "Rectangle Square 01"),
Rectangle_Square_Gradient_01 UMETA(DisplayName = "Rectangle Square Gradient 01"),
Vertical_Footer_01 UMETA(DisplayName = "Vertical Footer 01")
};

View File

@@ -9,12 +9,11 @@
void UUIServerCard_Base::NativePreConstruct()
{
Super::NativePreConstruct();
// Reaplica tema do botao antes de qualquer SetIsEnabled — garante que o
// background da variante "Primary" pinta tanto em Normal quanto Disabled.
if (Btn_Enter)
{
Btn_Enter->RefreshUIStyle();
}
// TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle.
// if (Btn_Enter)
// {
// Btn_Enter->RefreshUIStyle();
// }
}
void UUIServerCard_Base::NativeConstruct()
@@ -25,10 +24,11 @@ void UUIServerCard_Base::NativeConstruct()
Btn_Enter->OnClicked().AddUObject(this, &UUIServerCard_Base::HandleEnterClicked);
bBound = true;
}
if (Btn_Enter)
{
Btn_Enter->RefreshUIStyle();
}
// TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle.
// if (Btn_Enter)
// {
// Btn_Enter->RefreshUIStyle();
// }
}
void UUIServerCard_Base::NativeDestruct()
@@ -69,10 +69,11 @@ void UUIServerCard_Base::SetFromEntry(const FZMMOWorldEntry& InEntry)
// o usuario pode entrar no Lobby pra ver a lista de personagens, criar/
// deletar. O gate de "entrar no mundo" fica no proprio Lobby (botao
// "Entrar no Servidor"), que checa World.State antes do handoff.
if (Btn_Enter)
{
Btn_Enter->RefreshUIStyle();
}
// TODO(ui-system): reativar quando UUIButton_Base voltar a ter RefreshUIStyle.
// if (Btn_Enter)
// {
// Btn_Enter->RefreshUIStyle();
// }
}
void UUIServerCard_Base::HandleEnterClicked()

View File

@@ -1,434 +1,28 @@
#include "UIButton_Base.h"
#include "ZMMOThemeSubsystem.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "TimerManager.h"
#include "Components/Border.h"
#include "Styling/SlateBrush.h"
#include "Components/TextBlock.h"
#include "Components/Image.h"
#include "Components/SizeBox.h"
#include "Components/ScaleBox.h"
#include "Components/OverlaySlot.h"
#include "CommonTextBlock.h"
#include "CommonActionWidget.h"
#include "UI/UIStyleRow.h"
#include "UI/UIStyleTokens.h"
#include "Engine/DataTable.h"
namespace
const FUIStyle* UUIButton_Base::GetActiveUIStyle(FName ThemeId) const
{
/** Resolve o FUIStyle ativo; fallback p/ defaults (preview de designer). */
const FUIStyle& ResolveActiveStyle(const UUserWidget* Widget, const FUIStyle& Fallback)
const UDataTable* DT = LoadObject<UDataTable>(nullptr,
TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles"));
if (!DT)
{
if (Widget)
{
if (const UGameInstance* GI = Widget->GetGameInstance())
{
if (const UZMMOThemeSubsystem* Theme = GI->GetSubsystem<UZMMOThemeSubsystem>())
{
return Theme->GetActiveUIStyle();
}
}
}
return Fallback; // sem GameInstance (design-time): Aurora Arcana default
return nullptr;
}
if (const FUIStyleRow* Row = DT->FindRow<FUIStyleRow>(
ThemeId, TEXT("UUIButton_Base::GetActiveUIStyle"), false))
{
return &Row->Style;
}
return nullptr;
}
FUIStyleButtonVariant UUIButton_Base::ResolveVariant(const FUIStyleButton& Button) const
const FUIStyleButton* UUIButton_Base::GetActiveButtonStyle(FName ThemeId) const
{
// Compõe FLAT a partir dos 3 sub-mapas do tema (Backgrounds/Strokes/Fonts).
// Fallback inline → Defaults (constructor de FUIStyleButton popula
// Primary/Secondary/Danger/Ghost com defaults Aurora Arcana — GC-safe).
static const FUIStyleButton Defaults;
FUIStyleButtonVariant V;
const FUIStyleButtonBackground* BG = Button.Backgrounds.Find(Variant);
if (!BG) { BG = Defaults.Backgrounds.Find(Variant); }
if (!BG) { BG = Defaults.Backgrounds.Find(TEXT("Primary")); }
if (BG)
{
V.BgNormal = BG->BgNormal;
V.BgHover = BG->BgHover;
V.BgPressed = BG->BgPressed;
V.BgDisabled = BG->BgDisabled;
V.BackgroundTexture = BG->BackgroundTexture;
V.BackgroundMargin = BG->BackgroundMargin;
}
const FUIStyleButtonStroke* ST = Button.Strokes.Find(Variant);
if (!ST) { ST = Defaults.Strokes.Find(Variant); }
if (!ST) { ST = Defaults.Strokes.Find(TEXT("Primary")); }
if (ST)
{
V.BorderNormal = ST->BorderNormal;
V.BorderHover = ST->BorderHover;
V.BorderWidth = ST->BorderWidth;
V.CornerRadius = ST->CornerRadius;
}
const FUIStyleButtonFont* F = Button.Fonts.Find(Variant);
if (!F) { F = Defaults.Fonts.Find(Variant); }
if (!F) { F = Defaults.Fonts.Find(TEXT("Primary")); }
if (F)
{
V.TextColor = F->TextColor;
V.TextStyle = F->TextStyle;
}
return V;
}
TArray<FString> UUIButton_Base::GetVariantOptions() const
{
TArray<FString> Options;
for (const TCHAR* N : { TEXT("Primary"), TEXT("Secondary"), TEXT("Danger"), TEXT("Ghost") })
{
Options.Add(N);
}
if (const UDataTable* DT = LoadObject<UDataTable>(nullptr,
TEXT("/Game/ZMMO/Data/UI/DT_UI_Styles.DT_UI_Styles")))
{
if (const FUIStyleRow* Row = DT->FindRow<FUIStyleRow>(
FName(TEXT("Default")), TEXT("UIButtonVariantOptions"), false))
{
for (const TPair<FName, FUIStyleButtonBackground>& P : Row->Style.Button.Backgrounds) { Options.AddUnique(P.Key.ToString()); }
for (const TPair<FName, FUIStyleButtonStroke>& P : Row->Style.Button.Strokes) { Options.AddUnique(P.Key.ToString()); }
for (const TPair<FName, FUIStyleButtonFont>& P : Row->Style.Button.Fonts) { Options.AddUnique(P.Key.ToString()); }
}
}
return Options;
}
// ---- Hyper "Set Button Text" ----
void UUIButton_Base::SetButtonText(FText InText)
{
if (!ButtonText)
{
return;
}
const FText Final = bUppercaseText
? FText::FromString(InText.ToString().ToUpper())
: InText;
ButtonText->SetText(Final);
}
// ---- Hyper "Set Number Text" ----
void UUIButton_Base::SetNumberText()
{
if (!Number_Text)
{
return;
}
if (bShowNumberText)
{
Number_Text->SetText(FText::AsNumber(NumberTextAmount));
Number_Text->SetVisibility(ESlateVisibility::Visible);
}
else
{
Number_Text->SetVisibility(ESlateVisibility::Hidden);
}
}
// ---- Hyper "Set Icon" ----
void UUIButton_Base::SetIcon()
{
if (bUseIconInsteadOfText)
{
if (Image_Icon)
{
if (IconImage)
{
Image_Icon->SetBrushResourceObject(IconImage);
}
Image_Icon->SetVisibility(ESlateVisibility::Visible);
}
if (ButtonText)
{
ButtonText->SetVisibility(ESlateVisibility::Collapsed);
}
}
else
{
if (Image_Icon)
{
Image_Icon->SetVisibility(ESlateVisibility::Collapsed);
}
if (ButtonText)
{
ButtonText->SetVisibility(ESlateVisibility::Visible);
}
}
}
// ---- Hyper "Set Sizebox Master Dimmensions" ----
void UUIButton_Base::ApplySizeBox()
{
if (bDoNotOverrideWithSizebox || !SizeBox_Master)
{
return;
}
SizeBox_Master->SetHeightOverride(InHeightOverride);
SizeBox_Master->SetWidthOverride(InWidthOverride);
}
// ---- Hyper "Set InputAction Style" ----
void UUIButton_Base::ApplyInputActionStyle()
{
// InputActionWidget é privado em UCommonButtonBase (bind interno). Pega por
// nome — o widget do WBP chama-se "InputActionWidget" (bind do CommonUI).
UCommonActionWidget* IAW = Cast<UCommonActionWidget>(
GetWidgetFromName(TEXT("InputActionWidget")));
if (!IAW)
{
return;
}
if (bShowInputActionWidget)
{
IAW->SetVisibility(ESlateVisibility::Visible);
IAW->SetIsEnabled(true);
}
else
{
IAW->SetVisibility(ESlateVisibility::Collapsed);
IAW->SetIsEnabled(false);
}
if (UOverlaySlot* OS = Cast<UOverlaySlot>(IAW->Slot))
{
OS->SetPadding(InputPadding);
OS->SetHorizontalAlignment(InputHorzAlignment);
OS->SetVerticalAlignment(InputVertAlignment);
}
}
// ---- Hyper "Initialize Button" (orquestrador) ----
void UUIButton_Base::InitializeButton()
{
SetButtonText(ButtonTextValue);
SetNumberText();
SetIcon();
ApplySizeBox();
RefreshUIStyle();
}
// ---- Hyper "Update Button Style" / "Set Brush" → nossa camada FUIStyle ----
void UUIButton_Base::RefreshUIStyle()
{
const FUIStyle Fallback; // defaults do struct = paleta Aurora Arcana
const FUIStyle& ActiveStyle = ResolveActiveStyle(this, Fallback);
const FUIStyleButtonVariant& VariantStyle = ResolveVariant(ActiveStyle.Button);
// Fundo + borda no estado Normal (hover/pressed/disabled via NativeOn*).
ApplyVisualState(EUIButtonVisual::Normal);
if (ButtonText)
{
// Tipografia: CommonTextStyle da variante (padrão Hyper/CommonUI).
if (VariantStyle.TextStyle)
{
ButtonText->SetStyle(VariantStyle.TextStyle);
}
// Cor: FUIStyle manda na cor por tema (sobrepõe o CommonTextStyle).
ButtonText->SetColorAndOpacity(FSlateColor(VariantStyle.TextColor));
// Alinhamento L/C/R dentro da largura do botão.
ButtonText->SetJustification(TextAlign);
}
// Texto adapta ao tamanho do botão (ScaleToFit) ou usa tamanho fixo.
if (TextScaleBox)
{
TextScaleBox->SetStretch(bAutoSizeText ? EStretch::ScaleToFit : EStretch::None);
// Posiciona o bloco de texto L/C/R dentro do botão (slot do Overlay).
if (UOverlaySlot* OS = Cast<UOverlaySlot>(TextScaleBox->Slot))
{
EHorizontalAlignment H = HAlign_Center;
switch (TextAlign)
{
case ETextJustify::Left: H = HAlign_Left; break;
case ETextJustify::Right: H = HAlign_Right; break;
default: H = HAlign_Center; break;
}
OS->SetHorizontalAlignment(H);
OS->SetVerticalAlignment(VAlign_Center);
}
}
BP_ApplyUIStyle(ActiveStyle.Button, VariantStyle);
}
void UUIButton_Base::ApplyVisualState(EUIButtonVisual State)
{
if (!Background)
{
return;
}
const FUIStyle Fallback;
const FUIStyle& AS = ResolveActiveStyle(this, Fallback);
const FUIStyleButtonVariant& V = ResolveVariant(AS.Button);
FLinearColor Fill, Outline;
switch (State)
{
case EUIButtonVisual::Hovered: Fill = V.BgHover; Outline = V.BorderHover; break;
case EUIButtonVisual::Pressed: Fill = V.BgPressed; Outline = V.BorderHover; break;
case EUIButtonVisual::Disabled: Fill = V.BgDisabled; Outline = V.BorderNormal; break;
default: Fill = V.BgNormal; Outline = V.BorderNormal; break;
}
FSlateBrush Brush = Background->Background;
Brush.TintColor = FSlateColor(Fill);
if (bRoundedBackground)
{
// CornerRadius/BorderWidth agora vêm da variante composta (Stroke).
const float R = V.CornerRadius;
Brush.DrawAs = ESlateBrushDrawType::RoundedBox;
Brush.OutlineSettings = FSlateBrushOutlineSettings(
FVector4(R, R, R, R), FSlateColor(Outline), V.BorderWidth);
Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius;
}
else
{
Brush.DrawAs = ESlateBrushDrawType::Box;
Brush.OutlineSettings = FSlateBrushOutlineSettings(
FVector4(0, 0, 0, 0), FSlateColor(Outline), V.BorderWidth);
}
Background->SetBrush(Brush);
}
void UUIButton_Base::NativeOnHovered()
{
Super::NativeOnHovered();
ApplyVisualState(EUIButtonVisual::Hovered);
}
void UUIButton_Base::NativeOnUnhovered()
{
Super::NativeOnUnhovered();
ApplyVisualState(EUIButtonVisual::Normal);
}
void UUIButton_Base::NativeOnPressed()
{
Super::NativeOnPressed();
ApplyVisualState(EUIButtonVisual::Pressed);
}
void UUIButton_Base::NativeOnReleased()
{
Super::NativeOnReleased();
ApplyVisualState(IsHovered() ? EUIButtonVisual::Hovered : EUIButtonVisual::Normal);
}
void UUIButton_Base::NativeOnEnabled()
{
Super::NativeOnEnabled();
ApplyVisualState(EUIButtonVisual::Normal);
}
void UUIButton_Base::NativeOnDisabled()
{
Super::NativeOnDisabled();
ApplyVisualState(EUIButtonVisual::Disabled);
}
void UUIButton_Base::NativePreConstruct()
{
Super::NativePreConstruct();
InitializeButton();
ApplyInputActionStyle();
}
void UUIButton_Base::NativeConstruct()
{
Super::NativeConstruct();
if (!bThemeBound)
{
if (const UGameInstance* GI = GetGameInstance())
{
if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem<UZMMOThemeSubsystem>())
{
Theme->OnThemeChanged.AddDynamic(this, &UUIButton_Base::HandleThemeChanged);
bThemeBound = true;
}
}
}
InitializeButton();
ApplyInputActionStyle();
if (bDeactivatedOnConstructWithTimeout && Timeout > 0.f)
{
StartDeactivateTimeout();
}
}
void UUIButton_Base::NativeDestruct()
{
if (UWorld* World = GetWorld())
{
World->GetTimerManager().ClearTimer(InactivityTimer);
World->GetTimerManager().ClearTimer(TimerTickHandle);
}
if (bThemeBound)
{
if (const UGameInstance* GI = GetGameInstance())
{
if (UZMMOThemeSubsystem* Theme = GI->GetSubsystem<UZMMOThemeSubsystem>())
{
Theme->OnThemeChanged.RemoveDynamic(this, &UUIButton_Base::HandleThemeChanged);
}
}
bThemeBound = false;
}
Super::NativeDestruct();
}
// ---- Hyper "Deactivate Button On Timeout" ----
void UUIButton_Base::StartDeactivateTimeout()
{
UWorld* World = GetWorld();
if (!World)
{
return;
}
SetIsEnabled(false);
TimeRemaining = Timeout;
World->GetTimerManager().SetTimer(
InactivityTimer, this, &UUIButton_Base::HandleTimeoutFinished, Timeout, false);
if (TimeUpdateFrequency > 0.f)
{
World->GetTimerManager().SetTimer(
TimerTickHandle, this, &UUIButton_Base::HandleTimerTick,
TimeUpdateFrequency, true);
}
}
void UUIButton_Base::HandleTimeoutFinished()
{
if (UWorld* World = GetWorld())
{
World->GetTimerManager().ClearTimer(TimerTickHandle);
}
TimeRemaining = 0.f;
SetIsEnabled(true);
OnButtonActivatedByTimeout.Broadcast();
}
void UUIButton_Base::HandleTimerTick()
{
TimeRemaining = FMath::Max(0.f, TimeRemaining - TimeUpdateFrequency);
OnButtonTimerUpdated.Broadcast(TimeRemaining);
}
void UUIButton_Base::HandleThemeChanged(FName /*NewThemeId*/)
{
RefreshUIStyle();
const FUIStyle* UIStyle = GetActiveUIStyle(ThemeId);
return UIStyle ? &UIStyle->Button : nullptr;
}

View File

@@ -2,220 +2,55 @@
#include "CoreMinimal.h"
#include "CommonButtonBase.h"
#include "Layout/Margin.h"
#include "Types/SlateEnums.h"
#include "UI/UIStyleTypes.h"
#include "UI/UIStyleTokens.h"
#include "UIButton_Base.generated.h"
class UBorder;
class UTextBlock;
class UImage;
class USizeBox;
class UScaleBox;
class UImage;
class UCanvasPanel;
class UCommonTextBlock;
class UCommonActionWidget;
class UTextBlock;
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnUIButtonActivatedByTimeout);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnUIButtonTimerUpdated, float, TimeRemaining);
struct FUIStyle;
struct FUIStyleButton;
/**
* Botão base do ZMMO — porte 1:1 do UI_Button_Master do projeto Hyper para C++
* auto-configurável, trocando o Struct_Style_Button do Hyper pela camada de
* tema C++ (FUIStyle via UZMMOThemeSubsystem::GetActiveUIStyle()).
*
* Fundação: CommonUI (UCommonButtonBase) cuida de input/foco/click.
* Camada Abstract (padrão "_Abstract" do Hyper). WBP UI_Button_Base herda
* disto; UI_Button_Master herda do WBP. Os widgets visuais são opcionais
* (BindWidgetOptional) e nomeados como no Hyper.
*
* Auto-config: NativePreConstruct chama InitializeButton (text/number/icon/
* sizebox/estilo) + ApplyInputActionStyle. Reage à troca de tema.
* Botão base do ZMMO (CommonUI). Hoje expõe BindWidgetOptional dos widgets
* que o WBP UI_Button_Master_New consome, e helpers pra ler o estilo ativo
* de DT_UI_Styles. A aplicação visual (pintar brushes/cores/fonte) entra
* depois — esta camada só prepara o acesso.
*/
UCLASS(Abstract, Blueprintable)
class ZMMO_API UUIButton_Base : public UCommonButtonBase
{
GENERATED_BODY()
public:
// ---- Botão ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button")
EUIButtonShape ButtonTypeSelection = EUIButtonShape::Rectangle;
/**
* Variante visual do botão (dropdown vem de FUIStyle.Button.Backgrounds/
* Strokes/Fonts no DT_UI_Styles + as 4 clássicas como fallback).
* Data-driven via FName, sem enum fixo (padrão Hyper-style).
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button",
meta = (GetOptions = "GetVariantOptions"))
FName Variant = TEXT("Primary");
/** Opções do dropdown de Variant (data-driven do DT_UI_Styles). */
UFUNCTION()
TArray<FString> GetVariantOptions() const;
/** Hyper "Transform Policy = To Upper": força o texto em maiúsculas. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button")
bool bUppercaseText = true;
// ---- Text Style ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style")
FText ButtonTextValue = FText::FromString(TEXT("Button Text"));
/** Alinhamento do texto dentro do botão (esquerda/centro/direita). */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style")
TEnumAsByte<ETextJustify::Type> TextAlign = ETextJustify::Center;
/** true = texto escala p/ caber no tamanho do botão (ScaleBox ScaleToFit);
* false = usa o tamanho fixo da fonte da variante. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Text Style")
bool bAutoSizeText = true;
/** Arredonda o Background (RoundedBox) e desenha a borda na cor da
* variante. Desligado = caixa reta. Raio/largura vêm de FUIStyleButton. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Button")
bool bRoundedBackground = true;
// ---- Number Text ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text")
bool bShowNumberText = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Number Text")
int32 NumberTextAmount = 0;
// ---- Icon ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon")
bool bUseIconInsteadOfText = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon")
TObjectPtr<UObject> IconImage = nullptr;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Icon")
EUIIconType IconTypeSelection = EUIIconType::None;
// ---- Size ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size")
float InHeightOverride = 50.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size")
float InWidthOverride = 150.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Size")
bool bDoNotOverrideWithSizebox = false;
// ---- Input Action Style ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style")
bool bShowInputActionWidget = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style")
FMargin InputPadding = FMargin(0.f, 0.f, 0.f, -22.f);
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style")
TEnumAsByte<EHorizontalAlignment> InputHorzAlignment = HAlign_Center;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input Action Style")
TEnumAsByte<EVerticalAlignment> InputVertAlignment = VAlign_Bottom;
// ---- Deactivate by Timer ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer")
bool bDeactivatedOnConstructWithTimeout = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Deactivate By Timer")
float Timeout = 0.f;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Timer")
float TimeUpdateFrequency = 0.05f;
UPROPERTY(BlueprintReadOnly, Category = "Timer")
float TimeRemaining = 0.f;
UPROPERTY(BlueprintAssignable, Category = "Timer")
FOnUIButtonActivatedByTimeout OnButtonActivatedByTimeout;
UPROPERTY(BlueprintAssignable, Category = "Timer")
FOnUIButtonTimerUpdated OnButtonTimerUpdated;
// ---- API (porte 1:1 das funções do Hyper) ----
UFUNCTION(BlueprintCallable, Category = "UI Button")
void SetButtonText(FText InText);
UFUNCTION(BlueprintCallable, Category = "UI Button")
void SetNumberText();
UFUNCTION(BlueprintCallable, Category = "UI Button")
void SetIcon();
UFUNCTION(BlueprintCallable, Category = "UI Button")
void ApplySizeBox();
UFUNCTION(BlueprintCallable, Category = "UI Button")
void ApplyInputActionStyle();
/** Orquestrador (Hyper "Initialize Button"): aplica tudo + estilo do tema. */
UFUNCTION(BlueprintCallable, Category = "UI Button")
void InitializeButton();
/** Re-resolve os tokens do tema ativo e reaplica (Hyper "Update Button Style"). */
UFUNCTION(BlueprintCallable, Category = "UI Style")
void RefreshUIStyle();
/** Estado visual do botão (mapeia nos tokens BgNormal/Hover/Pressed/Disabled). */
enum class EUIButtonVisual : uint8 { Normal, Hovered, Pressed, Disabled };
/** Pinta o Background (fill + borda) conforme o estado, re-resolvendo o tema. */
void ApplyVisualState(EUIButtonVisual State);
protected:
virtual void NativePreConstruct() override;
virtual void NativeConstruct() override;
virtual void NativeDestruct() override;
// ---- Widgets BindWidgetOptional (espelham a árvore do UI_Button_Master_New) ----
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<USizeBox> SizeBox;
// Estados do UCommonButtonBase → repinta o Background pela variante.
virtual void NativeOnHovered() override;
virtual void NativeOnUnhovered() override;
virtual void NativeOnPressed() override;
virtual void NativeOnReleased() override;
virtual void NativeOnEnabled() override;
virtual void NativeOnDisabled() override;
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<UImage> ImgStoke;
/** Hook opcional: o WBP pode estender/sobrescrever a aplicação visual. */
UFUNCTION(BlueprintImplementableEvent, Category = "UI Style",
meta = (DisplayName = "Apply UI Style"))
void BP_ApplyUIStyle(const FUIStyleButton& Button, const FUIStyleButtonVariant& VariantStyle);
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<UCanvasPanel> CanvasPanel_Text;
FUIStyleButtonVariant ResolveVariant(const FUIStyleButton& Button) const;
// ---- Widgets visuais opcionais (nomes espelham o Hyper) ----
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<USizeBox> SizeBox_Master;
UPROPERTY(meta = (BindWidgetOptional))
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<UCommonTextBlock> ButtonText;
/** Envolve o ButtonText: escala o texto p/ caber no botão (bAutoSizeText). */
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UScaleBox> TextScaleBox;
UPROPERTY(meta = (BindWidgetOptional))
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<UTextBlock> Number_Text;
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UImage> Image_Icon;
UPROPERTY(BlueprintReadOnly, Category = "UI Button", meta = (BindWidgetOptional))
TObjectPtr<UImage> ImgIcon;
/** Fundo temável (nossa adição sobre o Hyper, p/ FUIStyle). */
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UBorder> Background;
// ---- Acesso ao estilo ativo (DT_UI_Styles) — C++ only ----
private:
void StartDeactivateTimeout();
void HandleTimeoutFinished();
void HandleTimerTick();
/** Carrega DT_UI_Styles e devolve o FUIStyle da row pedida (default "Default").
* Retorna nullptr se a DT ou a row não existirem. Ponteiro válido enquanto
* a DT estiver carregada — uso típico é leitura imediata, sem armazenar. */
const FUIStyle* GetActiveUIStyle(FName ThemeId = TEXT("Default")) const;
UFUNCTION()
void HandleThemeChanged(FName NewThemeId);
bool bThemeBound = false;
FTimerHandle InactivityTimer;
FTimerHandle TimerTickHandle;
/** Atalho: devolve só o bloco Button do FUIStyle ativo. */
const FUIStyleButton* GetActiveButtonStyle(FName ThemeId = TEXT("Default")) const;
};

View File

@@ -52,83 +52,43 @@ void UUIPanel_Base::RefreshUIStyle()
const FUIStyle AS = ResolvePanelStyle(this, Fallback);
const FUIStylePanel& P = AS.Panel;
// ---- Modo TEXTURA (padrão Hyper): brush por EUIPanelTexture ----
// Fiel ao UI_Panel_Master do Hyper: só faz SetBrush nos dois Borders
// (Find no mapa por chave). Sem padding/visibility — a arte da textura
// já traz a moldura; o conteúdo é injetado pelo WBP filho.
// Seed dos mapas da instância a partir do tema quando vazios — assim o
// Details mostra Panel Background/Outline preenchidos e editáveis (igual
// ao Hyper, cujo `Panels` é alimentado pelo estilo). Edições do designer
// têm prioridade; o tema (P) é fallback por chave.
if (Panels.PanelBackground.Num() == 0) { Panels.PanelBackground = P.PanelBackground; }
if (Panels.PanelOutline.Num() == 0) { Panels.PanelOutline = P.PanelOutline; }
// bUseTheme = true -> brushes vêm da DT (lookup pelas 3 chaves).
// bUseTheme = false -> brushes vêm dos 3 FSlateBrush em Panels (direto).
const FSlateBrush* BgTex = nullptr;
if (PanelTexture != EUIPanelTexture::None)
static const FSlateBrush NoDrawBrush = []
{
BgTex = Panels.PanelBackground.Find(PanelTexture);
if (!BgTex) { BgTex = P.PanelBackground.Find(PanelTexture); }
}
if (BgTex)
FSlateBrush B;
B.DrawAs = ESlateBrushDrawType::NoDrawType;
return B;
}();
auto ResolveFromTheme = [](EUIPanelTexture Key,
const TMap<EUIPanelTexture, FSlateBrush>& Map) -> const FSlateBrush*
{
Background->SetBrush(*BgTex);
if (Outline)
{
// Hyper: Find sempre seguido de SetBrush. No Hyper a entrada de
// outline ausente é um brush TRANSPARENTE (alpha 0) — não some
// widget, só não desenha. Um FSlateBrush default desenha um
// retângulo BRANCO; o equivalente correto é DrawAs=NoDrawType.
const FSlateBrush* OlTex = Panels.PanelOutline.Find(PanelTexture);
if (!OlTex) { OlTex = P.PanelOutline.Find(PanelTexture); }
static const FSlateBrush NoDrawBrush = []
{
FSlateBrush B;
B.DrawAs = ESlateBrushDrawType::NoDrawType;
return B;
}();
Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush);
}
BP_ApplyPanelStyle(P);
return;
}
return (Key == EUIPanelTexture::None) ? nullptr : Map.Find(Key);
};
const FSlateBrush* BgTex = bUseTheme
? ResolveFromTheme(BackgroundKey, P.PanelBackground)
: &Panels.PanelBackground;
Background->SetBrush(BgTex ? *BgTex : NoDrawBrush);
// ---- Modo COR (RoundedBox) ----
if (Outline)
{
Outline->SetVisibility(ESlateVisibility::Collapsed);
const FSlateBrush* OlTex = bUseTheme
? ResolveFromTheme(OutlineKey, P.PanelOutline)
: &Panels.PanelOutline;
Outline->SetBrush(OlTex ? *OlTex : NoDrawBrush);
}
FLinearColor Fill;
FLinearColor OutlineColor = P.BorderColor;
switch (PanelType)
if (OutlineEffect)
{
case EUIPanelType::Secondary: Fill = P.BgRaised; break;
case EUIPanelType::Tertiary: Fill = P.BgSunken; break;
case EUIPanelType::Card: Fill = P.CardSelectedBg; OutlineColor = P.CardSelectedBorder; break;
case EUIPanelType::Primary:
case EUIPanelType::None:
default: Fill = P.Bg; break;
const FSlateBrush* OeTex = bUseTheme
? ResolveFromTheme(OutlineEffectKey, P.PanelOutlineEffect)
: &Panels.PanelOutlineEffect;
OutlineEffect->SetBrush(OeTex ? *OeTex : NoDrawBrush);
}
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(OutlineColor), P.BorderWidth);
Brush.OutlineSettings.RoundingType = ESlateBrushRoundingType::FixedRadius;
}
else
{
Brush.DrawAs = ESlateBrushDrawType::Box;
Brush.OutlineSettings = FSlateBrushOutlineSettings(
FVector4(0, 0, 0, 0), FSlateColor(OutlineColor), P.BorderWidth);
}
Background->SetBrush(Brush);
Background->SetPadding(bUseSmallPadding ? P.PaddingSmall : P.Padding);
BP_ApplyPanelStyle(P);
}

View File

@@ -23,34 +23,31 @@ 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;
/**
* Conjunto de textura (modo Hyper). Se != None E houver brush no mapa
* FUIStylePanel.PanelBackground p/ esta chave, usa o painel TEXTURIZADO
* (brush bg + outline). Senão, cai no modo COR (RoundedBox acima).
* true = lê brushes da DT_UI_Styles (FUIStylePanel) — tema centralizado.
* false = ignora a DT e usa apenas `Panels` (brushes manuais no Details).
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel")
EUIPanelTexture PanelTexture = EUIPanelTexture::None;
bool bUseTheme = true;
/**
* Mapas de brush por instância — espelha o `Panels` do UI_Panel_Master
* do Hyper (Details mostra Panel Background / Panel Outline editáveis).
* Tem prioridade. Se não houver brush p/ a chave aqui, cai no tema
* (DT_UI_Styles). Em NativePreConstruct é semeado a partir do tema
* quando vazio, então por padrão o tema manda — como no Hyper.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel")
// ---- Modo TEMA: escolhe a chave do mapa FUIStylePanel pra cada Border. ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel",
meta = (EditCondition = "bUseTheme", EditConditionHides))
EUIPanelTexture BackgroundKey = EUIPanelTexture::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel",
meta = (EditCondition = "bUseTheme", EditConditionHides))
EUIPanelTexture OutlineKey = EUIPanelTexture::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel",
meta = (EditCondition = "bUseTheme", EditConditionHides))
EUIPanelTexture OutlineEffectKey = EUIPanelTexture::None;
// ---- Modo MANUAL: 3 brushes editáveis direto no Details. ----
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Panel",
meta = (EditCondition = "!bUseTheme", EditConditionHides))
FUIPanelBrushSet Panels;
/** Re-resolve os tokens do tema ativo e reaplica no Background. */
@@ -75,6 +72,10 @@ protected:
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UBorder> Outline;
/** Efeito sobre o contorno (glow/embossing/etc.) — pintado acima do Outline. */
UPROPERTY(meta = (BindWidgetOptional))
TObjectPtr<UBorder> OutlineEffect;
private:
UFUNCTION()
void HandleThemeChanged(FName NewThemeId);