Skip to content

Commit

Permalink
Add native windows file dialogs;
Browse files Browse the repository at this point in the history
Bump to version 1.1.2;
  • Loading branch information
onepiecefreak3 committed Sep 29, 2024
1 parent 1c03889 commit d9dc8d9
Show file tree
Hide file tree
Showing 3 changed files with 318 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ImGui.Forms/ImGui.Forms.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package >
<metadata>
<id>Imgui.Forms</id>
<version>1.1.1</version>
<version>1.1.2</version>
<description>A WinForms-inspired object-oriented framework around Dear ImGui (https://github.com/ocornut/imgui)</description>

<authors>onepiecefreak</authors>
Expand Down
163 changes: 163 additions & 0 deletions ImGui.Forms/Modals/IO/Windows/WindowsOpenFileDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using ImGui.Forms.Localization;

namespace ImGui.Forms.Modals.IO.Windows
{
public class WindowsOpenFileDialog
{
public LocalizedString Title { get; set; }
public bool Multiselect { get; set; } = false;
public string InitialDirectory { get; set; } = null;
public IList<FileFilter> Filters { get; set; } = new List<FileFilter> { new("All Files", "*") };
public bool ShowHidden { get; set; } = false;
public bool Success { get; private set; }
public string[] Files { get; private set; }

public async Task<DialogResult> ShowAsync()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return await ShowDefaultAsync();

await Task.Run(ShowOpenFileDialog);

return Success ? DialogResult.Ok : DialogResult.Cancel;
}

private async Task<DialogResult> ShowDefaultAsync()
{
var ofd = new OpenFileDialog
{
Caption = Title,
InitialDirectory = InitialDirectory
};

foreach (FileFilter filter in Filters)
ofd.FileFilters.Add(filter);

DialogResult result = await ofd.ShowAsync();
if (result == DialogResult.Ok)
Files = new[] { ofd.SelectedPath };

Success = result == DialogResult.Ok;
return result;
}

private void ShowOpenFileDialog()
{
const int MAX_FILE_LENGTH = 2048;

Success = false;
Files = null;

OpenFileName ofn = new OpenFileName();

ofn.structSize = Marshal.SizeOf(ofn);
ofn.filter = string.Join('\0', Filters.Select(f => f.Name + " (" + string.Join(';', f.Extensions.Select(e => "*." + e)) + ")\0"+ string.Join(';', f.Extensions.Select(e => "*." + e)))) + "\0";
ofn.fileTitle = new string(new char[MAX_FILE_LENGTH]);
ofn.maxFileTitle = ofn.fileTitle.Length;
ofn.initialDir = InitialDirectory;
ofn.title = Title;
ofn.flags = (int)OpenFileNameFlags.OFN_HIDEREADONLY | (int)OpenFileNameFlags.OFN_EXPLORER | (int)OpenFileNameFlags.OFN_FILEMUSTEXIST | (int)OpenFileNameFlags.OFN_PATHMUSTEXIST;

// Create buffer for file names
ofn.file = Marshal.AllocHGlobal(MAX_FILE_LENGTH * Marshal.SystemDefaultCharSize);
ofn.maxFile = MAX_FILE_LENGTH;

// Initialize buffer with NULL bytes
for (int i = 0; i < MAX_FILE_LENGTH * Marshal.SystemDefaultCharSize; i++)
{
Marshal.WriteByte(ofn.file, i, 0);
}

if (ShowHidden)
{
ofn.flags |= (int)OpenFileNameFlags.OFN_FORCESHOWHIDDEN;
}

if (Multiselect)
{
ofn.flags |= (int)OpenFileNameFlags.OFN_ALLOWMULTISELECT;
}

Success = GetOpenFileName(ofn);

if (Success)
{
nint filePointer = ofn.file;
long pointer = (long)filePointer;
string file = Marshal.PtrToStringAuto(filePointer);
List<string> strList = new List<string>();

// Retrieve file names
while (file.Length > 0)
{
strList.Add(file);

pointer += file.Length * Marshal.SystemDefaultCharSize + Marshal.SystemDefaultCharSize;
filePointer = (nint)pointer;
file = Marshal.PtrToStringAuto(filePointer);
}

if (strList.Count > 1)
{
Files = new string[strList.Count - 1];
for (int i = 1; i < strList.Count; i++)
{
Files[i - 1] = Path.Combine(strList[0], strList[i]);
}
}
else
{
Files = strList.ToArray();
}
}

Marshal.FreeHGlobal(ofn.file);
}

[DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool GetOpenFileName([In, Out] OpenFileName ofn);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class OpenFileName
{
public int structSize = 0;
public nint dlgOwner = nint.Zero;
public nint instance = nint.Zero;
public string filter;
public string customFilter;
public int maxCustFilter = 0;
public int filterIndex = 0;
public nint file;
public int maxFile = 0;
public string fileTitle;
public int maxFileTitle = 0;
public string initialDir;
public string title;
public int flags = 0;
public short fileOffset = 0;
public short fileExtension = 0;
public string defExt;
public nint custData = nint.Zero;
public nint hook = nint.Zero;
public string templateName;
public nint reservedPtr = nint.Zero;
public int reservedInt = 0;
public int flagsEx = 0;
}

private enum OpenFileNameFlags
{
OFN_HIDEREADONLY = 0x4,
OFN_FORCESHOWHIDDEN = 0x10000000,
OFN_ALLOWMULTISELECT = 0x200,
OFN_EXPLORER = 0x80000,
OFN_FILEMUSTEXIST = 0x1000,
OFN_PATHMUSTEXIST = 0x800
}
}
}
154 changes: 154 additions & 0 deletions ImGui.Forms/Modals/IO/Windows/WindowsSaveFileDialog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using ImGui.Forms.Localization;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace ImGui.Forms.Modals.IO.Windows
{
public class WindowsSaveFileDialog
{
public LocalizedString Title { get; set; }
public string InitialDirectory { get; set; } = null;
public IList<FileFilter> Filters { get; set; } = new List<FileFilter> { new("All Files", "*") };
public bool ShowHidden { get; set; } = false;
public bool Success { get; private set; }
public string[] Files { get; private set; }

public async Task<DialogResult> ShowAsync()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return await ShowDefaultAsync();

await Task.Run(ShowSaveFileDialog);

return Success ? DialogResult.Ok : DialogResult.Cancel;
}

private async Task<DialogResult> ShowDefaultAsync()
{
var sfd = new SaveFileDialog
{
Caption = Title,
InitialDirectory = InitialDirectory
};

DialogResult result = await sfd.ShowAsync();
if (result == DialogResult.Ok)
Files = new[] { sfd.SelectedPath };

Success = result == DialogResult.Ok;
return result;
}

private void ShowSaveFileDialog()
{
const int MAX_FILE_LENGTH = 2048;

Success = false;
Files = null;

OpenFileName ofn = new OpenFileName();

ofn.structSize = Marshal.SizeOf(ofn);
ofn.filter = string.Join('\0', Filters.Select(f => f.Name + " (" + string.Join(';', f.Extensions.Select(e => "*." + e)) + ")\0" + string.Join(';', f.Extensions.Select(e => "*." + e)))) + "\0";
ofn.fileTitle = new string(new char[MAX_FILE_LENGTH]);
ofn.maxFileTitle = ofn.fileTitle.Length;
ofn.initialDir = InitialDirectory;
ofn.title = Title;
ofn.flags = (int)OpenFileNameFlags.OFN_HIDEREADONLY | (int)OpenFileNameFlags.OFN_EXPLORER | (int)OpenFileNameFlags.OFN_FILEMUSTEXIST | (int)OpenFileNameFlags.OFN_PATHMUSTEXIST;

// Create buffer for file names
ofn.file = Marshal.AllocHGlobal(MAX_FILE_LENGTH * Marshal.SystemDefaultCharSize);
ofn.maxFile = MAX_FILE_LENGTH;

// Initialize buffer with NULL bytes
for (int i = 0; i < MAX_FILE_LENGTH * Marshal.SystemDefaultCharSize; i++)
{
Marshal.WriteByte(ofn.file, i, 0);
}

if (ShowHidden)
{
ofn.flags |= (int)OpenFileNameFlags.OFN_FORCESHOWHIDDEN;
}

Success = GetSaveFileName(ofn);

if (Success)
{
nint filePointer = ofn.file;
long pointer = (long)filePointer;
string file = Marshal.PtrToStringAuto(filePointer);
List<string> strList = new List<string>();

// Retrieve file names
while (file.Length > 0)
{
strList.Add(file);

pointer += file.Length * Marshal.SystemDefaultCharSize + Marshal.SystemDefaultCharSize;
filePointer = (nint)pointer;
file = Marshal.PtrToStringAuto(filePointer);
}

if (strList.Count > 1)
{
Files = new string[strList.Count - 1];
for (int i = 1; i < strList.Count; i++)
{
Files[i - 1] = Path.Combine(strList[0], strList[i]);
}
}
else
{
Files = strList.ToArray();
}
}

Marshal.FreeHGlobal(ofn.file);
}

[DllImport("comdlg32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern bool GetSaveFileName([In, Out] OpenFileName ofn);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private class OpenFileName
{
public int structSize = 0;
public nint dlgOwner = nint.Zero;
public nint instance = nint.Zero;
public string filter;
public string customFilter;
public int maxCustFilter = 0;
public int filterIndex = 0;
public nint file;
public int maxFile = 0;
public string fileTitle;
public int maxFileTitle = 0;
public string initialDir;
public string title;
public int flags = 0;
public short fileOffset = 0;
public short fileExtension = 0;
public string defExt;
public nint custData = nint.Zero;
public nint hook = nint.Zero;
public string templateName;
public nint reservedPtr = nint.Zero;
public int reservedInt = 0;
public int flagsEx = 0;
}

private enum OpenFileNameFlags
{
OFN_HIDEREADONLY = 0x4,
OFN_FORCESHOWHIDDEN = 0x10000000,
OFN_ALLOWMULTISELECT = 0x200,
OFN_EXPLORER = 0x80000,
OFN_FILEMUSTEXIST = 0x1000,
OFN_PATHMUSTEXIST = 0x800
}
}
}

0 comments on commit d9dc8d9

Please sign in to comment.