mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-13 12:14:16 +01:00
Currently, using the search bar and navigating into a folder keeps the same search query, so you need to clear it if you want to actually choose a file inside the folder you were searching for. This clears the search bar when navigating into a folder.
297 lines
9.2 KiB
C#
297 lines
9.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
|
|
using ImGuiNET;
|
|
|
|
namespace Dalamud.Interface.ImGuiFileDialog;
|
|
|
|
/// <summary>
|
|
/// A file or folder picker.
|
|
/// </summary>
|
|
public partial class FileDialog
|
|
{
|
|
/// <summary>
|
|
/// The flags used to draw the file picker window.
|
|
/// </summary>
|
|
#pragma warning disable SA1401
|
|
public ImGuiWindowFlags WindowFlags;
|
|
#pragma warning restore SA1401
|
|
|
|
private readonly string title;
|
|
private readonly int selectionCountMax;
|
|
private readonly ImGuiFileDialogFlags flags;
|
|
private readonly string id;
|
|
private readonly string defaultExtension;
|
|
private readonly string defaultFileName;
|
|
|
|
private bool visible;
|
|
|
|
private string currentPath;
|
|
private string fileNameBuffer = string.Empty;
|
|
|
|
private List<string> pathDecomposition = new();
|
|
private bool pathClicked = true;
|
|
private bool pathInputActivated = false;
|
|
private string pathInputBuffer = string.Empty;
|
|
|
|
private bool isModal = false;
|
|
private bool okResultToConfirm = false;
|
|
private bool isOk;
|
|
private bool wantsToQuit;
|
|
|
|
private bool createDirectoryMode = false;
|
|
private string createDirectoryBuffer = string.Empty;
|
|
|
|
private string searchBuffer = string.Empty;
|
|
|
|
private string lastSelectedFileName = string.Empty;
|
|
private List<string> selectedFileNames = new();
|
|
|
|
private float footerHeight = 0;
|
|
|
|
private string selectedSideBar = string.Empty;
|
|
private List<SideBarItem> quickAccess = new();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="FileDialog"/> class.
|
|
/// </summary>
|
|
/// <param name="id">A unique id for the dialog.</param>
|
|
/// <param name="title">The text which is shown at the top of the dialog.</param>
|
|
/// <param name="filters">Which file extension filters to apply. This should be left blank to select directories.</param>
|
|
/// <param name="path">The directory which the dialog should start inside of.</param>
|
|
/// <param name="defaultFileName">The default file or directory name.</param>
|
|
/// <param name="defaultExtension">The default extension when creating new files.</param>
|
|
/// <param name="selectionCountMax">The maximum amount of files or directories which can be selected. Set to 0 for an infinite number.</param>
|
|
/// <param name="isModal">Whether the dialog should be a modal popup.</param>
|
|
/// <param name="flags">Settings flags for the dialog, see <see cref="ImGuiFileDialogFlags"/>.</param>
|
|
public FileDialog(
|
|
string id,
|
|
string title,
|
|
string filters,
|
|
string path,
|
|
string defaultFileName,
|
|
string defaultExtension,
|
|
int selectionCountMax,
|
|
bool isModal,
|
|
ImGuiFileDialogFlags flags)
|
|
{
|
|
this.id = id;
|
|
this.title = title;
|
|
this.flags = flags;
|
|
this.selectionCountMax = selectionCountMax;
|
|
this.isModal = isModal;
|
|
this.WindowFlags = ImGuiWindowFlags.NoNav;
|
|
if (!isModal)
|
|
this.WindowFlags |= ImGuiWindowFlags.NoScrollbar;
|
|
|
|
this.currentPath = path;
|
|
this.defaultExtension = defaultExtension;
|
|
this.defaultFileName = defaultFileName;
|
|
|
|
this.ParseFilters(filters);
|
|
this.SetSelectedFilterWithExt(this.defaultExtension);
|
|
this.SetDefaultFileName();
|
|
this.SetPath(this.currentPath);
|
|
|
|
this.SetupSideBar();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shows the dialog.
|
|
/// </summary>
|
|
public void Show()
|
|
{
|
|
this.visible = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hides the dialog.
|
|
/// </summary>
|
|
public void Hide()
|
|
{
|
|
this.visible = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets whether a file or folder was successfully selected.
|
|
/// </summary>
|
|
/// <returns>The success state. Will be false if the selection was canceled or was otherwise unsuccessful.</returns>
|
|
public bool GetIsOk()
|
|
{
|
|
return this.isOk;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the result of the selection.
|
|
/// </summary>
|
|
/// <returns>The result of the selection (file or folder path). If multiple entries were selected, they are separated with commas.</returns>
|
|
[Obsolete("Use GetResults() instead.", true)]
|
|
public string GetResult()
|
|
{
|
|
return string.Join(',', this.GetResults());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the result of the selection.
|
|
/// </summary>
|
|
/// <returns>The list of selected paths.</returns>
|
|
public List<string> GetResults()
|
|
{
|
|
if (!this.flags.HasFlag(ImGuiFileDialogFlags.SelectOnly))
|
|
{
|
|
return new List<string> { this.GetFilePathName() };
|
|
}
|
|
|
|
if (this.IsDirectoryMode() && this.selectedFileNames.Count == 0)
|
|
{
|
|
return new List<string> { this.GetFilePathName() }; // current directory
|
|
}
|
|
|
|
var fullPaths = this.selectedFileNames.Where(x => !string.IsNullOrEmpty(x)).Select(x => Path.Combine(this.currentPath, x));
|
|
return fullPaths.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the current path of the dialog.
|
|
/// </summary>
|
|
/// <returns>The path of the directory which the dialog is current viewing.</returns>
|
|
public string GetCurrentPath()
|
|
{
|
|
if (this.IsDirectoryMode())
|
|
{
|
|
// combine path file with directory input
|
|
var selectedDirectory = this.fileNameBuffer;
|
|
if (!string.IsNullOrEmpty(selectedDirectory) && selectedDirectory != ".")
|
|
{
|
|
return string.IsNullOrEmpty(this.currentPath) ? selectedDirectory : Path.Combine(this.currentPath, selectedDirectory);
|
|
}
|
|
}
|
|
|
|
return this.currentPath;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set or remove a quick access folder for the navigation panel.
|
|
/// </summary>
|
|
/// <param name="name">The displayed name of the folder. If this name already exists, it will be overwritten.</param>
|
|
/// <param name="path">The new linked path. If this is empty, no link will be added and existing links will be removed.</param>
|
|
/// <param name="icon">The FontAwesomeIcon-ID of the icon displayed before the name.</param>
|
|
/// <param name="position">An optional position at which to insert the new link. If the link is updated, having this less than zero will keep its position.
|
|
/// Otherwise, invalid indices will insert it at the end.</param>
|
|
public void SetQuickAccess(string name, string path, FontAwesomeIcon icon, int position = -1)
|
|
{
|
|
var idx = this.quickAccess.FindIndex(q => q.Text.Equals(name, StringComparison.InvariantCultureIgnoreCase));
|
|
if (idx >= 0)
|
|
{
|
|
if (position >= 0 || path.Length == 0)
|
|
{
|
|
this.quickAccess.RemoveAt(idx);
|
|
}
|
|
else
|
|
{
|
|
this.quickAccess[idx] = new SideBarItem(name, path, icon);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (path.Length == 0) return;
|
|
|
|
if (position < 0 || position >= this.quickAccess.Count)
|
|
{
|
|
this.quickAccess.Add(new SideBarItem(name, path, icon));
|
|
}
|
|
else
|
|
{
|
|
this.quickAccess.Insert(position, new SideBarItem(name, path, icon));
|
|
}
|
|
}
|
|
|
|
private string GetFilePathName()
|
|
{
|
|
var path = this.GetCurrentPath();
|
|
var fileName = this.GetCurrentFileName();
|
|
|
|
if (!string.IsNullOrEmpty(fileName))
|
|
{
|
|
return Path.Combine(path, fileName);
|
|
}
|
|
|
|
return path;
|
|
}
|
|
|
|
private string GetCurrentFileName()
|
|
{
|
|
if (this.IsDirectoryMode())
|
|
{
|
|
return string.Empty;
|
|
}
|
|
|
|
var result = this.fileNameBuffer;
|
|
|
|
// a collection like {.cpp, .h}, so can't decide on an extension
|
|
if (this.selectedFilter.CollectionFilters is { Count: > 0 })
|
|
{
|
|
return result;
|
|
}
|
|
|
|
// a single one, like .cpp
|
|
if (!this.selectedFilter.Filter.Contains('*') && result != this.selectedFilter.Filter)
|
|
{
|
|
var lastPoint = result.LastIndexOf('.');
|
|
if (lastPoint != -1)
|
|
{
|
|
result = result[..lastPoint];
|
|
}
|
|
|
|
result += this.selectedFilter.Filter;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private void SetDefaultFileName()
|
|
{
|
|
this.fileNameBuffer = this.defaultFileName;
|
|
}
|
|
|
|
private void SetPath(string path)
|
|
{
|
|
this.searchBuffer = string.Empty;
|
|
this.selectedSideBar = string.Empty;
|
|
this.currentPath = path;
|
|
this.files.Clear();
|
|
this.pathDecomposition.Clear();
|
|
this.selectedFileNames.Clear();
|
|
if (this.IsDirectoryMode())
|
|
{
|
|
this.SetDefaultFileName();
|
|
}
|
|
|
|
this.ScanDir(this.currentPath);
|
|
}
|
|
|
|
private void SetCurrentDir(string path)
|
|
{
|
|
var dir = new DirectoryInfo(path);
|
|
this.currentPath = dir.FullName;
|
|
if (this.currentPath[^1] == Path.DirectorySeparatorChar)
|
|
{ // handle selecting a drive, like C: -> C:\
|
|
this.currentPath = this.currentPath[..^1];
|
|
}
|
|
|
|
this.pathInputBuffer = this.currentPath;
|
|
this.pathDecomposition = new List<string>(this.currentPath.Split(Path.DirectorySeparatorChar));
|
|
}
|
|
|
|
private bool IsDirectoryMode()
|
|
{
|
|
return this.filters.Count == 0;
|
|
}
|
|
|
|
private void ResetEvents()
|
|
{
|
|
this.pathClicked = false;
|
|
}
|
|
}
|