Browse Source

Implemented system dialogs

Nikita Tsukanov 8 years ago
parent
commit
cbc401b565

+ 1 - 1
src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj

@@ -64,7 +64,7 @@
     <Compile Include="PlatformIconLoader.cs" />
     <Compile Include="PopupImpl.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Stubs.cs" />
+    <Compile Include="SystemDialogs.cs" />
     <Compile Include="TopLevelImpl.cs" />
     <Compile Include="Interop\Utf8Buffer.cs" />
     <Compile Include="WindowImpl.cs" />

+ 10 - 0
src/Gtk/Avalonia.Gtk3/Interop/GObject.cs

@@ -48,5 +48,15 @@ namespace Avalonia.Gtk3.Interop
     {
         
     }
+
+    class GtkDialog : GtkWindow
+    {
+        
+    }
+
+    class GtkFileChooser : GtkDialog
+    {
+        
+    }
 }
 

+ 52 - 0
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@@ -95,6 +95,17 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
             public delegate void gtk_window_move(GtkWindow gtkWindow, int x, int y);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate GtkFileChooser gtk_file_chooser_dialog_new(Utf8Buffer title, GtkWindow parent, GtkFileChooserAction action, IntPtr ignore);
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public unsafe delegate GSList* gtk_file_chooser_get_filenames(GtkFileChooser chooser);
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_file_chooser_set_select_multiple(GtkFileChooser chooser, bool allow);
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_file_chooser_set_filename(GtkFileChooser chooser, Utf8Buffer file);
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+            public delegate void gtk_dialog_add_button(GtkDialog raw, Utf8Buffer button_text, GtkResponseType response_id);
+
 
 
 
@@ -203,6 +214,8 @@ namespace Avalonia.Gtk3.Interop
             public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data);
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
             public delegate ulong g_free(IntPtr data);
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
+            public unsafe delegate void g_slist_free(GSList* data);
             [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gio)]
             public delegate GInputStream g_memory_input_stream_new_from_data(IntPtr ptr, IntPtr len, IntPtr destroyCallback);
 
