137 lines
5.2 KiB
Markdown
137 lines
5.2 KiB
Markdown
|
# 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系统选取规则一样,也可以同时写多个后缀名。
|
|||
|
|
|||
|

|
|||
|
|
|||
|
## 原理:
|
|||
|
|
|||
|
在下面代码就可以看见FilePathFilter,bLongPackageName ,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));
|
|||
|
}
|
|||
|
```
|