First release
This commit is contained in:
commit
bb07ec43cd
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
bin/
|
||||
obj/
|
||||
/packages/
|
||||
riderModule.iml
|
||||
/_ReSharper.Caches/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "nativefiledialog"]
|
||||
path = nativefiledialog
|
||||
url = https://github.com/milleniumbug/nativefiledialog
|
22
NativeFileDialogSharp.sln
Normal file
22
NativeFileDialogSharp.sln
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeFileDialogSharp", "NativeFileDialogSharp\NativeFileDialogSharp.csproj", "{4127F279-9FD5-4C37-B904-242C124C1A07}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeFileDialogSharpSandbox", "NativeFileDialogSharpSandbox\NativeFileDialogSharpSandbox.csproj", "{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4127F279-9FD5-4C37-B904-242C124C1A07}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4127F279-9FD5-4C37-B904-242C124C1A07}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
56
NativeFileDialogSharp/Native/NativeFunctions.cs
Normal file
56
NativeFileDialogSharp/Native/NativeFunctions.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace NativeFileDialogSharp.Native;
|
||||
|
||||
public struct nfdpathset_t
|
||||
{
|
||||
public IntPtr buf;
|
||||
public IntPtr indices;
|
||||
public UIntPtr count;
|
||||
}
|
||||
|
||||
public enum nfdresult_t
|
||||
{
|
||||
NFD_ERROR,
|
||||
NFD_OKAY,
|
||||
NFD_CANCEL
|
||||
}
|
||||
|
||||
public static class NativeFunctions
|
||||
{
|
||||
public const string LibraryName = "nfd";
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe nfdresult_t NFD_OpenDialog(byte* filterList, byte* defaultPath, out IntPtr outPath);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe nfdresult_t NFD_OpenDialogMultiple(byte* filterList, byte* defaultPath, nfdpathset_t* outPaths);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe nfdresult_t NFD_SaveDialog(byte* filterList, byte* defaultPath, out IntPtr outPath);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe nfdresult_t NFD_PickFolder(byte* defaultPath, out IntPtr outPath);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe byte* NFD_GetError();
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe UIntPtr NFD_PathSet_GetCount(nfdpathset_t* pathSet);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe byte* NFD_PathSet_GetPath(nfdpathset_t* pathSet, UIntPtr index);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe void NFD_PathSet_Free(nfdpathset_t* pathSet);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe void NFD_Dummy();
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe IntPtr NFD_Malloc(UIntPtr bytes);
|
||||
|
||||
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern unsafe void NFD_Free(IntPtr ptr);
|
||||
}
|
30
NativeFileDialogSharp/NativeFileDialogSharp.csproj
Normal file
30
NativeFileDialogSharp/NativeFileDialogSharp.csproj
Normal file
@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<LangVersion>10</LangVersion>
|
||||
<PackageVersion>0.1</PackageVersion>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="libnfd.so">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="nfd.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="nfd.pdb">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
155
NativeFileDialogSharp/NativeWrappers.cs
Normal file
155
NativeFileDialogSharp/NativeWrappers.cs
Normal file
@ -0,0 +1,155 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NativeFileDialogSharp.Native;
|
||||
|
||||
public static class Dialog
|
||||
{
|
||||
private static byte[] ToUtf8(string s)
|
||||
{
|
||||
var byteCount = Encoding.UTF8.GetByteCount(s);
|
||||
var bytes = new byte[byteCount + 1];
|
||||
Encoding.UTF8.GetBytes(s.AsSpan(), bytes.AsSpan());
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private static unsafe Span<byte> MakeSpanFromNullTerminatedString(byte* nullTerminatedString)
|
||||
{
|
||||
int count = 0;
|
||||
var ptr = nullTerminatedString;
|
||||
while (*ptr != 0)
|
||||
{
|
||||
ptr++;
|
||||
count++;
|
||||
}
|
||||
|
||||
return new Span<byte>(nullTerminatedString, count);
|
||||
}
|
||||
|
||||
private static string FromUtf8(ReadOnlySpan<byte> input)
|
||||
{
|
||||
return Encoding.UTF8.GetString(input);
|
||||
}
|
||||
|
||||
public static unsafe DialogResult FileOpen(string filterList = null, string defaultPath = null)
|
||||
{
|
||||
fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null)
|
||||
fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null)
|
||||
{
|
||||
string path = null;
|
||||
string errorMessage = null;
|
||||
var result = NativeFunctions.NFD_OpenDialog(filterListNts, defaultPathNts, out IntPtr outPathIntPtr);
|
||||
if (result == nfdresult_t.NFD_ERROR)
|
||||
{
|
||||
errorMessage = FromUtf8(MakeSpanFromNullTerminatedString(NativeFunctions.NFD_GetError()));
|
||||
}
|
||||
else if (result == nfdresult_t.NFD_OKAY)
|
||||
{
|
||||
var outPathNts = (byte*)outPathIntPtr.ToPointer();
|
||||
path = FromUtf8(MakeSpanFromNullTerminatedString(outPathNts));
|
||||
NativeFunctions.NFD_Free(outPathIntPtr);
|
||||
}
|
||||
|
||||
return new DialogResult(result, path, null, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe DialogResult FileSave(string filterList = null, string defaultPath = null)
|
||||
{
|
||||
fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null)
|
||||
fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null)
|
||||
{
|
||||
string path = null;
|
||||
string errorMessage = null;
|
||||
var result = NativeFunctions.NFD_SaveDialog(filterListNts, defaultPathNts, out IntPtr outPathIntPtr);
|
||||
if (result == nfdresult_t.NFD_ERROR)
|
||||
{
|
||||
errorMessage = FromUtf8(MakeSpanFromNullTerminatedString(NativeFunctions.NFD_GetError()));
|
||||
}
|
||||
else if (result == nfdresult_t.NFD_OKAY)
|
||||
{
|
||||
var outPathNts = (byte*)outPathIntPtr.ToPointer();
|
||||
path = FromUtf8(MakeSpanFromNullTerminatedString(outPathNts));
|
||||
NativeFunctions.NFD_Free(outPathIntPtr);
|
||||
}
|
||||
|
||||
return new DialogResult(result, path, null, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe DialogResult FolderPicker(string defaultPath = null)
|
||||
{
|
||||
fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null)
|
||||
{
|
||||
string path = null;
|
||||
string errorMessage = null;
|
||||
var result = NativeFunctions.NFD_PickFolder(defaultPathNts, out IntPtr outPathIntPtr);
|
||||
if (result == nfdresult_t.NFD_ERROR)
|
||||
{
|
||||
errorMessage = FromUtf8(MakeSpanFromNullTerminatedString(NativeFunctions.NFD_GetError()));
|
||||
}
|
||||
else if (result == nfdresult_t.NFD_OKAY)
|
||||
{
|
||||
var outPathNts = (byte*)outPathIntPtr.ToPointer();
|
||||
path = FromUtf8(MakeSpanFromNullTerminatedString(outPathNts));
|
||||
NativeFunctions.NFD_Free(outPathIntPtr);
|
||||
}
|
||||
|
||||
return new DialogResult(result, path, null, errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
public static unsafe DialogResult FileOpenMultiple(string filterList = null, string defaultPath = null)
|
||||
{
|
||||
fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null)
|
||||
fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null)
|
||||
{
|
||||
List<string> paths = null;
|
||||
string errorMessage = null;
|
||||
nfdpathset_t pathSet;
|
||||
var result = NativeFunctions.NFD_OpenDialogMultiple(filterListNts, defaultPathNts, &pathSet);
|
||||
if (result == nfdresult_t.NFD_ERROR)
|
||||
{
|
||||
errorMessage = FromUtf8(MakeSpanFromNullTerminatedString(NativeFunctions.NFD_GetError()));
|
||||
}
|
||||
else if (result == nfdresult_t.NFD_OKAY)
|
||||
{
|
||||
var pathCount = (int)NativeFunctions.NFD_PathSet_GetCount(&pathSet).ToUInt32();
|
||||
paths = new List<string>(pathCount);
|
||||
for (int i = 0; i < pathCount; i++)
|
||||
{
|
||||
paths.Add(FromUtf8(MakeSpanFromNullTerminatedString(NativeFunctions.NFD_PathSet_GetPath(&pathSet, new UIntPtr((uint)i)))));
|
||||
}
|
||||
NativeFunctions.NFD_PathSet_Free(&pathSet);
|
||||
}
|
||||
|
||||
return new DialogResult(result, null, paths, errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class DialogResult
|
||||
{
|
||||
private readonly nfdresult_t result;
|
||||
|
||||
public string Path { get; }
|
||||
|
||||
public IReadOnlyList<string> Paths { get; }
|
||||
|
||||
public bool IsError => result == nfdresult_t.NFD_ERROR;
|
||||
|
||||
public string ErrorMessage { get; }
|
||||
|
||||
public bool IsCancelled => result == nfdresult_t.NFD_CANCEL;
|
||||
|
||||
public bool IsOk => result == nfdresult_t.NFD_OKAY;
|
||||
|
||||
internal DialogResult(nfdresult_t result, string path, IReadOnlyList<string> paths, string errorMessage)
|
||||
{
|
||||
this.result = result;
|
||||
Path = path;
|
||||
Paths = paths;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
}
|
BIN
NativeFileDialogSharp/libnfd.so
Executable file
BIN
NativeFileDialogSharp/libnfd.so
Executable file
Binary file not shown.
BIN
NativeFileDialogSharp/nfd.dll
Normal file
BIN
NativeFileDialogSharp/nfd.dll
Normal file
Binary file not shown.
BIN
NativeFileDialogSharp/nfd.pdb
Normal file
BIN
NativeFileDialogSharp/nfd.pdb
Normal file
Binary file not shown.
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NativeFileDialogSharp\NativeFileDialogSharp.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
12
NativeFileDialogSharpSandbox/Program.cs
Normal file
12
NativeFileDialogSharpSandbox/Program.cs
Normal file
@ -0,0 +1,12 @@
|
||||
// See https://aka.ms/new-console-template for more information
|
||||
|
||||
using NativeFileDialogSharp.Native;
|
||||
|
||||
var result = Dialog.FileOpenMultiple();
|
||||
|
||||
Console.WriteLine($"Path: {result.Path}, IsError {result.IsError}, IsOk {result.IsOk}, IsCancelled {result.IsCancelled}, ErrorMessage {result.ErrorMessage}");
|
||||
if (result.Paths != null)
|
||||
{
|
||||
Console.WriteLine("Paths");
|
||||
Console.WriteLine(string.Join("\n", result.Paths));
|
||||
}
|
1
nativefiledialog
Submodule
1
nativefiledialog
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 5018af9ba752cafeaed10b1ceba1b07dcb551923
|
Loading…
Reference in New Issue
Block a user