@@ -212,6 +225,9 @@ namespace Avalonia.Gtk3.Interop
             [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
             public delegate bool signal_generic(IntPtr gtkWidget, IntPtr userData);
 
+            [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+            public delegate bool signal_dialog_response(IntPtr gtkWidget, GtkResponseType response, IntPtr userData);
+
             [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
             public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData);
 
@@ -247,11 +263,17 @@ namespace Avalonia.Gtk3.Interop
         public static D.gtk_window_set_default_size GtkWindowSetDefaultSize;
         public static D.gtk_window_get_position GtkWindowGetPosition;
         public static D.gtk_window_move GtkWindowMove;
+        public static D.gtk_file_chooser_dialog_new GtkFileChooserDialogNew;
+        public static D.gtk_file_chooser_set_select_multiple GtkFileChooserSetSelectMultiple;
+        public static D.gtk_file_chooser_set_filename GtkFileChooserSetFilename;
+        public static D.gtk_file_chooser_get_filenames GtkFileChooserGetFilenames;
+        public static D.gtk_dialog_add_button GtkDialogAddButton;
         public static D.g_object_unref GObjectUnref;
         public static D.g_signal_connect_object GSignalConnectObject;
         public static D.g_signal_handler_disconnect GSignalHandlerDisconnect;
         public static D.g_timeout_add GTimeoutAdd;
         public static D.g_free GFree;
+        public static D.g_slist_free GSlistFree;
         public static D.g_memory_input_stream_new_from_data GMemoryInputStreamNewFromData;
         public static D.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered;
         public static D.gtk_widget_set_events GtkWidgetSetEvents;
@@ -467,6 +489,13 @@ namespace Avalonia.Gtk3.Interop
         public guint is_modifier;
     }
 
+    [StructLayout(LayoutKind.Sequential)]
+    unsafe struct GSList
+    {
+        public IntPtr Data;
+        public GSList* Next;
+    }
+
     [Flags]
     public enum GdkWindowState
     {
@@ -481,6 +510,29 @@ namespace Avalonia.Gtk3.Interop
         Ttiled = 256
     }
 
+    public enum GtkResponseType
+    {
+        Help = -11,
+        Apply = -10,
+        No = -9,
+        Yes = -8,
+        Close = -7,
+        Cancel = -6,
+        Ok = -5,
+        DeleteEvent = -4,
+        Accept = -3,
+        Reject = -2,
+        None = -1,
+    }
+
+    public enum GtkFileChooserAction
+    {
+        Open,
+        Save,
+        SelectFolder,
+        CreateFolder,
+    }
+
     [StructLayout(LayoutKind.Sequential)]
     struct GdkGeometry
     {

+ 0 - 21
src/Gtk/Avalonia.Gtk3/Stubs.cs

@@ -1,21 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Avalonia.Controls;
-using Avalonia.Controls.Platform;
-using Avalonia.Input.Platform;
-using Avalonia.Platform;
-
-//TODO: This file should be empty once everything is implemented
-
-namespace Avalonia.Gtk3
-{
-    class SystemDialogStub : ISystemDialogImpl
-    {
-        public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) => Task.FromResult(new string[0]);
-
-        public Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
-            => Task.FromResult((string) null);
-    }
-}

+ 94 - 0
src/Gtk/Avalonia.Gtk3/SystemDialogs.cs

@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Controls;
+using Avalonia.Controls.Platform;
+using Avalonia.Gtk3.Interop;
+using Avalonia.Input.Platform;
+using Avalonia.Platform;
+
+//TODO: This file should be empty once everything is implemented
+
+namespace Avalonia.Gtk3
+{
+    class SystemDialogStub : ISystemDialogImpl
+    {
+
+        unsafe static Task<string[]> ShowDialog(string title, GtkWindow parent, GtkFileChooserAction action,
+            bool multiselect, string initialFileName)
+        {
+            GtkFileChooser dlg;
+            using (var name = title != null ? new Utf8Buffer(title) : null)
+                dlg = Native.GtkFileChooserDialogNew(name, parent, action, IntPtr.Zero);
+            if (multiselect)
+                Native.GtkFileChooserSetSelectMultiple(dlg, true);
+
+            Native.GtkWindowSetModal(dlg, true);
+            var tcs = new TaskCompletionSource<string[]>();
+            List<IDisposable> disposables = null;
+            Action dispose = () =>
+            {
+                foreach (var d in disposables)
+                    d.Dispose();
+                disposables.Clear();
+            };
+            disposables = new List<IDisposable>
+            {
+                Signal.Connect<Native.D.signal_generic>(dlg, "close", delegate
+                {
+                    tcs.TrySetResult(null);
+                    dispose();
+                    return false;
+                }),
+                Signal.Connect<Native.D.signal_dialog_response>(dlg, "response", (_, resp, __)=>
+                {
+                    string[] result = null;
+                    if (resp == GtkResponseType.Accept)
+                    {
+                        var rlst = new List<string>();
+                        var gs = Native.GtkFileChooserGetFilenames(dlg);
+                        var cgs = gs;
+                        while (cgs != null)
+                        {
+                            if (cgs->Data != IntPtr.Zero)
+                                rlst.Add(Utf8Buffer.StringFromPtr(cgs->Data));
+                            cgs = cgs->Next;
+                        }
+                        Native.GSlistFree(gs);
+                        result = rlst.ToArray();
+                    }
+                    Native.GtkWidgetHide(dlg);
+                    dispose();
+                    tcs.TrySetResult(result);
+                    return false;
+                }),
+                dlg
+            };
+            using (var open = new Utf8Buffer("Open"))
+                Native.GtkDialogAddButton(dlg, open, GtkResponseType.Accept);
+            using (var open = new Utf8Buffer("Cancel"))
+                Native.GtkDialogAddButton(dlg, open, GtkResponseType.Cancel);
+            if(initialFileName!=null)
+                using (var fn = new Utf8Buffer(initialFileName))
+                    Native.GtkFileChooserSetFilename(dlg, fn);
+            Native.GtkWindowPresent(dlg);
+            return tcs.Task;
+        }
+
+        public Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent)
+        {
+            return ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
+                dialog is OpenFileDialog ? GtkFileChooserAction.Open : GtkFileChooserAction.Save,
+                (dialog as OpenFileDialog)?.AllowMultiple ?? false, dialog.InitialFileName);
+        }
+
+        public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent)
+        {
+            var res = await ShowDialog(dialog.Title, ((TopLevelImpl) parent)?.GtkWidget,
+                GtkFileChooserAction.SelectFolder, false, dialog.InitialDirectory);
+            return res?.FirstOrDefault();
+        }
+    }
+}

+ 1 - 1
src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Gtk3
 {
     abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle
     {
-        protected readonly GtkWindow GtkWidget;
+        public readonly GtkWindow GtkWidget;
         private IInputRoot _inputRoot;
         private readonly GtkImContext _imContext;
         private readonly FramebufferManager _framebuffer;