Browse Source

Merge branch 'master' into xaml-binding-datatemplates

Conflicts:
	samples/XamlTestApplication/Program.cs
Steven Kirk 10 years ago
parent
commit
2807fa3a9a
71 changed files with 806 additions and 426 deletions
  1. 6 0
      Perspex.sln
  2. 1 2
      samples/TestApplication/Program.cs
  3. 2 4
      samples/XamlTestApplication/App.cs
  4. 2 28
      samples/XamlTestApplication/Program.cs
  5. 4 12
      samples/XamlTestApplication/XamlTestApplication.csproj
  6. 30 0
      samples/XamlTestApplicationPcl/Properties/AssemblyInfo.cs
  7. 0 0
      samples/XamlTestApplicationPcl/ViewModels/MainWindowViewModel.cs
  8. 0 0
      samples/XamlTestApplicationPcl/ViewModels/TestItem.cs
  9. 0 0
      samples/XamlTestApplicationPcl/ViewModels/TestNode.cs
  10. 0 0
      samples/XamlTestApplicationPcl/Views/MainWindow.cs
  11. 0 0
      samples/XamlTestApplicationPcl/Views/MainWindow.paml
  12. 22 0
      samples/XamlTestApplicationPcl/XamlTestApp.cs
  13. 130 0
      samples/XamlTestApplicationPcl/XamlTestApplicationPcl.csproj
  14. 0 0
      samples/XamlTestApplicationPcl/github_icon.png
  15. 4 0
      samples/XamlTestApplicationPcl/packages.config
  16. 6 2
      src/Gtk/Perspex.Cairo/CairoPlatform.cs
  17. 8 3
      src/Gtk/Perspex.Cairo/Media/DrawingContext.cs
  18. 15 7
      src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs
  19. 7 6
      src/Gtk/Perspex.Cairo/Media/TileBrushes.cs
  20. 1 1
      src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj
  21. 63 0
      src/Gtk/Perspex.Cairo/RenderTarget.cs
  22. 0 81
      src/Gtk/Perspex.Cairo/Renderer.cs
  23. 26 9
      src/Gtk/Perspex.Gtk/WindowImpl.cs
  24. 1 1
      src/Perspex.Application/Application.cs
  25. 1 1
      src/Perspex.Controls/Border.cs
  26. 1 1
      src/Perspex.Controls/MenuItem.cs
  27. 9 1
      src/Perspex.Controls/Panel.cs
  28. 1 0
      src/Perspex.Controls/Perspex.Controls.csproj
  29. 1 1
      src/Perspex.Controls/Platform/ITopLevelImpl.cs
  30. 64 0
      src/Perspex.Controls/Platform/ITopLevelRenderer.cs
  31. 1 1
      src/Perspex.Controls/Presenters/TextPresenter.cs
  32. 1 1
      src/Perspex.Controls/TextBlock.cs
  33. 7 44
      src/Perspex.Controls/TopLevel.cs
  34. 1 1
      src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs
  35. 5 5
      src/Perspex.HtmlRenderer/HtmlControl.cs
  36. 1 1
      src/Perspex.SceneGraph/Media/IDrawingContext.cs
  37. 9 13
      src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs
  38. 76 0
      src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs
  39. 4 3
      src/Perspex.SceneGraph/Perspex.SceneGraph.csproj
  40. 2 2
      src/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs
  41. 29 0
      src/Perspex.SceneGraph/Platform/IRenderTarget.cs
  42. 1 9
      src/Perspex.SceneGraph/Platform/IRenderTargetBitmapImpl.cs
  43. 0 38
      src/Perspex.SceneGraph/Platform/IRenderer.cs
  44. 2 2
      src/Perspex.SceneGraph/Rendering/IRenderQueueManager.cs
  45. 1 6
      src/Perspex.SceneGraph/Rendering/IRenderRoot.cs
  46. 2 2
      src/Perspex.SceneGraph/Rendering/RenderQueueManager.cs
  47. 17 66
      src/Perspex.SceneGraph/Rendering/RendererBase.cs
  48. 3 3
      src/Perspex.SceneGraph/Visual.cs
  49. 26 26
      src/Perspex.Themes.Default/TextBoxStyle.cs
  50. 2 2
      src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs
  51. 3 3
      src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs
  52. 2 2
      src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs
  53. 6 3
      src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs
  54. 2 2
      src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs
  55. 10 5
      src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs
  56. 1 1
      src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj
  57. 1 1
      src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs
  58. 20 11
      src/Windows/Perspex.Direct2D1/RenderTarget.cs
  59. 2 0
      src/Windows/Perspex.Win32/SystemDialogImpl.cs
  60. 2 2
      src/Windows/Perspex.Win32/WindowImpl.cs
  61. 2 2
      tests/Perspex.Controls.UnitTests/ControlTests.cs
  62. 1 0
      tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj
  63. 150 0
      tests/Perspex.Controls.UnitTests/StackPanelTests.cs
  64. 2 2
      tests/Perspex.Controls.UnitTests/TestRoot.cs
  65. 3 3
      tests/Perspex.Controls.UnitTests/TopLevelTests.cs
  66. 2 2
      tests/Perspex.Layout.UnitTests/FullLayoutTests.cs
  67. BIN
      tests/Perspex.RenderTests/Shapes/RectangleTests.cs
  68. BIN
      tests/Perspex.RenderTests/TestBase.cs
  69. 2 2
      tests/Perspex.SceneGraph.UnitTests/TestRoot.cs
  70. BIN
      tests/TestFiles/Cairo/Shapes/Rectangle/Rectangle_Stroke_Fill_ClipToBounds.expected.png
  71. BIN
      tests/TestFiles/Direct2D1/Shapes/Rectangle/Rectangle_Stroke_Fill_ClipToBounds.expected.png

+ 6 - 0
Perspex.sln

