Browse Source

Merge branch 'master' into BuildScriptCleanup

pull/1663/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
b7e62055b6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs
  2. 6
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  3. 4
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  4. 15
      src/Avalonia.Base/Data/Core/ISettableNode.cs
  5. 6
      src/Avalonia.Base/Data/Core/IndexerNode.cs
  6. 17
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  7. 38
      src/Avalonia.Base/Data/Core/SettableNode.cs
  8. 5
      src/Avalonia.Controls/Platform/IWindowBaseImpl.cs
  9. 16
      src/Avalonia.Controls/Primitives/Popup.cs
  10. 37
      src/Avalonia.Controls/Primitives/ScrollBar.cs
  11. 53
      src/Avalonia.Controls/Primitives/ScrollEventType.cs
  12. 3
      src/Avalonia.Controls/ToolTip.cs
  13. 14
      src/Avalonia.Controls/WindowBase.cs
  14. 4
      src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
  15. 4
      src/Avalonia.DesignerSupport/Remote/Stubs.cs
  16. 2
      src/Avalonia.Visuals/Media/DrawingContext.cs
  17. 20
      src/Avalonia.Visuals/Rendering/IRendererFactory.cs
  18. 6
      src/Gtk/Avalonia.Gtk3/Interop/Native.cs
  19. 2
      src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
  20. 2
      src/Gtk/Avalonia.Gtk3/WindowImpl.cs
  21. 1
      src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
  22. 31
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  23. 8
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  24. 28
      src/Windows/Avalonia.Win32/WindowImpl.cs
  25. 49
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  26. 59
      tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

5
src/Android/Avalonia.Android/Platform/SkiaPlatform/PopupImpl.cs

@ -110,7 +110,10 @@ namespace Avalonia.Android.Platform.SkiaPlatform
{
//Not supported
}
public void SetTopmost(bool value)
{
//Not supported
}
}
}

