5.2 KiB
Raw Blame History

RelativeToGameDir

  • 功能描述: 如果系统目录选择框的结果为Project的子目录则转换为相对路径否则返回绝对路径。
  • 使用位置: UPROPERTY
  • 引擎模块: Path Property
  • 元数据类型: bool
  • 限制类型: FFilePath
  • 常用程度: ★★★

如果系统目录选择框的结果为Project的子目录则转换为相对路径否则返回绝对路径。

测试代码:

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

原理:

在下面代码就可以看见FilePathFilterbLongPackageName bRelativeToGameDir 的处理逻辑。

  • FileTypeFilter 设定扩展名到SFilePathPicker里
  • bLongPackageName 触发TryConvertFilenameToLongPackageName来转换路径。
  • bRelativeToGameDir 触发AbsolutePickedPath.RightChop(AbsoluteProjectDir.Len());来变成相对路径。
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));
}