@@ -101,6 +101,7 @@ EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Perspex.Markup.UnitTests", "tests\Perspex.Markup.UnitTests\Perspex.Markup.UnitTests.csproj", "{8EF392D5-1416-45AA-9956-7CBBC3229E8A}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BindingTest", "samples\BindingTest\BindingTest.csproj", "{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlTestApplicationPcl", "samples\XamlTestApplicationPcl\XamlTestApplicationPcl.csproj", "{EA113F1A-D8D7-4142-9948-353270E7EBAE}"
 EndProject
 Global
 	GlobalSection(SharedMSBuildProjectFiles) = preSolution
@@ -253,6 +254,10 @@ Global
 		{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162}.Release|Any CPU.Build.0 = Release|Any CPU
+		{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{EA113F1A-D8D7-4142-9948-353270E7EBAE}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -281,5 +286,6 @@ Global
 		{6417E941-21BC-467B-A771-0DE389353CE6} = {8B6A8209-894F-4BA1-B880-965FD453982C}
 		{8EF392D5-1416-45AA-9956-7CBBC3229E8A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{08B3E6B9-1CD5-443C-9F61-6D49D1C5F162} = {9B9E3891-2366-4253-A952-D08BCEB71098}
+		{EA113F1A-D8D7-4142-9948-353270E7EBAE} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 	EndGlobalSection
 EndGlobal

+ 1 - 2
samples/TestApplication/Program.cs

@@ -737,9 +737,7 @@ namespace TestApplication
                 Header = "Animations",
                 Content = new StackPanel
                 {
-					HorizontalAlignment = HorizontalAlignment.Left,
 					Orientation = Orientation.Vertical,
-					VerticalAlignment = VerticalAlignment.Top,
 					Gap = 4,
 					Margin = new Thickness(10),
                     Children = new Controls
@@ -767,6 +765,7 @@ namespace TestApplication
 						}),
 						new Canvas 
 						{
+                            ClipToBounds = false,
 							Children = new Controls 
 							{
 								(border1 = new Border

+ 2 - 4
samples/XamlTestApplication/App.cs

@@ -7,13 +7,11 @@ using Perspex.Themes.Default;
 
 namespace XamlTestApplication
 {
-    public class App : Application
+    public class App : XamlTestApp
     {
-        public App()
+        protected override void RegisterPlatform()
         {
-            RegisterServices();
             InitializeSubsystems((int)Environment.OSVersion.Platform);
-            Styles = new DefaultTheme();
         }
     }
 }

+ 2 - 28
samples/XamlTestApplication/Program.cs

@@ -13,23 +13,6 @@ using XamlTestApplication.Views;
 
 namespace XamlTestApplication
 {
-    internal class Item
-    {
-        public string Name { get; set; }
-        public string Value { get; set; }
-    }
-
-    internal class Node
-    {
-        public Node()
-        {
-            Children = new PerspexList<Node>();
-        }
-
-        public string Name { get; set; }
-        public PerspexList<Node> Children { get; set; }
-    }
-
     internal class Program
     {
         private static void Main()
@@ -38,21 +21,12 @@ namespace XamlTestApplication
 
             App application = new App
             {
-                DataTemplates = new DataTemplates
-                {
-                    new FuncTreeDataTemplate<Node>(
-                        x => new TextBlock { Text = x.Name },
-                        x => x.Children,
-                        x => true),
-                },
-            };
 
-            var testCommand = ReactiveCommand.Create();
-            testCommand.Subscribe(_ => Debug.WriteLine("Test command executed."));
+            };
 
             var window = new MainWindow();
             window.Show();
             Application.Current.Run(window);
         }
     }
-}
+}

+ 4 - 12
samples/XamlTestApplication/XamlTestApplication.csproj

@@ -79,10 +79,6 @@
     <Compile Include="App.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="ViewModels\MainWindowViewModel.cs" />
-    <Compile Include="ViewModels\TestItem.cs" />
-    <Compile Include="ViewModels\TestNode.cs" />
-    <Compile Include="Views\MainWindow.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="App.config" />
@@ -148,17 +144,13 @@
       <Project>{811A76CF-1CF6-440F-963B-BBE31BD72A82}</Project>
       <Name>Perspex.Win32</Name>
     </ProjectReference>
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="github_icon.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <ProjectReference Include="..\XamlTestApplicationPcl\XamlTestApplicationPcl.csproj">
+      <Project>{ea113f1a-d8d7-4142-9948-353270e7ebae}</Project>
+      <Name>XamlTestApplicationPcl</Name>
+    </ProjectReference>
   </ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
-    <EmbeddedResource Include="Views\MainWindow.paml">
-      <SubType>Designer</SubType>
-    </EmbeddedResource>
   </ItemGroup>
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <Import Project="..\..\src\Shared\perspex.platform.targets" />

+ 30 - 0
samples/XamlTestApplicationPcl/Properties/AssemblyInfo.cs

@@ -0,0 +1,30 @@
+using System.Resources;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("XamlTestApplicationPcl")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("XamlTestApplicationPcl")]
+[assembly: AssemblyCopyright("Copyright ©  2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 0 - 0
samples/XamlTestApplication/ViewModels/MainWindowViewModel.cs → samples/XamlTestApplicationPcl/ViewModels/MainWindowViewModel.cs


+ 0 - 0
samples/XamlTestApplication/ViewModels/TestItem.cs → samples/XamlTestApplicationPcl/ViewModels/TestItem.cs


+ 0 - 0
samples/XamlTestApplication/ViewModels/TestNode.cs → samples/XamlTestApplicationPcl/ViewModels/TestNode.cs


+ 0 - 0
samples/XamlTestApplication/Views/MainWindow.cs → samples/XamlTestApplicationPcl/Views/MainWindow.cs


+ 0 - 0
samples/XamlTestApplication/Views/MainWindow.paml → samples/XamlTestApplicationPcl/Views/MainWindow.paml


+ 22 - 0
samples/XamlTestApplicationPcl/XamlTestApp.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Perspex;
+using Perspex.Themes.Default;
+
+namespace XamlTestApplication
+{
+    public abstract class XamlTestApp : Application
+    {
+        protected abstract void RegisterPlatform();
+
+        public XamlTestApp()
+        {
+            RegisterServices();
+            RegisterPlatform();
+            Styles = new DefaultTheme();
+        }
+    }
+}

+ 130 - 0
samples/XamlTestApplicationPcl/XamlTestApplicationPcl.csproj

@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{EA113F1A-D8D7-4142-9948-353270E7EBAE}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>XamlTestApplication</RootNamespace>
+    <AssemblyName>XamlTestApplicationPcl</AssemblyName>
+    <DefaultLanguage>en-US</DefaultLanguage>
+    <FileAlignment>512</FileAlignment>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <TargetFrameworkProfile>Profile7</TargetFrameworkProfile>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <!-- A reference to the entire .NET Framework is automatically included -->
+    <EmbeddedResource Include="Views\MainWindow.paml">
+      <SubType>Designer</SubType>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="ViewModels\MainWindowViewModel.cs" />
+    <Compile Include="ViewModels\TestItem.cs" />
+    <Compile Include="ViewModels\TestNode.cs" />
+    <Compile Include="Views\MainWindow.cs" />
+    <Compile Include="XamlTestApp.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="github_icon.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\Markup\Perspex.Markup.Xaml\Perspex.Markup.Xaml.csproj">
+      <Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
+      <Name>Perspex.Markup.Xaml</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Animation\Perspex.Animation.csproj">
+      <Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
+      <Name>Perspex.Animation</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Application\Perspex.Application.csproj">
+      <Project>{799a7bb5-3c2c-48b6-85a7-406a12c420da}</Project>
+      <Name>Perspex.Application</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Base\Perspex.Base.csproj">
+      <Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
+      <Name>Perspex.Base</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Controls\Perspex.Controls.csproj">
+      <Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
+      <Name>Perspex.Controls</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Diagnostics\Perspex.Diagnostics.csproj">
+      <Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
+      <Name>Perspex.Diagnostics</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.HtmlRenderer\Perspex.HtmlRenderer.csproj">
+      <Project>{5fb2b005-0a7f-4dad-add4-3ed01444e63d}</Project>
+      <Name>Perspex.HtmlRenderer</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Input\Perspex.Input.csproj">
+      <Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
+      <Name>Perspex.Input</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Interactivity\Perspex.Interactivity.csproj">
+      <Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
+      <Name>Perspex.Interactivity</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Layout\Perspex.Layout.csproj">
+      <Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
+      <Name>Perspex.Layout</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.ReactiveUI\Perspex.ReactiveUI.csproj">
+      <Project>{6417b24e-49c2-4985-8db2-3ab9d898ec91}</Project>
+      <Name>Perspex.ReactiveUI</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.SceneGraph\Perspex.SceneGraph.csproj">
+      <Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
+      <Name>Perspex.SceneGraph</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Styling\Perspex.Styling.csproj">
+      <Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
+      <Name>Perspex.Styling</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\src\Perspex.Themes.Default\Perspex.Themes.Default.csproj">
+      <Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
+      <Name>Perspex.Themes.Default</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="Splat, Version=1.6.2.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Splat.1.6.2\lib\Portable-net45+win+wpa81+wp80\Splat.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 0 - 0
samples/XamlTestApplication/github_icon.png → samples/XamlTestApplicationPcl/github_icon.png


+ 4 - 0
samples/XamlTestApplicationPcl/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Splat" version="1.6.2" targetFramework="portable45-net45+win8" />
+</packages>

+ 6 - 2
src/Gtk/Perspex.Cairo/CairoPlatform.cs

@@ -36,9 +36,13 @@ namespace Perspex.Cairo
             return new FormattedTextImpl(s_pangoContext, text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight);
         }
 
-        public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height)
+        public IRenderTarget CreateRenderer(IPlatformHandle handle, double width, double height)
         {
-            return new Renderer(handle, width, height);
+            if (handle.HandleDescriptor != "GtkWindow")
+                throw new NotSupportedException(string.Format(
+                    "Don't know how to create a Cairo renderer from a '{0}' handle",
+                    handle.HandleDescriptor));
+            return new RenderTarget((Gtk.Window)handle, width, height);
         }
 
         public IRenderTargetBitmapImpl CreateRenderTargetBitmap(int width, int height)

+ 8 - 3
src/Gtk/Perspex.Cairo/Media/DrawingContext.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Linq;
 using System.Reactive.Disposables;
+using System.Runtime.InteropServices;
 using Perspex.Cairo.Media.Imaging;
 using Perspex.Media;
 
@@ -191,7 +192,7 @@ namespace Perspex.Cairo.Media
         /// </summary>
         /// <param name="brush">The brush.</param>
         /// <param name="rect">The rectangle bounds.</param>
-        public void FillRectange(Brush brush, Rect rect, float cornerRadius)
+        public void FillRectangle(Brush brush, Rect rect, float cornerRadius)
         {
 			using (var b = SetBrush(brush, rect.Size)) 
 			{
@@ -200,6 +201,8 @@ namespace Perspex.Cairo.Media
 			}
         }
 
+        private static Random Random = new Random();
+
         /// <summary>
         /// Pushes a clip rectange.
         /// </summary>
@@ -207,10 +210,11 @@ namespace Perspex.Cairo.Media
         /// <returns>A disposable used to undo the clip rectangle.</returns>
         public IDisposable PushClip(Rect clip)
         {
+            _context.Save();
             _context.Rectangle(clip.ToCairo());
             _context.Clip();
 
-            return Disposable.Create(() => _context.ResetClip());
+            return Disposable.Create(() => _context.Restore());
         }
 
         /// <summary>
@@ -238,11 +242,12 @@ namespace Perspex.Cairo.Media
         /// <returns>A disposable used to undo the transformation.</returns>
         public IDisposable PushTransform(Matrix matrix)
         {
+            _context.Save();
             _context.Transform(matrix.ToCairo());
 
             return Disposable.Create(() =>
             {
-                _context.Transform(matrix.Invert().ToCairo());
+               _context.Restore();
             });
         }
         

+ 15 - 7
src/Gtk/Perspex.Cairo/Media/Imaging/RenderTargetBitmapImpl.cs

@@ -2,7 +2,9 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using Perspex.Media;
 using Perspex.Platform;
+using Perspex.Rendering;
 
 namespace Perspex.Cairo.Media.Imaging
 {
@@ -10,10 +12,12 @@ namespace Perspex.Cairo.Media.Imaging
 
     public class RenderTargetBitmapImpl : IRenderTargetBitmapImpl
     {
+
+        private readonly RenderTarget _renderTarget;
         public RenderTargetBitmapImpl(Cairo.ImageSurface surface)
         {
             Surface = surface;
-            renderer = new Renderer(Surface);
+            _renderTarget = new RenderTarget(Surface);
         }
 
         public int PixelWidth => Surface.Width;
@@ -22,7 +26,7 @@ namespace Perspex.Cairo.Media.Imaging
 
         public void Dispose()
         {
-            renderer.Dispose();
+            _renderTarget.Dispose();
         }
 
         public Cairo.ImageSurface Surface
@@ -30,15 +34,19 @@ namespace Perspex.Cairo.Media.Imaging
             get;
         }
 
-        private Renderer renderer;
-        public void Render(IVisual visual)
+        public void Save(string fileName)
         {
-            renderer.Render(visual, new PlatformHandle(IntPtr.Zero, "RTB"));
+            Surface.WriteToPng(fileName);
         }
 
-        public void Save(string fileName)
+        public IDrawingContext CreateDrawingContext()
         {
-            Surface.WriteToPng(fileName);
+            return _renderTarget.CreateDrawingContext();
+        }
+
+        void IRenderTarget.Resize(int width, int height)
+        {
+            throw new NotSupportedException();
         }
     }
 }

+ 7 - 6
src/Gtk/Perspex.Cairo/Media/TileBrushes.cs

@@ -7,6 +7,7 @@ using Perspex.Cairo.Media.Imaging;
 using Perspex.Layout;
 using Perspex.Media;
 using Perspex.Platform;