6
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -11,6 +11,7 @@ namespace Avalonia.Data.Core
{
internal abstract class ExpressionNode : ISubject<object>
{
private static readonly object CacheInvalid = new object();
protected static readonly WeakReference UnsetReference =
new WeakReference(AvaloniaProperty.UnsetValue);
@ -18,6 +19,8 @@ namespace Avalonia.Data.Core
private IDisposable _valueSubscription;
private IObserver<object> _observer;
protected WeakReference LastValue { get; private set; }
public abstract string Description { get; }
public ExpressionNode Next { get; set; }
@ -61,6 +64,7 @@ namespace Avalonia.Data.Core
{
_valueSubscription?.Dispose();
_valueSubscription = null;
LastValue = null;
nextSubscription?.Dispose();
_observer = null;
});
@ -120,6 +124,7 @@ namespace Avalonia.Data.Core
if (notification == null)
{
LastValue = new WeakReference(value);
if (Next != null)
{
Next.Target = new WeakReference(value);
@ -131,6 +136,7 @@ namespace Avalonia.Data.Core
}
else
{
LastValue = new WeakReference(notification.Value);
if (Next != null)
{
Next.Target = new WeakReference(notification.Value);

4
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -154,7 +154,7 @@ namespace Avalonia.Data.Core
/// </returns>
public bool SetValue(object value, BindingPriority priority = BindingPriority.LocalValue)
{
if (Leaf is ISettableNode settable)
if (Leaf is SettableNode settable)
{
var node = _node;
while (node != null)
@ -188,7 +188,7 @@ namespace Avalonia.Data.Core
/// Gets the type of the expression result or null if the expression could not be
/// evaluated.
/// </summary>
public Type ResultType => (Leaf as ISettableNode)?.PropertyType;
public Type ResultType => (Leaf as SettableNode)?.PropertyType;
/// <summary>
/// Gets the leaf node.

15
src/Avalonia.Base/Data/Core/ISettableNode.cs

@ -1,15 +0,0 @@
using Avalonia.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Data.Core
{
interface ISettableNode
{
bool SetTargetValue(object value, BindingPriority priority);
Type PropertyType { get; }
}
}

6
src/Avalonia.Base/Data/Core/IndexerNode.cs

@ -15,7 +15,7 @@ using Avalonia.Data;
namespace Avalonia.Data.Core
{
internal class IndexerNode : ExpressionNode, ISettableNode
internal class IndexerNode : SettableNode
{
public IndexerNode(IList<string> arguments)
{
@ -52,7 +52,7 @@ namespace Avalonia.Data.Core
return Observable.Merge(inputs).StartWith(GetValue(target));
}
public bool SetTargetValue(object value, BindingPriority priority)
protected override bool SetTargetValueCore(object value, BindingPriority priority)
{
var typeInfo = Target.Target.GetType().GetTypeInfo();
var list = Target.Target as IList;
@ -154,7 +154,7 @@ namespace Avalonia.Data.Core
public IList<string> Arguments { get; }
public Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType;
public override Type PropertyType => GetIndexer(Target.Target.GetType().GetTypeInfo())?.PropertyType;
private object GetValue(object target)
{

17
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -10,7 +10,7 @@ using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core
{
internal class PropertyAccessorNode : ExpressionNode, ISettableNode
internal class PropertyAccessorNode : SettableNode
{
private readonly bool _enableValidation;
private IPropertyAccessor _accessor;
@ -23,13 +23,17 @@ namespace Avalonia.Data.Core
public override string Description => PropertyName;
public string PropertyName { get; }
public Type PropertyType => _accessor?.PropertyType;
public override Type PropertyType => _accessor?.PropertyType;
public bool SetTargetValue(object value, BindingPriority priority)
protected override bool SetTargetValueCore(object value, BindingPriority priority)
{
if (_accessor != null)
{
try { return _accessor.SetValue(value, priority); } catch { }
try
{
return _accessor.SetValue(value, priority);
}
catch { }
}
return false;
@ -56,7 +60,10 @@ namespace Avalonia.Data.Core
() =>
{
_accessor = accessor;
return Disposable.Create(() => _accessor = null);
return Disposable.Create(() =>
{
_accessor = null;
});
},
_ => accessor);
}

38
src/Avalonia.Base/Data/Core/SettableNode.cs

@ -0,0 +1,38 @@
using Avalonia.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Avalonia.Data.Core
{
internal abstract class SettableNode : ExpressionNode
{
public bool SetTargetValue(object value, BindingPriority priority)
{
if (ShouldNotSet(value))
{
return true;
}
return SetTargetValueCore(value, priority);
}
private bool ShouldNotSet(object value)
{
if (PropertyType == null)
{
return false;
}
if (PropertyType.IsValueType)
{
return LastValue?.Target != null && LastValue.Target.Equals(value);
}
return LastValue != null && Object.ReferenceEquals(LastValue?.Target, value);
}
protected abstract bool SetTargetValueCore(object value, BindingPriority priority);
public abstract Type PropertyType { get; }
}
}

5
src/Avalonia.Controls/Platform/IWindowBaseImpl.cs

@ -72,6 +72,11 @@ namespace Avalonia.Platform
///
void SetMinMaxSize(Size minSize, Size maxSize);
/// <summary>
/// Sets whether this window appears on top of all other windows
/// </summary>
void SetTopmost(bool value);
/// <summary>
/// Gets platform specific display information
/// </summary>

16
src/Avalonia.Controls/Primitives/Popup.cs

@ -70,6 +70,12 @@ namespace Avalonia.Controls.Primitives
public static readonly StyledProperty<bool> StaysOpenProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(StaysOpen), true);
/// <summary>
/// Defines the <see cref="Topmost"/> property.
/// </summary>
public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
private bool _isOpen;
private PopupRoot _popupRoot;
private TopLevel _topLevel;
@ -84,6 +90,7 @@ namespace Avalonia.Controls.Primitives
IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false);
ChildProperty.Changed.AddClassHandler<Popup>(x => x.ChildChanged);
IsOpenProperty.Changed.AddClassHandler<Popup>(x => x.IsOpenChanged);
TopmostProperty.Changed.AddClassHandler<Popup>((p, e) => p.PopupRoot.Topmost = (bool)e.NewValue);
}
/// <summary>
@ -194,6 +201,15 @@ namespace Avalonia.Controls.Primitives
set { SetValue(StaysOpenProperty, value); }
}
/// <summary>
/// Gets or sets whether this popup appears on top of all other windows
/// </summary>
public bool Topmost
{
get { return GetValue(TopmostProperty); }
set { SetValue(TopmostProperty, value); }
}
/// <summary>
/// Gets the root of the popup window.
/// </summary>

37
src/Avalonia.Controls/Primitives/ScrollBar.cs

@ -6,9 +6,21 @@ using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Input;
namespace Avalonia.Controls.Primitives
{
public class ScrollEventArgs : EventArgs
{
public ScrollEventArgs(ScrollEventType eventType, double newValue)
{
ScrollEventType = eventType;
NewValue = newValue;
}
public double NewValue { get; private set; }
public ScrollEventType ScrollEventType { get; private set; }
}
/// <summary>
/// A scrollbar control.
/// </summary>
@ -44,6 +56,9 @@ namespace Avalonia.Controls.Primitives
{
PseudoClass(OrientationProperty, o => o == Orientation.Vertical, ":vertical");
PseudoClass(OrientationProperty, o => o == Orientation.Horizontal, ":horizontal");
Thumb.DragDeltaEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragDelta, RoutingStrategies.Bubble);
Thumb.DragCompletedEvent.AddClassHandler<ScrollBar>(o => o.OnThumbDragComplete, RoutingStrategies.Bubble);
}
/// <summary>
@ -88,6 +103,8 @@ namespace Avalonia.Controls.Primitives
set { SetValue(OrientationProperty, value); }
}
public event EventHandler<ScrollEventArgs> Scroll;
/// <summary>
/// Calculates whether the scrollbar should be visible.
/// </summary>
@ -140,6 +157,8 @@ namespace Avalonia.Controls.Primitives
_pageUpButton = e.NameScope.Find<Button>("PART_PageUpButton");
_pageDownButton = e.NameScope.Find<Button>("PART_PageDownButton");
if (_lineUpButton != null)
{
_lineUpButton.Click += LineUpClick;
@ -184,21 +203,39 @@ namespace Avalonia.Controls.Primitives
private void SmallDecrement()
{
Value = Math.Max(Value - SmallChange * ViewportSize, Minimum);
OnScroll(ScrollEventType.SmallDecrement);
}
private void SmallIncrement()
{
Value = Math.Min(Value + SmallChange * ViewportSize, Maximum);
OnScroll(ScrollEventType.SmallIncrement);
}
private void LargeDecrement()
{
Value = Math.Max(Value - LargeChange * ViewportSize, Minimum);
OnScroll(ScrollEventType.LargeDecrement);
}
private void LargeIncrement()
{
Value = Math.Min(Value + LargeChange * ViewportSize, Maximum);
OnScroll(ScrollEventType.LargeIncrement);
}
private void OnThumbDragDelta(VectorEventArgs e)
{
OnScroll(ScrollEventType.ThumbTrack);
}
private void OnThumbDragComplete(VectorEventArgs e)
{
OnScroll(ScrollEventType.EndScroll);
}
protected void OnScroll(ScrollEventType scrollEventType)
{
Scroll?.Invoke(this, new ScrollEventArgs(scrollEventType, Value));
}
}
}

53
src/Avalonia.Controls/Primitives/ScrollEventType.cs

@ -0,0 +1,53 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Specifies the type of Avalonia.Controls.Primitives.ScrollBar.Scroll event
/// that occurred.
/// </summary>
public enum ScrollEventType
{
/// <summary>
/// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
/// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.SmallChange.
/// The Avalonia.Controls.Primitives.Thumb moved to the left for a horizontal
/// Avalonia.Controls.Primitives.ScrollBar or upward for a vertical Avalonia.Controls.Primitives.ScrollBar.
/// </summary>
SmallDecrement = 0,
/// <summary>
/// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
/// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.SmallChange.
/// The Avalonia.Controls.Primitives.Thumb moved to the right for a horizontal
/// Avalonia.Controls.Primitives.ScrollBar or downward for a vertical Avalonia.Controls.Primitives.ScrollBar.
/// </summary>
SmallIncrement = 1,
/// <summary>
/// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
/// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.LargeChange.
/// The Avalonia.Controls.Primitives.Thumb moved to the left for a horizontal
/// Avalonia.Controls.Primitives.ScrollBar or upward for a vertical Avalonia.Controls.Primitives.ScrollBar.
/// </summary>
LargeDecrement = 2,
/// <summary>
/// Specifies that the Avalonia.Controls.Primitives.Thumb moved a specified
/// distance, as determined by the value of Avalonia.Controls.Primitives.RangeBase.LargeChange.
/// The Avalonia.Controls.Primitives.Thumb moved to the right for a horizontal
/// Avalonia.Controls.Primitives.ScrollBar or downward for a vertical Avalonia.Controls.Primitives.ScrollBar.
/// </summary>
LargeIncrement = 3,
/// <summary>
/// The Avalonia.Controls.Primitives.Thumb was dragged and caused a Avalonia.UIElement.MouseMove
/// event. A Avalonia.Controls.Primitives.ScrollBar.Scroll event of this Avalonia.Controls.Primitives.ScrollEventType
/// may occur more than one time when the Avalonia.Controls.Primitives.Thumb
/// is dragged in the Avalonia.Controls.Primitives.ScrollBar.
/// </summary>
ThumbTrack = 4,
/// <summary>
/// Specifies that the Avalonia.Controls.Primitives.Thumb was dragged to a
/// new position and is now no longer being dragged by the user.
/// </summary>
EndScroll = 5
}
}

3
src/Avalonia.Controls/ToolTip.cs

@ -234,11 +234,12 @@ namespace Avalonia.Controls
{
Close();
_popup = new PopupRoot { Content = this };
_popup = new PopupRoot { Content = this, };
((ISetLogicalParent)_popup).SetParent(control);
_popup.Position = Popup.GetPosition(control, GetPlacement(control), _popup,
GetHorizontalOffset(control), GetVerticalOffset(control));
_popup.Show();
_popup.SnapInsideScreenEdges();
}
private void Close()

14
src/Avalonia.Controls/WindowBase.cs

@ -38,6 +38,9 @@ namespace Avalonia.Controls
o => o.Owner,
(o, v) => o.Owner = v);
public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<WindowBase, bool>(nameof(Topmost));
private bool _hasExecutedInitialLayoutPass;
private bool _isActive;
private bool _ignoreVisibilityChange;
@ -52,6 +55,8 @@ namespace Avalonia.Controls
MinHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, (double)e.NewValue), new Size(w.MaxWidth, w.MaxHeight)));
MaxWidthProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size((double)e.NewValue, w.MaxHeight)));
MaxHeightProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetMinMaxSize(new Size(w.MinWidth, w.MinHeight), new Size(w.MaxWidth, (double)e.NewValue)));
TopmostProperty.Changed.AddClassHandler<WindowBase>((w, e) => w.PlatformImpl?.SetTopmost((bool)e.NewValue));
}
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
@ -124,6 +129,15 @@ namespace Avalonia.Controls
set { SetAndRaise(OwnerProperty, ref _owner, value); }
}
/// <summary>
/// Gets or sets whether this window appears on top of all other windows
/// </summary>
public bool Topmost
{
get { return GetValue(TopmostProperty); }
set { SetValue(TopmostProperty, value); }
}
/// <summary>
/// Activates the window.
/// </summary>

