diff --git a/Avalonia.sln b/Avalonia.sln
index 0eda7e5fc7..46dafa9c76 100644
--- a/Avalonia.sln
+++ b/Avalonia.sln
@@ -130,6 +130,9 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog", "samples\ControlCatalog\ControlCatalog.csproj", "{D0A739B9-3C68-4BA6-A328-41606954B6BD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {BB1F7BB5-6AD4-4776-94D9-C09D0A972658} = {BB1F7BB5-6AD4-4776-94D9-C09D0A972658}
+ EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
EndProject
diff --git a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
index 6fec660e55..fb2eba3c2f 100644
--- a/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
+++ b/samples/ControlCatalog.Desktop/ControlCatalog.Desktop.csproj
@@ -5,7 +5,7 @@
Debug
AnyCPU
{2B888490-D14A-4BCA-AB4B-48676FA93C9B}
- WinExe
+ Exe
Properties
ControlCatalog.Desktop
ControlCatalog.Desktop
@@ -33,6 +33,9 @@
prompt
4
+
+
+
..\..\packages\Serilog.1.5.14\lib\net45\Serilog.dll
diff --git a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
index 5e645fb542..df5a712c18 100644
--- a/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
+++ b/src/Gtk/Avalonia.Gtk3/Avalonia.Gtk3.csproj
@@ -41,6 +41,7 @@
+
@@ -64,7 +65,7 @@
{62024B2D-53EB-4638-B26B-85EEAA54866E}
Avalonia.Input
-
+
{EB582467-6ABB-43A1-B052-E981BA910E3A}
Avalonia.SceneGraph
diff --git a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
index 164e987802..faf1983250 100644
--- a/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
+++ b/src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
@@ -10,6 +10,7 @@ using Avalonia.Gtk3.Interop;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Platform;
+using Avalonia.Rendering;
namespace Avalonia.Gtk3
{
@@ -35,6 +36,7 @@ namespace Avalonia.Gtk3
.Bind().ToConstant(Instance)
.Bind().ToConstant(Instance)
.Bind().ToSingleton()
+ .Bind().ToConstant(new DefaultRenderLoop(60))
.Bind().ToConstant(new PlatformIconLoaderStub());
}
@@ -67,11 +69,28 @@ namespace Avalonia.Gtk3
public IDisposable StartTimer(TimeSpan interval, Action tick)
{
- return null;
+ return GlibTimeout.StarTimer((uint) interval.TotalMilliseconds, tick);
}
+ private bool _signaled = false;
+ object _lock = new object();
+
public void Signal()
{
+ lock(_lock)
+ if (!_signaled)
+ {
+ _signaled = true;
+ GlibTimeout.Add(0, () =>
+ {
+ lock (_lock)
+ {
+ _signaled = false;
+ }
+ Signaled?.Invoke();
+ return false;
+ });
+ }
}
public event Action Signaled;
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
new file mode 100644
index 0000000000..9971d8881d
--- /dev/null
+++ b/src/Gtk/Avalonia.Gtk3/Interop/GlibTimeout.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Gtk3.Interop
+{
+ static class GlibTimeout
+ {
+ static bool Handler(IntPtr data)
+ {
+ var handle = GCHandle.FromIntPtr(data);
+ var cb = (Func) handle.Target;
+ if (!cb())
+ {
+ handle.Free();
+ return false;
+ }
+ return true;
+ }
+
+ private static readonly GCHandle PinnedHandle;
+ private static readonly Native.D.timeout_callback PinnedHandler;
+ static GlibTimeout()
+ {
+ PinnedHandler = Handler;
+
+ }
+
+
+ public static void Add(uint interval, Func callback)
+ {
+ var handle = GCHandle.Alloc(callback);
+ Native.GTimeoutAdd(interval, PinnedHandler, GCHandle.ToIntPtr(handle));
+ }
+
+ class Timer : IDisposable
+ {
+ public bool Stopped;
+ public void Dispose()
+ {
+
+ Stopped = true;
+ }
+ }
+
+ public static IDisposable StarTimer(uint interval, Action tick)
+ {
+ var timer = new Timer ();
+ GlibTimeout.Add(interval,
+ () =>
+ {
+ if (timer.Stopped)
+ return false;
+ tick();
+ return !timer.Stopped;
+ });
+
+ return timer;
+ }
+ }
+}
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
index a07d84c710..2a5abfe603 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Native.cs
@@ -36,7 +36,19 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate IntPtr gtk_widget_get_window(IntPtr gtkWidget);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate IntPtr gtk_widget_get_screen(IntPtr gtkWidget);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate IntPtr gtk_widget_set_double_buffered(IntPtr gtkWidget, bool value);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate IntPtr gtk_widget_set_events(IntPtr gtkWidget, uint flags);
+
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate int gdk_screen_get_height(IntPtr screen);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate int gdk_screen_get_width(IntPtr screen);
+
+
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_widget_realize(IntPtr gtkWidget);
@@ -50,12 +62,29 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_get_size(IntPtr gtkWindow, out int width, out int height);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_window_resize(IntPtr gtkWindow, int width, int height);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
+ public delegate void gtk_widget_queue_draw_area(IntPtr gtkWindow, int x, int y, int width, int height);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gdk)]
+ public delegate void gdk_window_invalidate_rect(IntPtr window, ref GdkRectangle rect, bool invalidate_children);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_connect_object(IntPtr instance, [MarshalAs(UnmanagedType.AnsiBStr)]string signal, IntPtr handler, IntPtr userData, int flags);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gobject)]
public delegate ulong g_signal_handler_disconnect(IntPtr instance, ulong connectionId);
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Glib)]
+ public delegate ulong g_timeout_add(uint interval, timeout_callback callback, IntPtr data);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool signal_widget_draw(IntPtr gtkWidget, IntPtr cairoContext, IntPtr userData);
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate bool signal_onevent(IntPtr gtkWidget, IntPtr ev, IntPtr userData);
+
+
+ [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+ public delegate bool timeout_callback(IntPtr data);
}
@@ -69,12 +98,19 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_widget_hide GtkWidgetHide;
public static D.gdk_get_native_handle GetNativeGdkWindowHandle;
public static D.gtk_widget_get_window GtkWidgetGetWindow;
+ public static D.gtk_widget_get_screen GtkWidgetGetScreen;
public static D.gtk_widget_realize GtkWidgetRealize;
public static D.gtk_window_get_size GtkWindowGetSize;
+ public static D.gtk_window_resize GtkWindowResize;
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.gtk_widget_set_double_buffered GtkWidgetSetDoubleBuffered;
-
+ public static D.gtk_widget_set_events GtkWidgetSetEvents;
+ public static D.gdk_window_invalidate_rect GdkWindowInvalidateRect;
+ public static D.gtk_widget_queue_draw_area GtkWidgetQueueDrawArea;
+ public static D.gdk_screen_get_height GdkScreenGetHeight;
+ public static D.gdk_screen_get_width GdkScreenGetWidth;
}
@@ -84,4 +120,21 @@ namespace Avalonia.Gtk3.Interop
TopLevel,
Popup
}
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct GdkRectangle
+ {
+ public int X, Y, Width, Height;
+
+ public static GdkRectangle FromRect(Rect rect)
+ {
+ return new GdkRectangle
+ {
+ X = (int) rect.X,
+ Y = (int) rect.Y,
+ Width = (int) rect.Width,
+ Height = (int) rect.Height
+ };
+ }
+ }
}
diff --git a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
index 2410af7591..15a2f90ade 100644
--- a/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
+++ b/src/Gtk/Avalonia.Gtk3/Interop/Resolver.cs
@@ -84,13 +84,14 @@ namespace Avalonia.Gtk3.Interop
fieldInfo.SetValue(null, Marshal.GetDelegateForFunctionPointer(funcPtr, fieldInfo.FieldType));
}
- var nativeHandleNames = new[] {"gdk_x11_window_get_xid", "gdk_win32_window_get_handle"};
+ var nativeHandleNames = new[] { "gdk_win32_window_get_handle", "gdk_x11_window_get_xid", "gdk_quartz_window_get_nswindow" };
foreach (var name in nativeHandleNames)
{
try
{
Native.GetNativeGdkWindowHandle = (Native.D.gdk_get_native_handle)Marshal
.GetDelegateForFunctionPointer(loader.GetProcAddress(gdk, name), typeof(Native.D.gdk_get_native_handle));
+ break;
}
catch { }
}
diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
index 2f6181c893..672e98fe18 100644
--- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Diagnostics;
using Avalonia.Controls;
using Avalonia.Gtk3.Interop;
using Avalonia.Input;
@@ -9,44 +11,69 @@ namespace Avalonia.Gtk3
{
abstract class TopLevelImpl : ITopLevelImpl, IPlatformHandle
{
- protected readonly IntPtr _gtkWidget;
+ protected readonly IntPtr GtkWidget;
private IInputRoot _inputRoot;
+ protected readonly List _disposables = new List();
public TopLevelImpl(IntPtr gtkWidget)
{
- _gtkWidget = gtkWidget;
+ GtkWidget = gtkWidget;
+ Native.GtkWidgetSetEvents(gtkWidget, uint.MaxValue);
Native.GtkWidgetRealize(gtkWidget);
- Native.GtkWidgetSetDoubleBuffered(gtkWidget, false);
- Signal.Connect(_gtkWidget, "draw", OnDraw);
+ Connect("draw", OnDraw);
+ Connect("configure-event", OnConfigured);
}
+ private bool OnConfigured(IntPtr gtkwidget, IntPtr ev, IntPtr userdata)
+ {
+ Debug.WriteLine("Configured");
+ Resized?.Invoke(ClientSize);
+ return false;
+ }
+
+
+ void Connect(string name, T handler) => _disposables.Add(Signal.Connect(GtkWidget, name, handler));
+
private bool OnDraw(IntPtr gtkwidget, IntPtr cairocontext, IntPtr userdata)
{
+ Debug.WriteLine("Draw");
Paint?.Invoke(new Rect(ClientSize));
return true;
}
public void Dispose()
{
- //STUB
+ foreach(var d in _disposables)
+ d.Dispose();
+ _disposables.Clear();
+ //TODO
}
public abstract Size ClientSize { get; set; }
- public Size MaxClientSize => new Size(1024, 768); //STUB
+ public Size MaxClientSize
+ {
+ get
+ {
+ var s = Native.GtkWidgetGetScreen(GtkWidget);
+ return new Size(Native.GdkScreenGetWidth(s), Native.GdkScreenGetHeight(s));
+ }
+ }
+
+
public double Scaling => 1; //TODO: Implement scaling
public IPlatformHandle Handle => this;
string IPlatformHandle.HandleDescriptor => "HWND";
- public Action Activated { get; set; }
- public Action Closed { get; set; }
- public Action Deactivated { get; set; }
- public Action Input { get; set; }
+ public Action Activated { get; set; } //TODO
+ public Action Closed { get; set; } //TODO
+ public Action Deactivated { get; set; } //TODO
+ public Action Input { get; set; } //TODO
public Action Paint { get; set; }
- public Action Resized { get; set; }
- public Action ScalingChanged { get; set; }
- public Action PositionChanged { get; set; }
+ public Action Resized { get; set; } //TODO
+ public Action ScalingChanged { get; set; } //TODO
+ public Action PositionChanged { get; set; } //TODO
public void Activate()
{
throw new NotImplementedException();
@@ -54,7 +81,7 @@ namespace Avalonia.Gtk3
public void Invalidate(Rect rect)
{
- throw new NotImplementedException();
+ Native.GtkWidgetQueueDrawArea(GtkWidget, (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height);
}
public void SetInputRoot(IInputRoot inputRoot) => _inputRoot = inputRoot;
@@ -74,9 +101,9 @@ namespace Avalonia.Gtk3
//STUB
}
- public void Show() => Native.GtkWindowPresent(_gtkWidget);
+ public void Show() => Native.GtkWindowPresent(GtkWidget);
- public void Hide() => Native.GtkWidgetHide(_gtkWidget);
+ public void Hide() => Native.GtkWidgetHide(GtkWidget);
public void BeginMoveDrag()
{
@@ -90,6 +117,7 @@ namespace Avalonia.Gtk3
public Point Position { get; set; }
- IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(_gtkWidget));
+ IntPtr IPlatformHandle.Handle => Native.GetNativeGdkWindowHandle(Native.GtkWidgetGetWindow(GtkWidget));
+ public IEnumerable