+using Perspex.Rendering;
 
 namespace Perspex.Cairo.Media
 {
@@ -100,7 +101,7 @@ namespace Perspex.Cairo.Media
             var intermediateSize = CalculateIntermediateSize(tileMode, targetSize, destinationRect.Size);
             
 			using (var intermediate = new ImageSurface(Format.ARGB32, (int)intermediateSize.Width, (int)intermediateSize.Height))
-            using (var context = new Context(intermediate))
+            using (var ctx = new RenderTarget(intermediate).CreateDrawingContext())
             {
                 Rect drawRect;
                 var transform = CalculateIntermediateTransform(
@@ -110,12 +111,12 @@ namespace Perspex.Cairo.Media
                     scale,
                     translate,
                     out drawRect);
-                var renderer = new Renderer(intermediate);
 
-                context.Rectangle(drawRect.ToCairo());
-                context.Clip();
-                context.Transform(transform.ToCairo());
-                renderer.Render(visual, new PlatformHandle(IntPtr.Zero, "RTB"), transform, drawRect);
+                using (ctx.PushClip(drawRect))
+                using (ctx.PushTransform(transform))
+                {
+                    ctx.Render(visual);
+                }
 
                 var result = new SurfacePattern(intermediate);
 

+ 1 - 1
src/Gtk/Perspex.Cairo/Perspex.Cairo.csproj

@@ -61,7 +61,7 @@
     <Compile Include="Media\RadialGradientBrushImpl.cs" />
     <Compile Include="Media\TileBrushes.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Renderer.cs" />
+    <Compile Include="RenderTarget.cs" />
     <Compile Include="CairoExtensions.cs" />
     <Compile Include="Media\StreamGeometryContextImpl.cs" />
     <Compile Include="Media\StreamGeometryImpl.cs" />

+ 63 - 0
src/Gtk/Perspex.Cairo/RenderTarget.cs

@@ -0,0 +1,63 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Runtime.InteropServices;
+using Perspex.Cairo.Media;
+using Perspex.Media;
+using Perspex.Platform;
+using Perspex.Rendering;
+
+namespace Perspex.Cairo
+{
+    using global::Cairo;
+
+    /// <summary>
+    /// A cairo render target.
+    /// </summary>
+    public class RenderTarget : IRenderTarget
+    {
+        private readonly Surface _surface;
+        private Gtk.Window _window;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="RenderTarget"/> class.
+        /// </summary>
+        /// <param name="window">The window.</param>
+        /// <param name="width">The width of the window.</param>
+        /// <param name="height">The height of the window.</param>
+        public RenderTarget(Gtk.Window window, double width, double height)
+        {
+            _window = window;
+        }
+
+        public RenderTarget(ImageSurface surface)
+        {
+            _surface = surface;
+        }
+
+        /// <summary>
+        /// Resizes the renderer.
+        /// </summary>
+        /// <param name="width">The new width.</param>
+        /// <param name="height">The new height.</param>
+        public  void Resize(int width, int height)
+        {
+            // Don't need to do anything here.
+        }
+
+
+        /// <summary>
+        /// Creates a cairo surface that targets a platform-specific resource.
+        /// </summary>
+        /// <returns>A surface wrapped in an <see cref="IDrawingContext"/>.</returns>
+        public IDrawingContext CreateDrawingContext()
+        {
+            if(_surface != null)
+                return new DrawingContext(_surface);
+            return new DrawingContext(_window.GdkWindow);
+        }
+        
+        public void Dispose() => _surface?.Dispose();
+    }
+}

+ 0 - 81
src/Gtk/Perspex.Cairo/Renderer.cs

@@ -1,81 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Runtime.InteropServices;
-using Perspex.Cairo.Media;
-using Perspex.Media;
-using Perspex.Platform;
-using Perspex.Rendering;
-
-namespace Perspex.Cairo
-{
-    using global::Cairo;
-
-    /// <summary>
-    /// A cairo renderer.
-    /// </summary>
-    public class Renderer : RendererBase
-    {
-        private readonly Surface _surface;
-        private Gdk.Window _window;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="Renderer"/> class.
-        /// </summary>
-        /// <param name="handle">The window handle.</param>
-        /// <param name="width">The width of the window.</param>
-        /// <param name="height">The height of the window.</param>
-        public Renderer(IPlatformHandle handle, double width, double height)
-        {
-        }
-
-        public Renderer(ImageSurface surface)
-        {
-            _surface = surface;
-        }
-
-        /// <summary>
-        /// Resizes the renderer.
-        /// </summary>
-        /// <param name="width">The new width.</param>
-        /// <param name="height">The new height.</param>
-        public override void Resize(int width, int height)
-        {
-            // Don't need to do anything here.
-        }
-
-
-        /// <summary>
-        /// Creates a cairo surface that targets a platform-specific resource.
-        /// </summary>
-        /// <param name="handle">The platform-specific handle.</param>
-        /// <returns>A surface wrapped in an <see cref="IDrawingContext"/>.</returns>
-        protected override IDrawingContext CreateDrawingContext(IPlatformHandle handle)
-        {
-            switch (handle.HandleDescriptor)
-            {
-                case "RTB":
-                    return new DrawingContext(_surface);
-                case "GdkWindow":
-                    if (_window == null)
-                        _window = new Gdk.Window(handle.Handle);
-
-                    return new DrawingContext(_window);
-                default:
-                    throw new NotSupportedException(string.Format(
-                        "Don't know how to create a Cairo renderer from a '{0}' handle",
-                        handle.HandleDescriptor));
-            }
-        }
-
-        [DllImport("user32.dll")]
-        private static extern IntPtr GetDC(IntPtr hwnd);
-
-        public override void Dispose()
-        {
-			if (_surface != null)
-		        _surface.Dispose();
-        }
-    }
-}

+ 26 - 9
src/Gtk/Perspex.Gtk/WindowImpl.cs

@@ -14,12 +14,10 @@ namespace Perspex.Gtk
 {
     using Gtk = global::Gtk;
 
-    public class WindowImpl : Gtk.Window, IWindowImpl
+    public class WindowImpl : Gtk.Window, IWindowImpl, IPlatformHandle
     {
         private IInputRoot _inputRoot;
-
-        private IPlatformHandle _windowHandle;
-
+        
         private Size _clientSize;
 
         private Gtk.IMContext _imContext;
@@ -46,7 +44,6 @@ namespace Perspex.Gtk
             Events = EventMask.PointerMotionMask |
               EventMask.ButtonPressMask |
               EventMask.ButtonReleaseMask;
-            _windowHandle = new PlatformHandle(Handle, "GtkWindow");
             _imContext = new Gtk.IMMulticontext();
             _imContext.Commit += ImContext_Commit;
         }
@@ -73,7 +70,8 @@ namespace Perspex.Gtk
             }
         }
 
-        IPlatformHandle ITopLevelImpl.Handle => _windowHandle;
+        IPlatformHandle ITopLevelImpl.Handle => this;
+        public string HandleDescriptor => "GtkWindow";
 
         public Action Activated { get; set; }
 
@@ -83,7 +81,7 @@ namespace Perspex.Gtk
 
         public Action<RawInputEventArgs> Input { get; set; }
 
-        public Action<Rect, IPlatformHandle> Paint { get; set; }
+        public Action<Rect> Paint { get; set; }
 
         public Action<Size> Resized { get; set; }
 
@@ -94,7 +92,9 @@ namespace Perspex.Gtk
 
         public void Invalidate(Rect rect)
         {
-            base.GdkWindow.InvalidateRect (new Rectangle ((int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height), true);
+            if (base.GdkWindow != null)
+                base.GdkWindow.InvalidateRect(
+                    new Rectangle((int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height), true);
         }
 
         public Point PointToScreen(Point point)
@@ -167,6 +167,23 @@ namespace Perspex.Gtk
             return true;
         }
 
+        protected override bool OnScrollEvent(EventScroll evnt)
+        {
+            double step = 1;
+            var delta = new Vector();
+            if (evnt.Direction == ScrollDirection.Down)
+                delta = new Vector(0, -step);
+            else if (evnt.Direction == ScrollDirection.Up)
+                delta = new Vector(0, step);
+            else if (evnt.Direction == ScrollDirection.Right)
+                delta = new Vector(-step, 0);
+            if (evnt.Direction == ScrollDirection.Left)
+                delta = new Vector(step, 0);
+            var e = new RawMouseWheelEventArgs(GtkMouseDevice.Instance, evnt.Time, _inputRoot, new Point(evnt.X, evnt.Y), delta, GetModifierKeys(evnt.State));
+            Input(e);
+            return base.OnScrollEvent(evnt);
+        }
+
         protected override bool OnButtonReleaseEvent(EventButton evnt)
         {
             var e = new RawMouseEventArgs(
@@ -223,7 +240,7 @@ namespace Perspex.Gtk
 
         protected override bool OnExposeEvent(EventExpose evnt)
         {
-            Paint(evnt.Area.ToPerspex(), GetHandle(evnt.Window));
+            Paint(evnt.Area.ToPerspex());
             return true;
         }
 

+ 1 - 1
src/Perspex.Application/Application.cs

@@ -168,7 +168,7 @@ namespace Perspex
                 .Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
                 .Bind<IStyler>().ToConstant(_styler)
                 .Bind<ILayoutManager>().ToTransient<LayoutManager>()
-                .Bind<IRenderManager>().ToTransient<RenderManager>();
+                .Bind<IRenderQueueManager>().ToTransient<RenderQueueManager>();
         }
 
         /// <summary>

+ 1 - 1
src/Perspex.Controls/Border.cs

@@ -93,7 +93,7 @@ namespace Perspex.Controls
 
             if (background != null)
             {
-                context.FillRectange(background, rect, cornerRadius);
+                context.FillRectangle(background, rect, cornerRadius);
             }
 
             if (borderBrush != null && borderThickness > 0)

+ 1 - 1
src/Perspex.Controls/MenuItem.cs

@@ -29,7 +29,7 @@ namespace Perspex.Controls
             Button.CommandProperty.AddOwner<MenuItem>();
 
         public static readonly PerspexProperty<KeyGesture> HotKeyProperty =
-            HotKeyManager.HotKeyProperty.AddOwner<Button>();
+            HotKeyManager.HotKeyProperty.AddOwner<MenuItem>();
 
         /// <summary>
         /// Defines the <see cref="CommandParameter"/> property.

+ 9 - 1
src/Perspex.Controls/Panel.cs

@@ -29,6 +29,14 @@ namespace Perspex.Controls
 
         private ILogical _childLogicalParent;
 
+        /// <summary>
+        /// Initializes static members of the <see cref="Panel"/> class.
+        /// </summary>
+        static Panel()
+        {
+            ClipToBoundsProperty.OverrideDefaultValue<Panel>(true);
+        }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="Panel"/> class.
         /// </summary>
@@ -173,7 +181,7 @@ namespace Perspex.Controls
             if (background != null)
             {
                 var renderSize = Bounds.Size;
-                context.FillRectange(background, new Rect(renderSize));
+                context.FillRectangle(background, new Rect(renderSize));
             }
 
             base.Render(context);

+ 1 - 0
src/Perspex.Controls/Perspex.Controls.csproj

@@ -43,6 +43,7 @@
     </Compile>
     <Compile Include="DockPanel.cs" />
     <Compile Include="HotkeyManager.cs" />
+    <Compile Include="Platform\ITopLevelRenderer.cs" />
     <Compile Include="SystemDialog.cs" />
     <Compile Include="Generators\ITreeItemContainerGenerator.cs" />
     <Compile Include="Generators\ItemContainers.cs" />

+ 1 - 1
src/Perspex.Controls/Platform/ITopLevelImpl.cs

@@ -22,7 +22,7 @@ namespace Perspex.Platform
 
         Action<RawInputEventArgs> Input { get; set; }
 
-        Action<Rect, IPlatformHandle> Paint { get; set; }
+        Action<Rect> Paint { get; set; }
 
         Action<Size> Resized { get; set; }
 

+ 64 - 0
src/Perspex.Controls/Platform/ITopLevelRenderer.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Perspex.Platform;
+using Perspex.Rendering;
+using Perspex.Threading;
+
+namespace Perspex.Controls.Platform
+{
+    public interface ITopLevelRenderer
+    {
+        void Attach(TopLevel topLevel);
+    }
+
+
+    class DefaultTopLevelRenderer : ITopLevelRenderer
+    {
+
+        public void Attach(TopLevel topLevel)
+        {
+            var resources = new List<IDisposable>();
+            var initialClientSize = topLevel.PlatformImpl.ClientSize;
+
+
+            var queueManager = ((IRenderRoot)topLevel).RenderQueueManager;
+
+            if (queueManager == null)
+                return;
+
+            var platformRender = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
+            if(platformRender == null)
+                return;
+
+            var viewport = platformRender
+                .CreateRenderer(topLevel.PlatformImpl.Handle, initialClientSize.Width, initialClientSize.Height);
+            resources.Add(viewport);
+
+
+            resources.Add(topLevel.GetObservable(TopLevel.ClientSizeProperty).Subscribe(clientSize =>
+            {
+                viewport.Resize((int) clientSize.Width, (int) clientSize.Height);
+            }));
+            resources.Add(queueManager.RenderNeeded.Subscribe(_
+                =>
+                Dispatcher.UIThread.InvokeAsync(() => topLevel.PlatformImpl.Invalidate(new Rect(topLevel.ClientSize)))));
+
+            topLevel.PlatformImpl.Paint = rect =>
+            {
+                viewport.Render(topLevel);
+                queueManager.RenderFinished();
+            };
+
+            topLevel.Closed += delegate
+            {
+                foreach (var disposable in resources)
+                    disposable.Dispose();
+                resources.Clear();
+            };
+
+        }
+    }
+}

+ 1 - 1
src/Perspex.Controls/Presenters/TextPresenter.cs

@@ -84,7 +84,7 @@ namespace Perspex.Controls.Presenters
 
                 foreach (var rect in rects)
                 {
-                    context.FillRectange(brush, rect);
+                    context.FillRectangle(brush, rect);
                 }
             }
 

+ 1 - 1
src/Perspex.Controls/TextBlock.cs

@@ -220,7 +220,7 @@ namespace Perspex.Controls
 
             if (background != null)
             {
-                context.FillRectange(background, new Rect(Bounds.Size));
+                context.FillRectangle(background, new Rect(Bounds.Size));
             }
 
             FormattedText.Constraint = Bounds.Size;

+ 7 - 44
src/Perspex.Controls/TopLevel.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Reactive.Disposables;
 using System.Reactive.Linq;
+using Perspex.Controls.Platform;
 using Perspex.Controls.Primitives;
 using Perspex.Input;
 using Perspex.Input.Raw;
@@ -43,8 +44,7 @@ namespace Perspex.Controls
         public static readonly PerspexProperty<IInputElement> PointerOverElementProperty =
             PerspexProperty.Register<TopLevel, IInputElement>(nameof(IInputRoot.PointerOverElement));
 
-        private readonly IRenderManager _renderManager;
-        private readonly IRenderer _renderer;
+        private readonly IRenderQueueManager _renderQueueManager;
         private readonly IInputManager _inputManager;
         private readonly IAccessKeyHandler _accessKeyHandler;
         private readonly IKeyboardNavigationHandler _keyboardNavigationHandler;
@@ -92,23 +92,18 @@ namespace Perspex.Controls
             _inputManager = TryGetService<IInputManager>(dependencyResolver);
             _keyboardNavigationHandler = TryGetService<IKeyboardNavigationHandler>(dependencyResolver);
             LayoutManager = TryGetService<ILayoutManager>(dependencyResolver);
-            _renderManager = TryGetService<IRenderManager>(dependencyResolver);
+            _renderQueueManager = TryGetService<IRenderQueueManager>(dependencyResolver);
+            (TryGetService<ITopLevelRenderer>(dependencyResolver) ?? new DefaultTopLevelRenderer()).Attach(this);
 
             PlatformImpl.SetInputRoot(this);
             PlatformImpl.Activated = HandleActivated;
             PlatformImpl.Deactivated = HandleDeactivated;
             PlatformImpl.Closed = HandleClosed;
             PlatformImpl.Input = HandleInput;
-            PlatformImpl.Paint = HandlePaint;
             PlatformImpl.Resized = HandleResized;
 
             Size clientSize = ClientSize = PlatformImpl.ClientSize;
 
-            if (renderInterface != null)
-            {
-                _renderer = renderInterface.CreateRenderer(PlatformImpl.Handle, clientSize.Width, clientSize.Height);
-            }
-
             if (LayoutManager != null)
             {
                 LayoutManager.Root = this;
@@ -116,11 +111,6 @@ namespace Perspex.Controls
                 LayoutManager.LayoutCompleted.Subscribe(_ => HandleLayoutCompleted());
             }
 
-            if (_renderManager != null)
-            {
-                _renderManager.RenderNeeded.Subscribe(_ => HandleRenderNeeded());
-            }
-
             if (_keyboardNavigationHandler != null)
             {
                 _keyboardNavigationHandler.SetOwner(this);
@@ -188,16 +178,11 @@ namespace Perspex.Controls
         {
             get;
         }
-
-        /// <summary>
-        /// Gets the window renderer.
-        /// </summary>
-        IRenderer IRenderRoot.Renderer => _renderer;
-
+        
         /// <summary>
         /// Gets the window render manager.
         /// </summary>
-        IRenderManager IRenderRoot.RenderManager => _renderManager;
+        IRenderQueueManager IRenderRoot.RenderQueueManager => _renderQueueManager;
 
         /// <summary>
         /// Gets the access key handler for the window.
@@ -297,7 +282,6 @@ namespace Perspex.Controls
             }
 
             ClientSize = clientSize;
-            _renderer.Resize((int)clientSize.Width, (int)clientSize.Height);
             LayoutManager.ExecuteLayoutPass();
             PlatformImpl.Invalidate(new Rect(clientSize));
         }
@@ -389,28 +373,7 @@ namespace Perspex.Controls
         /// </summary>
         private void HandleLayoutCompleted()
         {
-            _renderManager?.InvalidateRender(this);
-        }
-
-        /// <summary>
-        /// Handles a render request from <see cref="RenderManager.RenderNeeded"/>.
-        /// </summary>
-        private void HandleRenderNeeded()
-        {
-            Dispatcher.UIThread.InvokeAsync(
-                () => PlatformImpl.Invalidate(new Rect(ClientSize)),
-                DispatcherPriority.Render);
-        }
-
-        /// <summary>
-        /// Handles a paint request from <see cref="ITopLevelImpl.Paint"/>.
-        /// </summary>
-        /// <param name="rect">The rectangle to paint.</param>
-        /// <param name="handle">An optional platform-specific handle.</param>
-        private void HandlePaint(Rect rect, IPlatformHandle handle)
-        {
-            _renderer.Render(this, handle);
-            _renderManager.RenderFinished();
+            _renderQueueManager?.InvalidateRender(this);
         }
     }
 }

