Files
UnrealEngine/Engine/Source/Editor/InternationalizationSettings/Private/InternationalizationSettingsModelDetails.cpp
2025-05-18 13:04:45 +08:00

438 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "InternationalizationSettingsModelDetails.h"
#include "Containers/Array.h"
#include "Containers/UnrealString.h"
#include "Delegates/Delegate.h"
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "DetailWidgetRow.h"
#include "EdGraph/EdGraphSchema.h"
#include "Fonts/SlateFontInfo.h"
#include "Framework/SlateDelegates.h"
#include "HAL/Platform.h"
#include "HAL/PlatformCrt.h"
#include "Internationalization/Culture.h"
#include "Internationalization/CulturePointer.h"
#include "Internationalization/Internationalization.h"
#include "Internationalization/LocalizedTextSourceTypes.h"
#include "Internationalization/Text.h"
#include "Internationalization/TextLocalizationManager.h"
#include "InternationalizationSettingsModel.h"
#include "Layout/Children.h"
#include "Misc/AssertionMacros.h"
#include "Misc/Attribute.h"
#include "SCulturePicker.h"
#include "Styling/SlateTypes.h"
#include "Templates/Casts.h"
#include "Types/SlateEnums.h"
#include "Types/SlateStructs.h"
#include "UObject/Class.h"
#include "UObject/UObjectIterator.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "Widgets/Input/SCheckBox.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/SCompoundWidget.h"
#include "Widgets/Text/STextBlock.h"
class SWidget;
class UObject;
#define LOCTEXT_NAMESPACE "InternationalizationSettingsModelDetails"
TSharedRef<IDetailCustomization> FInternationalizationSettingsModelDetails::MakeInstance()
{
return MakeShareable(new FInternationalizationSettingsModelDetails);
}
namespace
{
struct FLocalizedCulturesFlyweight
{
TArray<FCultureRef> LocalizedCulturesForEditor;
TArray<FCultureRef> LocalizedCulturesForGame;
FLocalizedCulturesFlyweight()
{
constexpr bool bIncludeDerivedCultures = false;
{
const TArray<FString> LocalizedCultureNames = FTextLocalizationManager::Get().GetLocalizedCultureNames(ELocalizationLoadFlags::Editor);
LocalizedCulturesForEditor = FInternationalization::Get().GetAvailableCultures(LocalizedCultureNames, bIncludeDerivedCultures);
}
{
const TArray<FString> LocalizedCultureNames = FTextLocalizationManager::Get().GetLocalizedCultureNames(ELocalizationLoadFlags::Game);
LocalizedCulturesForGame = FInternationalization::Get().GetAvailableCultures(LocalizedCultureNames, bIncludeDerivedCultures);
}
}
};
class SEditorLanguageComboButton : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SEditorLanguageComboButton){}
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs, const TWeakObjectPtr<UInternationalizationSettingsModel>& InSettingsModel, const TSharedRef<FLocalizedCulturesFlyweight>& InLocalizedCulturesFlyweight)
{
SettingsModel = InSettingsModel;
LocalizedCulturesFlyweight = InLocalizedCulturesFlyweight;
ChildSlot
[
SNew(SCulturePickerCombo)
.Font(IPropertyTypeCustomizationUtils::GetRegularFont())
.SelectedCulture(this, &SEditorLanguageComboButton::GetSelectedCulture)
.OnSelectionChanged(this, &SEditorLanguageComboButton::OnSelectionChanged)
.IsCulturePickable(this, &SEditorLanguageComboButton::IsCulturePickable)
.DisplayNameFormat(SCulturePicker::ECultureDisplayFormat::ActiveAndNativeCultureDisplayName)
.ViewMode(SCulturePicker::ECulturesViewMode::Flat)
];
}
private:
TWeakObjectPtr<UInternationalizationSettingsModel> SettingsModel;
TSharedPtr<FLocalizedCulturesFlyweight> LocalizedCulturesFlyweight;
private:
FCulturePtr GetSelectedCulture() const
{
return FInternationalization::Get().GetCurrentLanguage();
}
void OnSelectionChanged(FCulturePtr SelectedCulture, ESelectInfo::Type SelectInfo)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetEditorLanguage(SelectedCulture.IsValid() ? SelectedCulture->GetName() : TEXT(""));
if (SelectedCulture.IsValid())
{
FInternationalization& I18N = FInternationalization::Get();
I18N.SetCurrentLanguage(SelectedCulture->GetName());
// Find all Schemas and force a visualization cache clear
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* CurrentClass = *ClassIt;
if (UEdGraphSchema* Schema = Cast<UEdGraphSchema>(CurrentClass->GetDefaultObject()))
{
Schema->ForceVisualizationCacheClear();
}
}
}
}
}
bool IsCulturePickable(FCulturePtr Culture)
{
TArray<FString> CultureNames = Culture->GetPrioritizedParentCultureNames();
for (const FString& CultureName : CultureNames)
{
if (LocalizedCulturesFlyweight->LocalizedCulturesForEditor.Contains(Culture))
{
return true;
}
}
return false;
}
};
class SEditorLocaleComboButton : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SEditorLocaleComboButton){}
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs, const TWeakObjectPtr<UInternationalizationSettingsModel>& InSettingsModel, const TSharedRef<FLocalizedCulturesFlyweight>& InLocalizedCulturesFlyweight)
{
SettingsModel = InSettingsModel;
LocalizedCulturesFlyweight = InLocalizedCulturesFlyweight;
ChildSlot
[
SNew(SCulturePickerCombo)
.Font(IPropertyTypeCustomizationUtils::GetRegularFont())
.SelectedCulture(this, &SEditorLocaleComboButton::GetSelectedCulture)
.OnSelectionChanged(this, &SEditorLocaleComboButton::OnSelectionChanged)
.DisplayNameFormat(SCulturePicker::ECultureDisplayFormat::ActiveAndNativeCultureDisplayName)
];
}
private:
TWeakObjectPtr<UInternationalizationSettingsModel> SettingsModel;
TSharedPtr<FLocalizedCulturesFlyweight> LocalizedCulturesFlyweight;
private:
FCulturePtr GetSelectedCulture() const
{
return FInternationalization::Get().GetCurrentLocale();
}
void OnSelectionChanged(FCulturePtr SelectedCulture, ESelectInfo::Type SelectInfo)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetEditorLocale(SelectedCulture.IsValid() ? SelectedCulture->GetName() : TEXT(""));
if (SelectedCulture.IsValid())
{
FInternationalization& I18N = FInternationalization::Get();
I18N.SetCurrentLocale(SelectedCulture->GetName());
// Find all Schemas and force a visualization cache clear
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* CurrentClass = *ClassIt;
if (UEdGraphSchema* Schema = Cast<UEdGraphSchema>(CurrentClass->GetDefaultObject()))
{
Schema->ForceVisualizationCacheClear();
}
}
}
}
}
};
class SPreviewGameLanguageComboButton : public SCompoundWidget
{
SLATE_BEGIN_ARGS(SPreviewGameLanguageComboButton){}
SLATE_END_ARGS()
public:
void Construct(const FArguments& InArgs, const TWeakObjectPtr<UInternationalizationSettingsModel>& InSettingsModel, const TSharedRef<FLocalizedCulturesFlyweight>& InLocalizedCulturesFlyweight)
{
SettingsModel = InSettingsModel;
LocalizedCulturesFlyweight = InLocalizedCulturesFlyweight;
ChildSlot
[
SNew(SCulturePickerCombo)
.Font(IPropertyTypeCustomizationUtils::GetRegularFont())
.SelectedCulture(this, &SPreviewGameLanguageComboButton::GetSelectedCulture)
.OnSelectionChanged(this, &SPreviewGameLanguageComboButton::OnSelectionChanged)
.IsCulturePickable(this, &SPreviewGameLanguageComboButton::IsCulturePickable)
.DisplayNameFormat(SCulturePicker::ECultureDisplayFormat::ActiveAndNativeCultureDisplayName)
.ViewMode(SCulturePicker::ECulturesViewMode::Flat)
];
}
private:
TWeakObjectPtr<UInternationalizationSettingsModel> SettingsModel;
TSharedPtr<FLocalizedCulturesFlyweight> LocalizedCulturesFlyweight;
private:
FCulturePtr GetSelectedCulture() const
{
FString PreviewGameLanguage;
if (SettingsModel.IsValid())
{
SettingsModel->GetPreviewGameLanguage(PreviewGameLanguage);
}
FCulturePtr Culture;
if (!PreviewGameLanguage.IsEmpty())
{
Culture = FInternationalization::Get().GetCulture(PreviewGameLanguage);
}
return Culture;
}
void OnSelectionChanged(FCulturePtr SelectedCulture, ESelectInfo::Type SelectInfo)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetPreviewGameLanguage(SelectedCulture.IsValid() ? SelectedCulture->GetName() : TEXT(""));
if (FTextLocalizationManager::Get().ShouldGameLocalizationPreviewAutoEnable() || FTextLocalizationManager::Get().IsGameLocalizationPreviewEnabled())
{
// Enable the preview again for the newly set culture
FTextLocalizationManager::Get().EnableGameLocalizationPreview();
}
}
}
bool IsCulturePickable(FCulturePtr Culture)
{
TArray<FString> CultureNames = Culture->GetPrioritizedParentCultureNames();
for (const FString& CultureName : CultureNames)
{
if (LocalizedCulturesFlyweight->LocalizedCulturesForGame.Contains(Culture))
{
return true;
}
}
return false;
}
};
}
void FInternationalizationSettingsModelDetails::CustomizeDetails(IDetailLayoutBuilder& DetailLayout)
{
const TWeakObjectPtr<UInternationalizationSettingsModel> SettingsModel = [&]()
{
TArray< TWeakObjectPtr<UObject> > ObjectsBeingCustomized;
DetailLayout.GetObjectsBeingCustomized(ObjectsBeingCustomized);
check(ObjectsBeingCustomized.Num() == 1);
return Cast<UInternationalizationSettingsModel>(ObjectsBeingCustomized.Top().Get());
}();
IDetailCategoryBuilder& DetailCategoryBuilder = DetailLayout.EditCategory("Internationalization", LOCTEXT("InternationalizationCategory", "Internationalization"));
const TSharedRef<FLocalizedCulturesFlyweight> LocalizedCulturesFlyweight = MakeShareable(new FLocalizedCulturesFlyweight());
// Editor Language Setting
const FText EditorLanguageSettingDisplayName = LOCTEXT("EditorLanguageSettingDisplayName", "Editor Language");
const FText EditorLanguageSettingToolTip = LOCTEXT("EditorLanguageSettingToolTip", "The language that the Editor should use for localization (the display language).");
DetailCategoryBuilder.AddCustomRow(EditorLanguageSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(EditorLanguageSettingDisplayName)
.ToolTipText(EditorLanguageSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
[
SNew(SEditorLanguageComboButton, SettingsModel, LocalizedCulturesFlyweight)
];
// Editor Locale Setting
const FText EditorLocaleSettingDisplayName = LOCTEXT("EditorLocaleSettingDisplayName", "Editor Locale");
const FText EditorLocaleSettingToolTip = LOCTEXT("EditorLocaleSettingToolTip", "The locale that the Editor should use for internationalization (numbers, dates, times, etc).");
DetailCategoryBuilder.AddCustomRow(EditorLocaleSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(EditorLocaleSettingDisplayName)
.ToolTipText(EditorLocaleSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
[
SNew(SEditorLocaleComboButton, SettingsModel, LocalizedCulturesFlyweight)
];
// Preview Game Language Setting
const FText PreviewGameLanguageSettingDisplayName = LOCTEXT("PreviewGameLanguageSettingDisplayName", "Preview Game Language");
const FText PreviewGameLanguageSettingToolTip = LOCTEXT("PreviewGameLanguageSettingToolTip", "The language to preview game localization in");
DetailCategoryBuilder.AddCustomRow(PreviewGameLanguageSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(PreviewGameLanguageSettingDisplayName)
.ToolTipText(PreviewGameLanguageSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
[
SNew(SPreviewGameLanguageComboButton, SettingsModel, LocalizedCulturesFlyweight)
];
// Localized Numeric Input
const FText NumericInputSettingDisplayName = LOCTEXT("LocalizedNumericInputLabel", "Use Localized Numeric Input");
const FText NumericInputSettingToolTip = LOCTEXT("LocalizedNumericInputTooltip", "Allow numbers to be displayed and modified in the format for the current locale, rather than in the language agnostic format.");
DetailCategoryBuilder.AddCustomRow(NumericInputSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(NumericInputSettingDisplayName)
.ToolTipText(NumericInputSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
.MaxDesiredWidth(300.0f)
[
SNew(SCheckBox)
.IsChecked_Lambda([=]()
{
return SettingsModel.IsValid() && SettingsModel->ShouldUseLocalizedNumericInput() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
})
.ToolTipText(NumericInputSettingToolTip)
.OnCheckStateChanged_Lambda([=](ECheckBoxState State)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetShouldUseLocalizedNumericInput(State == ECheckBoxState::Checked);
}
})
];
// Localized Property Names
const FText PropertyNamesSettingDisplayName = LOCTEXT("LocalizedEditorPropertyNamesLabel", "Use Localized Property Names");
const FText PropertyNamesSettingToolTip = LOCTEXT("LocalizedEditorPropertyNamesTooltip", "Toggle showing localized property names.");
DetailCategoryBuilder.AddCustomRow(PropertyNamesSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(PropertyNamesSettingDisplayName)
.ToolTipText(PropertyNamesSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
.MaxDesiredWidth(300.0f)
[
SNew(SCheckBox)
.IsChecked_Lambda([=]()
{
return SettingsModel.IsValid() && SettingsModel->ShouldUseLocalizedPropertyNames() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
})
.ToolTipText(PropertyNamesSettingToolTip)
.OnCheckStateChanged_Lambda([=](ECheckBoxState State)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetShouldUseLocalizedPropertyNames(State == ECheckBoxState::Checked);
FTextLocalizationManager::Get().RefreshResources();
}
})
];
// Localized Node and Pin Names
const FText NodeAndPinNamesSettingDisplayName = LOCTEXT("LocalizedGraphEditorNodeAndPinNamesLabel", "Use Localized Graph Editor Node and Pin Names");
const FText NodeAndPinNamesSettingToolTip = LOCTEXT("LocalizedGraphEditorNodeAndPinNamesTooltip", "Toggle localized node and pin names in all graph editors.");
DetailCategoryBuilder.AddCustomRow(NodeAndPinNamesSettingDisplayName)
.NameContent()
[
SNew(STextBlock)
.Text(NodeAndPinNamesSettingDisplayName)
.ToolTipText(NodeAndPinNamesSettingToolTip)
.Font(DetailLayout.GetDetailFont())
]
.ValueContent()
.MaxDesiredWidth(300.0f)
[
SNew(SCheckBox)
.IsChecked_Lambda([=]()
{
return SettingsModel.IsValid() && SettingsModel->ShouldUseLocalizedNodeAndPinNames() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
})
.ToolTipText(NodeAndPinNamesSettingToolTip)
.OnCheckStateChanged_Lambda([=](ECheckBoxState State)
{
if (SettingsModel.IsValid())
{
SettingsModel->SetShouldUseLocalizedNodeAndPinNames(State == ECheckBoxState::Checked);
// Find all Schemas and force a visualization cache clear
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* CurrentClass = *ClassIt;
if (UEdGraphSchema* Schema = Cast<UEdGraphSchema>(CurrentClass->GetDefaultObject()))
{
Schema->ForceVisualizationCacheClear();
}
}
}
})
];
}
#undef LOCTEXT_NAMESPACE