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);