2021-02-14 18:21:05 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "GameFeatureDataDetailsCustomization.h"
2023-01-12 01:48:34 -05:00
# include "GameFeaturePluginOperationResult.h"
2022-11-03 14:18:47 -04:00
# include "UObject/Package.h"
2023-01-12 01:48:34 -05:00
# include "GameFeaturesSubsystem.h"
2021-02-14 18:21:05 -04:00
# include "Widgets/SBoxPanel.h"
# include "Widgets/Input/SButton.h"
2021-03-03 19:18:27 -04:00
# include "Widgets/Images/SImage.h"
2021-02-27 03:58:53 -04:00
# include "SGameFeatureStateWidget.h"
2021-02-14 18:45:46 -04:00
# include "Widgets/Notifications/SErrorText.h"
2021-02-14 18:21:05 -04:00
# include "DetailLayoutBuilder.h"
# include "DetailWidgetRow.h"
2021-02-27 03:58:53 -04:00
# include "Interfaces/IPluginManager.h"
# include "Features/IPluginsEditorFeature.h"
# include "Features/EditorFeatures.h"
2021-03-03 19:18:27 -04:00
# include "Features/IModularFeatures.h"
# include "Misc/MessageDialog.h"
2021-05-17 16:10:04 -04:00
# include "Misc/Paths.h"
2021-02-27 03:58:53 -04:00
2021-02-14 18:21:05 -04:00
# include "GameFeatureData.h"
2022-04-11 13:33:21 -04:00
# include "GameFeatureTypes.h"
2023-01-12 01:48:34 -05:00
# include "Widgets/Text/STextBlock.h"
2021-02-14 18:21:05 -04:00
# define LOCTEXT_NAMESPACE "GameFeatures"
//////////////////////////////////////////////////////////////////////////
// FGameFeatureDataDetailsCustomization
TSharedRef < IDetailCustomization > FGameFeatureDataDetailsCustomization : : MakeInstance ( )
{
return MakeShareable ( new FGameFeatureDataDetailsCustomization ) ;
}
void FGameFeatureDataDetailsCustomization : : CustomizeDetails ( IDetailLayoutBuilder & DetailBuilder )
{
ErrorTextWidget = SNew ( SErrorText )
. ToolTipText ( LOCTEXT ( " ErrorTooltip " , " The error raised while attempting to change the state of this feature " ) ) ;
// Create a category so this is displayed early in the properties
2021-02-27 03:58:53 -04:00
IDetailCategoryBuilder & TopCategory = DetailBuilder . EditCategory ( " Feature State " , FText : : GetEmpty ( ) , ECategoryPriority : : Important ) ;
2021-02-14 18:21:05 -04:00
PluginURL . Reset ( ) ;
ObjectsBeingCustomized . Empty ( ) ;
DetailBuilder . GetObjectsBeingCustomized ( /*out*/ ObjectsBeingCustomized ) ;
2022-11-15 19:42:44 -05:00
if ( ObjectsBeingCustomized . Num ( ) = = 1 & & ! ObjectsBeingCustomized [ 0 ] - > GetPackage ( ) - > HasAnyPackageFlags ( PKG_ForDiffing ) )
2021-02-14 18:21:05 -04:00
{
const UGameFeatureData * GameFeature = CastChecked < const UGameFeatureData > ( ObjectsBeingCustomized [ 0 ] ) ;
TArray < FString > PathParts ;
GameFeature - > GetOutermost ( ) - > GetName ( ) . ParseIntoArray ( PathParts , TEXT ( " / " ) ) ;
UGameFeaturesSubsystem & Subsystem = UGameFeaturesSubsystem : : Get ( ) ;
2022-04-19 13:37:11 -04:00
Subsystem . GetPluginURLByName ( PathParts [ 0 ] , /*out*/ PluginURL ) ;
2021-02-27 03:58:53 -04:00
PluginPtr = IPluginManager : : Get ( ) . FindPlugin ( PathParts [ 0 ] ) ;
2021-02-14 18:21:05 -04:00
const float Padding = 8.0f ;
2021-02-27 03:58:53 -04:00
if ( PluginPtr . IsValid ( ) )
{
const FString ShortFilename = FPaths : : GetCleanFilename ( PluginPtr - > GetDescriptorFileName ( ) ) ;
FDetailWidgetRow & EditPluginRow = TopCategory . AddCustomRow ( LOCTEXT ( " InitialStateSearchText " , " Initial State Edit Plugin " ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " InitialState " , " Initial State " ) )
. ToolTipText ( LOCTEXT ( " InitialStateTooltip " , " The initial or default state of this game feature (determines the state that it will be in at game/editor startup) " ) )
. Font ( DetailBuilder . GetDetailFont ( ) )
]
. ValueContent ( )
[
SNew ( SHorizontalBox )
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( 0.0f , 0.0f , Padding , 0.0f )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. Text ( this , & FGameFeatureDataDetailsCustomization : : GetInitialStateText )
. Font ( DetailBuilder . GetDetailFont ( ) )
]
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. VAlign ( VAlign_Center )
[
SNew ( SButton )
. Text ( LOCTEXT ( " EditPluginButton " , " Edit Plugin " ) )
. OnClicked_Lambda ( [ this ] ( )
{
IModularFeatures & ModularFeatures = IModularFeatures : : Get ( ) ;
if ( ModularFeatures . IsModularFeatureAvailable ( EditorFeatures : : PluginsEditor ) )
{
ModularFeatures . GetModularFeature < IPluginsEditorFeature > ( EditorFeatures : : PluginsEditor ) . OpenPluginEditor ( PluginPtr . ToSharedRef ( ) , nullptr , FSimpleDelegate ( ) ) ;
}
else
{
FMessageDialog : : Open ( EAppMsgType : : Ok , LOCTEXT ( " CannotEditPlugin_PluginBrowserDisabled " , " Cannot open plugin editor because the PluginBrowser plugin is disabled) " ) ) ;
}
return FReply : : Handled ( ) ;
} )
]
] ;
}
2021-02-14 18:21:05 -04:00
FDetailWidgetRow & ControlRow = TopCategory . AddCustomRow ( LOCTEXT ( " ControlSearchText " , " Plugin State Control " ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " CurrentState " , " Current State " ) )
2021-02-27 03:58:53 -04:00
. ToolTipText ( LOCTEXT ( " CurrentStateTooltip " , " The current state of this game feature " ) )
2021-02-14 18:21:05 -04:00
. Font ( DetailBuilder . GetDetailFont ( ) )
]
. ValueContent ( )
. MinDesiredWidth ( 400.0f )
[
SNew ( SVerticalBox )
+ SVerticalBox : : Slot ( )
. AutoHeight ( )
[
2021-02-27 03:58:53 -04:00
SNew ( SGameFeatureStateWidget )
. CurrentState ( this , & FGameFeatureDataDetailsCustomization : : GetCurrentState )
. OnStateChanged ( this , & FGameFeatureDataDetailsCustomization : : ChangeDesiredState )
2021-02-14 18:21:05 -04:00
]
+ SVerticalBox : : Slot ( )
. HAlign ( HAlign_Left )
. Padding ( 0.0f , 4.0f , 0.0f , 0.0f )
[
SNew ( SHorizontalBox )
2023-08-01 15:30:32 -04:00
. Visibility ( this , & FGameFeatureDataDetailsCustomization : : GetVisbililty )
2021-02-14 18:21:05 -04:00
+ SHorizontalBox : : Slot ( )
. AutoWidth ( )
. Padding ( Padding )
. VAlign ( VAlign_Center )
[
SNew ( SImage )
. Image ( FAppStyle : : Get ( ) . GetBrush ( " Icons.Lock " ) )
]
+ SHorizontalBox : : Slot ( )
. FillWidth ( 1.0f )
. Padding ( FMargin ( 0.f , Padding , Padding , Padding ) )
. VAlign ( VAlign_Center )
[
SNew ( STextBlock )
. WrapTextAt ( 300.0f )
. Text ( LOCTEXT ( " Active_PreventingEditing " , " Deactivate the feature before editing the Game Feature Data " ) )
. Font ( DetailBuilder . GetDetailFont ( ) )
. ColorAndOpacity ( FAppStyle : : Get ( ) . GetSlateColor ( TEXT ( " Colors.AccentYellow " ) ) )
]
]
+ SVerticalBox : : Slot ( )
. HAlign ( HAlign_Center )
[
ErrorTextWidget . ToSharedRef ( )
]
] ;
2021-05-17 16:10:04 -04:00
FDetailWidgetRow & TagsRow = TopCategory . AddCustomRow ( LOCTEXT ( " TagSearchText " , " Gameplay Tag Config Path " ) )
. NameContent ( )
[
SNew ( STextBlock )
. Text ( LOCTEXT ( " TagConfigPath " , " Gameplay Tag Config Path " ) )
. ToolTipText ( LOCTEXT ( " TagConfigPathTooltip " , " Path to search for Gameplay Tag ini files. To create feature-specific tags use Add New Tag Source with this path and then Add New Gameplay Tag with that Tag Source. " ) )
. Font ( DetailBuilder . GetDetailFont ( ) )
]
. ValueContent ( )
. MinDesiredWidth ( 400.0f )
[
SNew ( STextBlock )
. Text ( this , & FGameFeatureDataDetailsCustomization : : GetTagConfigPathText )
. Font ( DetailBuilder . GetDetailFont ( ) )
] ;
2021-02-14 18:21:05 -04:00
//@TODO: This disables the mode switcher widget too (and it's a const cast hack...)
// if (IDetailsView* ConstHackDetailsView = const_cast<IDetailsView*>(DetailBuilder.GetDetailsView()))
// {
// ConstHackDetailsView->SetIsPropertyEditingEnabledDelegate(FIsPropertyEditingEnabled::CreateLambda([CapturedThis = this] { return CapturedThis->GetCurrentState() != EGameFeaturePluginState::Active; }));
// }
}
}
void FGameFeatureDataDetailsCustomization : : ChangeDesiredState ( EGameFeaturePluginState DesiredState )
{
2022-01-05 18:36:19 -05:00
EGameFeatureTargetState TargetState = EGameFeatureTargetState : : Installed ;
switch ( DesiredState )
{
case EGameFeaturePluginState : : Installed :
TargetState = EGameFeatureTargetState : : Installed ;
break ;
case EGameFeaturePluginState : : Registered :
TargetState = EGameFeatureTargetState : : Registered ;
break ;
case EGameFeaturePluginState : : Loaded :
TargetState = EGameFeatureTargetState : : Loaded ;
break ;
case EGameFeaturePluginState : : Active :
TargetState = EGameFeatureTargetState : : Active ;
break ;
}
2021-02-14 18:21:05 -04:00
ErrorTextWidget - > SetError ( FText : : GetEmpty ( ) ) ;
const TWeakPtr < FGameFeatureDataDetailsCustomization > WeakThisPtr = StaticCastSharedRef < FGameFeatureDataDetailsCustomization > ( AsShared ( ) ) ;
UGameFeaturesSubsystem & Subsystem = UGameFeaturesSubsystem : : Get ( ) ;
2022-01-05 18:36:19 -05:00
Subsystem . ChangeGameFeatureTargetState ( PluginURL , TargetState , FGameFeaturePluginDeactivateComplete : : CreateStatic ( & FGameFeatureDataDetailsCustomization : : OnOperationCompletedOrFailed , WeakThisPtr ) ) ;
2021-02-14 18:21:05 -04:00
}
EGameFeaturePluginState FGameFeatureDataDetailsCustomization : : GetCurrentState ( ) const
{
2024-01-26 15:43:27 -05:00
if ( PluginURL . IsEmpty ( ) )
{
return EGameFeaturePluginState : : Uninitialized ;
}
2021-02-14 18:21:05 -04:00
return UGameFeaturesSubsystem : : Get ( ) . GetPluginState ( PluginURL ) ;
}
2023-08-01 15:30:32 -04:00
EVisibility FGameFeatureDataDetailsCustomization : : GetVisbililty ( ) const
{
return ( GetCurrentState ( ) = = EGameFeaturePluginState : : Active ) ? EVisibility : : Visible : EVisibility : : Collapsed ;
}
2021-02-27 03:58:53 -04:00
FText FGameFeatureDataDetailsCustomization : : GetInitialStateText ( ) const
2021-02-14 18:21:05 -04:00
{
2021-02-27 03:58:53 -04:00
const EBuiltInAutoState AutoState = UGameFeaturesSubsystem : : DetermineBuiltInInitialFeatureState ( PluginPtr - > GetDescriptor ( ) . CachedJson , FString ( ) ) ;
const EGameFeaturePluginState InitialState = UGameFeaturesSubsystem : : ConvertInitialFeatureStateToTargetState ( AutoState ) ;
return SGameFeatureStateWidget : : GetDisplayNameOfState ( InitialState ) ;
2021-02-14 18:21:05 -04:00
}
2021-05-17 16:10:04 -04:00
FText FGameFeatureDataDetailsCustomization : : GetTagConfigPathText ( ) const
{
2024-01-26 15:43:27 -05:00
if ( PluginURL . IsEmpty ( ) )
{
return LOCTEXT ( " TagConfigPathInvalid " , " Invalid Plugin " ) ;
}
2021-05-17 16:10:04 -04:00
FString PluginFile = UGameFeaturesSubsystem : : Get ( ) . GetPluginFilenameFromPluginURL ( PluginURL ) ;
FString PluginFolder = FPaths : : GetPath ( PluginFile ) ;
FString TagFolder = PluginFolder / TEXT ( " Config " ) / TEXT ( " Tags " ) ;
if ( FPaths : : IsUnderDirectory ( TagFolder , FPaths : : ProjectDir ( ) ) )
{
FPaths : : MakePathRelativeTo ( TagFolder , * FPaths : : ProjectDir ( ) ) ;
}
return FText : : AsCultureInvariant ( TagFolder ) ;
}
2021-02-14 18:21:05 -04:00
void FGameFeatureDataDetailsCustomization : : OnOperationCompletedOrFailed ( const UE : : GameFeatures : : FResult & Result , const TWeakPtr < FGameFeatureDataDetailsCustomization > WeakThisPtr )
{
if ( Result . HasError ( ) )
{
TSharedPtr < FGameFeatureDataDetailsCustomization > StrongThis = WeakThisPtr . Pin ( ) ;
if ( StrongThis . IsValid ( ) )
{
StrongThis - > ErrorTextWidget - > SetError ( FText : : AsCultureInvariant ( Result . GetError ( ) ) ) ;
}
}
}
//////////////////////////////////////////////////////////////////////////
# undef LOCTEXT_NAMESPACE