+ 1 - 1
src/Perspex.HtmlRenderer/Adapters/GraphicsAdapter.cs

@@ -249,7 +249,7 @@ namespace TheArtOfDev.HtmlRenderer.Perspex.Adapters
 
         public override void DrawRectangle(RBrush brush, double x, double y, double width, double height)
         {
-            _g.FillRectange(((BrushAdapter) brush).Brush, new Rect(x, y, width, height));
+            _g.FillRectangle(((BrushAdapter) brush).Brush, new Rect(x, y, width, height));
         }
 
         public override void DrawImage(RImage image, RRect destRect, RRect srcRect)

+ 5 - 5
src/Perspex.HtmlRenderer/HtmlControl.cs

@@ -329,19 +329,19 @@ namespace Perspex.Controls.Html
         
         public override void Render(IDrawingContext context)
         {
-            context.FillRectange(Background,  new Rect(RenderSize));
+            context.FillRectangle(Background,  new Rect(RenderSize));
 
             if (BorderThickness != new Thickness(0) && BorderBrush != null)
             {
                 var brush = new SolidColorBrush(Colors.Black);
                 if (BorderThickness.Top > 0)
-                    context.FillRectange(brush, new Rect(0, 0, RenderSize.Width, BorderThickness.Top));
+                    context.FillRectangle(brush, new Rect(0, 0, RenderSize.Width, BorderThickness.Top));
                 if (BorderThickness.Bottom > 0)
-                    context.FillRectange(brush, new Rect(0, RenderSize.Height - BorderThickness.Bottom, RenderSize.Width, BorderThickness.Bottom));
+                    context.FillRectangle(brush, new Rect(0, RenderSize.Height - BorderThickness.Bottom, RenderSize.Width, BorderThickness.Bottom));
                 if (BorderThickness.Left > 0)
-                    context.FillRectange(brush, new Rect(0, 0, BorderThickness.Left, RenderSize.Height));
+                    context.FillRectangle(brush, new Rect(0, 0, BorderThickness.Left, RenderSize.Height));
                 if (BorderThickness.Right > 0)
-                    context.FillRectange(brush, new Rect(RenderSize.Width - BorderThickness.Right, 0, BorderThickness.Right, RenderSize.Height));
+                    context.FillRectangle(brush, new Rect(RenderSize.Width - BorderThickness.Right, 0, BorderThickness.Right, RenderSize.Height));
             }
 
             var htmlWidth = HtmlWidth(RenderSize);

+ 1 - 1
src/Perspex.SceneGraph/Media/IDrawingContext.cs

@@ -63,7 +63,7 @@ namespace Perspex.Media
         /// <param name="brush">The brush.</param>
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
-        void FillRectange(Brush brush, Rect rect, float cornerRadius = 0.0f);
+        void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0.0f);
 
         /// <summary>
         /// Pushes a clip rectange.

