// Copyright Epic Games, Inc. All Rights Reserved. #include "SlateFileDlgWindow.h" #include "SlateFileDialogsPrivate.h" #include "HAL/PlatformProcess.h" #include "GenericPlatform/GenericPlatformFile.h" #include "HAL/PlatformFileManager.h" #include "HAL/FileManager.h" #include "Modules/ModuleManager.h" #include "Widgets/SWindow.h" #include "Framework/Application/SlateApplication.h" #include "Textures/SlateIcon.h" #include "Framework/Commands/UIAction.h" #include "Framework/Docking/TabManager.h" #include "Widgets/Layout/SSpacer.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "Widgets/Input/SButton.h" #include "Widgets/Input/STextComboBox.h" #include "Widgets/Navigation/SBreadcrumbTrail.h" #include "Widgets/Text/SInlineEditableTextBlock.h" #include "DirectoryWatcherModule.h" #if PLATFORM_WINDOWS #include "Windows/WindowsHWrapper.h" #endif #define LOCTEXT_NAMESPACE "SlateFileDialogsNamespace" DEFINE_LOG_CATEGORY_STATIC(LogSlateFileDialogs, Log, All); //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- class FSlateFileDialogVisitor : public IPlatformFile::FDirectoryVisitor { public: FSlateFileDialogVisitor(TArray> &InFileList, TArray> &InFolderList, const FString& InFilterList) : FileList(InFileList), FolderList(InFolderList) { // Process the filters once rather than once for each file encountered InFilterList.ParseIntoArray(FilterList, TEXT(";"), true); // Remove cruft from the extension list for (int32 Index = 0; Index < FilterList.Num(); Index++) { FilterList[Index].ReplaceInline(TEXT(")"), TEXT("")); FilterList[Index] = FilterList[Index].TrimQuotes().TrimStartAndEnd(); } } virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override { int32 i; // break filename from path for (i = FCString::Strlen(FilenameOrDirectory) - 1; i >= 0; i--) { if (FilenameOrDirectory[i] == TCHAR('/')) { break; } } #if HIDE_HIDDEN_FILES if (FilenameOrDirectory[i + 1] == TCHAR('.')) { return true; } #endif FDateTime stamp = IFileManager::Get().GetTimeStamp(FilenameOrDirectory); FString ModDate = ""; FString FileSize = ""; if (bIsDirectory) { FolderList.Add(MakeShareable(new FFileEntry(FString(&FilenameOrDirectory[i + 1]), ModDate, FileSize, true))); } else { if (PassesFilterTest(&FilenameOrDirectory[i + 1])) { int64 size = IFileManager::Get().FileSize(FilenameOrDirectory); if (size < 1048576) { size = (size + 1023) / 1024; FileSize = FString::FromInt(size) + " KB"; } else { size /= 1024; if (size < 1048576) { size = (size + 1023) / 1024; FileSize = FString::FromInt(size) + " MB"; } else { size /= 1024; size = (size + 1023) / 1024; FileSize = FString::FromInt(size) + " GB"; } } ModDate = FString::Printf(TEXT("%02d/%02d/%04d "), stamp.GetMonth(), stamp.GetDay(), stamp.GetYear()); if (stamp.GetHour() == 0) { ModDate = ModDate + FString::Printf(TEXT("12:%02d AM"), stamp.GetMinute()); } else if (stamp.GetHour() < 12) { ModDate = ModDate + FString::Printf(TEXT("%2d:%02d AM"), stamp.GetHour12(), stamp.GetMinute()); } else { ModDate = ModDate + FString::Printf(TEXT("%2d:%02d PM"), stamp.GetHour12(), stamp.GetMinute()); } FileList.Add(MakeShareable(new FFileEntry(FString(&FilenameOrDirectory[i + 1]), ModDate, FileSize, false))); } } return true; } bool PassesFilterTest(const TCHAR* Filename) { if (FilterList.Num() == 0) { return true; // no filters. everything passes. } FString BaseFile = Filename; FString Extension = FPaths::GetExtension(BaseFile, true); if (!Extension.IsEmpty() && FCString::IsNumeric(*Extension)) { BaseFile.LeftChopInline(Extension.Len()); const int32 DotPos = BaseFile.Find(TEXT("."), ESearchCase::CaseSensitive, ESearchDir::FromEnd); if (DotPos != INDEX_NONE) { Extension = BaseFile.Right(DotPos); Extension += TEXT(".*"); } } // See if it matches any of the extensions for (const FString& FilterExt : FilterList) { if (FilterExt == TEXT("*") || FilterExt == TEXT(".*") || FilterExt == TEXT("*.*") || FilterExt.EndsWith(Extension)) { return true; } } return false; } private: TArray>& FileList; TArray>& FolderList; TArray FilterList; }; class FSlateFileDialogDirVisitor : public IPlatformFile::FDirectoryVisitor { public: FSlateFileDialogDirVisitor(TArray *InDirectoryNames) : DirectoryNames(InDirectoryNames) {} void SetResultPath(TArray *InDirectoryNames) { DirectoryNames = InDirectoryNames; } virtual bool Visit(const TCHAR* FilenameOrDirectory, bool bIsDirectory) override { int32 i; // break filename from path for (i = FCString::Strlen(FilenameOrDirectory) - 1; i >= 0; i--) { if (FilenameOrDirectory[i] == TCHAR('/')) break; } #if HIDE_HIDDEN_FILES if (FilenameOrDirectory[i + 1] == TCHAR('.')) { return true; } #endif if (bIsDirectory) { DirectoryNames->Add(FString(&FilenameOrDirectory[i + 1])); } return true; } private: TArray *DirectoryNames; }; //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- FSlateFileDlgWindow::FSlateFileDlgWindow(FSlateFileDialogsStyle *InStyleSet) { StyleSet = InStyleSet; } namespace { TSharedPtr FindMatchingWindowImpl(const TArray>& RootWindows, const TFunctionRef&)> Predicate) { for (const TSharedRef& Window : RootWindows) { if (Predicate(Window)) { return Window; } if (TSharedPtr ChildWindow = FindMatchingWindowImpl(Window->GetChildWindows(), Predicate)) { return ChildWindow; } } return nullptr; } TSharedPtr FindMatchingWindow(const TFunctionRef&)> Predicate) { return FindMatchingWindowImpl(FSlateApplication::Get().GetTopLevelWindows(), Predicate); } TSharedPtr GetParentWindowWidget(const void* ParentWindowHandle) { if (ParentWindowHandle) { TSharedPtr ParentWindow = FindMatchingWindow([ParentWindowHandle](const TSharedRef& Window) { const TSharedPtr NativeWindow = Window->GetNativeWindow(); return NativeWindow && NativeWindow->GetOSWindowHandle() == ParentWindowHandle; }); if (ParentWindow) { return ParentWindow; } } return FGlobalTabmanager::Get()->GetRootWindow(); } } bool FSlateFileDlgWindow::OpenFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray& OutFilenames, int32& OutFilterIndex, int32 DefaultFilterIndex) { FString StartDirectory = DefaultPath; TrimFilenameFromPath(StartDirectory); TSharedRef ModalWindow = SNew(SWindow) .SupportsMinimize(false) .SupportsMaximize(false) .Title(LOCTEXT("SlateFileDialogsOpenFile","Open File")) .CreateTitleBar(true) .MinHeight(400.0f) .MinWidth(600.0f) .ActivationPolicy(EWindowActivationPolicy::Always) .ClientSize(FVector2D(800, 500)); DialogWidget = SNew(SSlateFileOpenDlg) .bMultiSelectEnabled(Flags == 1) .ParentWindow(ModalWindow) .CurrentPath(StartDirectory) .Filters(FileTypes) .WindowTitleText(DialogTitle) .StyleSet(StyleSet); DialogWidget->SetOutNames(&OutFilenames); DialogWidget->SetOutFilterIndex(&OutFilterIndex); DialogWidget->SetDefaultFilterIndex(DefaultFilterIndex); ModalWindow->SetContent( DialogWidget.ToSharedRef() ); FSlateApplication::Get().AddModalWindow(ModalWindow, GetParentWindowWidget(ParentWindowHandle)); return (DialogWidget->GetResponse() == EResult::Accept && OutFilenames.Num() > 0); } bool FSlateFileDlgWindow::OpenFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray& OutFilenames) { int32 DummyIndex; return OpenFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, DummyIndex); } bool FSlateFileDlgWindow::OpenDirectoryDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, FString& OutFoldername) { int32 DummyIndex; TArray TempOut; FString Filters = ""; FString StartDirectory = DefaultPath; TrimFilenameFromPath(StartDirectory); TSharedRef ModalWindow = SNew(SWindow) .SupportsMinimize(false) .SupportsMaximize(false) .Title(LOCTEXT("SlateFileDialogsOpenDirectory","Open Directory")) .CreateTitleBar(true) .MinHeight(400.0f) .MinWidth(600.0f) .ActivationPolicy(EWindowActivationPolicy::Always) .ClientSize(FVector2D(800, 500)); DialogWidget = SNew(SSlateFileOpenDlg) .bMultiSelectEnabled(false) .ParentWindow(ModalWindow) .bDirectoriesOnly(true) .CurrentPath(StartDirectory) .WindowTitleText(DialogTitle) .StyleSet(StyleSet); DialogWidget->SetOutNames(&TempOut); DialogWidget->SetOutFilterIndex(&DummyIndex); ModalWindow->SetContent( DialogWidget.ToSharedRef() ); FSlateApplication::Get().AddModalWindow(ModalWindow, GetParentWindowWidget(ParentWindowHandle)); bool RC = (DialogWidget->GetResponse() == EResult::Accept && TempOut.Num() > 0); if (TempOut.Num() > 0) { OutFoldername = FPaths::ConvertRelativePathToFull(TempOut[0]); if (!OutFoldername.EndsWith(TEXT("/"))) { OutFoldername += TEXT("/"); } } return RC; } bool FSlateFileDlgWindow::SaveFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray& OutFilenames, int32& OutFilterIndex, int32 DefaultFilterIndex) { FString StartDirectory = DefaultPath; TrimFilenameFromPath(StartDirectory); TSharedRef ModalWindow = SNew(SWindow) .SupportsMinimize(false) .SupportsMaximize(false) .Title(LOCTEXT("SlateFileDialogsSaveFile","Save File")) .CreateTitleBar(true) .MinHeight(400.0f) .MinWidth(600.0f) .ActivationPolicy(EWindowActivationPolicy::Always) .ClientSize(FVector2D(800, 500)); DialogWidget = SNew(SSlateFileOpenDlg) .bMultiSelectEnabled(false) .ParentWindow(ModalWindow) .bSaveFile(true) .AcceptText(LOCTEXT("SlateFileDialogsSave","Save")) .CurrentPath(StartDirectory) .Filters(FileTypes) .WindowTitleText(DialogTitle) .StyleSet(StyleSet); DialogWidget->SetOutNames(&OutFilenames); DialogWidget->SetOutFilterIndex(&OutFilterIndex); DialogWidget->SetDefaultFilterIndex(DefaultFilterIndex); DialogWidget->SetDefaultFile(DefaultFile); ModalWindow->SetContent( DialogWidget.ToSharedRef() ); FSlateApplication::Get().AddModalWindow(ModalWindow, GetParentWindowWidget(ParentWindowHandle)); return (DialogWidget->GetResponse() == EResult::Accept && OutFilenames.Num() > 0); } bool FSlateFileDlgWindow::SaveFileDialog(const void* ParentWindowHandle, const FString& DialogTitle, const FString& DefaultPath, const FString& DefaultFile, const FString& FileTypes, uint32 Flags, TArray& OutFilenames) { int32 DummyIndex; return SaveFileDialog(ParentWindowHandle, DialogTitle, DefaultPath, DefaultFile, FileTypes, Flags, OutFilenames, DummyIndex); } void FSlateFileDlgWindow::TrimFilenameFromPath(FString &InPath) { if (InPath.Len() == 0 || !FPaths::FileExists(InPath)) { // No path given OR no file portion to trim return; } FPaths::CollapseRelativeDirectories(InPath); FString PathPart; FString FileNamePart; FString ExtensionPart; FPaths::Split(InPath, PathPart, FileNamePart, ExtensionPart); InPath = PathPart; } //----------------------------------------------------------------------------- // custom file dialog widget //----------------------------------------------------------------------------- BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SSlateFileOpenDlg::Construct(const FArguments& InArgs) { CurrentPath = InArgs._CurrentPath; Filters = InArgs._Filters; bMultiSelectEnabled = InArgs._bMultiSelectEnabled; bDirectoriesOnly = InArgs._bDirectoriesOnly; bSaveFile = InArgs._bSaveFile; WindowTitleText = InArgs._WindowTitleText; OutNames = InArgs._OutNames; OutFilterIndex = InArgs._OutFilterIndex; UserResponse = FSlateFileDlgWindow::Cancel; ParentWindow = InArgs._ParentWindow; StyleSet = InArgs._StyleSet; AcceptText = InArgs._AcceptText; DirNodeIndex = -1; FilterIndex = 0; ESelectionMode::Type SelectMode = bMultiSelectEnabled ? ESelectionMode::Multi : ESelectionMode::Single; struct EVisibility SaveFilenameVisibility = bDirectoriesOnly ? EVisibility::Collapsed : EVisibility::Visible; this->ChildSlot [ SNew(SBorder) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .Padding(FMargin(20.0f,20.0f)) .BorderImage(StyleSet->GetBrush("SlateFileDialogs.GroupBorder")) [ SNew(SVerticalBox) + SVerticalBox::Slot() // window title .HAlign(HAlign_Left) .VAlign(VAlign_Fill) .AutoHeight() .Padding(FMargin(0.0f, 0.0f, 0.0f, 20.0f)) [ SAssignNew(WindowTitle, STextBlock) .Text(FText::FromString(WindowTitleText)) .Font(StyleSet->GetFontStyle("SlateFileDialogs.DialogLarge")) .Justification(ETextJustify::Center) ] + SVerticalBox::Slot() // Path breadcrumbs .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .AutoHeight() .Padding(FMargin(0.0f, 0.0f, 0.0f, 10.0f)) [ SAssignNew(PathBreadcrumbTrail, SBreadcrumbTrail) .ButtonContentPadding(FMargin(2.0f, 2.0f)) .ButtonStyle(StyleSet->Get(), "SlateFileDialogs.FlatButton") .DelimiterImage(StyleSet->GetBrush("SlateFileDialogs.PathDelimiter")) .TextStyle(StyleSet->Get(), "SlateFileDialogs.PathText") .ShowLeadingDelimiter(false) .OnCrumbClicked(this, &SSlateFileOpenDlg::OnPathClicked) .GetCrumbMenuContent(this, &SSlateFileOpenDlg::OnGetCrumbDelimiterContent) .AddMetaData(FTagMetaData(TEXT("ContentBrowserPath"))) ] + SVerticalBox::Slot() // new directory .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .AutoHeight() .Padding(FMargin(0.0f, 0.0f, 0.0f, 10.0f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(FMargin(0.0f, 0.0f, 10.0f, 0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked(this, &SSlateFileOpenDlg::OnGoBackClick) .ContentPadding(FMargin(0.0f)) [ SNew(SImage) .Image(StyleSet->GetBrush("SlateFileDialogs.BrowseBack24")) ] ] + SHorizontalBox::Slot() .Padding(FMargin(0.0f, 0.0f, 40.0f, 0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked(this, &SSlateFileOpenDlg::OnGoForwardClick) .ContentPadding(FMargin(0.0f)) [ SNew(SImage) .Image(StyleSet->GetBrush("SlateFileDialogs.BrowseForward24")) ] ] + SHorizontalBox::Slot() .Padding(FMargin(0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked(this, &SSlateFileOpenDlg::OnNewDirectoryClick) .ContentPadding(FMargin(0.0f)) [ SNew(SImage) .Image(StyleSet->GetBrush("SlateFileDialogs.NewFolder24")) ] ] + SHorizontalBox::Slot() .Padding(FMargin(20.0f, 0.0f, 0.0f, 0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SAssignNew(NewDirectorySizeBox, SBox) .Padding(FMargin(0.0f)) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .WidthOverride(300.0f) .Visibility(EVisibility::Hidden) [ SNew(SBorder) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .Padding(FMargin(5.0f,0.0f)) .BorderBackgroundColor(FLinearColor(0.1f, 0.1f, 0.1f, 1.0f)) .BorderImage(StyleSet->GetBrush("SlateFileDialogs.WhiteBackground")) [ SAssignNew(NewDirectoryEditBox, SInlineEditableTextBlock) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .IsReadOnly(false) .Text(FText::GetEmpty()) .OnTextCommitted(this, &SSlateFileOpenDlg::OnNewDirectoryCommitted) .OnVerifyTextChanged(this, &SSlateFileOpenDlg::OnNewDirectoryTextChanged) ] ] ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .Padding(FMargin(20.0f, 0.0f, 0.0f, 0.0f)) .AutoWidth() [ SAssignNew(NewDirCancelButton, SButton) .ContentPadding(FMargin(5.0f, 5.0f)) .OnClicked(this, &SSlateFileOpenDlg::OnNewDirectoryAcceptCancelClick, FSlateFileDlgWindow::Cancel) .Text(LOCTEXT("SlateFileDialogsCancel","Cancel")) .Visibility(EVisibility::Hidden) ] ] + SVerticalBox::Slot() // new directory .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .AutoHeight() .Padding(FMargin(0.0f, 0.0f, 0.0f, 10.0f)) [ SAssignNew(DirErrorMsg, STextBlock) .Font(StyleSet->GetFontStyle("SlateFileDialogs.DialogBold")) .Justification(ETextJustify::Left) .ColorAndOpacity(FLinearColor::Yellow) .Text(LOCTEXT("SlateFileDialogsDirError", "Unable to create directory!")) .Visibility(EVisibility::Collapsed) ] + SVerticalBox::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .FillHeight(1.0f) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(FMargin(0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SVerticalBox) + SVerticalBox::Slot() .Padding(FMargin(10.0f)) .AutoHeight() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked(this, &SSlateFileOpenDlg::OnQuickLinkClick, FSlateFileDlgWindow::Project) .ContentPadding(FMargin(2.0f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SImage) .Image(StyleSet->GetBrush("SlateFileDialogs.Folder24")) ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .Text(LOCTEXT("ProjectsLabel", "Projects")) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .Justification(ETextJustify::Left) ] ] ] + SVerticalBox::Slot() .Padding(FMargin(10.0f)) .AutoHeight() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SButton) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .OnClicked(this, &SSlateFileOpenDlg::OnQuickLinkClick, FSlateFileDlgWindow::Engine) .ContentPadding(FMargin(2.0f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SImage) .Image(StyleSet->GetBrush("SlateFileDialogs.Folder24")) ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .AutoWidth() [ SNew(STextBlock) .Text(LOCTEXT("EngineLabel", "Engine")) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .Justification(ETextJustify::Left) ] ] ] ] + SHorizontalBox::Slot() // spacer .Padding(FMargin(0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SSpacer) .Size(FVector2D(20.0f, 1.0f)) ] + SHorizontalBox::Slot() // file list area .Padding(FMargin(0.0f, 0.0f, 20.0f, 0.0f)) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .FillWidth(1.0f) [ SNew(SBorder) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .Padding(FMargin(10.0f)) .BorderBackgroundColor(FLinearColor(0.10f, 0.10f, 0.10f, 1.0f)) .BorderImage(StyleSet->GetBrush("SlateFileDialogs.WhiteBackground")) [ SAssignNew(ListView, SListView>) // file list scroll .ListItemsSource(&LineItemArray) .SelectionMode(SelectMode) .OnGenerateRow(this, &SSlateFileOpenDlg::OnGenerateWidgetForList) .OnMouseButtonDoubleClick(this, &SSlateFileOpenDlg::OnItemDoubleClicked) .OnSelectionChanged(this, &SSlateFileOpenDlg::OnItemSelected) .HeaderRow ( SNew(SHeaderRow) .Visibility(EVisibility::Visible) + SHeaderRow::Column("Pathname") .DefaultLabel(LOCTEXT("SlateFileDialogsNameHeader", "Name")) .FillWidth(1.0f) + SHeaderRow::Column("ModDate") .DefaultLabel(LOCTEXT("SlateFileDialogsModDateHeader", "Date Modified")) .FixedWidth(170.0f) + SHeaderRow::Column("FileSize") .DefaultLabel(LOCTEXT("SlateFileDialogsFileSizeHeader", "File Size")) .FixedWidth(70.0f) ) ] ] ] + SVerticalBox::Slot() // save filename entry .HAlign(HAlign_Right) .VAlign(VAlign_Bottom) .Padding(FMargin(0.0f, 10.0f, 50.0f, 0.0f)) .AutoHeight() [ SAssignNew(SaveFilenameSizeBox, SBox) .Padding(FMargin(0.0f)) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .MinDesiredHeight(20.0f) .Visibility(SaveFilenameVisibility) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(FMargin(0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(STextBlock) .Text(LOCTEXT("FilenameLabel", "Filename:")) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .Justification(ETextJustify::Left) ] + SHorizontalBox::Slot() .Padding(FMargin(10.0f, 0.0f, 0.0f, 0.0f)) .AutoWidth() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) [ SNew(SBox) .Padding(FMargin(0.0f)) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .WidthOverride(300.0f) [ SNew(SBorder) .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .Padding(FMargin(5.0f,0.0f)) .BorderBackgroundColor(FLinearColor(0.1f, 0.1f, 0.1f, 1.0f)) .BorderImage(StyleSet->GetBrush("SlateFileDialogs.WhiteBackground")) [ SAssignNew(SaveFilenameEditBox, SInlineEditableTextBlock) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .IsReadOnly(false) .Text(FText::GetEmpty()) .OnTextCommitted(this, &SSlateFileOpenDlg::OnFileNameCommitted) ] ] ] ] ] + SVerticalBox::Slot() // cancel:accept buttons .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .AutoHeight() .Padding(FMargin(0.0f, 10.0f, 0.0f, 0.0f)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .Padding(FMargin(0.0f)) .AutoWidth() .HAlign(HAlign_Left) .VAlign(VAlign_Top) [ SAssignNew(FilterHBox, SHorizontalBox) + SHorizontalBox::Slot() .HAlign(HAlign_Left) .VAlign(VAlign_Bottom) .AutoWidth() .Padding(FMargin(0.0f)) [ SNew(STextBlock) .Text(bSaveFile ? LOCTEXT("SaveTypeLabel", "Save as type:") : LOCTEXT("FilterLabel", "Filter:")) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) .Justification(ETextJustify::Left) ] + SHorizontalBox::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .AutoWidth() .Padding(FMargin(10.0f, 0.0f, 0.0f, 0.0f)) [ SNew(SBox) .MinDesiredWidth(200.0f) .MaxDesiredWidth(200.0f) .Padding(FMargin(0.0f)) [ SAssignNew(FilterCombo, SComboBox>) .ContentPadding(FMargin(4.0f, 2.0f)) .OptionsSource(&FilterNameArray) .OnGenerateWidget_Lambda([this](TSharedPtr Item) { return SNew(SBox) .MaxDesiredWidth(600.0f) [ SNew(STextBlock) .Text(FText::FromString(*Item)) .Font(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")) ]; } ) .OnSelectionChanged(this, &SSlateFileOpenDlg::OnFilterChanged) [ SAssignNew(FilterComboBoxTitleBlock, STextBlock) ] ] ] ] + SHorizontalBox::Slot() .HAlign(HAlign_Fill) .VAlign(VAlign_Fill) .FillWidth(1.0f) [ SNew(SSpacer) .Size(FVector2D(1.0f, 1.0f)) ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .Padding(FMargin(0.0f, 0.0f, 20.0f, 0.0f)) .AutoWidth() [ SNew(SButton) .ContentPadding(FMargin(5.0f, 5.0f)) .IsEnabled(this, &SSlateFileOpenDlg::IsAcceptEnabled) .OnClicked(this, &SSlateFileOpenDlg::OnAcceptCancelClick, FSlateFileDlgWindow::Accept) .Text(AcceptText) ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) .VAlign(VAlign_Center) .Padding(FMargin(0.0f)) .AutoWidth() [ SNew(SButton) .ContentPadding(FMargin(5.0f, 5.0f)) .OnClicked(this, &SSlateFileOpenDlg::OnAcceptCancelClick, FSlateFileDlgWindow::Cancel) .Text(LOCTEXT("SlateFileDialogsCancel","Cancel")) ] ] ] ]; SaveFilename = ""; bNeedsBuilding = true; bRebuildDirPath = true; bDirectoryHasChanged = false; DirectoryWatcher = nullptr; if (CurrentPath.Len() > 0 && !CurrentPath.EndsWith("/")) { CurrentPath = CurrentPath + TEXT("/"); } HistoryIndex = 0; History.Add(CurrentPath); #if ENABLE_DIRECTORY_WATCHER if (!FModuleManager::Get().IsModuleLoaded("DirectoryWatcher")) { FModuleManager::Get().LoadModule("DirectoryWatcher"); } FDirectoryWatcherModule &DirWatcherModule = FModuleManager::LoadModuleChecked(TEXT("DirectoryWatcher")); DirectoryWatcher = DirWatcherModule.Get(); #endif } END_SLATE_FUNCTION_BUILD_OPTIMIZATION SSlateFileOpenDlg::~SSlateFileOpenDlg() { if (DirectoryWatcher && RegisteredPath.Len() > 0) { DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(RegisteredPath, OnDialogDirectoryChangedDelegateHandle); } } void SSlateFileOpenDlg::BuildDirectoryPath() { // Clean up path as needed. Fix slashes and convert to absolute path. FString NormPath = CurrentPath; FPaths::NormalizeFilename(NormPath); FPaths::RemoveDuplicateSlashes(NormPath); FString AbsPath = FPaths::ConvertRelativePathToFull(NormPath); TCHAR Temp[MAX_PATH_LENGTH]; DirectoryNodesArray.Empty(); FString BuiltPath; if (PLATFORM_WINDOWS) { int32 Idx; if (AbsPath.FindChar(TCHAR('/'), Idx)) { BuiltPath = BuiltPath + TEXT("/") + AbsPath.Left(Idx); } FCString::Strncpy(Temp, &AbsPath[Idx < AbsPath.Len() - 1 ? Idx + 1 : Idx], UE_ARRAY_COUNT(Temp)); DirectoryNodesArray.Add(FDirNode(AbsPath.Left(Idx), nullptr)); } else if (PLATFORM_LINUX) { // start with system base directory FCString::Strncpy(Temp, *AbsPath, UE_ARRAY_COUNT(Temp)); BuiltPath = "/"; DirectoryNodesArray.Add(FDirNode(FString(TEXT("/")), nullptr)); } else { checkf(false, TEXT("SlateDialogs will not work on this platform (modify SSlateFileOpenDlg::BuildDirectoryPath())")); } // break path into tokens TCHAR *ContextStr = nullptr; TCHAR *DirNode = FCString::Strtok(Temp, TEXT("/"), &ContextStr); while (DirNode) { FString Label = DirNode; DirectoryNodesArray.Add(FDirNode(Label, nullptr)); BuiltPath = BuiltPath + Label + TEXT("/"); DirNode = FCString::Strtok(nullptr, TEXT("/"), &ContextStr); } RefreshCrumbs(); } void SSlateFileOpenDlg::RefreshCrumbs() { // refresh crumb list if (PathBreadcrumbTrail.IsValid()) { PathBreadcrumbTrail->ClearCrumbs(); FString BuiltPath; if (PLATFORM_WINDOWS) { PathBreadcrumbTrail->PushCrumb(LOCTEXT("SlateFileDialogsSystem", "System"), FString("SYSTEM")); for (int32 i = 0; i < DirectoryNodesArray.Num(); i++) { BuiltPath = BuiltPath + DirectoryNodesArray[i].Label + TEXT("/"); PathBreadcrumbTrail->PushCrumb(FText::FromString(DirectoryNodesArray[i].Label), BuiltPath); } } else if (PLATFORM_LINUX) { BuiltPath = "/"; PathBreadcrumbTrail->PushCrumb(FText::FromString(BuiltPath), BuiltPath); for (int32 i = 1; i < DirectoryNodesArray.Num(); i++) { BuiltPath = BuiltPath + DirectoryNodesArray[i].Label + TEXT("/"); PathBreadcrumbTrail->PushCrumb(FText::FromString(DirectoryNodesArray[i].Label), BuiltPath); } } } } void SSlateFileOpenDlg::OnPathClicked(const FString& NewPath) { if (NewPath.Compare("SYSTEM") == 0) { // Ignore clicks on the virtual root. (Only happens for Windows systems.) return; } // set new current path and flag that we need to update directory display CurrentPath = NewPath; bRebuildDirPath = true; bNeedsBuilding = true; if ((History.Num()-HistoryIndex-1) > 0) { History.RemoveAt(HistoryIndex+1, History.Num()-HistoryIndex-1, EAllowShrinking::Yes); } History.Add(CurrentPath); HistoryIndex++; RefreshCrumbs(); } void SSlateFileOpenDlg::OnPathMenuItemClicked( FString ClickedPath ) { CurrentPath = ClickedPath; bRebuildDirPath = true; bNeedsBuilding = true; if ((History.Num()-HistoryIndex-1) > 0) { History.RemoveAt(HistoryIndex+1, History.Num()-HistoryIndex-1, EAllowShrinking::Yes); } History.Add(CurrentPath); HistoryIndex++; RefreshCrumbs(); } TSharedRef SSlateFileOpenDlg::OnGetCrumbDelimiterContent(const FString& CrumbData) const { TSharedPtr Widget = SNullWidget::NullWidget; TArray SubDirs; IFileManager& FileManager = IFileManager::Get(); FSlateFileDialogDirVisitor DirVisitor(&SubDirs); if (PLATFORM_WINDOWS) { if (CrumbData.Compare("SYSTEM") == 0) { // Windows doesn't have a root file system. So we need to provide a way to select system drives. // This is done by creating a virtual root using 'System' as the top node. int32 DrivesMask = #if PLATFORM_WINDOWS (int32)GetLogicalDrives() #else 0 #endif // PLATFORM_WINDOWS ; FMenuBuilder MenuBuilder(true, NULL); const TCHAR *DriveLetters = TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); FString Drive = TEXT("A:"); for (int32 i = 0; i < 26; i++) { if (DrivesMask & 0x01) { Drive[0] = DriveLetters[i]; MenuBuilder.AddMenuEntry( FText::FromString(Drive), FText::GetEmpty(), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(const_cast(this), &SSlateFileOpenDlg::OnPathMenuItemClicked, Drive + TEXT("/")))); } DrivesMask >>= 1; } return SNew(SVerticalBox) + SVerticalBox::Slot() .MaxHeight(400.0f) [ MenuBuilder.MakeWidget() ]; } } FileManager.IterateDirectory(*CrumbData, DirVisitor); if (SubDirs.Num() > 0) { SubDirs.Sort(); FMenuBuilder MenuBuilder( true, NULL ); for (int32 i = 0; i < SubDirs.Num(); i++) { const FString& SubDir = SubDirs[i]; MenuBuilder.AddMenuEntry( FText::FromString(SubDir), FText::GetEmpty(), FSlateIcon(), FUIAction(FExecuteAction::CreateSP(const_cast(this), &SSlateFileOpenDlg::OnPathMenuItemClicked, CrumbData + SubDir + TEXT("/")))); } Widget = SNew( SVerticalBox ) +SVerticalBox::Slot() .MaxHeight(400.0f) [ MenuBuilder.MakeWidget() ]; } return Widget.ToSharedRef(); } FReply SSlateFileOpenDlg::OnQuickLinkClick(FSlateFileDlgWindow::EResult ButtonID) { if (ButtonID == FSlateFileDlgWindow::Project) { // Taken from DesktopPlatform. We have to do this to avoid a circular dependency. const FString DefaultProjectSubFolder =TEXT("Unreal Projects"); CurrentPath = FPaths::ConvertRelativePathToFull(FString(FPlatformProcess::UserDir()) + DefaultProjectSubFolder + TEXT("/")); } if (ButtonID == FSlateFileDlgWindow::Engine) { CurrentPath = FPaths::ConvertRelativePathToFull(FPaths::EngineDir()); } if ((History.Num()-HistoryIndex-1) > 0) { History.RemoveAt(HistoryIndex+1, History.Num()-HistoryIndex-1, EAllowShrinking::Yes); } History.Add(CurrentPath); HistoryIndex++; bNeedsBuilding = true; bRebuildDirPath = true; return FReply::Handled(); } void SSlateFileOpenDlg::SetOutputFiles() { if (OutNames != nullptr) { TArray NamesArray; ParseTextField(NamesArray, SaveFilename); OutNames->Empty(); if (bDirectoriesOnly) { if (NamesArray.Num() > 0) { FString Path = CurrentPath + NamesArray[0]; OutNames->Add(Path); } else { // select the current directory OutNames->Add(CurrentPath); } } else { for (int32 i=0; i < NamesArray.Num(); i++) { FString Path = CurrentPath + NamesArray[i]; OutNames->Add(Path); } if (OutFilterIndex != nullptr) { *(OutFilterIndex) = FilterIndex; } } } } TSharedPtr SSlateFileOpenDlg::GetSoloDirectorySelected() const { TArray> SelectedItems = ListView->GetSelectedItems(); if (SelectedItems.Num() == 1 && SelectedItems[0]->bIsDirectory) { return SelectedItems[0]; } return nullptr; } bool SSlateFileOpenDlg::IsAcceptEnabled() const { if (bDirectoriesOnly) { return true; } TSharedPtr SoloSelectedDirectory = GetSoloDirectorySelected(); if (SoloSelectedDirectory.IsValid()) { return true; } else if (!SaveFilename.IsEmpty()) { return true; } return false; } FReply SSlateFileOpenDlg::OnAcceptCancelClick(FSlateFileDlgWindow::EResult ButtonID) { if (ButtonID == FSlateFileDlgWindow::Accept) { if (!bDirectoriesOnly) { TSharedPtr SoloSelectedDirectory = GetSoloDirectorySelected(); if (SoloSelectedDirectory.IsValid()) { OnItemDoubleClicked(SoloSelectedDirectory); return FReply::Handled(); } } SetOutputFiles(); } else { if (OutNames != nullptr) { OutNames->Empty(); } } UserResponse = ButtonID; ParentWindow.Pin()->RequestDestroyWindow(); return FReply::Handled(); } FReply SSlateFileOpenDlg::OnDirSublevelClick(int32 Level) { DirectoryNodesArray[DirNodeIndex].TextBlock->SetFont(StyleSet->GetFontStyle("SlateFileDialogs.Dialog")); FString NewPath = TEXT("/"); for (int32 i = 1; i <= Level; i++) { NewPath += DirectoryNodesArray[i].Label + TEXT("/"); } CurrentPath = NewPath; bRebuildDirPath = false; bNeedsBuilding = true; DirNodeIndex = Level; DirectoryNodesArray[DirNodeIndex].TextBlock->SetFont(StyleSet->GetFontStyle("SlateFileDialogs.DialogBold")); return FReply::Handled(); } void SSlateFileOpenDlg::Tick(const FGeometry &AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); if (DirectoryWatcher) { DirectoryWatcher->Tick(InDeltaTime); } if (bDirectoryHasChanged && !bNeedsBuilding) { ReadDir(true); RebuildFileTable(); ListView->RequestListRefresh(); bDirectoryHasChanged = false; } if (bNeedsBuilding) { // quick-link buttons to directory sublevels if (bRebuildDirPath) { BuildDirectoryPath(); } // Get directory contents and rebuild list ParseFilters(); ReadDir(); RebuildFileTable(); ListView->RequestListRefresh(); } bNeedsBuilding = false; bRebuildDirPath = false; } void SSlateFileOpenDlg::ReadDir(bool bIsRefresh) { if (DirectoryWatcher && RegisteredPath.Len() > 0 && !bIsRefresh) { DirectoryWatcher->UnregisterDirectoryChangedCallback_Handle(RegisteredPath, OnDialogDirectoryChangedDelegateHandle); RegisteredPath = TEXT(""); } IFileManager& FileManager = IFileManager::Get(); FilesArray.Empty(); FoldersArray.Empty(); FString FilterList; if (FilterListArray.Num() > 0 && FilterIndex >= 0) { FilterList = FilterListArray[FilterIndex]; } FSlateFileDialogVisitor DirVisitor(FilesArray, FoldersArray, FilterList); FileManager.IterateDirectory(*CurrentPath, DirVisitor); FilesArray.Sort(FFileEntry::ConstPredicate); FoldersArray.Sort(FFileEntry::ConstPredicate); if (DirectoryWatcher && !bIsRefresh) { DirectoryWatcher->RegisterDirectoryChangedCallback_Handle(CurrentPath, IDirectoryWatcher::FDirectoryChanged::CreateRaw(this, &SSlateFileOpenDlg::OnDirectoryChanged), OnDialogDirectoryChangedDelegateHandle, IDirectoryWatcher::WatchOptions::IncludeDirectoryChanges | IDirectoryWatcher::WatchOptions::IgnoreChangesInSubtree); RegisteredPath = CurrentPath; } } void SSlateFileOpenDlg::OnDirectoryChanged(const TArray &FileChanges) { bDirectoryHasChanged = true; } void SSlateFileOpenDlg::RebuildFileTable() { LineItemArray.Empty(); // directory entries for (int32 i = 0; i < FoldersArray.Num(); i++) { LineItemArray.Add(FoldersArray[i]); } // file entries if (bDirectoriesOnly == false) { for (int32 i = 0; i < FilesArray.Num(); i++) { LineItemArray.Add(FilesArray[i]); } } } TSharedRef SSlateFileOpenDlg::OnGenerateWidgetForList(TSharedPtr Item, const TSharedRef &OwnerTable) { return SNew(SSlateFileDialogRow, OwnerTable) .DialogItem(Item) .StyleSet(StyleSet); } void SSlateFileOpenDlg::OnItemDoubleClicked(TSharedPtr Item) { if (Item->bIsDirectory) { if (!bSaveFile) { SetDefaultFile(FString("")); } CurrentPath = CurrentPath + Item->Label + TEXT("/"); bNeedsBuilding = true; bRebuildDirPath = true; if ((History.Num()-HistoryIndex-1) > 0) { History.RemoveAt(HistoryIndex+1, History.Num()-HistoryIndex-1, EAllowShrinking::Yes); } History.Add(CurrentPath); HistoryIndex++; } else { SetOutputFiles(); UserResponse = FSlateFileDlgWindow::Accept; ParentWindow.Pin()->RequestDestroyWindow(); } } void SSlateFileOpenDlg::OnFilterChanged(TSharedPtr NewValue, ESelectInfo::Type SelectInfo) { for (int32 i = 0; i < FilterNameArray.Num(); i++) { if (NewValue.IsValid() && FilterNameArray[i].Get()->Compare(*NewValue.Get(), ESearchCase::CaseSensitive) == 0) { FilterIndex = i; break; } } bNeedsBuilding = true; } void SSlateFileOpenDlg::ParseTextField(TArray &FilenameArray, FString Files) { FString FileList = Files; FileList.TrimStartAndEndInline(); FilenameArray.Empty(); if (FileList.Len() > 0 && FileList[0] == TCHAR('"')) { FString TempName; SaveFilename.Empty(); for (int32 i = 0; i < FileList.Len(); ) { // find opening quote (") for (; i < FileList.Len() && FileList[i] != TCHAR('"'); i++); if (i >= FileList.Len()) { break; } // copy name until closing quote is found. TempName.Empty(); for (i++; i < FileList.Len() && FileList[i] != TCHAR('"'); i++) { TempName.AppendChar(FileList[i]); } if (i >= FileList.Len()) { break; } // check to see if file exists or if we're trying to save a file. if so, add it to list. if (FPaths::FileExists(CurrentPath + TempName) || bSaveFile) { FilenameArray.Add(TempName); } // if multiselect is off, don't bother parsing out any additional file names. if (!bMultiSelectEnabled) { break; } i++; } } else { TArray Extensions; FString FirstNonWilcardExtension; // get current filter extension if (!bDirectoriesOnly && GetFilterExtensions(Extensions)) { bool bSaveFilenameHasExtension = false; for ( const FString& Extension : Extensions ) { bSaveFilenameHasExtension = ( !IsWildcardExtension(Extension) && SaveFilename.EndsWith(Extension, ESearchCase::CaseSensitive) ); if ( !IsWildcardExtension(Extension) && FirstNonWilcardExtension.IsEmpty() ) { FirstNonWilcardExtension = Extension; } if ( bSaveFilenameHasExtension ) { break; } } // append extension to filename if user left it off if (!bSaveFilenameHasExtension && !FirstNonWilcardExtension.IsEmpty()) { Files = Files + FirstNonWilcardExtension; } } FilenameArray.Add(Files); } } void SSlateFileOpenDlg::SetDefaultFile(FString DefaultFile) { FString FileList = DefaultFile; FileList.TrimStartAndEndInline(); if (FileList.Len() > 0 && FileList[0] == TCHAR('"')) { TArray NamesArray; ParseTextField(NamesArray, FileList); SaveFilename.Empty(); for (int32 i = 0; i < NamesArray.Num(); i++) { SaveFilename = SaveFilename + TEXT("\"") + NamesArray[i] + TEXT("\" "); // if multiselect is off, don't bother adding any additional file names. if (!bMultiSelectEnabled) { break; } } } else { SaveFilename = FileList; } SaveFilenameEditBox->SetText(SaveFilename); } void SSlateFileOpenDlg::OnFileNameCommitted(const FText& InText, ETextCommit::Type InCommitType) { // update edit box unless user choose to escape out if (InCommitType != ETextCommit::OnCleared) { SaveFilename = InText.ToString(); ListView->ClearSelection(); SetDefaultFile(SaveFilename); } } void SSlateFileOpenDlg::OnItemSelected(TSharedPtr Item, ESelectInfo::Type SelectInfo) { if (Item.IsValid()) { FString FileList; if (!bDirectoriesOnly) { TArray> SelectedItems = ListView->GetSelectedItems(); for (int32 i = 0; i < SelectedItems.Num(); i++) { if (!SelectedItems[i]->bIsDirectory) { FileList = FileList + TEXT("\"") + SelectedItems[i]->Label + TEXT("\" "); } } } else { FileList = Item->Label; } // Update file name text as long as we aren't saving a file with a directory selected if (!(bSaveFile && Item->bIsDirectory)) { SetDefaultFile(FileList); } } } void SSlateFileOpenDlg::ParseFilters() { if (FilterCombo.IsValid() && FilterHBox.IsValid()) { if (Filters.Len() > 0) { if (FilterNameArray.Num() == 0) { TCHAR Temp[MAX_FILTER_LENGTH] = {0}; FCString::Strncpy(Temp, *Filters, UE_ARRAY_COUNT(Temp)); // break path into tokens TCHAR *ContextStr = nullptr; TCHAR *FilterDescription = FCString::Strtok(Temp, TEXT("|"), &ContextStr); TCHAR *FilterList; while (FilterDescription) { // filter wild cards FilterList = FCString::Strtok(nullptr, TEXT("|"), &ContextStr); FilterNameArray.Add(MakeShareable(new FString(FilterDescription))); FilterListArray.Add(FString(FilterList)); // next filter entry FilterDescription = FCString::Strtok(nullptr, TEXT("|"), &ContextStr); } } FilterComboBoxTitleBlock->SetText(FText::FromString(*FilterNameArray[FilterIndex])); } else { FilterNameArray.Empty(); FilterHBox->SetVisibility(EVisibility::Hidden); } } } bool SSlateFileOpenDlg::GetFilterExtensions(TArray& OutExtensions) { OutExtensions.Reset(); // check to see if filters were given if (Filters.Len() == 0) { return false; } // We have attempted to get the filter extension before parsing them if (FilterListArray.Num() == 0) { ParseFilters(); } if (!FilterListArray.IsValidIndex(FilterIndex)) { return false; } TArray Extensions; const bool bCullEmpty = true; if (FilterListArray[FilterIndex].ParseIntoArray(Extensions, TEXT(";"), bCullEmpty) > 0) { for (FString& Extension : Extensions) { // find start of extension int32 DotIndex; if (Extension.FindChar(TEXT('.'), DotIndex)) { Extension.RightChopInline(DotIndex); // strip any trailing junk for (TCHAR& ExtensionChar : Extension) { if (ExtensionChar == TEXT(' ') || ExtensionChar == TEXT(')') || ExtensionChar == TEXT(';')) { ExtensionChar = 0; break; } } Extension.TrimToNullTerminator(); // store result and clean up OutExtensions.Add(Extension); } else if (Extension[0] == TEXT('*')) { OutExtensions.Add(Extension); } } } return OutExtensions.Num() > 0; } bool SSlateFileOpenDlg::IsWildcardExtension(const FString& Extension) { return (Extension.Find(TEXT(".*")) >= 0) || (Extension.Find(TEXT("*")) >= 0); } void SSlateFileOpenDlg::OnNewDirectoryCommitted(const FText & InText, ETextCommit::Type InCommitType) { if (InCommitType == ETextCommit::OnEnter) { OnNewDirectoryAcceptCancelClick(FSlateFileDlgWindow::Accept); } else { OnNewDirectoryAcceptCancelClick(FSlateFileDlgWindow::Cancel); } } FReply SSlateFileOpenDlg::OnNewDirectoryClick() { NewDirectorySizeBox->SetVisibility(EVisibility::Visible); NewDirCancelButton->SetVisibility(EVisibility::Visible); NewDirectoryEditBox->SetText(FString("")); FSlateApplication::Get().SetKeyboardFocus(NewDirectoryEditBox); NewDirectoryEditBox->EnterEditingMode(); DirErrorMsg->SetVisibility(EVisibility::Collapsed); return FReply::Handled().SetUserFocus(NewDirectoryEditBox.ToSharedRef(), EFocusCause::SetDirectly); } bool SSlateFileOpenDlg::OnNewDirectoryTextChanged(const FText &InText, FText &ErrorMsg) { NewDirectoryName = InText.ToString(); return true; } FReply SSlateFileOpenDlg::OnNewDirectoryAcceptCancelClick(FSlateFileDlgWindow::EResult ButtonID) { if (ButtonID == FSlateFileDlgWindow::Accept) { NewDirectoryName.TrimStartAndEndInline(); if (NewDirectoryName.Len() > 0) { IPlatformFile &PlatformFile = FPlatformFileManager::Get().GetPlatformFile(); FString DirPath = CurrentPath + NewDirectoryName; if (!PlatformFile.CreateDirectory(*DirPath)) { DirErrorMsg->SetVisibility(EVisibility::Visible); return FReply::Handled(); } bDirectoryHasChanged = true; } } NewDirectorySizeBox->SetVisibility(EVisibility::Hidden); NewDirCancelButton->SetVisibility(EVisibility::Hidden); DirErrorMsg->SetVisibility(EVisibility::Collapsed); NewDirectoryEditBox->SetText(FString("")); return FReply::Handled(); } FReply SSlateFileOpenDlg::OnGoForwardClick() { if ((HistoryIndex+1) < History.Num()) { if (!bSaveFile) { SetDefaultFile(FString("")); } HistoryIndex++; CurrentPath = History[HistoryIndex]; bNeedsBuilding = true; bRebuildDirPath = true; bDirectoryHasChanged = false; } return FReply::Handled(); } FReply SSlateFileOpenDlg::OnGoBackClick() { if (HistoryIndex > 0) { if (!bSaveFile) { SetDefaultFile(FString("")); } HistoryIndex--; CurrentPath = History[HistoryIndex]; bNeedsBuilding = true; bRebuildDirPath = true; bDirectoryHasChanged = false; } return FReply::Handled(); } #undef LOCTEXT_NAMESPACE