345 lines
11 KiB
C#
345 lines
11 KiB
C#
#if UNITY_STANDALONE_LINUX || CT_DEVELOP
|
|
using System;
|
|
using UnityEngine;
|
|
|
|
namespace Crosstales.FB.Wrapper
|
|
{
|
|
/// <summary>File browser implementation for Linux (GTK).</summary>
|
|
public class FileBrowserLinux : BaseFileBrowserStandalone
|
|
{
|
|
#region Variables
|
|
|
|
private static FileBrowserLinux instance;
|
|
|
|
private static Action<string[]> _openFileCb;
|
|
private static Action<string[]> _openFolderCb;
|
|
private static Action<string> _saveFileCb;
|
|
|
|
private const char splitChar = (char)28;
|
|
|
|
#endregion
|
|
|
|
|
|
#region Constructor
|
|
|
|
public FileBrowserLinux()
|
|
{
|
|
instance = this;
|
|
|
|
Linux.NativeMethods.DialogInit();
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Implemented methods
|
|
|
|
public override bool canOpenMultipleFolders => true;
|
|
|
|
public override bool isPlatformSupported => Crosstales.FB.Util.Helper.isLinuxPlatform; // || Util.Helper.isLinuxEditor;
|
|
|
|
public override bool isWorkingInEditor => false;
|
|
|
|
public override string[] OpenFiles(string title, string directory, string defaultName, bool multiselect, params ExtensionFilter[] extensions)
|
|
{
|
|
if (!string.IsNullOrEmpty(defaultName))
|
|
Debug.LogWarning("'defaultName' is not supported under Linux.");
|
|
|
|
//resetOpenFiles();
|
|
|
|
return openFiles(title, directory, defaultName, multiselect, false, extensions);
|
|
}
|
|
|
|
public override string[] OpenFolders(string title, string directory, bool multiselect)
|
|
{
|
|
//resetOpenFolders();
|
|
|
|
return openFolders(title, directory, multiselect, false);
|
|
}
|
|
|
|
public override string SaveFile(string title, string directory, string defaultName, params ExtensionFilter[] extensions)
|
|
{
|
|
//resetSaveFile();
|
|
|
|
return saveFile(title, directory, defaultName, false, extensions);
|
|
}
|
|
|
|
public override void OpenFilesAsync(string title, string directory, string defaultName, bool multiselect, ExtensionFilter[] extensions, Action<string[]> cb)
|
|
{
|
|
if (!string.IsNullOrEmpty(defaultName))
|
|
Debug.LogWarning("'defaultName' is not supported under Linux.");
|
|
|
|
//resetOpenFiles();
|
|
|
|
_openFileCb = cb;
|
|
|
|
openFiles(title, directory, defaultName, multiselect, true, extensions);
|
|
}
|
|
|
|
public override void OpenFoldersAsync(string title, string directory, bool multiselect, Action<string[]> cb)
|
|
{
|
|
//resetOpenFolders();
|
|
|
|
_openFolderCb = cb;
|
|
|
|
openFolders(title, directory, multiselect, true);
|
|
}
|
|
|
|
public override void SaveFileAsync(string title, string directory, string defaultName, ExtensionFilter[] extensions, Action<string> cb)
|
|
{
|
|
//resetSaveFile();
|
|
|
|
_saveFileCb = cb;
|
|
|
|
saveFile(title, directory, defaultName, true, extensions);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region Private methods
|
|
|
|
private static string getFilterFromFileExtensionList(ExtensionFilter[] extensions)
|
|
{
|
|
if (extensions?.Length > 0)
|
|
{
|
|
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
|
|
|
for (int xx = 0; xx < extensions.Length; xx++)
|
|
{
|
|
ExtensionFilter filter = extensions[xx];
|
|
|
|
sb.Append(filter.Name);
|
|
sb.Append(";");
|
|
|
|
for (int ii = 0; ii < filter.Extensions.Length; ii++)
|
|
{
|
|
sb.Append(filter.Extensions[ii]);
|
|
|
|
if (ii + 1 < filter.Extensions.Length)
|
|
sb.Append(",");
|
|
}
|
|
|
|
if (xx + 1 < extensions.Length)
|
|
sb.Append("|");
|
|
}
|
|
|
|
if (Crosstales.FB.Util.Config.DEBUG)
|
|
Debug.Log($"getFilterFromFileExtensionList: {sb}");
|
|
|
|
return sb.ToString();
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
#endregion
|
|
|
|
private static string[] openFiles(string title, string directory, string defaultName, bool multiselect, bool isAsync, params ExtensionFilter[] extensions)
|
|
{
|
|
try
|
|
{
|
|
if (isAsync)
|
|
{
|
|
//TODO change to the same method as for macOS?
|
|
Linux.NativeMethods.DialogOpenFilePanelAsync(title, directory, getFilterFromFileExtensionList(extensions), multiselect, paths =>
|
|
{
|
|
if (string.IsNullOrEmpty(paths))
|
|
{
|
|
instance.CurrentOpenFiles = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFile = string.Empty;
|
|
|
|
_openFileCb?.Invoke(null);
|
|
}
|
|
else
|
|
{
|
|
string[] pathArray = paths.Split(splitChar);
|
|
|
|
instance.CurrentOpenFiles = pathArray;
|
|
instance.CurrentOpenSingleFile = pathArray[0];
|
|
|
|
_openFileCb?.Invoke(pathArray);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
string paths = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(Linux.NativeMethods.DialogOpenFilePanel(title, directory, getFilterFromFileExtensionList(extensions), multiselect));
|
|
|
|
if (string.IsNullOrEmpty(paths))
|
|
{
|
|
instance.CurrentOpenFiles = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFile = string.Empty;
|
|
|
|
return null;
|
|
}
|
|
|
|
string[] pathArray = paths.Split(splitChar);
|
|
|
|
instance.CurrentOpenFiles = pathArray;
|
|
instance.CurrentOpenSingleFile = pathArray[0];
|
|
|
|
return instance.CurrentOpenFiles;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
instance.CurrentOpenFiles = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFile = string.Empty;
|
|
|
|
Debug.LogError($"Open file dialog threw an error: {ex}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string[] openFolders(string title, string directory, bool multiselect, bool isAsync)
|
|
{
|
|
try
|
|
{
|
|
if (isAsync)
|
|
{
|
|
//TODO change to the same method as for macOS?
|
|
Linux.NativeMethods.DialogOpenFolderPanelAsync(title, directory, multiselect, paths =>
|
|
{
|
|
if (string.IsNullOrEmpty(paths))
|
|
{
|
|
instance.CurrentOpenFolders = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFolder = string.Empty;
|
|
|
|
_openFolderCb?.Invoke(null);
|
|
}
|
|
else
|
|
{
|
|
string[] pathArray = paths.Split(splitChar);
|
|
|
|
instance.CurrentOpenFolders = pathArray;
|
|
instance.CurrentOpenSingleFolder = pathArray[0];
|
|
|
|
_openFolderCb?.Invoke(pathArray);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
string paths = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(Linux.NativeMethods.DialogOpenFolderPanel(title, directory, multiselect));
|
|
|
|
if (string.IsNullOrEmpty(paths))
|
|
{
|
|
instance.CurrentOpenFolders = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFolder = string.Empty;
|
|
|
|
return null;
|
|
}
|
|
|
|
string[] pathArray = paths.Split(splitChar);
|
|
|
|
instance.CurrentOpenFolders = pathArray;
|
|
instance.CurrentOpenSingleFolder = pathArray[0];
|
|
|
|
return instance.CurrentOpenFolders;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
instance.CurrentOpenFolders = System.Array.Empty<string>();
|
|
instance.CurrentOpenSingleFolder = string.Empty;
|
|
|
|
Debug.LogError($"Folder dialog threw an error: {ex}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string saveFile(string title, string directory, string defaultName, bool isAsync, params ExtensionFilter[] extensions)
|
|
{
|
|
bool useFallback = extensions != null && extensions.Length == 1 && extensions[0].Extensions.Length == 1;
|
|
string fallbackExtension = useFallback ? $".{extensions[0].Extensions[0]}" : string.Empty;
|
|
|
|
try
|
|
{
|
|
if (isAsync)
|
|
{
|
|
//TODO change to the same method as for macOS?
|
|
Linux.NativeMethods.DialogSaveFilePanelAsync(title, directory, defaultName, getFilterFromFileExtensionList(extensions), path =>
|
|
{
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
instance.CurrentSaveFile = string.Empty;
|
|
|
|
_saveFileCb?.Invoke(null);
|
|
}
|
|
else
|
|
{
|
|
if (useFallback && !path.EndsWith(fallbackExtension))
|
|
path += fallbackExtension;
|
|
|
|
instance.CurrentSaveFile = path;
|
|
|
|
_saveFileCb?.Invoke(path);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
string path = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(Linux.NativeMethods.DialogSaveFilePanel(title, directory, defaultName, getFilterFromFileExtensionList(extensions)));
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
{
|
|
instance.CurrentSaveFile = string.Empty;
|
|
|
|
return null;
|
|
}
|
|
|
|
if (useFallback && !path.EndsWith(fallbackExtension))
|
|
path += fallbackExtension;
|
|
|
|
instance.CurrentSaveFile = path;
|
|
|
|
return instance.CurrentSaveFile;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
instance.CurrentSaveFile = string.Empty;
|
|
|
|
Debug.LogError($"Save file dialog threw an error: {ex}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Crosstales.FB.Wrapper.Linux
|
|
{
|
|
/// <summary>Native methods (bridge to Linux).</summary>
|
|
internal static class NativeMethods
|
|
{
|
|
[System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)]
|
|
public delegate void AsyncCallback(string path);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern void DialogInit();
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern IntPtr DialogOpenFilePanel(string title, string directory, string extension, bool multiselect);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern IntPtr DialogOpenFolderPanel(string title, string directory, bool multiselect);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern IntPtr DialogSaveFilePanel(string title, string directory, string defaultName, string extension);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern void DialogOpenFilePanelAsync(string title, string directory, string extension, bool multiselect, AsyncCallback callback);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern void DialogOpenFolderPanelAsync(string title, string directory, bool multiselect, AsyncCallback callback);
|
|
|
|
[System.Runtime.InteropServices.DllImport("FileBrowser")]
|
|
internal static extern void DialogSaveFilePanelAsync(string title, string directory, string defaultName, string extension, AsyncCallback callback);
|
|
}
|
|
}
|
|
#endif
|
|
// © 2019-2023 crosstales LLC (https://www.crosstales.com) |