+ 9 - 13
src/Perspex.SceneGraph/Media/Imaging/RenderTargetBitmap.cs

@@ -3,13 +3,14 @@
 
 using System;
 using Perspex.Platform;
+using Perspex.Rendering;
 
 namespace Perspex.Media.Imaging
 {
     /// <summary>
     /// A bitmap that holds the rendering of a <see cref="IVisual"/>.
     /// </summary>
-    public class RenderTargetBitmap : Bitmap, IDisposable
+    public class RenderTargetBitmap : Bitmap, IDisposable, IRenderTarget
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="RenderTargetBitmap"/> class.
@@ -26,18 +27,6 @@ namespace Perspex.Media.Imaging
         /// </summary>
         public new IRenderTargetBitmapImpl PlatformImpl => (IRenderTargetBitmapImpl)base.PlatformImpl;
 
-        /// <summary>
-        /// Renders an <see cref="IVisual"/> into the bitmap.
-        /// </summary>
-        /// <param name="visual">The visual to render.</param>
-        /// <remarks>
-        /// Before calling this method, ensure that <paramref name="visual"/> has been measured.
-        /// </remarks>
-        public void Render(IVisual visual)
-        {
-            PlatformImpl.Render(visual);
-        }
-
         /// <summary>
         /// Disposes of the bitmap.
         /// </summary>
@@ -57,5 +46,12 @@ namespace Perspex.Media.Imaging
             IPlatformRenderInterface factory = PerspexLocator.Current.GetService<IPlatformRenderInterface>();
             return factory.CreateRenderTargetBitmap(width, height);
         }
+
+        public IDrawingContext CreateDrawingContext() => PlatformImpl.CreateDrawingContext();
+
+        void IRenderTarget.Resize(int width, int height)
+        {
+            throw new NotSupportedException();
+        }
     }
 }

+ 76 - 0
src/Perspex.SceneGraph/Media/ValidatingDrawingContext.cs

@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Reactive.Disposables;
+using Perspex.Media.Imaging;
+
+namespace Perspex.Media
+{
+    public class ValidatingDrawingContext : IDrawingContext
+    {
+        private readonly IDrawingContext _base;
+
+        public ValidatingDrawingContext(IDrawingContext @base)
+        {
+            _base = @base;
+        }
+
+        public void Dispose()
+        {
+            _base.Dispose();
+        }
+
+        public Matrix CurrentTransform => _base.CurrentTransform;
+        public void DrawImage(IBitmap source, double opacity, Rect sourceRect, Rect destRect)
+        {
+            _base.DrawImage(source, opacity, sourceRect, destRect);
+        }
+
+        public void DrawLine(Pen pen, Point p1, Point p2)
+        {
+            _base.DrawLine(pen, p1, p2);
+        }
+
+        public void DrawGeometry(Brush brush, Pen pen, Geometry geometry)
+        {
+            _base.DrawGeometry(brush, pen, geometry);
+        }
+
+        public void DrawRectangle(Pen pen, Rect rect, float cornerRadius = 0)
+        {
+            _base.DrawRectangle(pen, rect, cornerRadius);
+        }
+
+        public void DrawText(Brush foreground, Point origin, FormattedText text)
+        {
+            _base.DrawText(foreground, origin, text);
+        }
+
+        public void FillRectangle(Brush brush, Rect rect, float cornerRadius = 0)
+        {
+            _base.FillRectangle(brush, rect, cornerRadius);
+        }
+
+
+        Stack<IDisposable> _stateStack = new Stack<IDisposable>();
+
+        IDisposable Transform(IDisposable disposable)
+        {
+            _stateStack.Push(disposable);
+            return Disposable.Create(() =>
+            {
+                var current = _stateStack.Peek();
+                if (current != disposable)
+                    throw new InvalidOperationException("Invalid push/pop order");
+                current.Dispose();
+                _stateStack.Pop();
+            });
+        }
+
+        public IDisposable PushClip(Rect clip) => Transform(_base.PushClip(clip));
+
+        public IDisposable PushOpacity(double opacity) => Transform(_base.PushOpacity(opacity));
+
+        public IDisposable PushTransform(Matrix matrix) => Transform(_base.PushTransform(matrix));
+    }
+}

+ 4 - 3
src/Perspex.SceneGraph/Perspex.SceneGraph.csproj

@@ -99,13 +99,14 @@
     <Compile Include="Media\Transform.cs" />
     <Compile Include="Media\TileBrush.cs" />
     <Compile Include="Media\ImageBush.cs" />
+    <Compile Include="Media\ValidatingDrawingContext.cs" />
     <Compile Include="Media\VisualBrush.cs" />
     <Compile Include="RelativePoint.cs" />
     <Compile Include="Platform\IFormattedTextImpl.cs" />
     <Compile Include="Platform\IBitmapImpl.cs" />
     <Compile Include="Platform\IGeometryImpl.cs" />
     <Compile Include="Platform\IPlatformRenderInterface.cs" />
-    <Compile Include="Platform\IRenderer.cs" />
+    <Compile Include="Platform\IRenderTarget.cs" />
     <Compile Include="Platform\IRenderTargetBitmapImpl.cs" />
     <Compile Include="Platform\IStreamGeometryContextImpl.cs" />
     <Compile Include="Platform\IStreamGeometryImpl.cs" />
@@ -114,9 +115,9 @@
     <Compile Include="RelativeRect.cs" />
     <Compile Include="Rect.cs" />
     <Compile Include="Rendering\IRenderRoot.cs" />
-    <Compile Include="Rendering\IRenderManager.cs" />
+    <Compile Include="Rendering\IRenderQueueManager.cs" />
     <Compile Include="Rendering\RendererBase.cs" />
-    <Compile Include="Rendering\RenderManager.cs" />
+    <Compile Include="Rendering\RenderQueueManager.cs" />
     <Compile Include="Size.cs" />
     <Compile Include="Thickness.cs" />
     <Compile Include="Vector.cs" />

+ 2 - 2
src/Perspex.SceneGraph/Platform/IPlatformRenderInterface.cs

@@ -49,8 +49,8 @@ namespace Perspex.Platform
         /// <param name="handle">The platform handle for the renderer.</param>
         /// <param name="width">The initial width of the render.</param>
         /// <param name="height">The initial height of the render.</param>
-        /// <returns>An <see cref="IRenderer"/>.</returns>
-        IRenderer CreateRenderer(IPlatformHandle handle, double width, double height);
+        /// <returns>An <see cref="IRenderTarget"/>.</returns>
+        IRenderTarget CreateRenderer(IPlatformHandle handle, double width, double height);
 
         /// <summary>
         /// Creates a render target bitmap implementation.

+ 29 - 0
src/Perspex.SceneGraph/Platform/IRenderTarget.cs

@@ -0,0 +1,29 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Perspex.Media;
+
+namespace Perspex.Platform
+{
+    /// <summary>
+    /// Defines a render target
+    /// </summary>
+    /// <remarks>
+    /// The interface used for obtaining drawing context from surfaces you can render on.
+    /// </remarks>
+    public interface IRenderTarget : IDisposable
+    {
+        /// <summary>
+        /// Creates an <see cref="IDrawingContext"/> for a rendering session.
+        /// </summary>
+        IDrawingContext CreateDrawingContext();
+
+        /// <summary>
+        /// Resizes the rendered viewport.
+        /// </summary>
+        /// <param name="width">The new width.</param>
+        /// <param name="height">The new height.</param>
+        void Resize(int width, int height);
+    }
+}

+ 1 - 9
src/Perspex.SceneGraph/Platform/IRenderTargetBitmapImpl.cs

@@ -9,15 +9,7 @@ namespace Perspex.Platform
     /// Defines the platform-specific interface for a
     /// <see cref="Perspex.Media.Imaging.RenderTargetBitmap"/>.
     /// </summary>
-    public interface IRenderTargetBitmapImpl : IBitmapImpl, IDisposable
+    public interface IRenderTargetBitmapImpl : IBitmapImpl, IRenderTarget
     {
-        /// <summary>
-        /// Renders an <see cref="IVisual"/> into the bitmap.
-        /// </summary>
-        /// <param name="visual">The visual to render.</param>
-        /// <remarks>
-        /// Before calling this method, ensure that <paramref name="visual"/> has been measured.
-        /// </remarks>
-        void Render(IVisual visual);
     }
 }

+ 0 - 38
src/Perspex.SceneGraph/Platform/IRenderer.cs

@@ -1,38 +0,0 @@
-// Copyright (c) The Perspex Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-
-namespace Perspex.Platform
-{
-    /// <summary>
-    /// Defines a renderer.
-    /// </summary>
-    /// <remarks>
-    /// The interface used to render <see cref="IVisual"/>s. You will usually want to inherit from
-    /// <see cref="Perspex.Rendering.RendererBase"/> rather than implementing the whole interface
-    /// as RenderBase has a default implementation for the non-platform specific parts of a
-    /// renderer.
-    /// </remarks>
-    public interface IRenderer : IDisposable
-    {
-        /// <summary>
-        /// Gets the number of times <see cref="Render"/> has been called.
-        /// </summary>
-        int RenderCount { get; }
-
-        /// <summary>
-        /// Renders the specified visual.
-        /// </summary>
-        /// <param name="visual">The visual to render.</param>
-        /// <param name="handle">An optional platform-specific handle.</param>
-        void Render(IVisual visual, IPlatformHandle handle);
-
-        /// <summary>
-        /// Resizes the rendered viewport.
-        /// </summary>
-        /// <param name="width">The new width.</param>
-        /// <param name="height">The new height.</param>
-        void Resize(int width, int height);
-    }
-}

+ 2 - 2
src/Perspex.SceneGraph/Rendering/IRenderManager.cs → src/Perspex.SceneGraph/Rendering/IRenderQueueManager.cs

