// Reglages par onglets. Voir LPSettingsSubsystem.h. #include "LPSettingsSubsystem.h" #include "Blueprint/UserWidget.h" #include "Components/Button.h" #include "Components/ButtonSlot.h" #include "Components/CanvasPanel.h" #include "Components/CanvasPanelSlot.h" #include "Components/CheckBox.h" #include "Components/HorizontalBox.h" #include "Components/HorizontalBoxSlot.h" #include "Components/Image.h" #include "Components/NativeWidgetHost.h" #include "Components/Overlay.h" #include "Components/OverlaySlot.h" #include "Components/ScrollBox.h" #include "Components/SizeBox.h" #include "Components/SpinBox.h" #include "Components/TextBlock.h" #include "Components/PanelWidget.h" #include "Components/VerticalBox.h" #include "Components/VerticalBoxSlot.h" #include "Engine/GameInstance.h" #include "Engine/Texture2D.h" #include "LPKeyboardSubsystem.h" #include "LifePlay.h" #include "Styling/CoreStyle.h" #include "Styling/SlateTypes.h" #include "UObject/TextProperty.h" namespace LPSettingsUi { const TCHAR* TabNames[] = { TEXT("General"), TEXT("Audio"), TEXT("Video"), TEXT("Keyboard"), TEXT("Lewd"), TEXT("Misc") }; constexpr int32 TabGeneral = 0; constexpr int32 TabAudio = 1; constexpr int32 TabVideo = 2; constexpr int32 TabKeyboard = 3; constexpr int32 TabLewd = 4; constexpr int32 TabMisc = 5; constexpr int32 TabAlways = -1; constexpr int32 TabHidden = -2; FString VolumeLabel(const FString& Text) { if (Text.Equals(TEXT("Volume - Sex Moans"), ESearchCase::IgnoreCase) || Text.Equals(TEXT("Volume - Sex Moan"), ESearchCase::IgnoreCase)) { return TEXT("Volume - SFX"); } if (Text.Equals(TEXT("Volume - Sex"), ESearchCase::IgnoreCase)) { return TEXT("Volume - Dialogue"); } return Text; } void RenameVolumes(UWidget* Widget) { if (!Widget) { return; } if (FTextProperty* Setting = CastField(Widget->GetClass()->FindPropertyByName(TEXT("SettingText")))) { const FString Next = VolumeLabel(Setting->GetPropertyValue_InContainer(Widget).ToString()); if (Next != Setting->GetPropertyValue_InContainer(Widget).ToString()) { Setting->SetPropertyValue_InContainer(Widget, FText::FromString(Next)); } } if (UTextBlock* Block = Cast(Widget)) { const FString Next = VolumeLabel(Block->GetText().ToString()); if (Next != Block->GetText().ToString()) { Block->SetText(FText::FromString(Next)); } } if (UUserWidget* User = Cast(Widget)) { RenameVolumes(User->GetRootWidget()); } if (UPanelWidget* Panel = Cast(Widget)) { for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index) { RenameVolumes(Panel->GetChildAt(Index)); } } } bool IsOpen(const UWidget* Widget) { if (!Widget) { return false; } const ESlateVisibility Visibility = Widget->GetVisibility(); return Visibility != ESlateVisibility::Collapsed && Visibility != ESlateVisibility::Hidden; } // Libelle visible seulement : les infobulles provoquent des faux positifs // (ex. "on screen" → Video, "prefer quality" → Video pour NewAnimationsOnly). FString RowText(const UWidget* Widget) { FString Text; if (!Widget) { return Text; } if (const FTextProperty* Setting = CastField(Widget->GetClass()->FindPropertyByName(TEXT("SettingText")))) { Text += Setting->GetPropertyValue_InContainer(Widget).ToString(); } TArray Pending; Pending.Add(const_cast(Widget)); while (Pending.Num() > 0) { UWidget* Current = Pending.Pop(EAllowShrinking::No); if (const UTextBlock* Block = Cast(Current)) { Text += TEXT(" ") + Block->GetText().ToString(); } if (const UUserWidget* User = Cast(Current)) { if (UWidget* Root = User->GetRootWidget()) { Pending.Add(Root); } } if (const UPanelWidget* Panel = Cast(Current)) { for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index) { Pending.Add(Panel->GetChildAt(Index)); } } } return Text; } int32 CategoryOf(const UWidget* Widget) { if (Widget && Widget->GetFName() == TEXT("BackButton")) { return TabAlways; } // Reglages qualite video remplaces par les lignes LPVideo* (LPVideoQualitySubsystem). if (Widget && (Widget->GetFName() == TEXT("ShadowQuality") || Widget->GetFName() == TEXT("PostProcessQuality") || Widget->GetFName() == TEXT("PostProcessAAQuality"))) { return TabHidden; } // Noms stables : evite les collisions de mots-cles sur le libelle. if (Widget) { const FName Name = Widget->GetFName(); if (Name == TEXT("NewAnimationsOnly")) { return TabGeneral; } if (Name == TEXT("ShowFPS") || Name == TEXT("ScreenPercentage") || Name == TEXT("FullscreenButton") || Name == TEXT("ToonShading") || Name == TEXT("Censore") || Name == TEXT("CPUSkinning") || Name == TEXT("DisableFurEffect")) { return TabVideo; } } if (Widget && (Widget->GetName().StartsWith(TEXT("LPColor")) || Widget->GetName().StartsWith(TEXT("LPVideo")))) { return TabVideo; } const FString Text = RowText(Widget).ToLower(); if (Text.Contains(TEXT("volume")) || Text.Contains(TEXT("audio")) || Text.Contains(TEXT("sound")) || Text.Contains(TEXT("music"))) { return TabAudio; } if (Text.Contains(TEXT("fps")) || Text.Contains(TEXT("screen percentage")) || Text.Contains(TEXT("shadow")) || Text.Contains(TEXT("fullscreen")) || Text.Contains(TEXT("window")) || Text.Contains(TEXT("toon")) || Text.Contains(TEXT("depth of field")) || Text.Contains(TEXT("censor")) || Text.Contains(TEXT("morph")) || Text.Contains(TEXT("dog")) || Text.Contains(TEXT("jiggle")) || Text.Contains(TEXT("resolution")) || Text.Contains(TEXT("vsync")) || Text.Contains(TEXT("brightness")) || (Text.Contains(TEXT("quality")) && (Text.Contains(TEXT("shadow")) || Text.Contains(TEXT("aa ")) || Text.Contains(TEXT("anti-alias")) || Text.Contains(TEXT("post process")) || Text.Contains(TEXT("pp ")) || Text.Contains(TEXT("overall")) || Text.Contains(TEXT("texture")) || Text.Contains(TEXT("effect"))))) { return TabVideo; } if (Text.Contains(TEXT("sex")) || Text.Contains(TEXT("condom")) || Text.Contains(TEXT("squirt")) || Text.Contains(TEXT("pregnan")) || Text.Contains(TEXT("underwear")) || Text.Contains(TEXT("orientation")) || Text.Contains(TEXT("trans")) || Text.Contains(TEXT("relationship")) || Text.Contains(TEXT("moan")) || Text.Contains(TEXT("lewd"))) { return TabLewd; } if (Text.Contains(TEXT("difficult")) || Text.Contains(TEXT("clock")) || Text.Contains(TEXT("camera")) || Text.Contains(TEXT("scene")) || Text.Contains(TEXT("pause")) || Text.Contains(TEXT("timeout")) || Text.Contains(TEXT("tutorial")) || Text.Contains(TEXT("marker")) || Text.Contains(TEXT("building")) || Text.Contains(TEXT("animation")) || Text.Contains(TEXT("action filter")) || Text.Contains(TEXT("dye")) || Text.Contains(TEXT("stat")) || Text.Contains(TEXT("child")) || Text.Contains(TEXT("redress"))) { return TabGeneral; } return TabMisc; } void WireMainMenuGI(UUserWidget* Menu, UGameInstance* GI) { if (!Menu || !GI) { return; } FObjectPropertyBase* Prop = CastField(Menu->GetClass()->FindPropertyByName(TEXT("GI"))); if (!Prop || Prop->GetObjectPropertyValue_InContainer(Menu)) { return; } if (!GI->IsA(Prop->PropertyClass)) { return; } Prop->SetObjectPropertyValue_InContainer(Menu, GI); UE_LOG(LogLifePlay, Display, TEXT("Reglages : MainMenu.GI rattache (evite les spinbox / cases None sur l'ecran titre)")); } void CollectCheckBoxes(UWidget* Widget, TArray& Out) { if (!Widget) { return; } if (UCheckBox* Box = Cast(Widget)) { Out.Add(Box); return; } if (UUserWidget* User = Cast(Widget)) { CollectCheckBoxes(User->GetRootWidget(), Out); } if (UPanelWidget* Panel = Cast(Widget)) { for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index) { CollectCheckBoxes(Panel->GetChildAt(Index), Out); } } } void CollectSpinBoxes(UWidget* Widget, TArray& Out) { if (!Widget) { return; } if (USpinBox* Box = Cast(Widget)) { Out.Add(Box); return; } if (UUserWidget* User = Cast(Widget)) { CollectSpinBoxes(User->GetRootWidget(), Out); } if (UPanelWidget* Panel = Cast(Widget)) { for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index) { CollectSpinBoxes(Panel->GetChildAt(Index), Out); } } } void TintBrush(FSlateBrush& Brush, const FLinearColor& Tint) { Brush.TintColor = FSlateColor(Tint); } // Cases et spinboxes BP : style Slate clair → illisible sur fond flou clair. // Le curseur Contrast du color grading les revelait sans corriger le style. void HardenSettingsControls(UScrollBox* Scroll) { if (!Scroll) { return; } FCheckBoxStyle CheckStyle = FCoreStyle::Get().GetWidgetStyle("Checkbox"); const FLinearColor Unchecked(0.08f, 0.08f, 0.08f, 1.f); const FLinearColor UncheckedHover(0.18f, 0.18f, 0.18f, 1.f); const FLinearColor Checked(0.12f, 0.42f, 0.95f, 1.f); const FLinearColor CheckedHover(0.2f, 0.5f, 1.f, 1.f); TintBrush(CheckStyle.UncheckedImage, Unchecked); TintBrush(CheckStyle.UncheckedHoveredImage, UncheckedHover); TintBrush(CheckStyle.UncheckedPressedImage, Unchecked); TintBrush(CheckStyle.CheckedImage, Checked); TintBrush(CheckStyle.CheckedHoveredImage, CheckedHover); TintBrush(CheckStyle.CheckedPressedImage, Checked); TintBrush(CheckStyle.UndeterminedImage, Unchecked); TintBrush(CheckStyle.UndeterminedHoveredImage, UncheckedHover); TintBrush(CheckStyle.UndeterminedPressedImage, Unchecked); CheckStyle.BorderBackgroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f)); CheckStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f)); FSpinBoxStyle SpinStyle = FCoreStyle::Get().GetWidgetStyle("SpinBox"); const FLinearColor SpinBg(0.06f, 0.06f, 0.06f, 0.95f); const FLinearColor SpinBgHover(0.12f, 0.12f, 0.12f, 0.98f); const FLinearColor SpinBgActive(0.16f, 0.16f, 0.16f, 1.f); TintBrush(SpinStyle.BackgroundBrush, SpinBg); TintBrush(SpinStyle.HoveredBackgroundBrush, SpinBgHover); TintBrush(SpinStyle.ActiveBackgroundBrush, SpinBgActive); TintBrush(SpinStyle.ActiveFillBrush, FLinearColor(0.2f, 0.45f, 0.9f, 0.85f)); SpinStyle.ForegroundColor = FSlateColor(FLinearColor(1.f, 1.f, 1.f, 1.f)); TArray Boxes; TArray Spins; for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index) { CollectCheckBoxes(Scroll->GetChildAt(Index), Boxes); CollectSpinBoxes(Scroll->GetChildAt(Index), Spins); } for (UCheckBox* Box : Boxes) { if (!Box) { continue; } Box->SetIsEnabled(true); Box->SetRenderOpacity(1.f); Box->SetWidgetStyle(CheckStyle); { FWidgetTransform Xform = Box->GetRenderTransform(); if (FMath::Max(FMath::Abs(Xform.Scale.X), FMath::Abs(Xform.Scale.Y)) < 1.5f) { Xform.Scale = FVector2D(2.f, 2.f); Box->SetRenderTransform(Xform); } } } for (USpinBox* Spin : Spins) { if (!Spin) { continue; } Spin->SetIsEnabled(true); Spin->SetRenderOpacity(1.f); 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 < 18.f) { Font.Size = 18.f; } Spin->SetFont(Font); } if (UWidget* ShowFPS = [&]() -> UWidget* { for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index) { if (UWidget* Child = Scroll->GetChildAt(Index)) { if (Child->GetFName() == TEXT("ShowFPS")) { return Child; } } } return nullptr; }()) { ShowFPS->SetIsEnabled(true); ShowFPS->SetRenderOpacity(1.f); } } UTextBlock* FirstText(UWidget* Widget) { if (!Widget) { return nullptr; } if (UTextBlock* Text = Cast(Widget)) { return Text; } if (UUserWidget* User = Cast(Widget)) { if (UTextBlock* Text = FirstText(User->GetRootWidget())) { return Text; } } if (UPanelWidget* Panel = Cast(Widget)) { for (int32 Index = 0; Index < Panel->GetChildrenCount(); ++Index) { if (UTextBlock* Text = FirstText(Panel->GetChildAt(Index))) { return Text; } } } return nullptr; } UImage* RoundIcon(UWidget* Owner, const TCHAR* TexturePath, float Size) { UImage* Image = NewObject(Owner); if (UTexture2D* Texture = LoadObject(nullptr, TexturePath)) { FSlateBrush Brush; Brush.SetResourceObject(Texture); Brush.ImageSize = FVector2D(Size, Size); Image->SetBrush(Brush); } return Image; } } void ULPSettingsSubsystem::Initialize(FSubsystemCollectionBase& Collection) { Super::Initialize(Collection); // Chaque frame : le BP Settings peut reforcer Visible sur toutes les lignes entre deux ticks. TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPSettingsSubsystem::Tick), 0.f); } void ULPSettingsSubsystem::Deinitialize() { FTSTicker::GetCoreTicker().RemoveTicker(TickHandle); Super::Deinitialize(); } bool ULPSettingsSubsystem::Tick(float DeltaTime) { (void)DeltaTime; PlaceSettingsButton(); UScrollBox* Scroll = nullptr; for (TObjectIterator It; It; ++It) { UScrollBox* Candidate = *It; if (!Candidate || Candidate->GetFName() != TEXT("SettingsScrollBox") || Candidate->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) { continue; } const UUserWidget* MenuOwner = Candidate->GetTypedOuter(); if (!MenuOwner || MenuOwner->HasAnyFlags(RF_ClassDefaultObject) || MenuOwner->GetGameInstance() != GetGameInstance()) { continue; } Scroll = Candidate; break; } if (!Scroll) { return true; } SettingsList = Scroll; const bool bOpen = LPSettingsUi::IsOpen(Scroll); if (TabBar.IsValid()) { TabBar->SetVisibility(bOpen ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); } if (KeyboardHost.IsValid() && !bOpen) { KeyboardHost->SetVisibility(ESlateVisibility::Collapsed); bKeyboardPage = false; } if (!bOpen) { bControlsHardened = false; return true; } if (UUserWidget* Menu = Scroll->GetTypedOuter()) { LPSettingsUi::WireMainMenuGI(Menu, GetGameInstance()); } EnsureTabs(Scroll); const int32 ChildCount = Scroll->GetChildrenCount(); if (!bControlsHardened || ChildCount != LastChildCount) { LPSettingsUi::HardenSettingsControls(Scroll); bControlsHardened = true; LastChildCount = ChildCount; } ApplyRows(); return true; } void ULPSettingsSubsystem::OpenSettings() { if (UButton* Source = SourceSettings.Get()) { Source->OnClicked.Broadcast(); } } void ULPSettingsSubsystem::PlaceSettingsButton() { UVerticalBox* Menu = nullptr; UButton* TitleMods = nullptr; UButton* Original = nullptr; UButton* Ours = nullptr; for (TObjectIterator It; It; ++It) { UVerticalBox* Box = *It; if (!Box || Box->GetFName() != TEXT("MainMenuOptions") || Box->HasAnyFlags(RF_ClassDefaultObject | RF_ArchetypeObject)) { continue; } const UUserWidget* MenuOwner = Box->GetTypedOuter(); if (!MenuOwner || MenuOwner->HasAnyFlags(RF_ClassDefaultObject) || MenuOwner->GetGameInstance() != GetGameInstance()) { continue; } UButton* FoundTitle = nullptr; for (int32 Index = 0; Index < Box->GetChildrenCount(); ++Index) { if (UButton* Child = Cast(Box->GetChildAt(Index))) { if (Child->GetFName() == TEXT("ModManagerTitleButton")) { FoundTitle = Child; break; } } } if (FoundTitle) { Menu = Box; TitleMods = FoundTitle; break; } } if (!Menu || !TitleMods) { return; } for (int32 Index = 0; Index < Menu->GetChildrenCount(); ++Index) { UButton* Child = Cast(Menu->GetChildAt(Index)); if (!Child) { continue; } if (Child->GetFName() == TEXT("SettingsButton")) { Original = Child; } else if (Child->GetFName() == TEXT("LPSettingsTitleButton")) { Ours = Child; } } if (Original) { SourceSettings = Original; } if (!Ours) { Ours = NewObject(Menu, TEXT("LPSettingsTitleButton")); Ours->SetStyle(TitleMods->GetStyle()); UOverlay* Icon = NewObject(Ours); UImage* Circle = LPSettingsUi::RoundIcon(Icon, TEXT("/Game/Heavy/New/whitecircle.whitecircle"), 50.f); UImage* Gear = LPSettingsUi::RoundIcon(Icon, TEXT("/Game/Heavy/New/settings.settings"), 42.f); if (UOverlaySlot* Slot = Cast(Icon->AddChild(Circle))) { Slot->SetHorizontalAlignment(HAlign_Fill); Slot->SetVerticalAlignment(VAlign_Fill); } if (UOverlaySlot* Slot = Cast(Icon->AddChild(Gear))) { Slot->SetHorizontalAlignment(HAlign_Fill); Slot->SetVerticalAlignment(VAlign_Fill); Slot->SetPadding(FMargin(6.f)); } USizeBox* IconFrame = NewObject(Ours); IconFrame->SetWidthOverride(50.f); IconFrame->SetHeightOverride(50.f); IconFrame->AddChild(Icon); UTextBlock* Label = NewObject(Ours); Label->SetText(FText::FromString(TEXT("Settings"))); if (const UTextBlock* Sample = LPSettingsUi::FirstText(TitleMods)) { Label->SetFont(Sample->GetFont()); Label->SetColorAndOpacity(Sample->GetColorAndOpacity()); Label->SetShadowOffset(Sample->GetShadowOffset()); } else { Label->SetFont(FCoreStyle::GetDefaultFontStyle("Regular", 26)); } UHorizontalBox* Row = NewObject(Ours); if (UHorizontalBoxSlot* Slot = Row->AddChildToHorizontalBox(IconFrame)) { Slot->SetPadding(FMargin(0.f, 0.f, 10.f, 0.f)); Slot->SetVerticalAlignment(VAlign_Center); } if (UHorizontalBoxSlot* Slot = Row->AddChildToHorizontalBox(Label)) { Slot->SetVerticalAlignment(VAlign_Center); } Ours->SetContent(Row); if (UButtonSlot* ContentSlot = Cast(Row->Slot)) { ContentSlot->SetHorizontalAlignment(HAlign_Left); ContentSlot->SetVerticalAlignment(VAlign_Center); } if (Original) { Ours->SetToolTipText(Original->GetToolTipText()); } FScriptDelegate Delegate; Delegate.BindUFunction(this, GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, OpenSettings)); Ours->OnClicked.Add(Delegate); UE_LOG(LogLifePlay, Display, TEXT("Reglages : entree Settings ajoutee sur l'ecran titre")); } // InsertChildAt ne réordonne que le tableau UMG. La boîte Slate, elle, garde le bouton en dernier, // donc sous Exit. On reconstruit les emplacements pour que l'ordre peint soit celui du tableau. if (OrderedMenu.Get() != Menu) { struct FKept { UWidget* Widget = nullptr; FMargin Padding; EHorizontalAlignment HAlign = HAlign_Fill; EVerticalAlignment VAlign = VAlign_Fill; FSlateChildSize Size; }; TArray Kept; for (int32 Index = 0; Index < Menu->GetChildrenCount(); ++Index) { UWidget* Child = Menu->GetChildAt(Index); if (!Child || Child == Ours) { continue; } FKept Item; Item.Widget = Child; if (const UVerticalBoxSlot* Slot = Cast(Child->Slot)) { Item.Padding = Slot->GetPadding(); Item.HAlign = Slot->GetHorizontalAlignment(); Item.VAlign = Slot->GetVerticalAlignment(); Item.Size = Slot->GetSize(); } Kept.Add(Item); } FKept SettingsItem; SettingsItem.Widget = Ours; SettingsItem.Padding = FMargin(0.f, 0.f, 0.f, 10.f); SettingsItem.HAlign = HAlign_Fill; if (const UVerticalBoxSlot* TitleSlot = Cast(TitleMods->Slot)) { SettingsItem.Padding = TitleSlot->GetPadding(); SettingsItem.HAlign = TitleSlot->GetHorizontalAlignment(); SettingsItem.VAlign = TitleSlot->GetVerticalAlignment(); SettingsItem.Size = TitleSlot->GetSize(); } int32 InsertAt = Kept.Num(); for (int32 Index = 0; Index < Kept.Num(); ++Index) { if (Kept[Index].Widget == TitleMods) { InsertAt = Index + 1; break; } } Kept.Insert(SettingsItem, InsertAt); Menu->ClearChildren(); for (const FKept& Item : Kept) { if (UVerticalBoxSlot* Slot = Cast(Menu->AddChild(Item.Widget))) { Slot->SetPadding(Item.Padding); Slot->SetHorizontalAlignment(Item.HAlign); Slot->SetVerticalAlignment(Item.VAlign); Slot->SetSize(Item.Size); } } OrderedMenu = Menu; UE_LOG(LogLifePlay, Display, TEXT("Reglages : Settings place apres Mod Manager")); } const ESlateVisibility TitleVisibility = TitleMods->GetVisibility(); if (Ours->GetVisibility() != TitleVisibility) { Ours->SetVisibility(TitleVisibility); } } void ULPSettingsSubsystem::EnsureTabs(UScrollBox* Scroll) { if (TabBar.IsValid()) { return; } UCanvasPanel* Canvas = Cast(Scroll->GetParent()); UCanvasPanelSlot* ScrollSlot = Cast(Scroll->Slot); if (!Canvas || !ScrollSlot) { return; } UHorizontalBox* Bar = NewObject(Canvas, TEXT("LPSettingsTabs")); UCanvasPanelSlot* BarSlot = Canvas->AddChildToCanvas(Bar); if (!BarSlot) { return; } BarSlot->SetAnchors(ScrollSlot->GetAnchors()); BarSlot->SetAlignment(ScrollSlot->GetAlignment()); BarSlot->SetPosition(ScrollSlot->GetPosition() - FVector2D(0.f, 46.f)); BarSlot->SetSize(FVector2D(ScrollSlot->GetSize().X, 40.f)); BarSlot->SetZOrder(ScrollSlot->GetZOrder() + 1); TabBar = Bar; TabLabels.Reset(); const FName Clicks[] = { GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowGeneral), GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowAudio), GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowVideo), GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowKeyboard), GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowLewd), GET_FUNCTION_NAME_CHECKED(ULPSettingsSubsystem, ShowMisc) }; for (int32 Index = 0; Index < UE_ARRAY_COUNT(LPSettingsUi::TabNames); ++Index) { UButton* Button = NewObject(Bar); UTextBlock* Label = NewObject(Button); Label->SetText(FText::FromString(LPSettingsUi::TabNames[Index])); Label->SetFont(FCoreStyle::GetDefaultFontStyle("Bold", 14)); Button->SetContent(Label); FButtonStyle Style = Button->GetStyle(); Style.NormalPadding = FMargin(10.f, 4.f); Style.PressedPadding = FMargin(10.f, 5.f, 10.f, 3.f); Button->SetStyle(Style); if (UHorizontalBoxSlot* Slot = Cast(Bar->AddChild(Button))) { Slot->SetPadding(FMargin(3.f, 0.f)); Slot->SetSize(FSlateChildSize(ESlateSizeRule::Automatic)); Slot->SetVerticalAlignment(VAlign_Center); } FScriptDelegate Delegate; Delegate.BindUFunction(this, Clicks[Index]); Button->OnClicked.Add(Delegate); TabLabels.Add(Label); } UNativeWidgetHost* Host = NewObject(Canvas, TEXT("LPSettingsKeyboard")); if (UCanvasPanelSlot* HostSlot = Canvas->AddChildToCanvas(Host)) { HostSlot->SetAnchors(ScrollSlot->GetAnchors()); HostSlot->SetAlignment(ScrollSlot->GetAlignment()); HostSlot->SetPosition(ScrollSlot->GetPosition() + FVector2D(0.f, 48.f)); const FVector2D Size = ScrollSlot->GetSize(); HostSlot->SetSize(FVector2D(Size.X, FMath::Max(120.f, Size.Y - 48.f))); HostSlot->SetZOrder(ScrollSlot->GetZOrder() + 1); } Host->SetVisibility(ESlateVisibility::Collapsed); KeyboardHost = Host; ActiveTab = 0; ApplyRows(); UE_LOG(LogLifePlay, Display, TEXT("Reglages : onglets General, Audio, Video, Keyboard, Lewd, Misc")); } void ULPSettingsSubsystem::SelectTab(int32 Index) { ActiveTab = Index; ApplyRows(); } void ULPSettingsSubsystem::ApplyRows() { UScrollBox* Scroll = SettingsList.Get(); if (!Scroll) { return; } for (int32 Index = 0; Index < TabLabels.Num(); ++Index) { if (UTextBlock* Label = TabLabels[Index].Get()) { const bool bOn = Index == ActiveTab; Label->SetColorAndOpacity(FSlateColor(bOn ? FLinearColor(1.f, 0.92f, 0.45f) : FLinearColor(0.75f, 0.75f, 0.75f))); } } for (int32 Index = 0; Index < Scroll->GetChildrenCount(); ++Index) { UWidget* Child = Scroll->GetChildAt(Index); if (!Child || Child->GetFName() == TEXT("LPKeyboardRow")) { if (Child) { Child->SetVisibility(ESlateVisibility::Collapsed); } continue; } LPSettingsUi::RenameVolumes(Child); const int32 Category = LPSettingsUi::CategoryOf(Child); // -1 : toujours visible (BackButton) ; -2 : toujours replie. const bool bShow = Category == LPSettingsUi::TabAlways || (Category != LPSettingsUi::TabHidden && ActiveTab != LPSettingsUi::TabKeyboard && Category == ActiveTab); Child->SetVisibility(bShow ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); } const bool bKeyboard = ActiveTab == LPSettingsUi::TabKeyboard; if (UNativeWidgetHost* Host = KeyboardHost.Get()) { Host->SetVisibility(bKeyboard ? ESlateVisibility::Visible : ESlateVisibility::Collapsed); if (bKeyboard && !bKeyboardPage) { if (ULPKeyboardSubsystem* Keys = GetGameInstance()->GetSubsystem()) { Host->SetContent(Keys->TakeEmbeddedPage()); bKeyboardPage = true; } } } } void ULPSettingsSubsystem::ShowGeneral() { SelectTab(LPSettingsUi::TabGeneral); } void ULPSettingsSubsystem::ShowAudio() { SelectTab(LPSettingsUi::TabAudio); } void ULPSettingsSubsystem::ShowVideo() { SelectTab(LPSettingsUi::TabVideo); } void ULPSettingsSubsystem::ShowKeyboard() { SelectTab(LPSettingsUi::TabKeyboard); } void ULPSettingsSubsystem::ShowLewd() { SelectTab(LPSettingsUi::TabLewd); } void ULPSettingsSubsystem::ShowMisc() { SelectTab(LPSettingsUi::TabMisc); }