Browse Source

Merge branch 'master' into leaks

Steven Kirk 10 years ago
parent
commit
cd721f1ae8
30 changed files with 533 additions and 84 deletions
  1. 16 0
      src/Android/Perspex.Android/Platform/SkiaPlatform/WindowImpl.cs
  2. 20 12
      src/Gtk/Perspex.Gtk/CursorFactory.cs
  3. 0 5
      src/Gtk/Perspex.Gtk/PopupImpl.cs
  4. 33 0
      src/Gtk/Perspex.Gtk/WindowImpl.cs
  5. 2 0
      src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs
  6. 1 0
      src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs
  7. 36 0
      src/Markup/Perspex.Markup.Xaml/Converters/CursorTypeConverter.cs
  8. 1 1
      src/Markup/Perspex.Markup.Xaml/OmniXAML
  9. 5 0
      src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj
  10. 4 0
      src/Markup/Perspex.Markup/Data/ExpressionObserver.cs
  11. 1 0
      src/Perspex.Controls/Perspex.Controls.csproj
  12. 0 9
      src/Perspex.Controls/Platform/IPopupImpl.cs
  13. 19 0
      src/Perspex.Controls/Platform/ITopLevelImpl.cs
  14. 2 2
      src/Perspex.Controls/Platform/IWindowImpl.cs
  15. 9 5
      src/Perspex.Controls/Platform/PlatformManager.cs
  16. 1 1
      src/Perspex.Controls/Primitives/Popup.cs
  17. 0 9
      src/Perspex.Controls/Primitives/PopupRoot.cs
  18. 1 1
      src/Perspex.Controls/ToolTip.cs
  19. 17 0
      src/Perspex.Controls/TopLevel.cs
  20. 18 0
      src/Perspex.Controls/Window.cs
  21. 17 0
      src/Perspex.Controls/WindowEdge.cs
  22. 9 1
      src/Perspex.Input/Cursors.cs
  23. 36 3
      src/Perspex.SceneGraph/Media/PathMarkupParser.cs
  24. 24 16
      src/Windows/Perspex.Win32/CursorFactory.cs
  25. 31 0
      src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs
  26. 0 12
      src/Windows/Perspex.Win32/PopupImpl.cs
  27. 89 0
      src/Windows/Perspex.Win32/WindowImpl.cs
  28. 20 5
      src/iOS/Perspex.iOS/PerspexView.cs
  29. 46 0
      tests/Perspex.Interactivity.UnitTests/InteractiveTests.cs
  30. 75 2
      tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs

+ 16 - 0
src/Android/Perspex.Android/Platform/SkiaPlatform/WindowImpl.cs

@@ -9,6 +9,7 @@ using Perspex.Input.Raw;
 using Perspex.Platform;
 using Perspex.Skia.Android;
 using System;
+using Perspex.Controls;
 
 namespace Perspex.Android.Platform.SkiaPlatform
 {
@@ -94,6 +95,9 @@ namespace Perspex.Android.Platform.SkiaPlatform
             this.Visibility = ViewStates.Invisible;
         }
 
+        public void SetSystemDecorations(bool enabled)
+        {
+        }
         public void Invalidate(Rect rect)
         {
             if (Holder?.Surface?.IsValid == true) base.Invalidate();
@@ -123,6 +127,18 @@ namespace Perspex.Android.Platform.SkiaPlatform
             this.Visibility = ViewStates.Visible;
         }
 
+        public void BeginMoveDrag()
+        {
+            //Not supported
+        }
+
+        public void BeginResizeDrag(WindowEdge edge)
+        {
+            //Not supported
+        }
+
+        public Point Position { get; set; }
+
         public IDisposable ShowDialog()
         {
             throw new NotImplementedException();

+ 20 - 12
src/Gtk/Perspex.Gtk/CursorFactory.cs

@@ -21,20 +21,28 @@ namespace Perspex.Gtk
         private static readonly Dictionary<StandardCursorType, object> CursorTypeMapping = new Dictionary
             <StandardCursorType, object>
         {
-            { StandardCursorType.AppStarting, CursorType.Watch },
-            { StandardCursorType.Arrow, CursorType.LeftPtr },
-            { StandardCursorType.Cross, CursorType.Cross },
-            { StandardCursorType.Hand, CursorType.Hand1 },
-            { StandardCursorType.Ibeam, CursorType.Xterm },
-            { StandardCursorType.No, Gtk.Stock.Cancel},
-            { StandardCursorType.SizeAll, CursorType.Sizing },
+            {StandardCursorType.AppStarting, CursorType.Watch},
+            {StandardCursorType.Arrow, CursorType.LeftPtr},
+            {StandardCursorType.Cross, CursorType.Cross},
+            {StandardCursorType.Hand, CursorType.Hand1},
+            {StandardCursorType.Ibeam, CursorType.Xterm},
+            {StandardCursorType.No, Gtk.Stock.Cancel},
+            {StandardCursorType.SizeAll, CursorType.Sizing},
             //{ StandardCursorType.SizeNorthEastSouthWest, 32643 },
-            { StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
+            {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
             //{ StandardCursorType.SizeNorthWestSouthEast, 32642 },
-            { StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow },
-            { StandardCursorType.UpArrow, CursorType.BasedArrowUp },
-            { StandardCursorType.Wait, CursorType.Watch },
-            { StandardCursorType.Help, Gtk.Stock.Help }
+            {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow},
+            {StandardCursorType.UpArrow, CursorType.BasedArrowUp},
+            {StandardCursorType.Wait, CursorType.Watch},
+            {StandardCursorType.Help, Gtk.Stock.Help},
+            {StandardCursorType.TopSide, CursorType.TopSide},
+            {StandardCursorType.BottomSize, CursorType.BottomSide},
+            {StandardCursorType.LeftSide, CursorType.LeftSide},
+            {StandardCursorType.RightSide, CursorType.RightSide},
+            {StandardCursorType.TopLeftCorner, CursorType.TopLeftCorner},
+            {StandardCursorType.TopRightCorner, CursorType.TopRightCorner},
+            {StandardCursorType.BottomLeftCorner, CursorType.BottomLeftCorner},
+            {StandardCursorType.BottomRightCorner, CursorType.BottomRightCorner}
         };
 
         private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =

+ 0 - 5
src/Gtk/Perspex.Gtk/PopupImpl.cs

@@ -12,10 +12,5 @@ namespace Perspex.Gtk
             : base(WindowType.Popup)
         {
         }
-
-        public void SetPosition(Point p)
-        {
-            Move((int)p.X, (int)p.Y);
-        }
     }
 }

+ 33 - 0
src/Gtk/Perspex.Gtk/WindowImpl.cs

@@ -11,6 +11,7 @@ using Perspex.Platform;
 using Perspex.Input;
 using Perspex.Threading;
 using Action = System.Action;
+using WindowEdge = Perspex.Controls.WindowEdge;
 
 namespace Perspex.Gtk
 {
@@ -162,6 +163,36 @@ namespace Perspex.Gtk
             GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor;
         }
 
+        public void BeginMoveDrag()
+        {
+            int x, y;
+            ModifierType mod;
+            Screen.RootWindow.GetPointer(out x, out y, out mod);
+            BeginMoveDrag(1, x, y, 0);
+        }
+
+        public void BeginResizeDrag(WindowEdge edge)
+        {
+            int x, y;
+            ModifierType mod;
+            Screen.RootWindow.GetPointer(out x, out y, out mod);
+            BeginResizeDrag((Gdk.WindowEdge) (int) edge, 1, x, y, 0);
+        }
+
+        public Point Position
+        {
+            get
+            {
+                int x, y;
+                GetPosition(out x, out y);
+                return new Point(x, y);
+            }
+            set
+            {
+                Move((int)value.X, (int)value.Y);
+            }
+        }
+
         public IDisposable ShowDialog()
         {
             Modal = true;
@@ -170,6 +201,8 @@ namespace Perspex.Gtk
             return Disposable.Empty;
         }
 
+        public void SetSystemDecorations(bool enabled) => Decorated = enabled;
+
         void ITopLevelImpl.Activate()
         {
             Activate();

+ 2 - 0
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

@@ -29,6 +29,8 @@ namespace Perspex.Markup.Xaml.Context
 
         public object Result => _objectAssembler.Result;
 
+        public InstanceLifeCycleHandler InstanceLifeCycleHandler { get; set; } = new InstanceLifeCycleHandler();
+
         public EventHandler<XamlSetValueEventArgs> XamlSetValueHandler { get; set; }
 
         public IWiringContext WiringContext => _objectAssembler.WiringContext;

+ 1 - 0
src/Markup/Perspex.Markup.Xaml/Context/PerspexWiringContext.cs

@@ -107,6 +107,7 @@ namespace Perspex.Markup.Xaml.Context
                 new TypeConverterRegistration(typeof(Thickness), new ThicknessTypeConverter()),
                 new TypeConverterRegistration(typeof(TimeSpan), new TimeSpanTypeConverter()),
                 new TypeConverterRegistration(typeof(Uri), new UriTypeConverter()),
+                new TypeConverterRegistration(typeof(Cursor), new CursorTypeConverter())
             };
 
             typeConverterProvider.AddAll(converters);

+ 36 - 0
src/Markup/Perspex.Markup.Xaml/Converters/CursorTypeConverter.cs

@@ -0,0 +1,36 @@
+// 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.Globalization;
+using OmniXaml.TypeConversion;
+using Perspex.Input;
+using Perspex.Media.Imaging;
+using Perspex.Platform;
+
+namespace Perspex.Markup.Xaml.Converters
+{
+    public class CursorTypeConverter : ITypeConverter
+    {
+        public bool CanConvertFrom(IXamlTypeConverterContext context, Type sourceType)
+        {
+            return sourceType == typeof(string);
+        }
+
+        public bool CanConvertTo(IXamlTypeConverterContext context, Type destinationType)
+        {
+            return false;
+        }
+
+        public object ConvertFrom(IXamlTypeConverterContext context, CultureInfo culture, object value)
+        {
+            var cursor = (StandardCursorType)Enum.Parse(typeof (StandardCursorType), ((string) value).Trim(), true);
+            return new Cursor(cursor);
+        }
+
+        public object ConvertTo(IXamlTypeConverterContext context, CultureInfo culture, object value, Type destinationType)
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 1 - 1
src/Markup/Perspex.Markup.Xaml/OmniXAML

@@ -1 +1 @@
-Subproject commit f2673838c0422ff0f6fdb3e4b34a5302971f59b5
+Subproject commit 0904dcb07175ca0cdf2ae1fda1434c0f1425a53e

+ 5 - 0
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

@@ -39,6 +39,7 @@
       <Link>Properties\SharedAssemblyInfo.cs</Link>
     </Compile>
     <Compile Include="Context\NameScopeWrapper.cs" />
+    <Compile Include="Converters\CursorTypeConverter.cs" />
     <Compile Include="Converters\PerspexListTypeConverter.cs" />
     <Compile Include="Converters\RelativeRectTypeConverter.cs" />
     <Compile Include="Data\IXamlBinding.cs" />
@@ -108,6 +109,7 @@
     <Compile Include="OmniXAML\Source\OmniXaml\IDeferredLoader.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IMarkupExtension.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\INameScope.cs" />
+    <Compile Include="OmniXAML\Source\OmniXaml\InstanceLifeCycleHandler.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\InstructionNode.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\InstructionTreeBuilder.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IObjectAssembler.cs" />
@@ -118,6 +120,7 @@
     <Compile Include="OmniXAML\Source\OmniXaml\ITypeProvider.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IValueConverter.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IWiringContext.cs" />
+    <Compile Include="OmniXAML\Source\OmniXaml\IXamlLoader.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IXamlParser.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IXamlParserFactory.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\IXamlXmlLoader.cs" />
@@ -129,6 +132,7 @@
     <Compile Include="OmniXAML\Source\OmniXaml\MemberReverserVisitor.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\NamespaceDeclaration.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\NamespacePrefix.cs" />
+    <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssemblerMixin.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Command.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\EndMemberCommand.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\EndObjectCommand.cs" />
@@ -140,6 +144,7 @@
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Commands\ValueCommand.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\ConstructionArgument.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\CurrentLevelWrapper.cs" />
+    <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\InstanceLifeCycleNotifier.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\InstanceProperties.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\Level.cs" />
     <Compile Include="OmniXAML\Source\OmniXaml\ObjectAssembler\NullLevel.cs" />

+ 4 - 0
src/Markup/Perspex.Markup/Data/ExpressionObserver.cs

@@ -201,6 +201,10 @@ namespace Perspex.Markup.Data
                     subscription.Dispose();
                 });
             }
+            else if (_rootObservable != null)
+            {
+                return _rootObservable.Subscribe(observer);
+            }
             else
             {
                 if (_update == null)

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

@@ -176,6 +176,7 @@
     <Compile Include="TreeView.cs" />
     <Compile Include="TreeViewItem.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="WindowEdge.cs" />
     <Compile Include="WrapPanel.cs" />
   </ItemGroup>
   <ItemGroup>

+ 0 - 9
src/Perspex.Controls/Platform/IPopupImpl.cs

@@ -8,15 +8,6 @@ namespace Perspex.Platform
     /// </summary>
     public interface IPopupImpl : ITopLevelImpl
     {
-        /// <summary>
-        /// Sets the position of the popup.
-        /// </summary>
-        /// <param name="p">The position, in screen coordinates.</param>
-        void SetPosition(Point p);
 
-        /// <summary>
-        /// Hides the popup.
-        /// </summary>
-        void Hide();
     }
 }

+ 19 - 0
src/Perspex.Controls/Platform/ITopLevelImpl.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
+using Perspex.Controls;
 using Perspex.Input;
 using Perspex.Input.Raw;
 
@@ -88,5 +89,23 @@ namespace Perspex.Platform
         /// Shows the toplevel.
         /// </summary>
         void Show();
+        
+        /// <summary>
+        /// Hides the window.
+        /// </summary>
+        void Hide();
+
+        /// <summary>
+        /// Starts moving a window with left button being held. Should be called from left mouse button press event handler.
+        /// </summary>
+        void BeginMoveDrag();
+
+        /// <summary>
+        /// Starts resizing a window. This function is used if an application has window resizing controls. 
+        /// Should be called from left mouse button press event handler
+        /// </summary>
+        void BeginResizeDrag(WindowEdge edge);
+
+        Point Position { get; set; }
     }
 }

+ 2 - 2
src/Perspex.Controls/Platform/IWindowImpl.cs

@@ -30,8 +30,8 @@ namespace Perspex.Platform
         IDisposable ShowDialog();
 
         /// <summary>
-        /// Hides the window.
+        /// Enables of disables system window decorations (title bar, buttons, etc)
         /// </summary>
-        void Hide();
+        void SetSystemDecorations(bool enabled);
     }
 }

+ 9 - 5
src/Perspex.Controls/Platform/PlatformManager.cs

@@ -155,11 +155,6 @@ namespace Perspex.Controls.Platform
                 set { _tl.Deactivated = value; }
             }
 
-            public void SetPosition(Point p)
-            {
-                _popup.SetPosition(p*ScalingFactor);
-            }
-            
             public void Dispose() => _tl.Dispose();
 
             public IPlatformHandle Handle => _tl.Handle;
@@ -174,10 +169,19 @@ namespace Perspex.Controls.Platform
             public void SetTitle(string title) => _window.SetTitle(title);
 
             public void Show() => _tl.Show();
+            public void BeginMoveDrag() => _tl.BeginMoveDrag();
+            public void BeginResizeDrag(WindowEdge edge) => _tl.BeginResizeDrag(edge);
+
+            public Point Position
+            {
+                get { return _tl.Position; }
+                set { _tl.Position = value; }
+            }
 
             public IDisposable ShowDialog() => _window.ShowDialog();
 
             public void Hide() => _popup.Hide();
+            public void SetSystemDecorations(bool enabled) => _window.SetSystemDecorations(enabled);
         }
 
         public static IWindowImpl CreateWindow()

+ 1 - 1
src/Perspex.Controls/Primitives/Popup.cs

@@ -169,7 +169,7 @@ namespace Perspex.Controls.Primitives
                 ((ISetLogicalParent)_popupRoot).SetParent(this);
             }
 
-            _popupRoot.SetPosition(GetPosition());
+            _popupRoot.Position = GetPosition();
             _popupRoot.AddHandler(PointerPressedEvent, MaybeClose, RoutingStrategies.Bubble, true);
 
             if (_topLevel != null)

+ 0 - 9
src/Perspex.Controls/Primitives/PopupRoot.cs

@@ -64,15 +64,6 @@ namespace Perspex.Controls.Primitives
         /// </summary>
         IVisual IHostedVisualTreeRoot.Host => Parent;
 
-        /// <summary>
-        /// Sets the position of the popup in screen coordinates.
-        /// </summary>
-        /// <param name="p">The position.</param>
-        public void SetPosition(Point p)
-        {
-            PlatformImpl.SetPosition(p);
-        }
-
         /// <summary>
         /// Hides the popup.
         /// </summary>

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

@@ -118,7 +118,7 @@ namespace Perspex.Controls
                 var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22);
 
                 ((ToolTip)s_popup.Content).Content = GetTip(control);
-                s_popup.SetPosition(position);
+                s_popup.Position = position;
                 s_popup.Show();
 
                 s_current = control;

+ 17 - 0
src/Perspex.Controls/TopLevel.cs

@@ -369,5 +369,22 @@ namespace Perspex.Controls
         {
             _renderQueueManager?.InvalidateRender(this);
         }
+
+        /// <summary>
+        /// Starts moving a window with left button being held. Should be called from left mouse button press event handler
+        /// </summary>
+        public void BeginMoveDrag() => PlatformImpl.BeginMoveDrag();
+
+        /// <summary>
+        /// Starts resizing a window. This function is used if an application has window resizing controls. 
+        /// Should be called from left mouse button press event handler
+        /// </summary>
+        public void BeginResizeDrag(WindowEdge edge) => PlatformImpl.BeginResizeDrag(edge);
+
+        public Point Position
+        {
+            get { return PlatformImpl.Position; }
+            set { PlatformImpl.Position = value; }
+        }
     }
 }

+ 18 - 0
src/Perspex.Controls/Window.cs

@@ -49,6 +49,12 @@ namespace Perspex.Controls
         public static readonly PerspexProperty<SizeToContent> SizeToContentProperty =
             PerspexProperty.Register<Window, SizeToContent>(nameof(SizeToContent));
 
+        /// <summary>
+        /// Enables of disables system window decorations (title bar, buttons, etc)
+        /// </summary>
+        public static readonly PerspexProperty<bool> HasSystemDecorationsProperty =
+            PerspexProperty.Register<Window, bool>(nameof(HasSystemDecorations), true);
+
         /// <summary>
         /// Defines the <see cref="Title"/> property.
         /// </summary>
@@ -66,6 +72,8 @@ namespace Perspex.Controls
         {
             BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
             TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue));
+            HasSystemDecorationsProperty.Changed.AddClassHandler<Window>(
+                (s, e) => s.PlatformImpl.SetSystemDecorations((bool) e.NewValue));
         }
 
         /// <summary>
@@ -114,6 +122,16 @@ namespace Perspex.Controls
             set { SetValue(TitleProperty, value); }
         }
 
+        /// <summary>
+        /// Enables of disables system window decorations (title bar, buttons, etc)
+        /// </summary>
+        /// 
+        public bool HasSystemDecorations
+        {
+            get { return GetValue(HasSystemDecorationsProperty); }
+            set { SetValue(HasSystemDecorationsProperty, value); }
+        }
+
         /// <inheritdoc/>
         Type IStyleable.StyleKey => typeof(Window);
 

+ 17 - 0
src/Perspex.Controls/WindowEdge.cs

@@ -0,0 +1,17 @@
+namespace Perspex.Controls
+{
+    
+    public enum WindowEdge
+    {
+        //Please don't reorder stuff here, I was lazy to write proper conversion code
+        //so the order of values is matching one from GTK
+        NorthWest = 0,
+        North,
+        NorthEast,
+        West,
+        East,
+        SouthWest,
+        South,
+        SouthEast,
+    }
+}

+ 9 - 1
src/Perspex.Input/Cursors.cs

@@ -30,7 +30,15 @@ namespace Perspex.Input
         No,
         Hand,
         AppStarting,
-        Help
+        Help,
+        TopSide,
+        BottomSize,
+        LeftSide,
+        RightSide,
+        TopLeftCorner,
+        TopRightCorner,
+        BottomLeftCorner,
+        BottomRightCorner
 
         // Not available in GTK directly, see http://www.pixelbeat.org/programming/x_cursors/ 
         // We might enable them later, preferably, by loading pixmax direclty from theme with fallback image

+ 36 - 3
src/Perspex.SceneGraph/Media/PathMarkupParser.cs

@@ -28,6 +28,8 @@ namespace Perspex.Media
             { 'v', Command.VerticalLineRelative },
             { 'C', Command.CubicBezierCurve },
             { 'c', Command.CubicBezierCurveRelative },
+            { 'A', Command.Arc },
+            { 'a', Command.Arc },
             { 'Z', Command.Close },
             { 'z', Command.Close },
         };
@@ -64,6 +66,7 @@ namespace Perspex.Media
             VerticalLineRelative,
             CubicBezierCurve,
             CubicBezierCurveRelative,
+            Arc,
             Close,
             Eof,
         }
@@ -98,8 +101,8 @@ namespace Perspex.Media
                                 _context.EndFigure(false);
                             }
 
-                            point = command == Command.Move ? 
-                                ReadPoint(reader) : 
+                            point = command == Command.Move ?
+                                ReadPoint(reader) :
                                 ReadRelativePoint(reader, point);
 
                             _context.BeginFigure(point, true);
@@ -144,6 +147,22 @@ namespace Perspex.Media
                                 _context.CubicBezierTo(point1, point2, point);
                                 break;
                             }
+                        case Command.Arc:
+                            {
+                                //example: A10,10 0 0,0 10,20
+                                //format - size rotationAngle isLargeArcFlag sweepDirectionFlag endPoint
+                                Size size = ReadSize(reader);
+                                ReadSeparator(reader);
+                                double rotationAngle = ReadDouble(reader);
+                                ReadSeparator(reader);
+                                bool isLargeArc = ReadBool(reader);
+                                ReadSeparator(reader);
+                                SweepDirection sweepDirection = ReadBool(reader) ? SweepDirection.Clockwise : SweepDirection.CounterClockwise;
+                                point = ReadPoint(reader);
+
+                                _context.ArcTo(point, size, rotationAngle, isLargeArc, sweepDirection);
+                                break;
+                            }
 
                         case Command.Close:
                             _context.EndFigure(true);
@@ -244,6 +263,20 @@ namespace Perspex.Media
             return new Point(x, y);
         }
 
+        private static Size ReadSize(StringReader reader)
+        {
+            ReadWhitespace(reader);
+            double x = ReadDouble(reader);
+            ReadSeparator(reader);
+            double y = ReadDouble(reader);
+            return new Size(x, y);
+        }
+
+        private static bool ReadBool(StringReader reader)
+        {
+            return ReadDouble(reader) != 0;
+        }
+
         private static Point ReadRelativePoint(StringReader reader, Point lastPoint)
         {
             ReadWhitespace(reader);
@@ -302,4 +335,4 @@ namespace Perspex.Media
             }
         }
     }
-}
+}

+ 24 - 16
src/Windows/Perspex.Win32/CursorFactory.cs

@@ -23,22 +23,30 @@ namespace Perspex.Win32
         private static readonly Dictionary<StandardCursorType, int> CursorTypeMapping = new Dictionary
             <StandardCursorType, int>
         {
-            { StandardCursorType.AppStarting, 32650 },
-            { StandardCursorType.Arrow, 32512 },
-            { StandardCursorType.Cross, 32515 },
-            { StandardCursorType.Hand, 32649 },
-            { StandardCursorType.Help, 32651 },
-            { StandardCursorType.Ibeam, 32513 },
-            { StandardCursorType.No, 32648 },
-            { StandardCursorType.SizeAll, 32646 },
-
-            // { StandardCursorType.SizeNorthEastSouthWest, 32643 },
-            { StandardCursorType.SizeNorthSouth, 32645 },
-
-            // { StandardCursorType.SizeNorthWestSouthEast, 32642 },
-            { StandardCursorType.SizeWestEast, 32644 },
-            { StandardCursorType.UpArrow, 32516 },
-            { StandardCursorType.Wait, 32514 }
+            {StandardCursorType.AppStarting, 32650},
+            {StandardCursorType.Arrow, 32512},
+            {StandardCursorType.Cross, 32515},
+            {StandardCursorType.Hand, 32649},
+            {StandardCursorType.Help, 32651},
+            {StandardCursorType.Ibeam, 32513},
+            {StandardCursorType.No, 32648},
+            {StandardCursorType.SizeAll, 32646},
+            {StandardCursorType.UpArrow, 32516},
+            {StandardCursorType.SizeNorthSouth, 32645},
+            {StandardCursorType.SizeWestEast, 32644},
+            {StandardCursorType.Wait, 32514},
+            //Same as SizeNorthSouth
+            {StandardCursorType.TopSide, 32645},
+            {StandardCursorType.BottomSize, 32645},
+            //Same as SizeWestEast
+            {StandardCursorType.LeftSide, 32644},
+            {StandardCursorType.RightSide, 32644},
+            //Using SizeNorthWestSouthEast
+            {StandardCursorType.TopLeftCorner, 32642},
+            {StandardCursorType.BottomRightCorner, 32642},
+            //Using SizeNorthEastSouthWest
+            {StandardCursorType.TopRightCorner, 32643},
+            {StandardCursorType.BottomLeftCorner, 32643},
         };
 
         private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =

+ 31 - 0
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@@ -217,6 +217,34 @@ namespace Perspex.Win32.Interop
             WA_CLICKACTIVE,
         }
 
+        public enum HitTestValues
+        {
+            HTERROR = -2,
+            HTTRANSPARENT = -1,
+            HTNOWHERE = 0,
+            HTCLIENT = 1,
+            HTCAPTION = 2,
+            HTSYSMENU = 3,
+            HTGROWBOX = 4,
+            HTMENU = 5,
+            HTHSCROLL = 6,
+            HTVSCROLL = 7,
+            HTMINBUTTON = 8,
+            HTMAXBUTTON = 9,
+            HTLEFT = 10,
+            HTRIGHT = 11,
+            HTTOP = 12,
+            HTTOPLEFT = 13,
+            HTTOPRIGHT = 14,
+            HTBOTTOM = 15,
+            HTBOTTOMLEFT = 16,
+            HTBOTTOMRIGHT = 17,
+            HTBORDER = 18,
+            HTOBJECT = 19,
+            HTCLOSE = 20,
+            HTHELP = 21
+        }
+
         [Flags]
         public enum WindowStyles : uint
         {
@@ -571,6 +599,9 @@ namespace Perspex.Win32.Interop
         [DllImport("user32.dll", SetLastError = true)]
         public static extern uint GetWindowLong(IntPtr hWnd, int nIndex);
 
+        [DllImport("user32.dll", SetLastError = true)]
+        public static extern uint SetWindowLong(IntPtr hWnd, int nIndex, uint value);
+
         [DllImport("user32.dll")]
         public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
 

+ 0 - 12
src/Windows/Perspex.Win32/PopupImpl.cs

@@ -9,18 +9,6 @@ namespace Perspex.Win32
 {
     public class PopupImpl : WindowImpl, IPopupImpl
     {
-        public void SetPosition(Point p)
-        {
-            UnmanagedMethods.SetWindowPos(
-                Handle.Handle,
-                IntPtr.Zero,
-                (int)p.X,
-                (int)p.Y,
-                0,
-                0,
-                UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
-        }
-
         public override void Show()
         {
             UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);

+ 89 - 0
src/Windows/Perspex.Win32/WindowImpl.cs

@@ -37,6 +37,8 @@ namespace Perspex.Win32
 
         private bool _isActive;
 
+        private bool _decorated = true;
+
         public WindowImpl()
         {
             CreateWindow();
@@ -145,6 +147,47 @@ namespace Perspex.Win32
             UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Hide);
         }
 
+        public void SetSystemDecorations(bool value)
+        {
+            if (value == _decorated)
+                return;
+            var style = (UnmanagedMethods.WindowStyles) UnmanagedMethods.GetWindowLong(_hwnd, -16);
+            style |= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
+            if (!value)
+                style ^= UnmanagedMethods.WindowStyles.WS_OVERLAPPEDWINDOW;
+
+            UnmanagedMethods.RECT windowRect;
+
+            UnmanagedMethods.GetWindowRect(_hwnd, out windowRect);
+            Rect newRect;
+            var oldThickness = BorderThickness;
+
+            UnmanagedMethods.SetWindowLong(_hwnd, -16, (uint) style);
+            if (value)
+            {
+                var thickness = BorderThickness;
+                newRect = new Rect(
+                    windowRect.left - thickness.Left,
+                    windowRect.top - thickness.Top,
+                    (windowRect.right - windowRect.left) + (thickness.Left + thickness.Right),
+                    (windowRect.bottom - windowRect.top) + (thickness.Top + thickness.Bottom));
+            }
+            else
+                newRect = new Rect(
+                    windowRect.left + oldThickness.Left,
+                    windowRect.top + oldThickness.Top,
+                    (windowRect.right - windowRect.left) - (oldThickness.Left + oldThickness.Right),
+                    (windowRect.bottom - windowRect.top) - (oldThickness.Top + oldThickness.Bottom));
+            UnmanagedMethods.SetWindowPos(_hwnd, IntPtr.Zero, (int) newRect.X, (int) newRect.Y, (int) newRect.Width,
+                (int) newRect.Height,
+                UnmanagedMethods.SetWindowPosFlags.SWP_NOZORDER | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
+
+
+            _decorated = value;
+
+
+        }
+
         public void Invalidate(Rect rect)
         {
             var r = new UnmanagedMethods.RECT
@@ -180,6 +223,52 @@ namespace Perspex.Win32
             UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Normal);
         }
 
+        public void BeginMoveDrag()
+        {
+            UnmanagedMethods.DefWindowProc(_hwnd, (int) UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
+                new IntPtr((int)UnmanagedMethods.HitTestValues.HTCAPTION), IntPtr.Zero);
+        }
+
+        static readonly Dictionary<WindowEdge, UnmanagedMethods.HitTestValues> EdgeDic = new Dictionary<WindowEdge, UnmanagedMethods.HitTestValues>
+        {
+            {WindowEdge.East, UnmanagedMethods.HitTestValues.HTRIGHT},
+            {WindowEdge.North, UnmanagedMethods.HitTestValues.HTTOP },
+            {WindowEdge.NorthEast, UnmanagedMethods.HitTestValues.HTTOPRIGHT },
+            {WindowEdge.NorthWest, UnmanagedMethods.HitTestValues.HTTOPLEFT },
+            {WindowEdge.South, UnmanagedMethods.HitTestValues.HTBOTTOM },
+            {WindowEdge.SouthEast, UnmanagedMethods.HitTestValues.HTBOTTOMRIGHT },
+            {WindowEdge.SouthWest, UnmanagedMethods.HitTestValues.HTBOTTOMLEFT },
+            {WindowEdge.West, UnmanagedMethods.HitTestValues.HTLEFT}
+        };
+
+        public void BeginResizeDrag(WindowEdge edge)
+        {
+            UnmanagedMethods.DefWindowProc(_hwnd, (int) UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
+                new IntPtr((int) EdgeDic[edge]), IntPtr.Zero);
+        }
+
+        public Point Position
+        {
+            get
+            {
+                UnmanagedMethods.RECT rc;
+                UnmanagedMethods.GetWindowRect(_hwnd, out rc);
+                return new Point(rc.left, rc.top);
+            }
+            set
+            {
+                UnmanagedMethods.SetWindowPos(
+                    Handle.Handle,
+                    IntPtr.Zero,
+                    (int) value.X,
+                    (int) value.Y,
+                    0,
+                    0,
+                    UnmanagedMethods.SetWindowPosFlags.SWP_NOSIZE | UnmanagedMethods.SetWindowPosFlags.SWP_NOACTIVATE);
+
+            }
+        }
+
         public virtual IDisposable ShowDialog()
         {
             var disabled = s_instances.Where(x => x != this && x.IsEnabled).ToList();

+ 20 - 5
src/iOS/Perspex.iOS/PerspexView.cs

@@ -16,6 +16,7 @@ using Perspex.Skia.iOS;
 using UIKit;
 using Perspex.iOS.Specific;
 using ObjCRuntime;
+using Perspex.Controls;
 
 namespace Perspex.iOS
 {
@@ -107,6 +108,18 @@ namespace Perspex.iOS
             _keyboardHelper.ActivateAutoShowKeybord();
         }
 
+        public void BeginMoveDrag()
+        {
+            //Not supported
+        }
+
+        public void BeginResizeDrag(WindowEdge edge)
+        {
+            //Not supported
+        }
+
+        public Point Position { get; set; }
+
         public Size MaxClientSize => Bounds.Size.ToPerspex();
         public void SetTitle(string title)
         {
@@ -124,6 +137,11 @@ namespace Perspex.iOS
             //Not supported
         }
 
+        public void SetSystemDecorations(bool enabled)
+        {
+            //Not supported
+        }
+
         public override void TouchesEnded(NSSet touches, UIEvent evt)
         {
             var touch = touches.AnyObject as UITouch;
@@ -168,14 +186,11 @@ namespace Perspex.iOS
                         RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
                 else
                 {
-                    double x = location.X - _touchLastPoint.X;
-                    double y = location.Y - _touchLastPoint.Y;
+                    //magic number based on test - correction of 0.02 is working perfect
                     double correction = 0.02;
-                    var scale = PerspexLocator.Current.GetService<IPlatformSettings>().RenderScalingFactor;
-                    scale = 1;
 
                     Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint)touch.Timestamp,
-                        _inputRoot, location, new Vector(x * correction / scale, y * correction / scale), InputModifiers.LeftMouseButton));
+                        _inputRoot, location, (location - _touchLastPoint)* correction, InputModifiers.LeftMouseButton));
                 }
                 _touchLastPoint = location;
             }

+ 46 - 0
tests/Perspex.Interactivity.UnitTests/InteractiveTests.cs

@@ -117,6 +117,52 @@ namespace Perspex.Interactivity.UnitTests
             invoked);
         }
 
+        [Fact]
+        public void Handled_Bubbled_Event_Should_Not_Propogate_Further()
+        {
+            var ev = new RoutedEvent("test", RoutingStrategies.Bubble, typeof(RoutedEventArgs), typeof(TestInteractive));
+            var invoked = new List<string>();
+
+            EventHandler<RoutedEventArgs> handler = (s, e) =>
+            {
+                var t = (TestInteractive)s;
+                invoked.Add(t.Name);
+                e.Handled = t.Name == "2b";
+            };
+
+            var target = CreateTree(ev, handler, RoutingStrategies.Bubble);
+
+            var args = new RoutedEventArgs(ev, target);
+            target.RaiseEvent(args);
+
+            Assert.Equal(new[] { "2b" }, invoked);
+        }
+
+        [Fact]
+        public void Handled_Tunnelled_Event_Should_Not_Propogate_Further()
+        {
+            var ev = new RoutedEvent(
+                "test",
+                RoutingStrategies.Bubble | RoutingStrategies.Tunnel, 
+                typeof(RoutedEventArgs), 
+                typeof(TestInteractive));
+            var invoked = new List<string>();
+
+            EventHandler<RoutedEventArgs> handler = (s, e) =>
+            {
+                var t = (TestInteractive)s;
+                invoked.Add(t.Name);
+                e.Handled = t.Name == "2b";
+            };
+
+            var target = CreateTree(ev, handler, RoutingStrategies.Bubble | RoutingStrategies.Tunnel);
+
+            var args = new RoutedEventArgs(ev, target);
+            target.RaiseEvent(args);
+
+            Assert.Equal(new[] { "1", "2b" }, invoked);
+        }
+
         [Fact]
         public void Direct_Subscription_Should_Not_Catch_Tunneling_Or_Bubbling()
         {

+ 75 - 2
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs

@@ -10,7 +10,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
     public class BindingTests_ElementName
     {
         [Fact]
-        public void Should_Bind_To_Element()
+        public void Should_Bind_To_Element_Path()
         {
             TextBlock target;
             var root = new TestRoot
@@ -44,7 +44,42 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
         }
 
         [Fact]
-        public void Should_Bind_To_Later_Added_Element()
+        public void Should_Bind_To_Element()
+        {
+            TextBlock source;
+            ContentControl target;
+
+            var root = new TestRoot
+            {
+                Child = new StackPanel
+                {
+                    Children = new Controls.Controls
+                    {
+                        (source = new TextBlock
+                        {
+                            Name = "source",
+                            Text = "foo",
+                        }),
+                        (target = new ContentControl
+                        {
+                            Name = "target",
+                        })
+                    }
+                }
+            };
+
+            var binding = new Binding
+            {
+                ElementName = "source",
+            };
+
+            binding.Bind(target, ContentControl.ContentProperty);
+
+            Assert.Same(source, target.Content);
+        }
+
+        [Fact]
+        public void Should_Bind_To_Later_Added_Element_Path()
         {
             TextBlock target;
             StackPanel stackPanel;
@@ -79,5 +114,43 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
 
             Assert.Equal("foo", target.Text);
         }
+
+        [Fact]
+        public void Should_Bind_To_Later_Added_Element()
+        {
+            ContentControl target;
+            StackPanel stackPanel;
+
+            var root = new TestRoot
+            {
+                Child = stackPanel = new StackPanel
+                {
+                    Children = new Controls.Controls
+                    {
+                        (target = new ContentControl
+                        {
+                            Name = "target",
+                        }),
+                    }
+                }
+            };
+
+            var binding = new Binding
+            {
+                ElementName = "source",
+            };
+
+            binding.Bind(target, ContentControl.ContentProperty);
+
+            var source = new TextBlock
+            {
+                Name = "source",
+                Text = "foo",
+            };
+
+            stackPanel.Children.Add(source);
+
+            Assert.Same(source, target.Content);
+        }
     }
 }