// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "ModuleUIPrivatePCH.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SModuleUI::Construct(const SModuleUI::FArguments& InArgs) { #define LOCTEXT_NAMESPACE "ModuleUI" ChildSlot .Padding( FMargin(8) ) [ SNew( SVerticalBox ) // List of modules + SVerticalBox::Slot() .FillHeight( 1.0f ) // We want the list to stretch vertically to fill up the user-resizable space [ SAssignNew( ModuleListView, SModuleListView ) .ItemHeight( 24 ) .ListItemsSource( &ModuleListItems ) .OnGenerateRow( this, &SModuleUI::OnGenerateWidgetForModuleListView ) .HeaderRow ( SNew(SHeaderRow) +SHeaderRow::Column("ModuleName") .DefaultLabel(NSLOCTEXT("ModuleUI", "ModuleName", "Module").ToString()) .FillWidth(200) +SHeaderRow::Column("ModuleActions") .DefaultLabel(NSLOCTEXT("ModuleUI", "ModuleActions", "Actions").ToString()) .FillWidth(1000) ) ] ]; #undef LOCTEXT_NAMESPACE // Register to find out about module changes FModuleManager::Get().OnModulesChanged().AddSP( this, &SModuleUI::OnModulesChanged ); // Gather data from module manager UpdateModuleListItems(); } END_SLATE_FUNCTION_BUILD_OPTIMIZATION SModuleUI::~SModuleUI() { // Unregister callbacks FModuleManager::Get().OnModulesChanged().RemoveAll( this ); } TSharedRef SModuleUI::OnGenerateWidgetForModuleListView(TSharedPtr< FModuleListItem > InItem, const TSharedRef& OwnerTable) { #define LOCTEXT_NAMESPACE "ModuleUI" class SModuleItemWidget : public SMultiColumnTableRow< TSharedPtr< FModuleListItem > > { public: SLATE_BEGIN_ARGS( SModuleItemWidget ){} SLATE_END_ARGS() void Construct( const FArguments& InArgs, const TSharedRef& InOwnerTable, TSharedPtr InListItem ) { Item = InListItem; SMultiColumnTableRow< TSharedPtr< FModuleListItem > >::Construct( FSuperRowType::FArguments(), InOwnerTable ); } TSharedRef GenerateWidgetForColumn( const FName& ColumnName ) { if ( ColumnName == "ModuleName" ) { return SNew( STextBlock ) .Text( Item->ModuleName.ToString() ); } else if ( ColumnName == "ModuleActions" ) { return SNew( SHorizontalBox ) // Load button + SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) [ SNew( SButton ) .Visibility( Item.ToSharedRef(), &FModuleListItem::GetVisibilityBasedOnUnloadedState ) .Text( LOCTEXT("Load", "Load") ) .OnClicked( Item.ToSharedRef(), &FModuleListItem::OnLoadClicked ) ] // Unload button + SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) [ SNew( SButton ) .Visibility( Item.ToSharedRef(), &FModuleListItem::GetVisibilityBasedOnLoadedAndShutdownableState ) .Text( LOCTEXT("Unload", "Unload") ) .OnClicked( Item.ToSharedRef(), &FModuleListItem::OnUnloadClicked ) ] // Reload button + SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) [ SNew( SButton ) .Visibility( Item.ToSharedRef(), &FModuleListItem::GetVisibilityBasedOnReloadableState ) .Text( LOCTEXT("Reload", "Reload") ) .OnClicked( Item.ToSharedRef(), &FModuleListItem::OnReloadClicked ) ] // Recompile button + SHorizontalBox::Slot() .AutoWidth() .Padding( 2.0f, 0.0f ) [ SNew( SButton ) .Visibility( Item.ToSharedRef(), &FModuleListItem::GetVisibilityBasedOnRecompilableState ) .Text( LOCTEXT("Recompile", "Recompile") ) .OnClicked( Item.ToSharedRef(), &FModuleListItem::OnRecompileClicked ) ]; } else { return SNew(STextBlock) .Text(LOCTEXT("UnknownColumn", "Unknown Column")); } } TSharedPtr< FModuleListItem > Item; }; return SNew( SModuleItemWidget, OwnerTable, InItem ); #undef LOCTEXT_NAMESPACE } void SModuleUI::OnModulesChanged( FName ModuleThatChanged, EModuleChangeReason::Type ReasonForChange ) { // @todo: Consider using dirty bit for this instead, refresh on demand UpdateModuleListItems(); } void SModuleUI::UpdateModuleListItems() { ModuleListItems.Reset(); TArray< FModuleManager::FModuleStatus > ModuleStatuses; FModuleManager::Get().QueryModules( ModuleStatuses ); for( TArray< FModuleManager::FModuleStatus >::TConstIterator ModuleIt( ModuleStatuses ); ModuleIt; ++ModuleIt ) { const FModuleManager::FModuleStatus& ModuleStatus = *ModuleIt; FName ModuleName(*ModuleStatus.Name); TSharedRef< FModuleListItem > NewItem( new FModuleListItem() ); NewItem->ModuleName = ModuleName; ModuleListItems.Add( NewItem ); } // Sort the list of modules alphabetically struct FModuleSorter { FORCEINLINE bool operator()( const TSharedPtr& A, const TSharedPtr& B ) const { return A->ModuleName < B->ModuleName; } }; ModuleListItems.Sort( FModuleSorter() ); // Update the list view if we have one if( ModuleListView.IsValid() ) { ModuleListView->RequestListRefresh(); } } FReply SModuleUI::FModuleListItem::OnLoadClicked() { GEngine->DeferredCommands.Add( FString::Printf( TEXT( "Module Load %s" ), *ModuleName.ToString() ) ); return FReply::Handled(); } FReply SModuleUI::FModuleListItem::OnUnloadClicked() { GEngine->DeferredCommands.Add( FString::Printf( TEXT( "Module Unload %s" ), *ModuleName.ToString() ) ); return FReply::Handled(); } FReply SModuleUI::FModuleListItem::OnReloadClicked() { GEngine->DeferredCommands.Add( FString::Printf( TEXT( "Module Reload %s" ), *ModuleName.ToString() ) ); return FReply::Handled(); } FReply SModuleUI::FModuleListItem::OnRecompileClicked() { const bool bShowProgressDialog = true; const bool bShowCancelButton = false; FFormatNamedArguments Args; Args.Add( TEXT("ModuleName"), FText::FromName( ModuleName ) ); GWarn->BeginSlowTask( FText::Format( NSLOCTEXT("ModuleUI", "Recompile_SlowTaskName", "Compiling {ModuleName}..."), Args ), bShowProgressDialog, bShowCancelButton ); // Does the module have any UObject classes in it? If so we'll use HotReload to recompile it. FModuleManager::FModuleStatus ModuleStatus; if( ensure( FModuleManager::Get().QueryModule( ModuleName, ModuleStatus ) ) ) { //@todo This is for content-only packages that show up in the // Module UI... don't crash when recompile is clicked if (FPaths::IsProjectFilePathSet()) { if (ModuleStatus.bIsLoaded == false) { if (FPlatformFileManager::Get().GetPlatformFile().FileExists(*ModuleStatus.FilePath) == false) { UE_LOG(LogTemp, Display, TEXT("Unable to recompile module %s... Is it a content-only module?"), *ModuleName.ToString()); GWarn->EndSlowTask(); return FReply::Handled(); } } } TArray< UPackage* > PackagesToRebind; if( ModuleStatus.bIsLoaded ) { const bool bIsHotReloadable = FModuleManager::Get().DoesLoadedModuleHaveUObjects( ModuleName ); if( bIsHotReloadable ) { // Is there a UPackage with the same name as this module? FString PotentialPackageName = FString(TEXT("/Script/")) + ModuleName.ToString(); UPackage* Package = FindPackage( NULL, *PotentialPackageName); if( Package != NULL ) { PackagesToRebind.Add( Package ); } } } if( PackagesToRebind.Num() > 0 ) { // Perform a hot reload const bool bWaitForCompletion = true; RebindPackages( PackagesToRebind, TArray(), bWaitForCompletion, *GLog ); } else { // Perform a regular unload, then reload const bool bReloadAfterRecompile = true; FModuleManager::Get().RecompileModule( ModuleName, bReloadAfterRecompile, *GLog ); } } GWarn->EndSlowTask(); return FReply::Handled(); } EVisibility SModuleUI::FModuleListItem::GetVisibilityBasedOnLoadedAndShutdownableState() const { if ( GIsSavingPackage || GIsGarbageCollecting ) { return EVisibility::Hidden; } const bool bIsHotReloadable = FModuleManager::Get().DoesLoadedModuleHaveUObjects(ModuleName); const bool bCanShutDown = ( FModuleManager::Get().IsModuleLoaded(ModuleName) && !bIsHotReloadable && FModuleManager::Get().GetModuleInterface(ModuleName).SupportsDynamicReloading() ); return bCanShutDown ? EVisibility::Visible : EVisibility::Hidden; } EVisibility SModuleUI::FModuleListItem::GetVisibilityBasedOnReloadableState() const { return GetVisibilityBasedOnLoadedAndShutdownableState(); }; EVisibility SModuleUI::FModuleListItem::GetVisibilityBasedOnRecompilableState() const { if ( GIsSavingPackage || GIsGarbageCollecting ) { return EVisibility::Hidden; } const bool bIsHotReloadable = FModuleManager::Get().DoesLoadedModuleHaveUObjects(ModuleName); const bool bCanReload = ( !FModuleManager::Get().IsModuleLoaded(ModuleName) || FModuleManager::Get().GetModuleInterface(ModuleName).SupportsDynamicReloading() || bIsHotReloadable ); return bCanReload ? EVisibility::Visible : EVisibility::Hidden; } EVisibility SModuleUI::FModuleListItem::GetVisibilityBasedOnUnloadedState() const { return FModuleManager::Get().IsModuleLoaded( ModuleName ) ? EVisibility::Hidden : EVisibility::Visible; }