diff --git a/Cairo/Perspex.Cairo/Media/DrawingContext.cs b/Cairo/Perspex.Cairo/Media/DrawingContext.cs
index 75076e6be3..c3519627e6 100644
--- a/Cairo/Perspex.Cairo/Media/DrawingContext.cs
+++ b/Cairo/Perspex.Cairo/Media/DrawingContext.cs
@@ -24,27 +24,54 @@ namespace Perspex.Cairo.Media
///
private Context context;
+ ///
+ /// The cairo surface.
+ ///
+ private Surface surface;
+
///
/// Initializes a new instance of the class.
///
/// The target surface.
public DrawingContext(Surface surface)
{
+ this.surface = surface;
this.context = new Context(surface);
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The GDK drawable.
+ public DrawingContext(Gdk.Drawable drawable)
+ {
+ this.Drawable = drawable;
+ this.context = Gdk.CairoHelper.Create(drawable);
+ }
+
public Matrix CurrentTransform
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
+ public Gdk.Drawable Drawable
+ {
+ get;
+ private set;
+ }
+
///
/// Ends a draw operation.
///
public void Dispose()
{
this.context.Dispose();
+
+ if (this.surface != null)
+ {
+ this.surface.Dispose();
+ }
}
public void DrawImage(IBitmap bitmap, double opacity, Rect sourceRect, Rect destRect)
diff --git a/Cairo/Perspex.Cairo/Perspex.Cairo.csproj b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
index 1226e2afab..e851745378 100644
--- a/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
+++ b/Cairo/Perspex.Cairo/Perspex.Cairo.csproj
@@ -30,6 +30,9 @@
4
+
+
+
gtk-sharp-2.0
diff --git a/Cairo/Perspex.Cairo/Renderer.cs b/Cairo/Perspex.Cairo/Renderer.cs
index 17d9fa60ce..75115fda69 100644
--- a/Cairo/Perspex.Cairo/Renderer.cs
+++ b/Cairo/Perspex.Cairo/Renderer.cs
@@ -18,11 +18,6 @@ namespace Perspex.Cairo
///
public class Renderer : IRenderer
{
- ///
- /// The handle of the window to draw to.
- ///
- private IPlatformHandle handle;
-
///
/// Initializes a new instance of the class.
///
@@ -31,17 +26,16 @@ namespace Perspex.Cairo
/// The height of the window.
public Renderer(IPlatformHandle handle, double width, double height)
{
- this.handle = handle;
}
///
/// Renders the specified visual.
///
/// The visual to render.
- public void Render(IVisual visual)
+ /// A handle to the drawable.
+ public void Render(IVisual visual, IPlatformHandle handle)
{
- using (var surface = CreateSurface(this.handle))
- using (DrawingContext context = new DrawingContext(surface))
+ using (DrawingContext context = CreateContext(handle))
{
this.Render(visual, context);
}
@@ -54,7 +48,7 @@ namespace Perspex.Cairo
/// The new height.
public void Resize(int width, int height)
{
- // Don't need to do anything here as we create a new Win32Surface on each render.
+ // Don't need to do anything here.
}
[DllImport("user32.dll")]
@@ -65,14 +59,16 @@ namespace Perspex.Cairo
///
/// The platform-specific handle.
/// A surface.
- private static Surface CreateSurface(IPlatformHandle handle)
+ private static DrawingContext CreateContext(IPlatformHandle handle)
{
switch (handle.HandleDescriptor)
{
case "HWND":
- return new Win32Surface(GetDC(handle.Handle));
+ return new DrawingContext(new Win32Surface(GetDC(handle.Handle)));
case "HDC":
- return new Win32Surface(handle.Handle);
+ return new DrawingContext(new Win32Surface(handle.Handle));
+ case "GdkWindow":
+ return new DrawingContext(new Gdk.Window(handle.Handle));
default:
throw new NotSupportedException(string.Format(
"Don't know how to create a Cairo renderer from a '{0}' handle",
diff --git a/Gtk/Perspex.Gtk/GtkExtensions.cs b/Gtk/Perspex.Gtk/GtkExtensions.cs
new file mode 100644
index 0000000000..c8bd6df652
--- /dev/null
+++ b/Gtk/Perspex.Gtk/GtkExtensions.cs
@@ -0,0 +1,18 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Gtk
+{
+ using Gtk = global::Gtk;
+
+ public static class GtkExtensions
+ {
+ public static Rect ToPerspex(this Gdk.Rectangle rect)
+ {
+ return new Rect(rect.Left, rect.Top, rect.Right, rect.Bottom);
+ }
+ }
+}
diff --git a/Gtk/Perspex.Gtk/Perspex.Gtk.csproj b/Gtk/Perspex.Gtk/Perspex.Gtk.csproj
index 1f6e0ef54a..57dacc99e5 100644
--- a/Gtk/Perspex.Gtk/Perspex.Gtk.csproj
+++ b/Gtk/Perspex.Gtk/Perspex.Gtk.csproj
@@ -55,6 +55,7 @@
+
diff --git a/Gtk/Perspex.Gtk/WindowImpl.cs b/Gtk/Perspex.Gtk/WindowImpl.cs
index f22b931b97..113fe6b250 100644
--- a/Gtk/Perspex.Gtk/WindowImpl.cs
+++ b/Gtk/Perspex.Gtk/WindowImpl.cs
@@ -21,11 +21,11 @@ namespace Perspex.Gtk
public WindowImpl ()
{
this.inner = new Gtk.Window(Gtk.WindowType.Toplevel);
-
- // TODO: Use ?. operator on these when it's available.
- this.inner.FocusActivated += (s, a) => this.Activated.Invoke(this, EventArgs.Empty);
- this.inner.Destroyed += (s, a) => this.Closed.Invoke(this, EventArgs.Empty);
- this.inner.ConfigureEvent += (s, a) => this.Resized.Invoke(this, new RawSizeEventArgs(a.Event.Width, a.Event.Height));
+ this.inner.DefaultSize = new Gdk.Size(640, 480);
+ this.inner.FocusActivated += (s, a) => this.Activated();
+ this.inner.Destroyed += (s, a) => this.Closed();
+ this.inner.ConfigureEvent += (s, a) => this.Resized(new Size(a.Event.Width, a.Event.Height));
+ this.inner.ExposeEvent += (s, a) => this.Paint(a.Event.Area.ToPerspex(), GetHandle(a.Event.Window));
this.Handle = new PlatformHandle(this.inner.Handle, "GtkWindow");
}
@@ -47,13 +47,20 @@ namespace Perspex.Gtk
private set;
}
- public event EventHandler Activated;
+ public Action Activated { get; set; }
+
+ public Action Closed { get; set; }
- public event EventHandler Closed;
+ public Action Input { get; set; }
- public event EventHandler Input;
+ public Action Paint { get; set; }
- public event EventHandler Resized;
+ public Action Resized { get; set; }
+
+ public void Invalidate(Rect rect)
+ {
+ this.inner.QueueDraw();
+ }
public void SetOwner(Window window)
{
@@ -69,5 +76,10 @@ namespace Perspex.Gtk
{
this.inner.Show();
}
+
+ private IPlatformHandle GetHandle(Gdk.Window window)
+ {
+ return new PlatformHandle(window.Handle, "GdkWindow");
+ }
}
}
\ No newline at end of file
diff --git a/Perspex.Controls/Platform/IWindowImpl.cs b/Perspex.Controls/Platform/IWindowImpl.cs
index c877eb3f06..5b4d5a6b72 100644
--- a/Perspex.Controls/Platform/IWindowImpl.cs
+++ b/Perspex.Controls/Platform/IWindowImpl.cs
@@ -12,17 +12,21 @@ namespace Perspex.Platform
public interface IWindowImpl
{
- event EventHandler Activated;
+ Size ClientSize { get; }
- event EventHandler Closed;
+ IPlatformHandle Handle { get; }
- event EventHandler Input;
+ Action Activated { get; set; }
- event EventHandler Resized;
+ Action Closed { get; set; }
- Size ClientSize { get; }
+ Action Input { get; set; }
- IPlatformHandle Handle { get; }
+ Action Paint { get; set; }
+
+ Action Resized { get; set; }
+
+ void Invalidate(Rect rect);
void SetTitle(string title);
diff --git a/Perspex.Controls/Window.cs b/Perspex.Controls/Window.cs
index 6f2b40c2d6..890d1e2260 100644
--- a/Perspex.Controls/Window.cs
+++ b/Perspex.Controls/Window.cs
@@ -63,10 +63,11 @@ namespace Perspex.Controls
}
this.impl.SetOwner(this);
- this.impl.Activated += this.HandleActivated;
- this.impl.Closed += this.HandleClosed;
- this.impl.Input += this.HandleInput;
- this.impl.Resized += this.HandleResized;
+ this.impl.Activated = this.HandleActivated;
+ this.impl.Closed = this.HandleClosed;
+ this.impl.Input = this.HandleInput;
+ this.impl.Paint = this.HandlePaint;
+ this.impl.Resized = this.HandleResized;
Size clientSize = this.ClientSize = this.impl.ClientSize;
this.dispatcher = Dispatcher.UIThread;
@@ -114,7 +115,7 @@ namespace Perspex.Controls
this.LayoutPass();
}
- private void HandleActivated(object sender, EventArgs e)
+ private void HandleActivated()
{
if (this.Activated != null)
{
@@ -122,7 +123,7 @@ namespace Perspex.Controls
}
}
- private void HandleClosed(object sender, EventArgs e)
+ private void HandleClosed()
{
if (this.Closed != null)
{
@@ -130,7 +131,7 @@ namespace Perspex.Controls
}
}
- private void HandleInput(object sender, RawInputEventArgs e)
+ private void HandleInput(RawInputEventArgs e)
{
this.inputManager.Process(e);
}
@@ -145,27 +146,31 @@ namespace Perspex.Controls
this.dispatcher.InvokeAsync(this.RenderVisualTree, DispatcherPriority.Render);
}
- private void HandleResized(object sender, RawSizeEventArgs e)
+ private void HandlePaint(Rect rect, IPlatformHandle handle)
{
- this.ClientSize = e.Size;
- this.renderer.Resize((int)e.Size.Width, (int)e.Size.Height);
+ this.renderer.Render(this, handle);
+ this.RenderManager.RenderFinished();
+ }
+
+ private void HandleResized(Size size)
+ {
+ this.ClientSize = size;
+ this.renderer.Resize((int)size.Width, (int)size.Height);
this.LayoutManager.ExecuteLayoutPass();
- this.RenderVisualTree();
+ this.impl.Invalidate(new Rect(this.ClientSize));
}
private void LayoutPass()
{
this.LayoutManager.ExecuteLayoutPass();
- this.renderer.Render(this);
- this.RenderManager.RenderFinished();
+ this.impl.Invalidate(new Rect(this.ClientSize));
}
private void RenderVisualTree()
{
if (!this.LayoutManager.LayoutQueued)
{
- this.renderer.Render(this);
- this.RenderManager.RenderFinished();
+ this.impl.Invalidate(new Rect(this.ClientSize));
}
}
}
diff --git a/Perspex.SceneGraph/Platform/IRenderer.cs b/Perspex.SceneGraph/Platform/IRenderer.cs
index e251fe0f6e..0d7dc136e2 100644
--- a/Perspex.SceneGraph/Platform/IRenderer.cs
+++ b/Perspex.SceneGraph/Platform/IRenderer.cs
@@ -14,7 +14,8 @@ namespace Perspex.Platform
/// Renders the specified visual.
///
/// The visual to render.
- void Render(IVisual visual);
+ /// An optional platform-specific handle.
+ void Render(IVisual visual, IPlatformHandle handle);
///
/// Resizes the rendered viewport.
diff --git a/TestApplication/TestApplication-Mono.csproj b/TestApplication/TestApplication-Mono.csproj
index bebd9eafda..b9f08f0d00 100644
--- a/TestApplication/TestApplication-Mono.csproj
+++ b/TestApplication/TestApplication-Mono.csproj
@@ -17,7 +17,7 @@
true
full
false
- bin\Debug\
+ bin\Debug-Mono\
TRACE;DEBUG;PERSPEX_CAIRO;PERSPEX_GTK
prompt
4
@@ -27,7 +27,7 @@
AnyCPU
pdbonly
true
- bin\Release\
+ bin\Release-Mono\
TRACE
prompt
4
diff --git a/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs b/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
index cc7ef83380..45c7c9ef86 100644
--- a/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
+++ b/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
@@ -35,7 +35,7 @@ namespace Perspex.Direct2D1.Media
public void Render(IVisual visual)
{
Renderer renderer = new Renderer(this.target);
- renderer.Render(visual);
+ renderer.Render(visual, null);
}
}
}
diff --git a/Windows/Perspex.Direct2D1/Renderer.cs b/Windows/Perspex.Direct2D1/Renderer.cs
index 9af56cdfc1..41b26588a7 100644
--- a/Windows/Perspex.Direct2D1/Renderer.cs
+++ b/Windows/Perspex.Direct2D1/Renderer.cs
@@ -85,7 +85,8 @@ namespace Perspex.Direct2D1
/// Renders the specified visual.
///
/// The visual to render.
- public void Render(IVisual visual)
+ /// Unused.
+ public void Render(IVisual visual, IPlatformHandle handle)
{
using (DrawingContext context = new DrawingContext(this.renderTarget, this.DirectWriteFactory))
{
diff --git a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
index 6c550a914e..001fef6489 100644
--- a/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
+++ b/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
@@ -398,6 +398,12 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
+ [DllImport("user32.dll")]
+ public static extern bool GetUpdateRect(IntPtr hwnd, out RECT lpRect, bool bErase);
+
+ [DllImport("user32.dll")]
+ public static extern bool InvalidateRect(IntPtr hWnd, ref RECT lpRect, bool bErase);
+
[DllImport("user32.dll")]
public static extern bool KillTimer(IntPtr hWnd, IntPtr uIDEvent);
diff --git a/Windows/Perspex.Win32/WindowImpl.cs b/Windows/Perspex.Win32/WindowImpl.cs
index d257d69f55..7065d9f760 100644
--- a/Windows/Perspex.Win32/WindowImpl.cs
+++ b/Windows/Perspex.Win32/WindowImpl.cs
@@ -9,20 +9,12 @@ namespace Perspex.Win32
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
- using System.Reactive.Linq;
using System.Runtime.InteropServices;
using Perspex.Controls;
- using Perspex.Controls.Presenters;
- using Perspex.Diagnostics;
- using Perspex.Input;
using Perspex.Input.Raw;
- using Perspex.Layout;
using Perspex.Platform;
- using Perspex.Rendering;
- using Perspex.Threading;
using Perspex.Win32.Input;
using Perspex.Win32.Interop;
- using Splat;
public class WindowImpl : IWindowImpl
{
@@ -39,13 +31,15 @@ namespace Perspex.Win32
this.CreateWindow();
}
- public event EventHandler Activated;
+ public Action Activated { get; set; }
- public event EventHandler Closed;
+ public Action Closed { get; set; }
- public event EventHandler Input;
+ public Action Input { get; set; }
- public event EventHandler Resized;
+ public Action Paint { get; set; }
+
+ public Action Resized { get; set; }
public Size ClientSize
{
@@ -63,6 +57,20 @@ namespace Perspex.Win32
private set;
}
+ public void Invalidate(Rect rect)
+ {
+ this.Paint(rect, this.Handle);
+ //var r = new UnmanagedMethods.RECT
+ //{
+ // left = (int)rect.X,
+ // top = (int)rect.Y,
+ // right = (int)rect.Right,
+ // bottom = (int)rect.Bottom,
+ //};
+
+ //UnmanagedMethods.InvalidateRect(this.hwnd, ref r, false);
+ }
+
public void SetOwner(Window owner)
{
this.owner = owner;
@@ -137,17 +145,11 @@ namespace Perspex.Win32
switch ((UnmanagedMethods.WindowsMessage)msg)
{
case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
- if (this.Activated != null)
- {
- this.Activated(this, EventArgs.Empty);
- }
+ this.Activated();
break;
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
- if (this.Closed != null)
- {
- this.Closed(this, EventArgs.Empty);
- }
+ this.Closed();
break;
case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
@@ -183,17 +185,25 @@ namespace Perspex.Win32
new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
+ // TODO: For some reason WM_PAINT getting called continuously - investigate.
+
+ //case UnmanagedMethods.WindowsMessage.WM_PAINT:
+ // UnmanagedMethods.RECT r;
+ // UnmanagedMethods.GetUpdateRect(this.hwnd, out r, false);
+ // this.Paint(new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top));
+ // return IntPtr.Zero;
+
case UnmanagedMethods.WindowsMessage.WM_SIZE:
if (this.Resized != null)
{
- this.Resized(this, new RawSizeEventArgs((int)lParam & 0xffff, (int)lParam >> 16));
+ this.Resized(new Size((int)lParam & 0xffff, (int)lParam >> 16));
}
return IntPtr.Zero;
}
if (e != null && this.Input != null)
{
- this.Input(this, e);
+ this.Input(e);
}
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);