4
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@ -106,5 +106,9 @@ namespace Avalonia.DesignerSupport.Remote
public void CanResize(bool value)
{
}
public void SetTopmost(bool value)
{
}
}
}

4
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@ -104,6 +104,10 @@ namespace Avalonia.DesignerSupport.Remote
public void CanResize(bool value)
{
}
public void SetTopmost(bool value)
{
}
}
class ClipboardStub : IClipboard

2
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -59,7 +59,7 @@ namespace Avalonia.Media
//HACK: This is a temporary hack that is used in the render loop
//to update TransformedBounds property
[Obsolete("HACK for render loop, don't use")]
internal Matrix CurrentContainerTransform => _currentContainerTransform;
public Matrix CurrentContainerTransform => _currentContainerTransform;
/// <summary>
/// Draws a bitmap image.

20
src/Avalonia.Visuals/Rendering/IRendererFactory.cs

@ -0,0 +1,20 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
namespace Avalonia.Rendering
{
/// <summary>
/// Defines the interface for a renderer factory.
/// </summary>
public interface IRendererFactory
{
/// <summary>
/// Creates a renderer.
/// </summary>
/// <param name="root">The root visual.</param>
/// <param name="renderLoop">The render loop.</param>
IRenderer Create(IRenderRoot root, IRenderLoop renderLoop);
}
}

