diff --git a/Perspex.Base/Perspex.Base.csproj b/Perspex.Base/Perspex.Base.csproj
index 5ebb0ac957..cbffed72d5 100644
--- a/Perspex.Base/Perspex.Base.csproj
+++ b/Perspex.Base/Perspex.Base.csproj
@@ -45,6 +45,7 @@
+
diff --git a/Perspex.Base/Platform/IPlatformHandle.cs b/Perspex.Base/Platform/IPlatformHandle.cs
new file mode 100644
index 0000000000..f0144dc990
--- /dev/null
+++ b/Perspex.Base/Platform/IPlatformHandle.cs
@@ -0,0 +1,26 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Platform
+{
+ using System;
+
+ ///
+ /// Represents a platform-specific handle.
+ ///
+ public interface IPlatformHandle
+ {
+ ///
+ /// Gets the handle.
+ ///
+ IntPtr Handle { get; }
+
+ ///
+ /// Gets an optional string that describes what represents.
+ ///
+ string HandleDescriptor { get; }
+ }
+}
diff --git a/Perspex.Controls/Perspex.Controls.csproj b/Perspex.Controls/Perspex.Controls.csproj
index 9f67148d6e..221f2731c3 100644
--- a/Perspex.Controls/Perspex.Controls.csproj
+++ b/Perspex.Controls/Perspex.Controls.csproj
@@ -41,6 +41,7 @@
+
@@ -69,6 +70,7 @@
+
diff --git a/Perspex.Controls/Platform/IWindowImpl.cs b/Perspex.Controls/Platform/IWindowImpl.cs
new file mode 100644
index 0000000000..c877eb3f06
--- /dev/null
+++ b/Perspex.Controls/Platform/IWindowImpl.cs
@@ -0,0 +1,33 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Platform
+{
+ using System;
+ using Perspex.Controls;
+ using Perspex.Input.Raw;
+
+ public interface IWindowImpl
+ {
+ event EventHandler Activated;
+
+ event EventHandler Closed;
+
+ event EventHandler Input;
+
+ event EventHandler Resized;
+
+ Size ClientSize { get; }
+
+ IPlatformHandle Handle { get; }
+
+ void SetTitle(string title);
+
+ void SetOwner(Window window);
+
+ void Show();
+ }
+}
diff --git a/Perspex.Controls/Window.cs b/Perspex.Controls/Window.cs
new file mode 100644
index 0000000000..f0e03be3d7
--- /dev/null
+++ b/Perspex.Controls/Window.cs
@@ -0,0 +1,156 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Controls
+{
+ using System;
+ using System.Reactive.Linq;
+ using Perspex.Input;
+ using Perspex.Layout;
+ using Perspex.Media;
+ using Perspex.Platform;
+ using Perspex.Rendering;
+ using Perspex.Styling;
+ using Perspex.Threading;
+ using Splat;
+
+ public class Window : ContentControl, ILayoutRoot, IRenderRoot, ICloseable
+ {
+ public static readonly PerspexProperty ClientSizeProperty =
+ PerspexProperty.Register("ClientSize");
+
+ public static readonly PerspexProperty TitleProperty =
+ PerspexProperty.Register("Title", "Window");
+
+ private IWindowImpl impl;
+
+ private Dispatcher dispatcher;
+
+ private IRenderer renderer;
+
+ private IInputManager inputManager;
+
+ public event EventHandler Activated;
+
+ public event EventHandler Closed;
+
+ static Window()
+ {
+ BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
+ }
+
+ public Window()
+ {
+ IPlatformRenderInterface renderInterface = Locator.Current.GetService();
+
+ this.impl = Locator.Current.GetService();
+ this.inputManager = Locator.Current.GetService();
+
+ if (this.impl == null)
+ {
+ throw new InvalidOperationException(
+ "Could not create window implementation: maybe no windowing subsystem was initialized?");
+ }
+
+ if (this.inputManager == null)
+ {
+ throw new InvalidOperationException(
+ "Could not create input manager: maybe Application.RegisterServices() wasn't called?");
+ }
+
+ this.impl.SetOwner(this);
+ this.impl.Activated += this.HandleActivated;
+ this.impl.Closed += this.HandleClosed;
+
+ Size clientSize = this.ClientSize = this.impl.ClientSize;
+ this.dispatcher = Dispatcher.UIThread;
+ this.renderer = renderInterface.CreateRenderer(this.impl.Handle, clientSize.Width, clientSize.Height);
+
+ this.LayoutManager = new LayoutManager(this);
+ this.LayoutManager.LayoutNeeded.Subscribe(_ => this.HandleLayoutNeeded());
+
+ this.RenderManager = new RenderManager();
+ this.RenderManager.RenderNeeded.Subscribe(_ => this.HandleRenderNeeded());
+
+ this.GetObservable(TitleProperty).Subscribe(s => this.impl.SetTitle(s));
+
+ IStyler styler = Locator.Current.GetService();
+ styler.ApplyStyles(this);
+ }
+
+ public Size ClientSize
+ {
+ get { return this.GetValue(ClientSizeProperty); }
+ set { this.SetValue(ClientSizeProperty, value); }
+ }
+
+ public string Title
+ {
+ get { return this.GetValue(TitleProperty); }
+ set { this.SetValue(TitleProperty, value); }
+ }
+
+ public ILayoutManager LayoutManager
+ {
+ get;
+ private set;
+ }
+
+ public IRenderManager RenderManager
+ {
+ get;
+ private set;
+ }
+
+ public void Show()
+ {
+ this.impl.Show();
+ this.LayoutPass();
+ }
+
+ private void HandleActivated(object sender, EventArgs e)
+ {
+ if (this.Activated != null)
+ {
+ this.Activated(this, EventArgs.Empty);
+ }
+ }
+
+ private void HandleClosed(object sender, EventArgs e)
+ {
+ if (this.Closed != null)
+ {
+ this.Closed(this, EventArgs.Empty);
+ }
+ }
+
+ private void HandleLayoutNeeded()
+ {
+ this.dispatcher.InvokeAsync(this.LayoutPass, DispatcherPriority.Render);
+ }
+
+ private void HandleRenderNeeded()
+ {
+ this.dispatcher.InvokeAsync(this.RenderVisualTree, DispatcherPriority.Render);
+ }
+
+ private void LayoutPass()
+ {
+ this.LayoutManager.ExecuteLayoutPass();
+ this.renderer.Render(this);
+ this.RenderManager.RenderFinished();
+ }
+
+ private void RenderVisualTree()
+ {
+ if (!this.LayoutManager.LayoutQueued)
+ {
+ this.renderer.Render(this);
+ this.RenderManager.RenderFinished();
+ }
+ }
+ }
+}
diff --git a/Perspex.Input/Perspex.Input.csproj b/Perspex.Input/Perspex.Input.csproj
index 02125ec8e6..ec3798df40 100644
--- a/Perspex.Input/Perspex.Input.csproj
+++ b/Perspex.Input/Perspex.Input.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Perspex.Input/Raw/RawInputEventArgs.cs b/Perspex.Input/Raw/RawInputEventArgs.cs
index 0e9a08c6fe..6c4266818e 100644
--- a/Perspex.Input/Raw/RawInputEventArgs.cs
+++ b/Perspex.Input/Raw/RawInputEventArgs.cs
@@ -7,11 +7,6 @@
namespace Perspex.Input.Raw
{
using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using Perspex.Layout;
public class RawInputEventArgs : EventArgs
{
diff --git a/Perspex.Input/Raw/RawMouseEventArgs.cs b/Perspex.Input/Raw/RawMouseEventArgs.cs
index 41da9ec88a..c13f41d564 100644
--- a/Perspex.Input/Raw/RawMouseEventArgs.cs
+++ b/Perspex.Input/Raw/RawMouseEventArgs.cs
@@ -33,6 +33,7 @@ namespace Perspex.Input.Raw
this.Type = type;
}
+ //TODO: This should probably be IInputRoot or something.
public ILayoutRoot Root { get; private set; }
public Point Position { get; private set; }
diff --git a/Perspex.Input/Raw/RawSizeEventArgs.cs b/Perspex.Input/Raw/RawSizeEventArgs.cs
new file mode 100644
index 0000000000..69f06c6964
--- /dev/null
+++ b/Perspex.Input/Raw/RawSizeEventArgs.cs
@@ -0,0 +1,25 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2013 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Input.Raw
+{
+ using System;
+
+ public class RawSizeEventArgs : EventArgs
+ {
+ public RawSizeEventArgs(Size size)
+ {
+ this.Size = size;
+ }
+
+ public RawSizeEventArgs(double width, double height)
+ {
+ this.Size = new Size(width, height);
+ }
+
+ public Size Size { get; private set; }
+ }
+}
diff --git a/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs b/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs
index a5bb3be06c..b5ade8f65e 100644
--- a/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs
+++ b/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs
@@ -17,7 +17,7 @@ namespace Perspex.Platform
IStreamGeometryImpl CreateStreamGeometry();
- IRenderer CreateRenderer(IntPtr handle, double width, double height);
+ IRenderer CreateRenderer(IPlatformHandle handle, double width, double height);
IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height);
diff --git a/Perspex.Themes.Default/DefaultTheme.cs b/Perspex.Themes.Default/DefaultTheme.cs
index 0d4e986785..0a184873ab 100644
--- a/Perspex.Themes.Default/DefaultTheme.cs
+++ b/Perspex.Themes.Default/DefaultTheme.cs
@@ -25,6 +25,7 @@ namespace Perspex.Themes.Default
this.Add(new TextBoxStyle());
this.Add(new TreeViewStyle());
this.Add(new TreeViewItemStyle());
+ this.Add(new WindowStyle());
}
}
}
diff --git a/Perspex.Themes.Default/Perspex.Themes.Default.csproj b/Perspex.Themes.Default/Perspex.Themes.Default.csproj
index ad58628f48..abb1b7a2b0 100644
--- a/Perspex.Themes.Default/Perspex.Themes.Default.csproj
+++ b/Perspex.Themes.Default/Perspex.Themes.Default.csproj
@@ -66,6 +66,7 @@
+
diff --git a/Perspex.Themes.Default/WindowStyle.cs b/Perspex.Themes.Default/WindowStyle.cs
new file mode 100644
index 0000000000..479fc5f19c
--- /dev/null
+++ b/Perspex.Themes.Default/WindowStyle.cs
@@ -0,0 +1,43 @@
+// -----------------------------------------------------------------------
+//
+// Copyright 2014 MIT Licence. See licence.md for more information.
+//
+// -----------------------------------------------------------------------
+
+namespace Perspex.Themes.Default
+{
+ using System.Linq;
+ using Perspex.Controls;
+ using Perspex.Controls.Presenters;
+ using Perspex.Media;
+ using Perspex.Styling;
+
+ public class WindowStyle : Styles
+ {
+ public WindowStyle()
+ {
+ this.AddRange(new[]
+ {
+ new Style(x => x.OfType())
+ {
+ Setters = new[]
+ {
+ new Setter(Window.TemplateProperty, ControlTemplate.Create(this.Template)),
+ },
+ },
+ });
+ }
+
+ private Control Template(Window control)
+ {
+ return new Border
+ {
+ [~Border.BackgroundProperty] = control[~Window.BackgroundProperty],
+ Content = new ContentPresenter
+ {
+ [~ContentPresenter.ContentProperty] = control[~Window.ContentProperty],
+ }
+ };
+ }
+ }
+}
diff --git a/Windows/Perspex.Direct2D1/Direct2D1Platform.cs b/Windows/Perspex.Direct2D1/Direct2D1Platform.cs
index e89e721207..9199583e6b 100644
--- a/Windows/Perspex.Direct2D1/Direct2D1Platform.cs
+++ b/Windows/Perspex.Direct2D1/Direct2D1Platform.cs
@@ -44,9 +44,18 @@ namespace Perspex.Direct2D1
return new BitmapImpl(imagingFactory, width, height);
}
- public IRenderer CreateRenderer(IntPtr handle, double width, double height)
+ public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height)
{
- return new Renderer(handle, width, height);
+ if (handle.HandleDescriptor == "HWND")
+ {
+ return new Renderer(handle.Handle, width, height);
+ }
+ else
+ {
+ throw new NotSupportedException(string.Format(
+ "Don't know how to create a Direct2D1 renderer from a '{0}' handle",
+ handle.HandleDescriptor));
+ }
}
public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)
diff --git a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs
index df190b0a9d..c5bee8c6a8 100644
--- a/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs
+++ b/Windows/Perspex.Win32/Input/WindowsKeyboardDevice.cs
@@ -7,6 +7,7 @@
namespace Perspex.Win32.Input
{
using System.Text;
+ using Perspex.Controls;
using Perspex.Input;
using Perspex.Win32.Interop;
diff --git a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs
index ade33bbd01..abf36d40a3 100644
--- a/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs
+++ b/Windows/Perspex.Win32/Input/WindowsMouseDevice.cs
@@ -19,7 +19,7 @@ namespace Perspex.Win32.Input
get { return instance; }
}
- public Window CurrentWindow
+ public WindowImpl CurrentWindow
{
get;
set;
@@ -35,7 +35,7 @@ namespace Perspex.Win32.Input
{
UnmanagedMethods.POINT p;
UnmanagedMethods.GetCursorPos(out p);
- UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle, ref p);
+ UnmanagedMethods.ScreenToClient(this.CurrentWindow.Handle.Handle, ref p);
return new Point(p.X, p.Y);
}
}
diff --git a/Windows/Perspex.Win32/Perspex.Win32.csproj b/Windows/Perspex.Win32/Perspex.Win32.csproj
index fae0a97eee..b21e8ea241 100644
--- a/Windows/Perspex.Win32/Perspex.Win32.csproj
+++ b/Windows/Perspex.Win32/Perspex.Win32.csproj
@@ -60,7 +60,7 @@
-
+
diff --git a/Windows/Perspex.Win32/Win32Platform.cs b/Windows/Perspex.Win32/Win32Platform.cs
index a0de44a705..0ba4cb8589 100644
--- a/Windows/Perspex.Win32/Win32Platform.cs
+++ b/Windows/Perspex.Win32/Win32Platform.cs
@@ -24,6 +24,7 @@ namespace Perspex.Win32
public static void Initialize()
{
var locator = Locator.CurrentMutable;
+ locator.Register(() => new WindowImpl(), typeof(IWindowImpl));
locator.Register(() => WindowsKeyboardDevice.Instance, typeof(IKeyboardDevice));
locator.Register(() => instance, typeof(IPlatformThreadingInterface));
}
diff --git a/Windows/Perspex.Win32/Window.cs b/Windows/Perspex.Win32/WindowImpl.cs
similarity index 53%
rename from Windows/Perspex.Win32/Window.cs
rename to Windows/Perspex.Win32/WindowImpl.cs
index f97479b273..aae7099aa4 100644
--- a/Windows/Perspex.Win32/Window.cs
+++ b/Windows/Perspex.Win32/WindowImpl.cs
@@ -1,5 +1,5 @@
// -----------------------------------------------------------------------
-//
+//
// Copyright 2014 MIT Licence. See licence.md for more information.
//
// -----------------------------------------------------------------------
@@ -24,134 +24,58 @@ namespace Perspex.Win32
using Perspex.Win32.Interop;
using Splat;
- public class Window : ContentControl, ILayoutRoot, IRenderRoot, ICloseable
+ public class WindowImpl : IWindowImpl
{
- public static readonly PerspexProperty TitleProperty = PerspexProperty.Register("Title");
-
private UnmanagedMethods.WndProc wndProcDelegate;
private string className;
- private Dispatcher dispatcher;
-
- private IRenderer renderer;
+ private IntPtr hwnd;
- private IInputManager inputManager;
+ private Window owner;
- public Window()
+ public WindowImpl()
{
- IPlatformRenderInterface factory = Locator.Current.GetService();
-
this.CreateWindow();
- Size clientSize = this.ClientSize;
- this.dispatcher = Dispatcher.UIThread;
- this.LayoutManager = new LayoutManager(this);
- this.RenderManager = new RenderManager();
- this.renderer = factory.CreateRenderer(this.Handle, (int)clientSize.Width, (int)clientSize.Height);
- this.inputManager = Locator.Current.GetService();
- this.Template = ControlTemplate.Create(this.DefaultTemplate);
-
- this.LayoutManager.LayoutNeeded.Subscribe(x =>
- {
- this.dispatcher.InvokeAsync(
- () =>
- {
- this.LayoutManager.ExecuteLayoutPass();
- this.renderer.Render(this);
- this.RenderManager.RenderFinished();
- },
- DispatcherPriority.Render);
- });
-
- this.GetObservable(TitleProperty).Subscribe(s => UnmanagedMethods.SetWindowText(Handle, s));
-
- this.RenderManager.RenderNeeded
- .Where(_ => !this.LayoutManager.LayoutQueued)
- .Subscribe(x =>
- {
- this.dispatcher.InvokeAsync(
- () =>
- {
- if (!this.LayoutManager.LayoutQueued)
- {
- this.renderer.Render(this);
- this.RenderManager.RenderFinished();
- }
- },
- DispatcherPriority.Render);
- });
}
public event EventHandler Activated;
public event EventHandler Closed;
- public string Title
- {
- get { return this.GetValue(TitleProperty); }
- set { this.SetValue(TitleProperty, value); }
- }
+ public event EventHandler Input;
+
+ public event EventHandler Resized;
public Size ClientSize
{
get
{
UnmanagedMethods.RECT rect;
- UnmanagedMethods.GetClientRect(this.Handle, out rect);
+ UnmanagedMethods.GetClientRect(this.hwnd, out rect);
return new Size(rect.right, rect.bottom);
}
}
- public IntPtr Handle
+ public IPlatformHandle Handle
{
get;
private set;
}
- public ILayoutManager LayoutManager
+ public void SetOwner(Window owner)
{
- get;
- private set;
+ this.owner = owner;
}
- public IRenderManager RenderManager
+ public void SetTitle(string title)
{
- get;
- private set;
+ UnmanagedMethods.SetWindowText(this.hwnd, title);
}
public void Show()
{
- UnmanagedMethods.ShowWindow(this.Handle, 1);
- }
-
- protected override void OnPreviewKeyDown(KeyEventArgs e)
- {
- if (e.Key == Key.F12)
- {
- Window window = new Window
- {
- Content = new DevTools
- {
- Root = this,
- },
- };
-
- window.Show();
- }
- }
-
- private Control DefaultTemplate(Window c)
- {
- Border border = new Border();
- border.Background = new Perspex.Media.SolidColorBrush(0xffffffff);
- ContentPresenter contentPresenter = new ContentPresenter();
- contentPresenter.Bind(
- ContentPresenter.ContentProperty,
- this.GetObservable(Window.ContentProperty),
- BindingPriority.Style);
- border.Content = contentPresenter;
- return border;
+ UnmanagedMethods.ShowWindow(this.hwnd, 1);
}
private void CreateWindow()
@@ -181,7 +105,7 @@ namespace Perspex.Win32
throw new Win32Exception();
}
- this.Handle = UnmanagedMethods.CreateWindowEx(
+ this.hwnd = UnmanagedMethods.CreateWindowEx(
0,
atom,
null,
@@ -195,36 +119,12 @@ namespace Perspex.Win32
IntPtr.Zero,
IntPtr.Zero);
- if (this.Handle == IntPtr.Zero)
+ if (this.hwnd == IntPtr.Zero)
{
throw new Win32Exception();
}
- }
-
- private void OnActivated()
- {
- WindowsKeyboardDevice.Instance.WindowActivated(this);
-
- if (this.Activated != null)
- {
- this.Activated(this, EventArgs.Empty);
- }
- }
- private void OnResized(int width, int height)
- {
- this.renderer.Resize(width, height);
- this.LayoutManager.ExecuteLayoutPass();
- this.renderer.Render(this);
- this.RenderManager.RenderFinished();
- }
-
- private void OnClosed()
- {
- if (this.Closed != null)
- {
- this.Closed(this, EventArgs.Empty);
- }
+ this.Handle = new PlatformHandle(this.hwnd);
}
[SuppressMessage("Microsoft.StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", Justification = "Using Win32 naming for consistency.")]
@@ -237,11 +137,17 @@ namespace Perspex.Win32
switch ((UnmanagedMethods.WindowsMessage)msg)
{
case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
- this.OnActivated();
+ if (this.Activated != null)
+ {
+ this.Activated(this, EventArgs.Empty);
+ }
break;
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
- this.OnClosed();
+ if (this.Closed != null)
+ {
+ this.Closed(this, EventArgs.Empty);
+ }
break;
case UnmanagedMethods.WindowsMessage.WM_KEYDOWN:
@@ -255,39 +161,54 @@ namespace Perspex.Win32
case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
e = new RawMouseEventArgs(
- WindowsMouseDevice.Instance,
- this,
+ WindowsMouseDevice.Instance,
+ this.owner,
RawMouseEventType.LeftButtonDown,
new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
case UnmanagedMethods.WindowsMessage.WM_LBUTTONUP:
e = new RawMouseEventArgs(
- WindowsMouseDevice.Instance,
- this,
+ WindowsMouseDevice.Instance,
+ this.owner,
RawMouseEventType.LeftButtonUp,
new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
case UnmanagedMethods.WindowsMessage.WM_MOUSEMOVE:
e = new RawMouseEventArgs(
- WindowsMouseDevice.Instance,
- this,
+ WindowsMouseDevice.Instance,
+ this.owner,
RawMouseEventType.Move,
new Point((uint)lParam & 0xffff, (uint)lParam >> 16));
break;
case UnmanagedMethods.WindowsMessage.WM_SIZE:
- this.OnResized((int)lParam & 0xffff, (int)lParam >> 16);
+ if (this.Resized != null)
+ {
+ this.Resized(this, new RawSizeEventArgs((int)lParam & 0xffff, (int)lParam >> 16));
+ }
return IntPtr.Zero;
}
- if (e != null)
+ if (e != null && this.Input != null)
{
- this.inputManager.Process(e);
+ this.Input(this, e);
}
return UnmanagedMethods.DefWindowProc(hWnd, msg, wParam, lParam);
}
+
+ private class PlatformHandle : IPlatformHandle
+ {
+ public PlatformHandle(IntPtr hwnd)
+ {
+ this.Handle = hwnd;
+ }
+
+ public IntPtr Handle { get; private set; }
+
+ public string HandleDescriptor { get { return "HWND"; } }
+ }
}
}