<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Unreal 擴展 內容瀏覽器 編輯器 快捷鍵 命令 ContentBrowser Editor Extender Command Shortcut Key

      需求:快速定位Unreal資源在Window文件管理器中的位置

      拆解需求

      • 在Unreal內容瀏覽器中選中資源或者文件夾右鍵菜單中 “在瀏覽器中顯示”已經此功能

      • 為了更快速可以添加快捷鍵方式

      • 直接修改源碼發現修改的文件有點多,所以選擇擴展命令方式實現

      遇到的問題

      • 快捷鍵不響應

        • 文件類型菜單和資源類型菜單是2種 分別直接添加命令 快捷鍵不響應

          void FMyAssetToolsModule::RegisterMenus()
          {
          	{
          		UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("ContentBrowser.FolderContextMenu");
          		{
          			FToolMenuSection& Section = Menu->FindOrAddSection("PathViewFolderOptions");
          			AddExploreFolderCommand(Section);
          		}
          	}
          
          	{
          		UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("ContentBrowser.AssetContextMenu");
          		{
          			FToolMenuSection& Section = Menu->FindOrAddSection("AssetContextExploreMenuOptions");
          			AddExploreFolderCommand(Section);
          		}
          	}
          }
          
          void FMyAssetToolsModule::AddExploreFolderCommand(FToolMenuSection& Section) const
          {
          	FFormatNamedArguments Args;
          	Args.Add(TEXT("FileManagerName"), FPlatformMisc::GetFileManagerName());
          	const FText LabelOverride = FText::Format(NSLOCTEXT("GenericPlatform", "ShowInFileManager", "Show in {FileManagerName}"), Args);
          
          	Section.AddMenuEntryWithCommandList(FMyAssetToolsCommands::Get().ExploreFolder, PluginCommands, LabelOverride,NSLOCTEXT("ContentBrowser", "ExploreTooltip", "Finds this folder on disk."),
          	                                    FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.BrowseContent"));
          }
          
        • 必須獲取內容瀏覽器命令擴展委托添加命令 才會響應快捷鍵

          FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
          
          	TArray<FContentBrowserCommandExtender>& CBCommandExtenderDelegates = ContentBrowserModule.GetAllContentBrowserCommandExtenders();
          	CBCommandExtenderDelegates.Add(FContentBrowserCommandExtender::CreateRaw(this, &FMyAssetToolsModule::OnExtendContentBrowserCommands));
          	ContentBrowserCommandExtenderDelegateHandle = CBCommandExtenderDelegates.Last().GetHandle();
          
        • 可以只添加命令 不添加菜單 通過快捷鍵響應更符合需求

      • Window磁盤路徑不對

        • 通過內容瀏覽器返回選中的數據結構 再去查找Window磁盤路徑 有些文件夾沒辦法查找正確

          const FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
          
          TArray<FAssetData> SelectedAssets;
          ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets);
          
          TArray<FString> SelectedFolders;
          ContentBrowserModule.Get().GetSelectedFolders(SelectedFolders);
          
          
          for (auto SelectedAsset : SelectedAssets)
          {
              // 獲取資源后綴 .uasset .map
              const FString* PackageExtension = SelectedAsset.GetPackage()->ContainsMap() ? &FPackageName::GetMapPackageExtension() : &FPackageName::GetAssetPackageExtension();
          
              FString OutFilename;
          
              // 獲取相對路徑 ../../../../Content/xxx/xxx/aaa.uasset
              FPackageName::TryConvertLongPackageNameToFilename(SelectedAsset.PackageName.ToString(), OutFilename, *PackageExtension);
          
              // 獲取絕對路徑 D:/Project/Content/xxx/xxx/aaa.uasset
              const FString AbsolutePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*OutFilename);
          
              FPlatformProcess::ExploreFolder(*AbsolutePath);
          }
          
          //掛載路徑轉換為虛擬路徑 /All/Plugins -> /Plugins or /All/Game -> /Game */
          TArray<FString> InvariantPaths;
          for (const FString& VirtualPath : SelectedFolders)
          {
              FString InvariantPath;
              if (IContentBrowserDataModule::Get().GetSubsystem()->TryConvertVirtualPath(VirtualPath, InvariantPath) == EContentBrowserPathType::Internal)
              {
                  InvariantPaths.Add(InvariantPath);
              }
          }
          
          //這種方式沒辦法處理Class文件和引擎目錄文件
          for (const FString& InvariantPath : InvariantPaths)
          {
              FString OutFilename;
              FPackageName::TryConvertLongPackageNameToFilename(InvariantPath, OutFilename);
          
              const FString AbsolutePath = IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*OutFilename);
          
              FPlatformProcess::ExploreFolder(*AbsolutePath);
          }
          
        • 必須獲取內容瀏覽器中的FContentBrowserItem對象查找Window磁盤路徑

        • 源碼沒有直接返回選中FContentBrowserItem對象的接口 除非改源碼

        • 但是閱讀源碼發現ContentBrowserUtils::TryGetItemFromUserProvidedPath可以通過路徑名返回對應FContentBrowserItem對象

        • 這個是搜索框的功能接口 可以直接拿來用

        • 在拷貝源碼中的ContentBrowserUtils::ExploreFolders來打開目錄

          void FMyAssetToolsModule::OnExtendContentBrowserCommands(TSharedRef<FUICommandList> CommandList, FOnContentBrowserGetSelection GetSelectionDelegate) const
          {
          	CommandList->MapAction(FMyAssetToolsCommands::Get().ExploreFolder, 
          		FExecuteAction::CreateLambda([this, GetSelectionDelegate]
          		{
          			TArray<FAssetData> SelectedAssets;
          			TArray<FString> SelectedFolders;
          			GetSelectionDelegate.Execute(SelectedAssets, SelectedFolders);
          
          			TArray<FContentBrowserItem> InItems;
          
          			for (auto SelectedAsset : SelectedAssets)
          			{
          				FString ObjectPathString =  SelectedAsset.GetObjectPathString();
          				InItems.Add(ContentBrowserUtils_TryGetItemFromUserProvidedPath(ObjectPathString));
          			}
          
          			for (auto SelectedFolder : SelectedFolders)
          			{
          				InItems.Add(ContentBrowserUtils_TryGetItemFromUserProvidedPath(SelectedFolder));
          			}
          
          			ContentBrowserUtils_ExploreFolders(InItems);
          
          			// 需要修改源碼的實現方式
          			// TArray<FContentBrowserItem> SelectedItems;
          			// ContentBrowserModule.Get().GetSelectedItems(SelectedItems);
          			//
          			// ContentBrowserUtils_ExploreFolders(SelectedItems);
          		})
          	);
          }
          

      完整代碼

      #pragma once
      
      #include "Framework/Commands/Commands.h"
      #include "MyAssetToolsStyle.h"
      
      class FMyAssetToolsCommands : public TCommands<FMyAssetToolsCommands>
      {
      public:
      
      	FMyAssetToolsCommands()
      		: TCommands<FMyAssetToolsCommands>(TEXT("MyAssetTools"), NSLOCTEXT("Contexts", "MyAssetTools", "MyAssetTools Plugin"), NAME_None, FMyAssetToolsStyle::GetStyleSetName())
      	{
      	}
      
      	// TCommands<> interface
      	virtual void RegisterCommands() override;
      
      public:
      	TSharedPtr< FUICommandInfo> ExploreFolder;
      };
      
      
      #include "MyAssetToolsCommands.h"
      
      #define LOCTEXT_NAMESPACE "FMyAssetToolsModule"
      
      void FMyAssetToolsCommands::RegisterCommands()
      {
      	UI_COMMAND(ExploreFolder, "在瀏覽器中顯示", "在磁盤上查找此資產。", EUserInterfaceActionType::Button, FInputChord(EModifierKey::Control | EModifierKey::Shift, EKeys::B));
      }
      
      #undef LOCTEXT_NAMESPACE
      
      
      #pragma once
      
      #include "ContentBrowserDelegates.h"
      #include "Modules/ModuleManager.h"
      
      class FToolBarBuilder;
      class FMenuBuilder;
      
      class FMyAssetToolsModule : public IModuleInterface
      {
      public:
      
      	virtual void StartupModule() override;
      	virtual void ShutdownModule() override;
      	
      private:
      	void OnExtendContentBrowserCommands(TSharedRef<FUICommandList> CommandList, FOnContentBrowserGetSelection GetSelectionDelegate) const;
      
      private:
      	TSharedPtr<class FUICommandList> PluginCommands;
      	FDelegateHandle ContentBrowserCommandExtenderDelegateHandle;
      };
      
      
      #include "MyAssetTools.h"
      #include "ContentBrowserModule.h"
      #include "IContentBrowserDataModule.h"
      #include "IContentBrowserSingleton.h"
      #include "MyAssetToolsCommands.h"
      #include "Misc/MessageDialog.h"
      #include "ToolMenus.h"
      
      static const FName MyAssetToolsTabName("MyAssetTools");
      
      #define LOCTEXT_NAMESPACE "FMyAssetToolsModule"
      
      void FMyAssetToolsModule::StartupModule()
      {
      	FMyAssetToolsCommands::Register();
      
      	FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
      
      	TArray<FContentBrowserCommandExtender>& CBCommandExtenderDelegates = ContentBrowserModule.GetAllContentBrowserCommandExtenders();
      	CBCommandExtenderDelegates.Add(FContentBrowserCommandExtender::CreateRaw(this, &FMyAssetToolsModule::OnExtendContentBrowserCommands));
      	ContentBrowserCommandExtenderDelegateHandle = CBCommandExtenderDelegates.Last().GetHandle();
      }
      
      void FMyAssetToolsModule::ShutdownModule()
      {
      	FMyAssetToolsCommands::Unregister();
      
      	if ((GIsEditor && !IsRunningCommandlet()) && UObjectInitialized() && FSlateApplication::IsInitialized())
      	{
      		FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>(TEXT("ContentBrowser"));
      		
      		TArray<FContentBrowserCommandExtender>& CBCommandExtenderDelegates = ContentBrowserModule.GetAllContentBrowserCommandExtenders();
      		CBCommandExtenderDelegates.RemoveAll([this](const FContentBrowserCommandExtender& Delegate) { return Delegate.GetHandle() == ContentBrowserCommandExtenderDelegateHandle; });
      	}
      }
      
      //ContentBrowserUtils類中的TryGetItemFromUserProvidedPath方法
      FContentBrowserItem ContentBrowserUtils_TryGetItemFromUserProvidedPath(FStringView RequestedPathView)
      {
      	// For all types of accepted input we can trim a trailing slash if it exists
      	if (RequestedPathView.EndsWith('/'))
      	{
      		RequestedPathView.LeftChopInline(1);
      	}
      
      	UContentBrowserDataSubsystem* ContentBrowserData = IContentBrowserDataModule::Get().GetSubsystem();
      	FName RequestedPath(RequestedPathView);
      
      	// If the path is already a valid virtual path, go there
      	FContentBrowserItem Item = ContentBrowserData->GetItemAtPath(RequestedPath, EContentBrowserItemTypeFilter::IncludeAll);
      	if (Item.IsValid())
      	{
      		return Item;
      	}
      
      	// If the path is a non-virtual path like /Game/Maps transform it into a virtual path and try and find an item there
      	FName VirtualPath = ContentBrowserData->ConvertInternalPathToVirtual(RequestedPath);
      	if (!VirtualPath.IsNone())
      	{
      		Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeAll);
      		if (Item.IsValid())
      		{
      			return Item;
      		}
      	}
      
      	// If the string is a complete object path (with or without class), sync to that asset
      	FStringView ObjectPathView = RequestedPathView;
      	if (FPackageName::IsValidObjectPath(ObjectPathView) || FPackageName::ParseExportTextPath(RequestedPathView, nullptr, &ObjectPathView))
      	{
      		VirtualPath = ContentBrowserData->ConvertInternalPathToVirtual(FName(ObjectPathView));
      		Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFiles);
      		if (Item.IsValid())
      		{
      			return Item;
      		}
      	}
      
      	auto GetItemFromPackageName = [ContentBrowserData](const FString& PackageName) -> FContentBrowserItem {
      		// Packages like /Game/Characters/Knight do not map to virtual paths in data source, assets like /Game/Characters/Knight.Knight do.
      		// See if there's an item if we duplicate the last part of the path 
      		FName InternalPath(TStringBuilder<320>(InPlace, PackageName, TEXTVIEW("."), FPackageName::GetShortName(PackageName)));
      		FName VirtualPath = ContentBrowserData->ConvertInternalPathToVirtual(InternalPath);
      		FContentBrowserItem Item = ContentBrowserData->GetItemAtPath(VirtualPath, EContentBrowserItemTypeFilter::IncludeFiles);
      		if (Item.IsValid())
      		{
      			return Item;
      		}
      
      		// Otherwise go up to the package path and enumerate items to see if there's an asset with the desired package name 
      		FString PackagePath = FPackageName::GetLongPackagePath(PackageName);
      		VirtualPath = ContentBrowserData->ConvertInternalPathToVirtual(FName(PackagePath));
      		FContentBrowserDataFilter Filter;
      		Filter.bRecursivePaths = false;
      		Filter.ItemTypeFilter = EContentBrowserItemTypeFilter::IncludeFiles;
      		ContentBrowserData->EnumerateItemsUnderPath(VirtualPath, Filter, [&Item, &PackageName](FContentBrowserItem&& InItem) { 
      			FName InternalPath = InItem.GetInternalPath();	
      			if (WriteToString<256>(InternalPath).ToView().StartsWith(PackageName))
      			{
      				Item = MoveTemp(InItem);
      				return false; 
      			}
      			return true;
      		});
      		if (Item.IsValid())
      		{
      			return Item;
      		}
      		return FContentBrowserItem();
      	};
      
      	// If the string is an incomplete virtual path that looks more like a package name 
      	// e.g. /All/Game/Maps/Arena rather than /Game/Maps/Arena or /All/Game/Maps/Arena.Arena
      	// try and convert it to an internal path, then try and use it as a package name 
      	{
      		FName ConvertedPath;
      		FString PackageName;
      		if (ContentBrowserData->TryConvertVirtualPath(RequestedPath, ConvertedPath) == EContentBrowserPathType::Internal)
      		{
      			if (FPackageName::IsValidLongPackageName(WriteToString<256>(ConvertedPath)))
      			{
      				PackageName = ConvertedPath.ToString();
      				Item = GetItemFromPackageName(PackageName);
      				if (Item.IsValid())
      				{
      					return Item;
      				}
      			}
      		}
      	}
      
      	// If the string is a filesystem path to a package, sync to that asset
      	FString PackageName;
      	if (FPackageName::IsValidLongPackageName(PackageName) || FPackageName::TryConvertFilenameToLongPackageName(FString(RequestedPathView), PackageName))
      	{
      		Item = GetItemFromPackageName(PackageName);
      		if (Item.IsValid())
      		{
      			return Item;
      		}
      	}
      
      	// Try and remove elements from the end of the path until it's a valid virtual path 
      	FPathViews::IterateAncestors(RequestedPathView, [RequestedPathView, ContentBrowserData, &Item](FStringView InAncestor){
      		if (RequestedPathView == InAncestor)
      		{
      			return true;
      		}
      		FName AncestorName(InAncestor);
      		Item = ContentBrowserData->GetItemAtPath(AncestorName, EContentBrowserItemTypeFilter::IncludeFolders);
      		if (Item.IsValid())
      		{
      			return false;
      		}
      		return true;
      	});
      	if (Item.IsValid())
      	{
      		return Item;
      	}
      
      	return FContentBrowserItem();
      }
      
      //ContentBrowserUtils類中的ExploreFolders方法
      void ContentBrowserUtils_ExploreFolders(const TArray<FContentBrowserItem>& InItems)
      {
      	TArray<FString> ExploreItems;
      
      	for (const FContentBrowserItem& SelectedItem : InItems)
      	{
      		FString ItemFilename;
      		if (SelectedItem.GetItemPhysicalPath(ItemFilename))
      		{
      			const bool bExists = SelectedItem.IsFile() ? FPaths::FileExists(ItemFilename) : FPaths::DirectoryExists(ItemFilename);
      			if (bExists)
      			{
      				ExploreItems.Add(IFileManager::Get().ConvertToAbsolutePathForExternalAppForRead(*ItemFilename));
      			}
      		}
      	}
      
      	const int32 BatchSize = 10;
      	const FText FileManagerName = FPlatformMisc::GetFileManagerName();
      	const bool bHasMultipleBatches = ExploreItems.Num() > BatchSize;
      	for (int32 i = 0; i < ExploreItems.Num(); ++i)
      	{
      		bool bIsBatchBoundary = (i % BatchSize) == 0;
      		if (bHasMultipleBatches && bIsBatchBoundary)
      		{
      			int32 RemainingCount = ExploreItems.Num() - i;
      			int32 NextCount = FMath::Min(BatchSize, RemainingCount);
      			FText Prompt = FText::Format(LOCTEXT("ExecuteExploreConfirm", "Show {0} {0}|plural(one=item,other=items) in {1}?\nThere {2}|plural(one=is,other=are) {2} remaining."), NextCount, FileManagerName, RemainingCount);
      			if (FMessageDialog::Open(EAppMsgType::YesNo, Prompt) != EAppReturnType::Yes)
      			{
      				return;
      			}
      		}
      
      		FPlatformProcess::ExploreFolder(*ExploreItems[i]);
      	}
      }
      
      void FMyAssetToolsModule::OnExtendContentBrowserCommands(TSharedRef<FUICommandList> CommandList, FOnContentBrowserGetSelection GetSelectionDelegate) const
      {
      	CommandList->MapAction(FMyAssetToolsCommands::Get().ExploreFolder, 
      		FExecuteAction::CreateLambda([this, GetSelectionDelegate]
      		{
      			TArray<FAssetData> SelectedAssets;
      			TArray<FString> SelectedFolders;
      			GetSelectionDelegate.Execute(SelectedAssets, SelectedFolders);
      
      			TArray<FContentBrowserItem> InItems;
      
      			for (auto SelectedAsset : SelectedAssets)
      			{
      				FString ObjectPathString =  SelectedAsset.GetObjectPathString();
      				InItems.Add(ContentBrowserUtils_TryGetItemFromUserProvidedPath(ObjectPathString));
      			}
      
      			for (auto SelectedFolder : SelectedFolders)
      			{
      				InItems.Add(ContentBrowserUtils_TryGetItemFromUserProvidedPath(SelectedFolder));
      			}
      
      			ContentBrowserUtils_ExploreFolders(InItems);
      		})
      	);
      }
      
      #undef LOCTEXT_NAMESPACE
      
      IMPLEMENT_MODULE(FMyAssetToolsModule, MyAssetTools)
      
      
      posted @ 2025-04-30 16:00  鄒強  閱讀(133)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 99精品全国免费观看视频| 久久综合色之久久综合色| 巨爆乳中文字幕爆乳区| 最新国产AV最新国产在钱| 免费无码av片在线观看网站| 国产精品视频中文字幕| 人妻熟女欲求不满在线| 人人妻人人妻人人片色av| 换着玩人妻中文字幕| 国产AV福利第一精品| 一区二区三区四区亚洲自拍| 久久中文字幕一区二区| 日韩欧美一中文字暮专区| 国产成人精品a视频| 国产蜜臀在线一区二区三区| 精品亚洲无人区一区二区| 亚洲日韩性欧美中文字幕| 偷拍一区二区三区在线视频| 国产综合内射日韩久| 亚洲日本一区二区三区在线播放| 久久精品亚洲成在人线av麻豆| 亚洲第一二三区日韩国产| 亚洲精品日韩精品久久| 北安市| 亚洲欧洲日产国码无码网站| 又粗又紧又湿又爽的视频| 视频二区中文字幕在线| 国产精品免费久久久免费| 97欧美精品系列一区二区| 人妻中文字幕精品系列| 国产一区二区三区精品综合| 国精品午夜福利视频| 泉州市| 中文字幕乱妇无码AV在线| 波多野结衣的av一区二区三区| 午夜射精日本三级| 国产日韩久久免费影院| 在线观看精品日本一区二| 蜜臀久久99精品久久久久久| 色噜噜狠狠一区二区三区果冻| 国内精品无码一区二区三区|