6
src/Gtk/Avalonia.Gtk3/Interop/Native.cs

@ -262,10 +262,13 @@ namespace Avalonia.Gtk3.Interop
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_unmaximize(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_close(GtkWindow window);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_keep_above(GtkWindow gtkWindow, bool setting);
[UnmanagedFunctionPointer(CallingConvention.Cdecl), GtkImport(GtkDll.Gtk)]
public delegate void gtk_window_set_geometry_hints(GtkWindow window, IntPtr geometry_widget, ref GdkGeometry geometry, GdkWindowHints geom_mask);
@ -473,6 +476,7 @@ namespace Avalonia.Gtk3.Interop
public static D.gtk_window_maximize GtkWindowMaximize;
public static D.gtk_window_unmaximize GtkWindowUnmaximize;
public static D.gtk_window_close GtkWindowClose;
public static D.gtk_window_set_keep_above GtkWindowSetKeepAbove;
public static D.gdk_window_begin_move_drag GdkWindowBeginMoveDrag;
public static D.gdk_window_begin_resize_drag GdkWindowBeginResizeDrag;
public static D.gdk_event_request_motions GdkEventRequestMotions;

2
src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs

@ -416,6 +416,8 @@ namespace Avalonia.Gtk3
public void Hide() => Native.GtkWidgetHide(GtkWidget);
public void SetTopmost(bool value) => Native.GtkWindowSetKeepAbove(GtkWidget, value);
void GetGlobalPointer(out int x, out int y)
{
int mask;

2
src/Gtk/Avalonia.Gtk3/WindowImpl.cs

@ -81,7 +81,7 @@ namespace Avalonia.Gtk3
public void ShowTaskbarIcon(bool value) => Native.GtkWindowSetSkipTaskbarHint(GtkWidget, !value);
public void CanResize(bool value) => Native.GtkWindowSetResizable(GtkWidget, value);
class EmptyDisposable : IDisposable
{

1
src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs

@ -123,6 +123,7 @@ namespace Avalonia.MonoMac
public void Hide() => Window?.OrderOut(Window);
public void SetTopmost(bool value) => Window.Level = value ? NSWindowLevel.Floating : NSWindowLevel.Normal;
public void BeginMoveDrag()
{

31
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -560,13 +560,11 @@ namespace Avalonia.Skia
}
measured = LineBreak(Text, curOff, length, _paint, constraint, out trailingnumber);
AvaloniaFormattedTextLine line = new AvaloniaFormattedTextLine();
line.TextLength = measured;
line.Start = curOff;
subString = Text.Substring(line.Start, line.TextLength);
lineWidth = _paint.MeasureText(subString);
line.Start = curOff;
line.Length = measured - trailingnumber;
line.Width = lineWidth;
line.Height = _lineHeight;
@ -575,10 +573,33 @@ namespace Avalonia.Skia
_skiaLines.Add(line);
curY += _lineHeight;
curY += mLeading;
curOff += measured;
//if this is the last line and there are trailing newline characters then
//insert a additional line
if (curOff >= length)
{
var subStringMinusNewlines = subString.TrimEnd('\n', '\r');
var lengthDiff = subString.Length - subStringMinusNewlines.Length;
if (lengthDiff > 0)
{
AvaloniaFormattedTextLine lastLine = new AvaloniaFormattedTextLine();
lastLine.TextLength = lengthDiff;
lastLine.Start = curOff - lengthDiff;
var lastLineSubString = Text.Substring(line.Start, line.TextLength);
var lastLineWidth = _paint.MeasureText(lastLineSubString);
lastLine.Length = 0;
lastLine.Width = lastLineWidth;
lastLine.Height = _lineHeight;
lastLine.Top = curY;
_skiaLines.Add(lastLine);
curY += _lineHeight;
curY += mLeading;
}
}
}
// Now convert to Avalonia data formats

8
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -78,6 +78,14 @@ namespace Avalonia.Win32.Interop
SWP_RESIZE = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
}
public static class WindowPosZOrder
{
public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
public static readonly IntPtr HWND_TOP = new IntPtr(0);
public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
}
public enum SizeCommand
{
Restored,

28
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -32,6 +32,7 @@ namespace Avalonia.Win32
private bool _trackingMouse;
private bool _decorated = true;
private bool _resizable = true;
private bool _topmost = false;
private double _scaling = 1;
private WindowState _showWindowState;
private WindowState _lastWindowState;
@ -124,9 +125,12 @@ namespace Avalonia.Win32
public IRenderer CreateRenderer(IRenderRoot root)
{
var loop = AvaloniaLocator.Current.GetService<IRenderLoop>();
return Win32Platform.UseDeferredRendering ?
(IRenderer)new DeferredRenderer(root, loop) :
new ImmediateRenderer(root);
var customRendererFactory = AvaloniaLocator.Current.GetService<IRendererFactory>();
if (customRendererFactory != null)
return customRendererFactory.Create(root, loop);
return Win32Platform.UseDeferredRendering ? (IRenderer)new DeferredRenderer(root, loop) : new ImmediateRenderer(root);
}
public void Resize(Size value)
@ -838,7 +842,7 @@ namespace Avalonia.Win32
var cx = Math.Abs(monitorInfo.rcWork.right - x);
var cy = Math.Abs(monitorInfo.rcWork.bottom - y);
SetWindowPos(_hwnd, new IntPtr(-2), x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
SetWindowPos(_hwnd, WindowPosZOrder.HWND_NOTOPMOST, x, y, cx, cy, SetWindowPosFlags.SWP_SHOWWINDOW);
}
}
}
@ -901,5 +905,21 @@ namespace Avalonia.Win32
_resizable = value;
}
public void SetTopmost(bool value)
{
if (value == _topmost)
{
return;
}
IntPtr hWndInsertAfter = value ? WindowPosZOrder.HWND_TOPMOST : WindowPosZOrder.HWND_NOTOPMOST;
UnmanagedMethods.SetWindowPos(_hwnd,
hWndInsertAfter,
0, 0, 0, 0,
SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE);
_topmost = value;
}
}
}