@@ -7,9 +7,9 @@ using System.Reactive;
 namespace Perspex.Rendering
 {
     /// <summary>
-    /// Defines the interface for a <see cref="RenderManager"/>.
+    /// Defines the interface for a <see cref="RenderQueueManager"/>.
     /// </summary>
-    public interface IRenderManager
+    public interface IRenderQueueManager
     {
         /// <summary>
         /// Gets an observable that is fired whenever a render is required.

+ 1 - 6
src/Perspex.SceneGraph/Rendering/IRenderRoot.cs

@@ -10,15 +10,10 @@ namespace Perspex.Rendering
     /// </summary>
     public interface IRenderRoot
     {
-        /// <summary>
-        /// Gets the renderer for the tree.
-        /// </summary>
-        IRenderer Renderer { get; }
-
         /// <summary>
         /// Gets the render manager which schedules renders.
         /// </summary>
-        IRenderManager RenderManager { get; }
+        IRenderQueueManager RenderQueueManager { get; }
 
         /// <summary>
         /// Translates a point to screen co-ordinates.

+ 2 - 2
src/Perspex.SceneGraph/Rendering/RenderManager.cs → src/Perspex.SceneGraph/Rendering/RenderQueueManager.cs

@@ -10,7 +10,7 @@ namespace Perspex.Rendering
     /// <summary>
     /// Schedules the rendering of a tree.
     /// </summary>
-    public class RenderManager : IRenderManager
+    public class RenderQueueManager : IRenderQueueManager
     {
         private readonly Subject<Unit> _renderNeeded = new Subject<Unit>();
 
@@ -34,8 +34,8 @@ namespace Perspex.Rendering
         {
             if (!_renderQueued)
             {
-                _renderNeeded.OnNext(Unit.Default);
                 _renderQueued = true;
+                _renderNeeded.OnNext(Unit.Default);
             }
         }
 

+ 17 - 66
src/Perspex.SceneGraph/Rendering/RendererBase.cs

@@ -12,103 +12,54 @@ namespace Perspex.Rendering
     /// Base class for standard renderers.
     /// </summary>
     /// <remarks>
-    /// This class provides implements the platform-independent parts of <see cref="IRenderer"/>.
+    /// This class provides implements the platform-independent parts of <see cref="IRenderTarget"/>.
     /// </remarks>
-    public abstract class RendererBase : IRenderer
+    public static class RendererMixin
     {
-        /// <summary>
-        /// Gets the number of times <see cref="Render(IVisual, IPlatformHandle)"/> has been called.
-        /// </summary>
-        public int RenderCount
-        {
-            get;
-            private set;
-        }
-
-        public abstract void Dispose();
-
         /// <summary>
         /// Renders the specified visual.
         /// </summary>
+        /// <param name="renderTarget">IRenderer instance</param>
         /// <param name="visual">The visual to render.</param>
-        /// <param name="handle">An optional platform-specific handle.</param>
-        public virtual void Render(IVisual visual, IPlatformHandle handle)
+        public static void Render(this IRenderTarget renderTarget, IVisual visual)
         {
-            Render(visual, handle, Matrix.Identity);
-            ++RenderCount;
+            using (var ctx = renderTarget.CreateDrawingContext())
+                ctx.Render(visual);
         }
 
-        /// <summary>
-        /// Renders the specified visual with the specified transform and clip.
-        /// </summary>
-        /// <param name="visual">The visual to render.</param>
-        /// <param name="handle">An optional platform-specific handle.</param>
-        /// <param name="transform">The transform.</param>
-        /// <param name="clip">An optional clip rectangle.</param>
-        public virtual void Render(IVisual visual, IPlatformHandle handle, Matrix transform, Rect? clip = null)
-        {
-            using (var context = CreateDrawingContext(handle))
-            using (clip.HasValue ? context.PushClip(clip.Value) : null)
-            {
-                Render(visual, context, Matrix.Identity, transform);
-            }
-        }
-
-        /// <summary>
-        /// Resizes the rendered viewport.
-        /// </summary>
-        /// <param name="width">The new width.</param>
-        /// <param name="height">The new height.</param>
-        public abstract void Resize(int width, int height);
 
-        /// <summary>
-        /// When overriden by a derived class creates an <see cref="IDrawingContext"/> for a
-        /// rendering session.
-        /// </summary>
-        /// <param name="handle">The handle to use to create the context.</param>
-        /// <returns>An <see cref="IDrawingContext"/>.</returns>
-        protected abstract IDrawingContext CreateDrawingContext(IPlatformHandle handle);
 
         /// <summary>
         /// Renders the specified visual.
         /// </summary>
         /// <param name="visual">The visual to render.</param>
+        /// 
         /// <param name="context">The drawing context.</param>
-        /// <param name="translation">The current translation.</param>
-        /// <param name="transform">The current transform.</param>
-        protected virtual void Render(IVisual visual, IDrawingContext context, Matrix translation, Matrix transform)
+        public static void Render(this IDrawingContext context, IVisual visual)
         {
             var opacity = visual.Opacity;
-
             if (visual.IsVisible && opacity > 0)
             {
-                // Translate any existing transform into this controls coordinate system.
-                Matrix offset = Matrix.CreateTranslation(visual.Bounds.Position);
-                transform = offset * transform * -offset;
+                var m = Matrix.CreateTranslation(visual.Bounds.Position);
 
-                // Update the current offset.
-                translation *= Matrix.CreateTranslation(visual.Bounds.Position);
+                var renderTransform = Matrix.Identity;
 
-                // Apply the control's render transform, if any.
                 if (visual.RenderTransform != null)
                 {
-                    offset = Matrix.CreateTranslation(visual.TransformOrigin.ToPixels(visual.Bounds.Size));
-                    transform *= -offset * visual.RenderTransform.Value * offset;
+                    var origin = visual.TransformOrigin.ToPixels(new Size(visual.Bounds.Width, visual.Bounds.Height));
+                    var offset = Matrix.CreateTranslation(origin);
+                    renderTransform = (-offset)*visual.RenderTransform.Value*(offset);
                 }
+                m = context.CurrentTransform.Invert()*renderTransform*m*context.CurrentTransform;
 
-                // Draw the control and its children.
-                var m = transform * translation;
-                var d = context.PushTransform(m);
-
+                using (context.PushTransform(m))
                 using (context.PushOpacity(opacity))
-                using (visual.ClipToBounds ? context.PushClip(visual.Bounds) : null)
+                using (visual.ClipToBounds ? context.PushClip(new Rect(visual.Bounds.Size)) : null)
                 {
                     visual.Render(context);
-                    d.Dispose();
-
                     foreach (var child in visual.VisualChildren.OrderBy(x => x.ZIndex))
                     {
-                        Render(child, context, translation, transform);
+                        context.Render(child);
                     }
                 }
             }

+ 3 - 3
src/Perspex.SceneGraph/Visual.cs

@@ -22,7 +22,7 @@ namespace Perspex
     /// </summary>
     /// <remarks>
     /// The <see cref="Visual"/> class acts as a node in the Perspex scene graph and holds
-    /// all the information needed for an <see cref="IRenderer"/> to render the control.
+    /// all the information needed for an <see cref="IRenderTarget"/> to render the control.
     /// To traverse the scene graph (aka Visual Tree), use the extension methods defined
     /// in <see cref="VisualExtensions"/>.
     /// </remarks>
@@ -222,9 +222,9 @@ namespace Perspex
                 .OfType<IRenderRoot>()
                 .FirstOrDefault();
 
-            if (root != null && root.RenderManager != null)
+            if (root != null && root.RenderQueueManager != null)
             {
-                root.RenderManager.InvalidateRender(this);
+                root.RenderQueueManager.InvalidateRender(this);
             }
         }
 

+ 26 - 26
src/Perspex.Themes.Default/TextBoxStyle.cs

@@ -60,35 +60,35 @@ namespace Perspex.Themes.Default
                 [~Border.BorderBrushProperty] = control[~TemplatedControl.BorderBrushProperty],
                 [~Border.BorderThicknessProperty] = control[~TemplatedControl.BorderThicknessProperty],
 
-                Child = new StackPanel
+                Child = new ScrollViewer
                 {
-                    Children = new Controls.Controls
+                    [~ScrollViewer.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
+                    [~ScrollViewer.HorizontalScrollBarVisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty],
+                    [~ScrollViewer.VerticalScrollBarVisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty],
+                    Content = new StackPanel
                     {
-                        new TextBlock
+                        Children = new Controls.Controls
                         {
-                            Name  = "floatingWatermark",
-                            Foreground = SolidColorBrush.Parse("#007ACC"),
-                            FontSize = 10,
-                            [~TextBlock.TextProperty] = control[~TextBox.WatermarkProperty],
-                            [~TextBlock.IsVisibleProperty] = control[~TextBox.TextProperty].Cast<string>().Select(x => (object)(!string.IsNullOrEmpty(x) && control.UseFloatingWatermark))
-                        },
-                        new Panel
-                        {
-                            Children = new Controls.Controls
+                            new TextBlock
                             {
-                                new TextBlock
-                                {
-                                    Name = "watermark",
-                                    Opacity = 0.5,
-                                    [~TextBlock.TextProperty] = control[~TextBox.WatermarkProperty],
-                                    [~TextBlock.IsVisibleProperty] = control[~TextBox.TextProperty].Cast<string>().Select(x => (object)string.IsNullOrEmpty(x))
-                                },
-                                new ScrollViewer
+                                Name  = "floatingWatermark",
+                                Foreground = SolidColorBrush.Parse("#007ACC"),
+                                FontSize = 10,
+                                [~TextBlock.TextProperty] = control[~TextBox.WatermarkProperty],
+                                [~TextBlock.IsVisibleProperty] = control[~TextBox.TextProperty].Cast<string>().Select(x => (object)(!string.IsNullOrEmpty(x) && control.UseFloatingWatermark))
+                            },
+                            new Panel
+                            {
+                                Children = new Controls.Controls
                                 {
-                                    [~ScrollViewer.CanScrollHorizontallyProperty] = control[~ScrollViewer.CanScrollHorizontallyProperty],
-                                    [~ScrollViewer.HorizontalScrollBarVisibilityProperty] = control[~ScrollViewer.HorizontalScrollBarVisibilityProperty],
-                                    [~ScrollViewer.VerticalScrollBarVisibilityProperty] = control[~ScrollViewer.VerticalScrollBarVisibilityProperty],
-                                    Content = new TextPresenter
+                                    new TextBlock
+                                    {
+                                        Name = "watermark",
+                                        Opacity = 0.5,
+                                        [~TextBlock.TextProperty] = control[~TextBox.WatermarkProperty],
+                                        [~TextBlock.IsVisibleProperty] = control[~TextBox.TextProperty].Cast<string>().Select(x => (object)string.IsNullOrEmpty(x))
+                                    },
+                                    new TextPresenter
                                     {
                                         Name = "textPresenter",
                                         [~TextPresenter.CaretIndexProperty] = control[~TextBox.CaretIndexProperty],
@@ -99,9 +99,9 @@ namespace Perspex.Themes.Default
                                     }
                                 }
                             }
-                        }                        
+                        }
                     }
-                },
+                }
             };
 
             return result;

+ 2 - 2
src/Windows/Perspex.Direct2D1/Direct2D1Platform.cs

@@ -41,11 +41,11 @@ namespace Perspex.Direct2D1
             return new FormattedTextImpl(text, fontFamily, fontSize, fontStyle, textAlignment, fontWeight);
         }
 
-        public IRenderer CreateRenderer(IPlatformHandle handle, double width, double height)
+        public IRenderTarget CreateRenderer(IPlatformHandle handle, double width, double height)
         {
             if (handle.HandleDescriptor == "HWND")
             {
-                return new Renderer(handle.Handle, width, height);
+                return new RenderTarget(handle.Handle, width, height);
             }
             else
             {

+ 3 - 3
src/Windows/Perspex.Direct2D1/Media/DrawingContext.cs

@@ -18,7 +18,7 @@ namespace Perspex.Direct2D1.Media
         /// <summary>
         /// The Direct2D1 render target.
         /// </summary>
-        private readonly RenderTarget _renderTarget;
+        private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
 
         /// <summary>
         /// The DirectWrite factory.
@@ -31,7 +31,7 @@ namespace Perspex.Direct2D1.Media
         /// <param name="renderTarget">The render target to draw to.</param>
         /// <param name="directWriteFactory">The DirectWrite factory.</param>
         public DrawingContext(
-            RenderTarget renderTarget,
+            SharpDX.Direct2D1.RenderTarget renderTarget,
             SharpDX.DirectWrite.Factory directWriteFactory)
         {
             _renderTarget = renderTarget;
@@ -199,7 +199,7 @@ namespace Perspex.Direct2D1.Media
         /// <param name="brush">The brush.</param>
         /// <param name="rect">The rectangle bounds.</param>
         /// <param name="cornerRadius">The corner radius.</param>
-        public void FillRectange(Perspex.Media.Brush brush, Rect rect, float cornerRadius)
+        public void FillRectangle(Perspex.Media.Brush brush, Rect rect, float cornerRadius)
         {
             using (var b = CreateBrush(brush, rect.Size))
             {

+ 2 - 2
src/Windows/Perspex.Direct2D1/Media/ImageBrushImpl.cs

@@ -11,7 +11,7 @@ namespace Perspex.Direct2D1.Media
     {
         public ImageBrushImpl(
             ImageBrush brush,
-            RenderTarget target,
+            SharpDX.Direct2D1.RenderTarget target,
             Size targetSize)
         {
             if (brush.Source == null)
@@ -59,7 +59,7 @@ namespace Perspex.Direct2D1.Media
 
         private BitmapBrush CreateDirectBrush(
             ImageBrush brush,
-            RenderTarget target,
+            SharpDX.Direct2D1.RenderTarget target,
             Bitmap image, 
             Rect sourceRect, 
             Rect destinationRect)

+ 6 - 3
src/Windows/Perspex.Direct2D1/Media/Imaging/RenderTargetBitmapImpl.cs

@@ -2,7 +2,9 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using Perspex.Media;
 using Perspex.Platform;
+using Perspex.Rendering;
 using SharpDX.Direct2D1;
 using SharpDX.WIC;
 
@@ -36,10 +38,11 @@ namespace Perspex.Direct2D1.Media
             // TODO:
         }
 
-        public void Render(IVisual visual)
+        public IDrawingContext CreateDrawingContext() => new RenderTarget(_target).CreateDrawingContext();
+
+        void IRenderTarget.Resize(int width, int height)
         {
-            Renderer renderer = new Renderer(_target);
-            renderer.Render(visual, null);
+            throw new NotSupportedException();
         }
     }
 }

+ 2 - 2
src/Windows/Perspex.Direct2D1/Media/PerspexTextRenderer.cs

@@ -12,13 +12,13 @@ namespace Perspex.Direct2D1.Media
     {
         private readonly DrawingContext _context;
 
-        private readonly RenderTarget _renderTarget;
+        private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
 
         private readonly Brush _foreground;
 
         public PerspexTextRenderer(
             DrawingContext context,
-            RenderTarget target,
+            SharpDX.Direct2D1.RenderTarget target,
             Brush foreground)
         {
             _context = context;

+ 10 - 5
src/Windows/Perspex.Direct2D1/Media/VisualBrushImpl.cs

@@ -4,6 +4,7 @@
 using System;
 using Perspex.Layout;
 using Perspex.Media;
+using Perspex.Rendering;
 using SharpDX.Direct2D1;
 
 namespace Perspex.Direct2D1.Media
@@ -12,7 +13,7 @@ namespace Perspex.Direct2D1.Media
     {
         public VisualBrushImpl(
             VisualBrush brush,
-            RenderTarget target,
+            SharpDX.Direct2D1.RenderTarget target,
             Size targetSize)
         {
             var visual = brush.Visual;
@@ -50,10 +51,14 @@ namespace Perspex.Direct2D1.Media
                     scale,
                     translate,
                     out drawRect);
-                var renderer = new Renderer(intermediate);
-
-                renderer.Render(visual, null, transform, drawRect);
-
+                var renderer = new RenderTarget(intermediate);
+
+                using (var ctx = renderer.CreateDrawingContext())
+                using (ctx.PushClip(drawRect))
+                using (ctx.PushTransform(transform))
+                {
+                    ctx.Render(visual);
+                }
                 this.PlatformBrush = new BitmapBrush(
                     target,
                     intermediate.Bitmap,

+ 1 - 1
src/Windows/Perspex.Direct2D1/Perspex.Direct2D1.csproj

@@ -89,7 +89,7 @@
     <Compile Include="Media\FormattedTextImpl.cs" />
     <Compile Include="PrimitiveExtensions.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Renderer.cs" />
+    <Compile Include="RenderTarget.cs" />
   </ItemGroup>
   <ItemGroup>
     <None Include="app.config" />

+ 1 - 1
src/Windows/Perspex.Direct2D1/PrimitiveExtensions.cs

@@ -69,7 +69,7 @@ namespace Perspex.Direct2D1
         /// <param name="pen">The pen to convert.</param>
         /// <param name="target">The render target.</param>
         /// <returns>The Direct2D brush.</returns>
-        public static StrokeStyle ToDirect2DStrokeStyle(this Perspex.Media.Pen pen, RenderTarget target)
+        public static StrokeStyle ToDirect2DStrokeStyle(this Perspex.Media.Pen pen, SharpDX.Direct2D1.RenderTarget target)
         {
             if (pen.DashStyle != null)
             {

+ 20 - 11
src/Windows/Perspex.Direct2D1/Renderer.cs → src/Windows/Perspex.Direct2D1/RenderTarget.cs

@@ -12,20 +12,20 @@ using DwFactory = SharpDX.DirectWrite.Factory;
 
 namespace Perspex.Direct2D1
 {
-    public class Renderer : RendererBase
+    public class RenderTarget : IRenderTarget
     {
         /// <summary>
         /// The render target.
         /// </summary>
-        private readonly RenderTarget _renderTarget;
+        private readonly SharpDX.Direct2D1.RenderTarget _renderTarget;
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="Renderer"/> class.
+        /// Initializes a new instance of the <see cref="RenderTarget"/> class.
         /// </summary>
         /// <param name="hwnd">The window handle.</param>
         /// <param name="width">The width of the window.</param>
         /// <param name="height">The height of the window.</param>
-        public Renderer(IntPtr hwnd, double width, double height)
+        public RenderTarget(IntPtr hwnd, double width, double height)
         {
             Direct2DFactory = PerspexLocator.Current.GetService<Factory>();
             DirectWriteFactory = PerspexLocator.Current.GetService<DwFactory>();
@@ -48,10 +48,10 @@ namespace Perspex.Direct2D1
         }
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="Renderer"/> class.
+        /// Initializes a new instance of the <see cref="RenderTarget"/> class.
         /// </summary>
         /// <param name="renderTarget">The render target.</param>
-        public Renderer(RenderTarget renderTarget)
+        public RenderTarget(SharpDX.Direct2D1.RenderTarget renderTarget)
         {
             Direct2DFactory = PerspexLocator.Current.GetService<Factory>();
             DirectWriteFactory = PerspexLocator.Current.GetService<DwFactory>();
@@ -77,7 +77,7 @@ namespace Perspex.Direct2D1
         /// </summary>
         /// <param name="width">The new width.</param>
         /// <param name="height">The new height.</param>
-        public override void Resize(int width, int height)
+        public void Resize(int width, int height)
         {
             WindowRenderTarget window = _renderTarget as WindowRenderTarget;
 
@@ -91,17 +91,26 @@ namespace Perspex.Direct2D1
             window.Resize(new Size2(width, height));
         }
 
+        IDrawingContext Wrap(IDrawingContext ctx)
+        {
+#if DEBUG
+            return new ValidatingDrawingContext(ctx);
+#endif
+#pragma warning disable 162
+            return ctx;
+#pragma warning restore 162
+        }
+
         /// <summary>
         /// Creates a drawing context for a rendering session.
         /// </summary>
-        /// <param name="handle">The platform handle. Unused.</param>
         /// <returns>An <see cref="IDrawingContext"/>.</returns>
-        protected override IDrawingContext CreateDrawingContext(IPlatformHandle handle)
+        public IDrawingContext CreateDrawingContext()
         {
-            return new DrawingContext(_renderTarget, DirectWriteFactory);
+            return Wrap(new DrawingContext(_renderTarget, DirectWriteFactory));
         }
 
-        public override void Dispose()
+        public void Dispose()
         {
             _renderTarget.Dispose();
         }

+ 2 - 0
src/Windows/Perspex.Win32/SystemDialogImpl.cs

@@ -41,6 +41,8 @@ namespace Perspex.Win32
 
                 var defExt = (dialog as SaveFileDialog)?.DefaultExtension;
                 var buffer = new char[256];
+                dialog.InitialFileName?.CopyTo(0, buffer, 0, dialog.InitialFileName.Length);
+
                 fixed (char* pBuffer = buffer)
                 fixed (char* pFilterBuffer = filterBuffer)
                 fixed (char* pDefExt = defExt)

+ 2 - 2
src/Windows/Perspex.Win32/WindowImpl.cs

@@ -51,7 +51,7 @@ namespace Perspex.Win32
 
         public Action<RawInputEventArgs> Input { get; set; }
 
-        public Action<Rect, IPlatformHandle> Paint { get; set; }
+        public Action<Rect> Paint { get; set; }
 
         public Action<Size> Resized { get; set; }
 
@@ -405,7 +405,7 @@ namespace Perspex.Win32
                         {
                             UnmanagedMethods.RECT r;
                             UnmanagedMethods.GetUpdateRect(_hwnd, out r, false);
-                            Paint(new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top), Handle);
+                            Paint(new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top));
                             UnmanagedMethods.EndPaint(_hwnd, ref ps);
                         }
                     }

+ 2 - 2
tests/Perspex.Controls.UnitTests/ControlTests.cs

@@ -76,12 +76,12 @@ namespace Perspex.Controls.UnitTests
                 get { throw new NotImplementedException(); }
             }
 
-            public IRenderer Renderer
+            public IRenderTarget RenderTarget
             {
                 get { throw new NotImplementedException(); }
             }
 
-            public IRenderManager RenderManager
+            public IRenderQueueManager RenderQueueManager
             {
                 get { throw new NotImplementedException(); }
             }

+ 1 - 0
tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj

@@ -85,6 +85,7 @@
     <Compile Include="ContentPresenterTests.cs" />
     <Compile Include="BorderTests.cs" />
     <Compile Include="Mixins\SelectableMixinTests.cs" />
+    <Compile Include="StackPanelTests.cs" />
     <Compile Include="Primitives\TrackTests.cs" />
     <Compile Include="Primitives\PopupTests.cs" />
     <Compile Include="DropDownTests.cs" />

+ 150 - 0
tests/Perspex.Controls.UnitTests/StackPanelTests.cs

@@ -0,0 +1,150 @@
+// Copyright (c) The Perspex Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Perspex.Controls;
+using Xunit;
+
+namespace Perspex.Controls.UnitTests
+{
+    public class StackPanelTests
+    {
+        [Fact]
+        public void Lays_Out_Children_Vertically()
+        {
+            var target = new StackPanel
+            {
+                Children = new Controls
+                {
+                    new Border { Height = 20, Width = 120 },
+                    new Border { Height = 30 },
+                    new Border { Height = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(120, 100), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds);
+            Assert.Equal(new Rect(0, 20, 120, 30), target.Children[1].Bounds);
+            Assert.Equal(new Rect(0, 50, 120, 50), target.Children[2].Bounds);
+        }
+
+        [Fact]
+        public void Lays_Out_Children_Horizontally()
+        {
+            var target = new StackPanel
+            {
+                Orientation = Orientation.Horizontal,
+                Children = new Controls
+                {
+                    new Border { Width = 20, Height = 120 },
+                    new Border { Width = 30 },
+                    new Border { Width = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(100, 120), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds);
+            Assert.Equal(new Rect(20, 0, 30, 120), target.Children[1].Bounds);
+            Assert.Equal(new Rect(50, 0, 50, 120), target.Children[2].Bounds);
+        }
+
+        [Fact]
+        public void Lays_Out_Children_Vertically_With_Gap()
+        {
+            var target = new StackPanel
+            {
+                Gap = 10,
+                Children = new Controls
+                {
+                    new Border { Height = 20, Width = 120 },
+                    new Border { Height = 30 },
+                    new Border { Height = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(120, 130), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds);
+            Assert.Equal(new Rect(0, 30, 120, 30), target.Children[1].Bounds);
+            Assert.Equal(new Rect(0, 70, 120, 50), target.Children[2].Bounds);
+        }
+
+        [Fact]
+        public void Lays_Out_Children_Horizontally_With_Gap()
+        {
+            var target = new StackPanel
+            {
+                Gap = 10,
+                Orientation = Orientation.Horizontal,
+                Children = new Controls
+                {
+                    new Border { Width = 20, Height = 120 },
+                    new Border { Width = 30 },
+                    new Border { Width = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(130, 120), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds);
+            Assert.Equal(new Rect(30, 0, 30, 120), target.Children[1].Bounds);
+            Assert.Equal(new Rect(70, 0, 50, 120), target.Children[2].Bounds);
+        }
+
+        [Fact]
+        public void Lays_Out_Children_Vertically_Even_If_Larger_Than_Panel()
+        {
+            var target = new StackPanel
+            {
+                Height = 60,
+                Children = new Controls
+                {
+                    new Border { Height = 20, Width = 120 },
+                    new Border { Height = 30 },
+                    new Border { Height = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(120, 60), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 120, 20), target.Children[0].Bounds);
+            Assert.Equal(new Rect(0, 20, 120, 30), target.Children[1].Bounds);
+            Assert.Equal(new Rect(0, 50, 120, 50), target.Children[2].Bounds);
+        }
+
+        [Fact]
+        public void Lays_Out_Children_Horizontally_Even_If_Larger_Than_Panel()
+        {
+            var target = new StackPanel
+            {
+                Width = 60,
+                Orientation = Orientation.Horizontal,
+                Children = new Controls
+                {
+                    new Border { Width = 20, Height = 120 },
+                    new Border { Width = 30 },
+                    new Border { Width = 50 },
+                }
+            };
+
+            target.Measure(Size.Infinity);
+            target.Arrange(new Rect(target.DesiredSize));
+
+            Assert.Equal(new Size(60, 120), target.Bounds.Size);
+            Assert.Equal(new Rect(0, 0, 20, 120), target.Children[0].Bounds);
+            Assert.Equal(new Rect(20, 0, 30, 120), target.Children[1].Bounds);
+            Assert.Equal(new Rect(50, 0, 50, 120), target.Children[2].Bounds);
+        }
+    }
+}

+ 2 - 2
tests/Perspex.Controls.UnitTests/TestRoot.cs

@@ -15,12 +15,12 @@ namespace Perspex.Controls.UnitTests
 
         public ILayoutManager LayoutManager => new Mock<ILayoutManager>().Object;
 
-        public IRenderer Renderer
+        public IRenderTarget RenderTarget
         {
             get { throw new NotImplementedException(); }
         }
 
-        public IRenderManager RenderManager
+        public IRenderQueueManager RenderQueueManager
         {
             get { throw new NotImplementedException(); }
         }

+ 3 - 3
tests/Perspex.Controls.UnitTests/TopLevelTests.cs

@@ -180,7 +180,7 @@ namespace Perspex.Controls.UnitTests
                 var target = new TestTopLevel(impl.Object);
                 completed.OnNext(Unit.Default);
 
-                var renderManagerMock = Mock.Get(PerspexLocator.Current.GetService<IRenderManager>());
+                var renderManagerMock = Mock.Get(PerspexLocator.Current.GetService<IRenderQueueManager>());
                 renderManagerMock.Verify(x => x.InvalidateRender(target));
             }
         }
@@ -313,7 +313,7 @@ namespace Perspex.Controls.UnitTests
             var globalStyles = new Mock<IGlobalStyles>();
             var layoutManager = fixture.Create<ILayoutManager>();
             var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            var renderManager = fixture.Create<IRenderManager>();
+            var renderManager = fixture.Create<IRenderQueueManager>();
             var windowImpl = new Mock<IWindowImpl>();
             var theme = new Styles();
 
@@ -327,7 +327,7 @@ namespace Perspex.Controls.UnitTests
                 .Bind<ILayoutManager>().ToConstant(layoutManager)
                 .Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
                 .Bind<IPlatformThreadingInterface>().ToConstant(new Mock<IPlatformThreadingInterface>().Object)
-                .Bind<IRenderManager>().ToConstant(renderManager)
+                .Bind<IRenderQueueManager>().ToConstant(renderManager)
                 .Bind<IStyler>().ToConstant(new Styler());
         }
 

+ 2 - 2
tests/Perspex.Layout.UnitTests/FullLayoutTests.cs

@@ -134,7 +134,7 @@ namespace Perspex.Layout.UnitTests
             var formattedText = fixture.Create<IFormattedTextImpl>();
             var globalStyles = new Mock<IGlobalStyles>();
             var renderInterface = fixture.Create<IPlatformRenderInterface>();
-            var renderManager = fixture.Create<IRenderManager>();
+            var renderManager = fixture.Create<IRenderQueueManager>();
             var theme = new DefaultTheme();
             var windowImpl = new Mock<IWindowImpl>();
 
@@ -148,7 +148,7 @@ namespace Perspex.Layout.UnitTests
                 .Bind<ILayoutManager>().ToConstant(new LayoutManager())
                 .Bind<IPlatformRenderInterface>().ToConstant(renderInterface)
                 .Bind<IPlatformThreadingInterface>().ToConstant(new Mock<IPlatformThreadingInterface>().Object)
-                .Bind<IRenderManager>().ToConstant(renderManager)
+                .Bind<IRenderQueueManager>().ToConstant(renderManager)
                 .Bind<IStyler>().ToConstant(new Styler())
                 .Bind<IWindowImpl>().ToConstant(windowImpl.Object);
         }

BIN
tests/Perspex.RenderTests/Shapes/RectangleTests.cs


BIN
tests/Perspex.RenderTests/TestBase.cs


+ 2 - 2
tests/Perspex.SceneGraph.UnitTests/TestRoot.cs

@@ -9,12 +9,12 @@ namespace Perspex.SceneGraph.UnitTests
 {
     public class TestRoot : TestVisual, IRenderRoot
     {
-        public IRenderer Renderer
+        public IRenderTarget RenderTarget
         {
             get { throw new NotImplementedException(); }
         }
 
-        public IRenderManager RenderManager
+        public IRenderQueueManager RenderQueueManager
         {
             get { throw new NotImplementedException(); }
         }

BIN
tests/TestFiles/Cairo/Shapes/Rectangle/Rectangle_Stroke_Fill_ClipToBounds.expected.png


BIN
tests/TestFiles/Direct2D1/Shapes/Rectangle/Rectangle_Stroke_Fill_ClipToBounds.expected.png