258 lines
8 KiB
C++
258 lines
8 KiB
C++
// Lot LISIB - textes lisibles hors du panneau Settings. Voir LPReadableTextSubsystem.h.
|
|
|
|
#include "LPReadableTextSubsystem.h"
|
|
|
|
#include "Blueprint/UserWidget.h"
|
|
#include "Components/EditableTextBox.h"
|
|
#include "Components/PanelWidget.h"
|
|
#include "Components/SpinBox.h"
|
|
#include "Engine/GameInstance.h"
|
|
#include "LPWidgetStyleLibrary.h"
|
|
#include "LifePlay.h"
|
|
#include "Styling/CoreStyle.h"
|
|
#include "Styling/SlateTypes.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
namespace LPReadableText
|
|
{
|
|
// Intervalle entre deux passes : les menus crees entre-temps prennent le style au plus 0,25 s apres leur creation.
|
|
constexpr float PassInterval = 0.25f;
|
|
// Tailles minimales approuvees par l'utilisateur (LISIB-SPEC Q4) : les tailles plus grandes de l'asset sont gardees.
|
|
constexpr float MinSpinFontSize = 16.f;
|
|
constexpr float MinTextBoxFontSize = 14.f;
|
|
|
|
void TintBrush(FSlateBrush& Brush, const FLinearColor& Tint)
|
|
{
|
|
Brush.TintColor = FSlateColor(Tint);
|
|
}
|
|
|
|
// Comme ULPSettingsSubsystem (LPSettingsSubsystem.cpp, HardenSettingsControls), plus le remplissage au repos : le
|
|
// moteur le peint en gris clair quand la SpinBox a une plage (SSpinBox.cpp:240-241), cas des lignes de morphes.
|
|
FSpinBoxStyle MakeSpinStyle()
|
|
{
|
|
FSpinBoxStyle SpinStyle = FCoreStyle::Get().GetWidgetStyle<FSpinBoxStyle>("SpinBox");
|
|
TintBrush(SpinStyle.BackgroundBrush, FLinearColor(0.06f, 0.06f, 0.06f, 0.95f));
|
|
TintBrush(SpinStyle.HoveredBackgroundBrush, FLinearColor(0.12f, 0.12f, 0.12f, 0.98f));
|
|
TintBrush(SpinStyle.ActiveBackgroundBrush, FLinearColor(0.16f, 0.16f, 0.16f, 1.f));
|
|
TintBrush(SpinStyle.ActiveFillBrush, FLinearColor(0.2f, 0.45f, 0.9f, 0.85f));
|
|
TintBrush(SpinStyle.InactiveFillBrush, FLinearColor(0.1f, 0.22f, 0.45f, 0.9f));
|
|
// Le style de base (FCoreStyle::Get() = FAppStyle, Starship) fixe deja un remplissage de survol gris
|
|
// (StarshipCoreStyle.cpp:620) : on le teinte comme le remplissage au repos.
|
|
TintBrush(SpinStyle.HoveredFillBrush, FLinearColor(0.1f, 0.22f, 0.45f, 0.9f));
|
|
SpinStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f));
|
|
return SpinStyle;
|
|
}
|
|
|
|
void MakeSpinReadable(USpinBox* Spin, const FSpinBoxStyle& SpinStyle)
|
|
{
|
|
// USpinBox::SetWidgetStyle recopie dans son membre et Slate pointe sur ce membre (SpinBox.cpp:111, :149-154) :
|
|
// pas de pointeur vers une temporaire, contrairement a la Text Box (ERR-088).
|
|
Spin->SetWidgetStyle(SpinStyle);
|
|
Spin->SetForegroundColor(FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f)));
|
|
FSlateFontInfo Font = Spin->GetFont();
|
|
Font.OutlineSettings.OutlineSize = 2;
|
|
Font.OutlineSettings.bApplyOutlineToDropShadows = true;
|
|
Font.OutlineSettings.OutlineColor = FLinearColor(0.f, 0.f, 0.f, 0.95f);
|
|
if (Font.Size < MinSpinFontSize)
|
|
{
|
|
Font.Size = MinSpinFontSize;
|
|
}
|
|
Spin->SetFont(Font);
|
|
if (Spin->GetRenderOpacity() < 1.f)
|
|
{
|
|
Spin->SetRenderOpacity(1.f);
|
|
}
|
|
}
|
|
|
|
// Pas et decimales demandes par l'utilisateur pendant le PIE LISIB-J1 (2026-10-10) : l'asset ne fixe aucun pas
|
|
// (Delta 0) et la SpinBox affiche jusqu'a 6 decimales (ex. 1,054199). Pose en jeu, sans modifier l'asset (gel).
|
|
struct FSpinStep
|
|
{
|
|
const TCHAR* Menu;
|
|
const TCHAR* Widget;
|
|
float Delta;
|
|
int32 MaxFractionalDigits;
|
|
};
|
|
const FSpinStep SpinSteps[] = {
|
|
{ TEXT("CharGen"), TEXT("MoanPitch"), 0.01f, 2 }, // Sex Moan Pitch Multiplier
|
|
{ TEXT("CharGen"), TEXT("VarianceSpinBox"), 0.1f, 1 }, // Save Preset, Variance
|
|
{ TEXT("StatMenu"), TEXT("VarianceSpinBox"), 0.1f, 1 },
|
|
};
|
|
|
|
void ApplySpinStep(USpinBox* Spin, const FString& MenuName)
|
|
{
|
|
for (const FSpinStep& Step : SpinSteps)
|
|
{
|
|
if (MenuName == Step.Menu && Spin->GetFName() == Step.Widget)
|
|
{
|
|
Spin->SetDelta(Step.Delta);
|
|
Spin->SetMaxFractionalDigits(Step.MaxFractionalDigits);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vrai si la Text Box a ete modifiee. Seules les Text Box sans couleur de texte fixee changent de couleur ; celles
|
|
// dont l'asset fixe une couleur (CharGen.FilenameEntry depuis STE-TXT) la gardent.
|
|
bool MakeTextBoxReadable(UEditableTextBox* Box)
|
|
{
|
|
FEditableTextBoxStyle Style = Box->GetWidgetStyle();
|
|
bool bChanged = false;
|
|
if (!Style.TextStyle.ColorAndOpacity.IsColorSpecified())
|
|
{
|
|
Style.TextStyle.ColorAndOpacity = FSlateColor(FLinearColor(0.f, 0.f, 0.f, 1.f));
|
|
bChanged = true;
|
|
}
|
|
if (Style.TextStyle.Font.Size < MinTextBoxFontSize)
|
|
{
|
|
Style.TextStyle.Font.Size = MinTextBoxFontSize;
|
|
bChanged = true;
|
|
}
|
|
if (bChanged)
|
|
{
|
|
ULPWidgetStyleLibrary::SetEditableTextBoxStyle(Box, Style);
|
|
}
|
|
return bChanged;
|
|
}
|
|
|
|
// Vrai si le widget est sous la liste des reglages (SettingsScrollBox de MainMenu), y compris a travers les
|
|
// widgets utilisateur imbriques (lignes SettingSlider, SettingCheckbox).
|
|
bool IsUnderSettings(const UWidget* Widget)
|
|
{
|
|
const UWidget* Current = Widget;
|
|
for (int32 Depth = 0; Current && Depth < 64; ++Depth)
|
|
{
|
|
if (Current->GetFName() == TEXT("SettingsScrollBox"))
|
|
{
|
|
return true;
|
|
}
|
|
if (const UPanelWidget* Parent = Current->GetParent())
|
|
{
|
|
Current = Parent;
|
|
}
|
|
else
|
|
{
|
|
// Racine d'un arbre de widgets : on remonte au widget utilisateur qui le possede.
|
|
Current = Current->GetTypedOuter<UUserWidget>();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Nom du menu proprietaire, sans le suffixe _C (ex. "CharGen").
|
|
FString MenuNameOf(const UWidget* Widget)
|
|
{
|
|
const UUserWidget* Owner = Widget->GetTypedOuter<UUserWidget>();
|
|
FString OwnerName = Owner ? Owner->GetClass()->GetName() : TEXT("?");
|
|
OwnerName.RemoveFromEnd(TEXT("_C"));
|
|
return OwnerName;
|
|
}
|
|
|
|
FString DescribeWidget(const UWidget* Widget)
|
|
{
|
|
return MenuNameOf(Widget) + TEXT(".") + Widget->GetName();
|
|
}
|
|
}
|
|
|
|
void ULPReadableTextSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPReadableTextSubsystem::Tick), 0.f);
|
|
}
|
|
|
|
void ULPReadableTextSubsystem::Deinitialize()
|
|
{
|
|
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
|
Done.Empty();
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
bool ULPReadableTextSubsystem::Tick(float DeltaTime)
|
|
{
|
|
SinceLastPass += DeltaTime;
|
|
if (SinceLastPass >= LPReadableText::PassInterval)
|
|
{
|
|
SinceLastPass = 0.f;
|
|
ApplyReadableStyles();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ULPReadableTextSubsystem::IsGameWidget(const UWidget* Widget) const
|
|
{
|
|
if (!Widget || Widget->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject))
|
|
{
|
|
return false;
|
|
}
|
|
const UUserWidget* Owner = Widget->GetTypedOuter<UUserWidget>();
|
|
return Owner && !Owner->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject) && Owner->GetGameInstance() == GetGameInstance();
|
|
}
|
|
|
|
void ULPReadableTextSubsystem::ApplyReadableStyles()
|
|
{
|
|
// Les widgets detruits sortent de l'ensemble.
|
|
for (auto It = Done.CreateIterator(); It; ++It)
|
|
{
|
|
if (!It->IsValid())
|
|
{
|
|
It.RemoveCurrent();
|
|
}
|
|
}
|
|
|
|
int32 NewSpins = 0;
|
|
int32 NewTextBoxes = 0;
|
|
TOptional<FSpinBoxStyle> SpinStyle;
|
|
|
|
for (TObjectIterator<USpinBox> It; It; ++It)
|
|
{
|
|
USpinBox* Spin = *It;
|
|
if (!IsGameWidget(Spin) || Done.Contains(Spin))
|
|
{
|
|
continue;
|
|
}
|
|
Done.Add(Spin);
|
|
if (LPReadableText::IsUnderSettings(Spin))
|
|
{
|
|
continue;
|
|
}
|
|
if (!SpinStyle.IsSet())
|
|
{
|
|
SpinStyle = LPReadableText::MakeSpinStyle();
|
|
}
|
|
LPReadableText::MakeSpinReadable(Spin, SpinStyle.GetValue());
|
|
LPReadableText::ApplySpinStep(Spin, LPReadableText::MenuNameOf(Spin));
|
|
++NewSpins;
|
|
const FString Name = LPReadableText::DescribeWidget(Spin);
|
|
if (!Logged.Contains(Name))
|
|
{
|
|
Logged.Add(Name);
|
|
UE_LOG(LogLifePlay, Log, TEXT("LISIB : SpinBox lisible %s"), *Name);
|
|
}
|
|
}
|
|
|
|
for (TObjectIterator<UEditableTextBox> It; It; ++It)
|
|
{
|
|
UEditableTextBox* Box = *It;
|
|
if (!IsGameWidget(Box) || Done.Contains(Box))
|
|
{
|
|
continue;
|
|
}
|
|
Done.Add(Box);
|
|
if (LPReadableText::IsUnderSettings(Box) || !LPReadableText::MakeTextBoxReadable(Box))
|
|
{
|
|
continue;
|
|
}
|
|
++NewTextBoxes;
|
|
const FString Name = LPReadableText::DescribeWidget(Box);
|
|
if (!Logged.Contains(Name))
|
|
{
|
|
Logged.Add(Name);
|
|
UE_LOG(LogLifePlay, Log, TEXT("LISIB : Text Box lisible %s"), *Name);
|
|
}
|
|
}
|
|
|
|
if (NewSpins > 0 || NewTextBoxes > 0)
|
|
{
|
|
UE_LOG(LogLifePlay, Verbose, TEXT("LISIB : passe, %d SpinBox et %d Text Box restylees"), NewSpins, NewTextBoxes);
|
|
}
|
|
}
|