Browse Source

Merge branch 'master' into leaks

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

16
src/Android/Perspex.Android/Platform/SkiaPlatform/WindowImpl.cs

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

32
src/Gtk/Perspex.Gtk/CursorFactory.cs

@ -21,20 +21,28 @@ namespace Perspex.Gtk
private static readonly Dictionary<StandardCursorType, object> CursorTypeMapping = new Dictionary private static readonly Dictionary<StandardCursorType, object> CursorTypeMapping = new Dictionary
<StandardCursorType, object> <StandardCursorType, object>
{ {
{ StandardCursorType.AppStarting, CursorType.Watch }, {StandardCursorType.AppStarting, CursorType.Watch},
{ StandardCursorType.Arrow, CursorType.LeftPtr }, {StandardCursorType.Arrow, CursorType.LeftPtr},
{ StandardCursorType.Cross, CursorType.Cross }, {StandardCursorType.Cross, CursorType.Cross},
{ StandardCursorType.Hand, CursorType.Hand1 }, {StandardCursorType.Hand, CursorType.Hand1},
{ StandardCursorType.Ibeam, CursorType.Xterm }, {StandardCursorType.Ibeam, CursorType.Xterm},
{ StandardCursorType.No, Gtk.Stock.Cancel}, {StandardCursorType.No, Gtk.Stock.Cancel},
{ StandardCursorType.SizeAll, CursorType.Sizing }, {StandardCursorType.SizeAll, CursorType.Sizing},
//{ StandardCursorType.SizeNorthEastSouthWest, 32643 }, //{ StandardCursorType.SizeNorthEastSouthWest, 32643 },
{ StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow}, {StandardCursorType.SizeNorthSouth, CursorType.SbVDoubleArrow},
//{ StandardCursorType.SizeNorthWestSouthEast, 32642 }, //{ StandardCursorType.SizeNorthWestSouthEast, 32642 },
{ StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow }, {StandardCursorType.SizeWestEast, CursorType.SbHDoubleArrow},
{ StandardCursorType.UpArrow, CursorType.BasedArrowUp }, {StandardCursorType.UpArrow, CursorType.BasedArrowUp},
{ StandardCursorType.Wait, CursorType.Watch }, {StandardCursorType.Wait, CursorType.Watch},
{ StandardCursorType.Help, Gtk.Stock.Help } {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 = private static readonly Dictionary<StandardCursorType, IPlatformHandle> Cache =

5
src/Gtk/Perspex.Gtk/PopupImpl.cs

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

33
src/Gtk/Perspex.Gtk/WindowImpl.cs

@ -11,6 +11,7 @@ using Perspex.Platform;
using Perspex.Input; using Perspex.Input;
using Perspex.Threading; using Perspex.Threading;
using Action = System.Action; using Action = System.Action;
using WindowEdge = Perspex.Controls.WindowEdge;
namespace Perspex.Gtk namespace Perspex.Gtk
{ {
@ -162,6 +163,36 @@ namespace Perspex.Gtk
GdkWindow.Cursor = cursor != null ? new Gdk.Cursor(cursor.Handle) : DefaultCursor; 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() public IDisposable ShowDialog()
{ {
Modal = true; Modal = true;
@ -170,6 +201,8 @@ namespace Perspex.Gtk
return Disposable.Empty; return Disposable.Empty;
} }
public void SetSystemDecorations(bool enabled) => Decorated = enabled;
void ITopLevelImpl.Activate() void ITopLevelImpl.Activate()
{ {
Activate(); Activate();

2
src/Markup/Perspex.Markup.Xaml/Context/PerspexObjectAssembler.cs

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

1
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(Thickness), new ThicknessTypeConverter()),
new TypeConverterRegistration(typeof(TimeSpan), new TimeSpanTypeConverter()), new TypeConverterRegistration(typeof(TimeSpan), new TimeSpanTypeConverter()),
new TypeConverterRegistration(typeof(Uri), new UriTypeConverter()), new TypeConverterRegistration(typeof(Uri), new UriTypeConverter()),
new TypeConverterRegistration(typeof(Cursor), new CursorTypeConverter())
}; };
typeConverterProvider.AddAll(converters); typeConverterProvider.AddAll(converters);

36
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();
}
}
}

2
src/Markup/Perspex.Markup.Xaml/OmniXAML

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

5
src/Markup/Perspex.Markup.Xaml/Perspex.Markup.Xaml.csproj

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

4
src/Markup/Perspex.Markup/Data/ExpressionObserver.cs

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

1
src/Perspex.Controls/Perspex.Controls.csproj

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

9
src/Perspex.Controls/Platform/IPopupImpl.cs

@ -8,15 +8,6 @@ namespace Perspex.Platform
/// </summary> /// </summary>
public interface IPopupImpl : ITopLevelImpl 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
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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using Perspex.Controls;
using Perspex.Input; using Perspex.Input;
using Perspex.Input.Raw; using Perspex.Input.Raw;
@ -88,5 +89,23 @@ namespace Perspex.Platform
/// Shows the toplevel. /// Shows the toplevel.
/// </summary> /// </summary>
void Show(); 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; }
} }
} }

