vault backup: 2024-10-12 17:19:45

This commit is contained in:
2024-10-12 17:19:46 +08:00
parent ff94ddca61
commit 244c0c52f6
960 changed files with 31348 additions and 10 deletions

View File

@@ -0,0 +1,201 @@
# ContentDir
- **功能描述:** 使用UE的风格来选择Content以及子目录。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** bool
- **限制类型:** FDirectoryPath
- **关联项:** [RelativePath](../RelativePath.md), [RelativeToGameContentDir](../RelativeToGameContentDir.md)
- **常用程度:** ★★★
使用UE的风格来选择Content以及子目录。
默认情况下选择一个目录会弹出windows默认的选择目录对话框因为FDirectoryPath 你确实可以用来选择到windows系统里任意的目录可能你的项目就是这么需求的。但如果你确实就是想要选择一个UE Content下目录这个时候你指定ContentDirUE就可以为你弹出一个专门的UE选择目录对话框更加的便利也避免出错。
在使用FDirectoryPath的时候ContentDir和LongPackageName是等价的。
## 测试代码:
```cpp
UCLASS(BlueprintType)
class INSIDER_API UMyProperty_Path :public UObject
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DirectoryPath)
FDirectoryPath MyDirectory_Default;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DirectoryPath, meta = (ContentDir))
FDirectoryPath MyDirectory_ContentDir;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DirectoryPath, meta = (LongPackageName))
FDirectoryPath MyDirectory_LongPackageName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DirectoryPath, meta = (RelativeToGameContentDir))
FDirectoryPath MyDirectory_RelativeToGameContentDir;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = DirectoryPath, meta = (RelativePath))
FDirectoryPath MyDirectory_RelativePath;
};
```
## 测试结果:
- 默认的MyDirectory_Default会打开系统对话框可以选择任何目录。
- MyDirectory_ContentDir和MyDirectory_LongPackageName会如图所示弹出UE风格的对话框来选择目录。
- MyDirectory_RelativeToGameContentDir和MyDirectory_RelativePath都会弹出系统对话框不同的是MyDirectory_RelativeToGameContentDir最终的目录会限制在Content目录下如果选择别的目录会弹出错误警告结果是个相对路径。MyDirectory_RelativePath的结果也是个相对路径但是可以选择任意目录。
![Untitled](Untitled.png)
## 原理:
FDirectoryPath的编辑有FDirectoryPathStructCustomization来定制化。根据其代码可见如果有ContentDir或LongPackageName则则是个ContentDir则会采用OnPickContent来选择目录。内部再用ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)来创建专门的目录选择菜单。
否则走到OnPickDirectory分支会采用DesktopPlatform->OpenDirectoryDialog来打开系统的对话框。
从源码也可以看出:
bRelativeToGameContentDir会导致Directory.RightChopInline(AbsoluteGameContentDir.Len(), EAllowShrinking::No); 把Conent路径的左边部分切掉。
bUseRelativePath会触发Directory = IFileManager::Get().ConvertToRelativePath(*Directory);,把路径转换成相对路径。
```cpp
/** Structure for directory paths that are displayed in the editor with a picker UI. */
USTRUCT(BlueprintType)
struct FDirectoryPath
{
GENERATED_BODY()
/**
* The path to the directory.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Path)
FString Path;
};
RegisterCustomPropertyTypeLayout("DirectoryPath", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FDirectoryPathStructCustomization::MakeInstance));
RegisterCustomPropertyTypeLayout("FilePath", FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FFilePathStructCustomization::MakeInstance));
void FDirectoryPathStructCustomization::CustomizeHeader( TSharedRef<IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
{
TSharedPtr<IPropertyHandle> PathProperty = StructPropertyHandle->GetChildHandle("Path");
const bool bRelativeToGameContentDir = StructPropertyHandle->HasMetaData( TEXT("RelativeToGameContentDir") );
const bool bUseRelativePath = StructPropertyHandle->HasMetaData( TEXT("RelativePath") );
const bool bContentDir = StructPropertyHandle->HasMetaData( TEXT("ContentDir") ) || StructPropertyHandle->HasMetaData(TEXT("LongPackageName"));
AbsoluteGameContentDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectContentDir());
if(bContentDir)
{
PickerWidget = SAssignNew(PickerButton, SButton)
.ButtonStyle( FAppStyle::Get(), "HoverHintOnly" )
.ToolTipText( LOCTEXT( "FolderComboToolTipText", "Choose a content directory") )
.OnClicked( this, &FDirectoryPathStructCustomization::OnPickContent, PathProperty.ToSharedRef() )
.ContentPadding(2.0f)
.ForegroundColor( FSlateColor::UseForeground() )
.IsFocusable(false)
.IsEnabled(this, &FDirectoryPathStructCustomization::IsBrowseEnabled, StructPropertyHandle)
[
SNew(SImage)
.Image(FAppStyle::GetBrush("PropertyWindow.Button_Ellipsis"))
.ColorAndOpacity(FSlateColor::UseForeground())
];
}
else
{
PickerWidget = SAssignNew(BrowseButton, SButton)
.ButtonStyle( FAppStyle::Get(), "HoverHintOnly" )
.ToolTipText( LOCTEXT( "FolderButtonToolTipText", "Choose a directory from this computer") )
.OnClicked( this, &FDirectoryPathStructCustomization::OnPickDirectory, PathProperty.ToSharedRef(), bRelativeToGameContentDir, bUseRelativePath )
.ContentPadding( 2.0f )
.ForegroundColor( FSlateColor::UseForeground() )
.IsFocusable( false )
.IsEnabled( this, &FDirectoryPathStructCustomization::IsBrowseEnabled, StructPropertyHandle )
[
SNew( SImage )
.Image( FAppStyle::GetBrush("PropertyWindow.Button_Ellipsis") )
.ColorAndOpacity( FSlateColor::UseForeground() )
];
}
}
FReply FDirectoryPathStructCustomization::OnPickContent(TSharedRef<IPropertyHandle> PropertyHandle)
{
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
FPathPickerConfig PathPickerConfig;
PropertyHandle->GetValue(PathPickerConfig.DefaultPath);
PathPickerConfig.bAllowContextMenu = false;
PathPickerConfig.OnPathSelected = FOnPathSelected::CreateSP(this, &FDirectoryPathStructCustomization::OnPathPicked, PropertyHandle);
FMenuBuilder MenuBuilder(true, NULL);
MenuBuilder.AddWidget(SNew(SBox)
.WidthOverride(300.0f)
.HeightOverride(300.0f)
[
ContentBrowserModule.Get().CreatePathPicker(PathPickerConfig)
], FText());
PickerMenu = FSlateApplication::Get().PushMenu(PickerButton.ToSharedRef(),
FWidgetPath(),
MenuBuilder.MakeWidget(),
FSlateApplication::Get().GetCursorPos(),
FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu)
);
return FReply::Handled();
}
FReply FDirectoryPathStructCustomization::OnPickDirectory(TSharedRef<IPropertyHandle> PropertyHandle, const bool bRelativeToGameContentDir, const bool bUseRelativePath) const
{
FString Directory;
IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get();
if (DesktopPlatform)
{
TSharedPtr<SWindow> ParentWindow = FSlateApplication::Get().FindWidgetWindow(BrowseButton.ToSharedRef());
void* ParentWindowHandle = (ParentWindow.IsValid() && ParentWindow->GetNativeWindow().IsValid()) ? ParentWindow->GetNativeWindow()->GetOSWindowHandle() : nullptr;
FString StartDirectory = FEditorDirectories::Get().GetLastDirectory(ELastDirectory::GENERIC_IMPORT);
if (bRelativeToGameContentDir && !IsValidPath(StartDirectory, bRelativeToGameContentDir))
{
StartDirectory = AbsoluteGameContentDir;
}
// Loop until; a) the user cancels (OpenDirectoryDialog returns false), or, b) the chosen path is valid (IsValidPath returns true)
for (;;)
{
if (DesktopPlatform->OpenDirectoryDialog(ParentWindowHandle, LOCTEXT("FolderDialogTitle", "Choose a directory").ToString(), StartDirectory, Directory))
{
FText FailureReason;
if (IsValidPath(Directory, bRelativeToGameContentDir, &FailureReason))
{
FEditorDirectories::Get().SetLastDirectory(ELastDirectory::GENERIC_IMPORT, Directory);
if (bRelativeToGameContentDir)
{
Directory.RightChopInline(AbsoluteGameContentDir.Len(), EAllowShrinking::No);
}
else if (bUseRelativePath)
{
Directory = IFileManager::Get().ConvertToRelativePath(*Directory);
}
PropertyHandle->SetValue(Directory);
}
else
{
StartDirectory = Directory;
FMessageDialog::Open(EAppMsgType::Ok, FailureReason);
continue;
}
}
break;
}
}
return FReply::Handled();
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 KiB

View File

@@ -0,0 +1,10 @@
# FilePathFilter
- **功能描述:** 设定文件选择器的扩展名,规则符合系统对话框的格式规范,可以填写多个扩展名。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** string="abc"
- **限制类型:** FFilePath
- **常用程度:** ★★★
一般常见的用法是”.umap”“.uasset”之类的。但也可以支持采用“描述 | *.后缀名”的格式自己书写过滤方式规则同windows系统选取规则一样也可以同时写多个后缀名。

View File

@@ -0,0 +1,14 @@
# LongPackageName
- **功能描述:** 使用UE的风格来选择Content以及子目录或者把文件路径转换为长包名。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** bool
- **限制类型:** FDirectoryPathFFilePath
- **常用程度:** ★★★
LongPackageName可以同时用在FDirectoryPath和FFilePath都限制选取范围在Content目录内。
用在FDirectoryPath上的时候限制目录为Content或其子目录。
用在FFilePath的时候限制选择范围为Content里的资产最终把选取的文件路径转换为“/Game/ObjectPath”这种对象的路径名。

View File

@@ -0,0 +1,14 @@
# RelativePath
- **功能描述:** 使得系统目录选择对话框的结果为当前运行exe的相对路径。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** bool
- **限制类型:** FDirectoryPath
- **关联项:** [ContentDir](ContentDir/ContentDir.md)
当前目录为D:\github\GitWorkspace\Hello\Binaries\Win64就是exe所在的工作目录。选择的目录会被转换为相对路径。
```cpp
Directory = IFileManager::Get().ConvertToRelativePath(*Directory);
```

View File

@@ -0,0 +1,14 @@
# RelativeToGameContentDir
- **功能描述:** 使得系统目录选择对话框的结果为相对Content的相对路径。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** bool
- **限制类型:** FDirectoryPath
- **关联项:** [ContentDir](ContentDir/ContentDir.md)
限制目录选择的结果必须是Conent目录或其子目录否则会弹出报错信息。转换的逻辑为把左侧的Content路径裁切掉。
```cpp
Directory.RightChopInline(AbsoluteGameContentDir.Len(), EAllowShrinking::No);
```

View File

@@ -0,0 +1,136 @@
# RelativeToGameDir
- **功能描述:** 如果系统目录选择框的结果为Project的子目录则转换为相对路径否则返回绝对路径。
- **使用位置:** UPROPERTY
- **引擎模块:** Path Property
- **元数据类型:** bool
- **限制类型:** FFilePath
- **常用程度:** ★★★
如果系统目录选择框的结果为Project的子目录则转换为相对路径否则返回绝对路径。
## 测试代码:
```cpp
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath)
FFilePath MyFilePath_Default;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath, meta = (LongPackageName))
FFilePath MyFilePath_LongPackageName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath, meta = (RelativeToGameDir))
FFilePath MyFilePath_RelativeToGameDir;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath, meta = (FilePathFilter = "umap"))
FFilePath MyFilePath_FilePathFilter;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath, meta = (FilePathFilter = "My Config Files|*.ini"))
FFilePath MyFilePath_FilePathFilter_INI;
```
## 测试结果:
- FFilePath弹出的都为Windows系统的默认文件选择对话框。
- MyFilePath_Default弹出默认的系统文件选择对话框可以选择任何路径的任何文件。
- MyFilePath_LongPackageName限制选择范围为Content下的资产否则会弹出报错。结果路径会被转换为/Game/ObjectPath这种长的包名。
- MyFilePath_RelativeToGameDir如果选择结果为Project目录uproject所在的目录下的子文件则返回结果会相对路径。否则直接返回绝对路径。
- MyFilePath_FilePathFilter可以选择任何目录下的指定后缀名的文件。代码里示例为umap则只能选择关卡文件。
- MyFilePath_FilePathFilter_INI演示了只能选取ini文件。FilePathFilter 支持我们采用“描述 | *.后缀名”的格式自己书写过滤方式规则同windows系统选取规则一样也可以同时写多个后缀名。
![Untitled](Untitled.png)
## 原理:
在下面代码就可以看见FilePathFilterbLongPackageName bRelativeToGameDir 的处理逻辑。
- FileTypeFilter 设定扩展名到SFilePathPicker里
- bLongPackageName 触发TryConvertFilenameToLongPackageName来转换路径。
- bRelativeToGameDir 触发AbsolutePickedPath.RightChop(AbsoluteProjectDir.Len());来变成相对路径。
```cpp
USTRUCT(BlueprintType)
struct FFilePath
{
GENERATED_BODY()
/**
* The path to the file.
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = FilePath)
FString FilePath;
};
void FFilePathStructCustomization::CustomizeHeader( TSharedRef<IPropertyHandle> StructPropertyHandle, class FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& StructCustomizationUtils )
{
const FString& MetaData = StructPropertyHandle->GetMetaData(TEXT("FilePathFilter"));
bLongPackageName = StructPropertyHandle->HasMetaData(TEXT("LongPackageName"));
bRelativeToGameDir = StructPropertyHandle->HasMetaData(TEXT("RelativeToGameDir"));
if (MetaData.IsEmpty())
{
FileTypeFilter = TEXT("All files (*.*)|*.*");
}
else
{
if (MetaData.Contains(TEXT("|"))) // If MetaData follows the Description|ExtensionList format, use it as is
{
FileTypeFilter = MetaData;
}
else
{
FileTypeFilter = FString::Printf(TEXT("%s files (*.%s)|*.%s"), *MetaData, *MetaData, *MetaData);
}
}
}
void FFilePathStructCustomization::HandleFilePathPickerPathPicked( const FString& PickedPath )
{
FString FinalPath = PickedPath;
if (bLongPackageName)
{
FString LongPackageName;
FString StringFailureReason;
if (FPackageName::TryConvertFilenameToLongPackageName(PickedPath, LongPackageName, &StringFailureReason) == false)
{
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(StringFailureReason));
}
FinalPath = LongPackageName;
}
else if (bRelativeToGameDir && !PickedPath.IsEmpty())
{
//A filepath under the project directory will be made relative to the project directory
//Otherwise, the absolute path will be returned unless it doesn't exist, the current path will
//be kept. This can happen if it's already relative to project dir (tabbing when selected)
const FString ProjectDir = FPaths::ProjectDir();
const FString AbsoluteProjectDir = FPaths::ConvertRelativePathToFull(ProjectDir);
const FString AbsolutePickedPath = FPaths::ConvertRelativePathToFull(PickedPath);
//Verify if absolute path to file exists. If it was already relative to content directory
//the absolute will be to binaries and will possibly be garbage
if (FPaths::FileExists(AbsolutePickedPath))
{
//If file is part of the project dir, chop the project dir part
//Otherwise, use the absolute path
if (AbsolutePickedPath.StartsWith(AbsoluteProjectDir))
{
FinalPath = AbsolutePickedPath.RightChop(AbsoluteProjectDir.Len());
}
else
{
FinalPath = AbsolutePickedPath;
}
}
else
{
//If absolute file doesn't exist, it might already be relative to project dir
//If not, then it might be a manual entry, so keep it untouched either way
FinalPath = PickedPath;
}
}
PathStringProperty->SetValue(FinalPath);
FEditorDirectories::Get().SetLastDirectory(ELastDirectory::GENERIC_OPEN, FPaths::GetPath(PickedPath));
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB