// Copyright (c), Firelight Technologies Pty, Ltd. 2024. #include "FMODSettingsCustomization.h" #include "DetailCategoryBuilder.h" #include "DetailLayoutBuilder.h" #include "DetailWidgetRow.h" #include "FMODSettings.h" #include "IDetailPropertyRow.h" #include "Settings/ProjectPackagingSettings.h" #include "Styling/SlateColor.h" #include "Widgets/DeclarativeSyntaxSupport.h" #include "Widgets/SWidget.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SWidgetSwitcher.h" #include "Widgets/Images/SImage.h" #include "Widgets/Text/STextBlock.h" #include "Widgets/Input/SButton.h" #define LOCTEXT_NAMESPACE "FMODSettings" class SSettingsMessage : public SCompoundWidget { SLATE_BEGIN_ARGS(SSettingsMessage) {} // Called when the Setup button is clicked SLATE_EVENT(FSimpleDelegate, OnSetupClicked) SLATE_END_ARGS() public: void Construct(const FArguments& InArgs) { TSharedRef SettingsOkayWidget = MakeRow( "SettingsEditor.GoodIcon", LOCTEXT("SettingsOkayText", "FMOD Settings are valid, run the Validate FMOD command to perform additional checking."), FText() ); TSharedRef NoContentDirWidget = MakeRow( "SettingsEditor.WarningIcon", LOCTEXT("NoContentDirText", "Bank Output Directory directory has not been set."), FText() ); TSharedRef PackagingSettingsBadWidget = MakeRow( "SettingsEditor.WarningIcon", LOCTEXT("PackagingSettingsBadText", "The packaging settings for copying the FMOD bank files to staging are not correct. It is recommended that:\n" " - The bank output directory for the Desktop platform (or the forced platform if set) is added to the \"Additional Non-Asset Directories To Copy\" list.\n" " - That no other directory containing FMOD banks or assets is added to either the \"Additional Non-Asset Directories To Copy\" list " "or the \"Additional Non-Asset Directories to Package\" list.\n" " - The Generated Assets are added to the \"Additional Asset Directories to Cook\" list." ), LOCTEXT("FixPackagingSettings", "Fix") ); ChildSlot [ SNew(SBorder) .BorderBackgroundColor(this, &SSettingsMessage::GetBorderColor) .BorderImage(FAppStyle::GetBrush("ToolPanel.LightGroupBorder")) .Padding(8.0f) [ SNew(SWidgetSwitcher) .WidgetIndex(this, &SSettingsMessage::GetSetupStateAsInt) + SWidgetSwitcher::Slot() [ SettingsOkayWidget ] + SWidgetSwitcher::Slot() [ NoContentDirWidget ] + SWidgetSwitcher::Slot() [ PackagingSettingsBadWidget ] ] ]; UpdateState(); } void Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { UpdateState(); } private: FSlateColor GetBorderColor() const { if (SettingsState == UFMODSettings::Okay) { return FLinearColor::Green; } else { return FLinearColor(0.8f, 0, 0); } } TSharedRef MakeRow(FName IconName, FText Message, FText ButtonMessage) { TSharedRef Result = SNew(SHorizontalBox) // Status icon + SHorizontalBox::Slot().AutoWidth().VAlign(VAlign_Center)[SNew(SImage).Image(FAppStyle::GetBrush(IconName))] // Notice + SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(16.0f, 0.0f) .VAlign(VAlign_Center)[ SNew(STextBlock) .ColorAndOpacity(FLinearColor::White) .ShadowColorAndOpacity(FLinearColor::Black) .ShadowOffset(FVector2D::UnitVector) .AutoWrapText(true) .Text(Message) ]; if (!ButtonMessage.IsEmpty()) { Result->AddSlot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SButton) .OnClicked(this, &SSettingsMessage::OnButtonPressed) .Text(ButtonMessage) ]; } return Result; } FReply OnButtonPressed() { const UFMODSettings& Settings = *GetDefault(); UProjectPackagingSettings* PackagingSettings = Cast(UProjectPackagingSettings::StaticClass()->GetDefaultObject()); if (SettingsState == UFMODSettings::PackagingSettingsBad) { // Remove any bad entries for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.Num();) { if (PackagingSettings->DirectoriesToAlwaysStageAsNonUFS[i].Path.StartsWith(Settings.BankOutputDirectory.Path)) { PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.RemoveAt(i); } else { ++i; } } for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysStageAsUFS.Num();) { if (PackagingSettings->DirectoriesToAlwaysStageAsUFS[i].Path.StartsWith(Settings.BankOutputDirectory.Path)) { PackagingSettings->DirectoriesToAlwaysStageAsUFS.RemoveAt(i); } else { ++i; } } for (int i = 0; i < PackagingSettings->DirectoriesToAlwaysCook.Num();) { if (PackagingSettings->DirectoriesToAlwaysCook[i].Path.StartsWith(Settings.GetFullContentPath())) { PackagingSettings->DirectoriesToAlwaysCook.RemoveAt(i); } else { ++i; } } // Add correct entry FDirectoryPath BankPath; BankPath.Path = Settings.GetDesktopBankPath(); PackagingSettings->DirectoriesToAlwaysStageAsNonUFS.Add(BankPath); FDirectoryPath generatedFolder; for (FString folder : Settings.GeneratedFolders) { generatedFolder.Path = Settings.GetFullContentPath() / folder; PackagingSettings->DirectoriesToAlwaysCook.Add(generatedFolder); } PackagingSettings->TryUpdateDefaultConfigFile(); } UpdateState(); return FReply::Handled(); } int32 GetSetupStateAsInt() const { return (int32)SettingsState; } void UpdateState() { const UFMODSettings& Settings = *GetDefault(); SettingsState = Settings.Check(); } private: UFMODSettings::EProblem SettingsState; }; TSharedRef FFMODSettingsCustomization::MakeInstance() { return MakeShareable(new FFMODSettingsCustomization); } FFMODSettingsCustomization::FFMODSettingsCustomization() { } void FFMODSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder &DetailLayout) { IDetailCategoryBuilder &PackagingCategory = DetailLayout.EditCategory(TEXT("Notice"), FText::GetEmpty(), ECategoryPriority::Important); TSharedRef PlatformSetupMessage = SNew(SSettingsMessage); PackagingCategory.AddCustomRow(LOCTEXT("Warning", "Warning"), false).WholeRowWidget[PlatformSetupMessage]; } #undef LOCTEXT_NAMESPACE