4
src/Perspex.Controls/Platform/IWindowImpl.cs

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

14
src/Perspex.Controls/Platform/PlatformManager.cs

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

2
src/Perspex.Controls/Primitives/Popup.cs

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

9
src/Perspex.Controls/Primitives/PopupRoot.cs

@ -64,15 +64,6 @@ namespace Perspex.Controls.Primitives
/// </summary> /// </summary>
IVisual IHostedVisualTreeRoot.Host => Parent; 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> /// <summary>
/// Hides the popup. /// Hides the popup.
/// </summary> /// </summary>

2
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); var position = control.PointToScreen(cp ?? new Point(0, 0)) + new Vector(0, 22);
((ToolTip)s_popup.Content).Content = GetTip(control); ((ToolTip)s_popup.Content).Content = GetTip(control);
s_popup.SetPosition(position); s_popup.Position = position;
s_popup.Show(); s_popup.Show();
s_current = control; s_current = control;

17
src/Perspex.Controls/TopLevel.cs

@ -369,5 +369,22 @@ namespace Perspex.Controls
{ {
_renderQueueManager?.InvalidateRender(this); _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
src/Perspex.Controls/Window.cs

@ -49,6 +49,12 @@ namespace Perspex.Controls
public static readonly PerspexProperty<SizeToContent> SizeToContentProperty = public static readonly PerspexProperty<SizeToContent> SizeToContentProperty =
PerspexProperty.Register<Window, SizeToContent>(nameof(SizeToContent)); 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> /// <summary>
/// Defines the <see cref="Title"/> property. /// Defines the <see cref="Title"/> property.
/// </summary> /// </summary>
@ -66,6 +72,8 @@ namespace Perspex.Controls
{ {
BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White);
TitleProperty.Changed.AddClassHandler<Window>((s, e) => s.PlatformImpl.SetTitle((string)e.NewValue)); 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> /// <summary>
@ -114,6 +122,16 @@ namespace Perspex.Controls
set { SetValue(TitleProperty, value); } 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/> /// <inheritdoc/>
Type IStyleable.StyleKey => typeof(Window); Type IStyleable.StyleKey => typeof(Window);

17
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,
}
}

10
src/Perspex.Input/Cursors.cs

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

39
src/Perspex.SceneGraph/Media/PathMarkupParser.cs

@ -28,6 +28,8 @@ namespace Perspex.Media
{ 'v', Command.VerticalLineRelative }, { 'v', Command.VerticalLineRelative },
{ 'C', Command.CubicBezierCurve }, { 'C', Command.CubicBezierCurve },
{ 'c', Command.CubicBezierCurveRelative }, { 'c', Command.CubicBezierCurveRelative },
{ 'A', Command.Arc },
{ 'a', Command.Arc },
{ 'Z', Command.Close }, { 'Z', Command.Close },
{ 'z', Command.Close }, { 'z', Command.Close },
}; };
@ -64,6 +66,7 @@ namespace Perspex.Media
VerticalLineRelative, VerticalLineRelative,
CubicBezierCurve, CubicBezierCurve,
CubicBezierCurveRelative, CubicBezierCurveRelative,
Arc,
Close, Close,
Eof, Eof,
} }
@ -98,8 +101,8 @@ namespace Perspex.Media
_context.EndFigure(false); _context.EndFigure(false);
} }
point = command == Command.Move ? point = command == Command.Move ?
ReadPoint(reader) : ReadPoint(reader) :
ReadRelativePoint(reader, point); ReadRelativePoint(reader, point);
_context.BeginFigure(point, true); _context.BeginFigure(point, true);
@ -144,6 +147,22 @@ namespace Perspex.Media
_context.CubicBezierTo(point1, point2, point); _context.CubicBezierTo(point1, point2, point);
break; 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: case Command.Close:
_context.EndFigure(true); _context.EndFigure(true);
@ -244,6 +263,20 @@ namespace Perspex.Media
return new Point(x, y); 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) private static Point ReadRelativePoint(StringReader reader, Point lastPoint)
{ {
ReadWhitespace(reader); ReadWhitespace(reader);
@ -302,4 +335,4 @@ namespace Perspex.Media
} }
} }
} }
} }

40
src/Windows/Perspex.Win32/CursorFactory.cs

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

31
src/Windows/Perspex.Win32/Interop/UnmanagedMethods.cs

@ -217,6 +217,34 @@ namespace Perspex.Win32.Interop
WA_CLICKACTIVE, 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] [Flags]
public enum WindowStyles : uint public enum WindowStyles : uint
{ {
@ -571,6 +599,9 @@ namespace Perspex.Win32.Interop
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
public static extern uint GetWindowLong(IntPtr hWnd, int nIndex); 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")] [DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect); public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

12
src/Windows/Perspex.Win32/PopupImpl.cs

@ -9,18 +9,6 @@ namespace Perspex.Win32
{ {
public class PopupImpl : WindowImpl, IPopupImpl 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() public override void Show()
{ {
UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate); UnmanagedMethods.ShowWindow(Handle.Handle, UnmanagedMethods.ShowWindowCommand.ShowNoActivate);

89
src/Windows/Perspex.Win32/WindowImpl.cs

@ -37,6 +37,8 @@ namespace Perspex.Win32
private bool _isActive; private bool _isActive;
private bool _decorated = true;
public WindowImpl() public WindowImpl()
{ {
CreateWindow(); CreateWindow();
@ -145,6 +147,47 @@ namespace Perspex.Win32
UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Hide); 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) public void Invalidate(Rect rect)
{ {
var r = new UnmanagedMethods.RECT var r = new UnmanagedMethods.RECT
@ -180,6 +223,52 @@ namespace Perspex.Win32
UnmanagedMethods.ShowWindow(_hwnd, UnmanagedMethods.ShowWindowCommand.Normal); 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() public virtual IDisposable ShowDialog()
{ {
var disabled = s_instances.Where(x => x != this && x.IsEnabled).ToList(); var disabled = s_instances.Where(x => x != this && x.IsEnabled).ToList();

25
src/iOS/Perspex.iOS/PerspexView.cs

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

46
tests/Perspex.Interactivity.UnitTests/InteractiveTests.cs

@ -117,6 +117,52 @@ namespace Perspex.Interactivity.UnitTests
invoked); 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] [Fact]
public void Direct_Subscription_Should_Not_Catch_Tunneling_Or_Bubbling() public void Direct_Subscription_Should_Not_Catch_Tunneling_Or_Bubbling()
{ {

77
tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_ElementName.cs

@ -10,7 +10,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
public class BindingTests_ElementName public class BindingTests_ElementName
{ {
[Fact] [Fact]
public void Should_Bind_To_Element() public void Should_Bind_To_Element_Path()
{ {
TextBlock target; TextBlock target;
var root = new TestRoot var root = new TestRoot
@ -44,7 +44,42 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
} }
[Fact] [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; TextBlock target;
StackPanel stackPanel; StackPanel stackPanel;
@ -79,5 +114,43 @@ namespace Perspex.Markup.Xaml.UnitTests.Data
Assert.Equal("foo", target.Text); 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);
}
} }
} }

Loading…
Cancel
Save