49
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -457,6 +457,28 @@ namespace Avalonia.Base.UnitTests
Assert.True(target.IsAnimating(Class1.FooProperty));
}
[Fact]
public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation()
{
var target = new Class1();
var source = new TestTwoWayBindingViewModel();
target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source });
Assert.False(source.SetterCalled);
}
[Fact]
public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer()
{
var target = new Class1();
var source = new TestTwoWayBindingViewModel();
target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source });
Assert.False(source.SetterCalled);
}
/// <summary>
/// Returns an observable that returns a single value but does not complete.
/// </summary>
@ -545,5 +567,32 @@ namespace Avalonia.Base.UnitTests
}
}
}
private class TestTwoWayBindingViewModel
{
private double _value;
public double Value
{
get => _value;
set
{
_value = value;
SetterCalled = true;
}
}
public double this[int index]
{
get => _value;
set
{
_value = value;
SetterCalled = true;
}
}
public bool SetterCalled { get; private set; }
}
}
}

59
tests/Avalonia.Controls.UnitTests/Primitives/ScrollBarTests.cs

@ -5,6 +5,7 @@ using System;
using System.Linq;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Media;
using Xunit;
@ -59,6 +60,64 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(50, target.Value);
}
[Fact]
public void Thumb_DragDelta_Event_Should_Raise_Scroll_Event()
{
var target = new ScrollBar
{
Template = new FuncControlTemplate<ScrollBar>(Template),
};
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var raisedEvent = Assert.Raises<ScrollEventArgs>(
handler => target.Scroll += handler,
handler => target.Scroll -= handler,
() =>
{
var ev = new VectorEventArgs
{
RoutedEvent = Thumb.DragDeltaEvent,
Vector = new Vector(0, 0)
};
track.Thumb.RaiseEvent(ev);
});
Assert.Equal(ScrollEventType.ThumbTrack, raisedEvent.Arguments.ScrollEventType);
}
[Fact]
public void Thumb_DragComplete_Event_Should_Raise_Scroll_Event()
{
var target = new ScrollBar
{
Template = new FuncControlTemplate<ScrollBar>(Template),
};
target.ApplyTemplate();
var track = (Track)target.GetTemplateChildren().First(x => x.Name == "track");
var raisedEvent = Assert.Raises<ScrollEventArgs>(
handler => target.Scroll += handler,
handler => target.Scroll -= handler,
() =>
{
var ev = new VectorEventArgs
{
RoutedEvent = Thumb.DragCompletedEvent,
Vector = new Vector(0, 0)
};
track.Thumb.RaiseEvent(ev);
});
Assert.Equal(ScrollEventType.EndScroll, raisedEvent.Arguments.ScrollEventType);
}
[Fact]
public void ScrollBar_Can_AutoHide()
{

Loading…
Cancel
Save