Browse Source

Merge branch 'master' into fixes/3180

pull/3186/head
Benedikt Stebner 7 years ago
committed by GitHub
parent
commit
cdf9761dc3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      azure-pipelines.yml
  2. 23
      src/Avalonia.Base/EnumExtensions.cs
  3. 2
      src/Avalonia.Controls/WrapPanel.cs
  4. 13
      src/Avalonia.Input/MouseDevice.cs
  5. 9
      src/Avalonia.Input/PointerPoint.cs
  6. 21
      src/Avalonia.Input/TouchDevice.cs
  7. 1
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  8. 5
      src/Avalonia.Native/WindowImplBase.cs
  9. 2
      src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs
  10. 2
      src/Avalonia.X11/X11Platform.cs
  11. 10
      src/Avalonia.X11/X11Window.cs
  12. 12
      src/Avalonia.X11/XI2Manager.cs
  13. 34
      src/Skia/Avalonia.Skia/FormattedTextImpl.cs
  14. 8
      src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs
  15. 39
      src/Windows/Avalonia.Win32/WindowImpl.cs
  16. 78
      tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs
  17. 75
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

2
azure-pipelines.yml

@ -52,7 +52,7 @@ jobs:
sdk: 'macosx10.14'
configuration: 'Release'
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
xcodeVersion: 'default' # Options: 8, 9, default, specifyPath
xcodeVersion: '10' # Options: 8, 9, default, specifyPath
args: '-derivedDataPath ./'
- task: CmdLine@2

23
src/Avalonia.Base/EnumExtensions.cs

@ -0,0 +1,23 @@
// 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;
using System.Runtime.CompilerServices;
namespace Avalonia
{
/// <summary>
/// Provides extension methods for enums.
/// </summary>
public static class EnumExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool HasFlagCustom<T>(this T value, T flag) where T : unmanaged, Enum
{
var intValue = *(int*)&value;
var intFlag = *(int*)&flag;
return (intValue & intFlag) == intFlag;
}
}
}

2
src/Avalonia.Controls/WrapPanel.cs

@ -42,7 +42,7 @@ namespace Avalonia.Controls
/// </summary>
static WrapPanel()
{
AffectsMeasure<WrapPanel>(OrientationProperty);
AffectsMeasure<WrapPanel>(OrientationProperty, ItemWidthProperty, ItemHeightProperty);
}
/// <summary>

13
src/Avalonia.Input/MouseDevice.cs

@ -14,13 +14,14 @@ namespace Avalonia.Input
/// <summary>
/// Represents a mouse device.
/// </summary>
public class MouseDevice : IMouseDevice
public class MouseDevice : IMouseDevice, IDisposable
{
private int _clickCount;
private Rect _lastClickRect;
private ulong _lastClickTime;
private readonly Pointer _pointer;
private bool _disposed;
public MouseDevice(Pointer pointer = null)
{
@ -126,7 +127,9 @@ namespace Avalonia.Input
{
Contract.Requires<ArgumentNullException>(e != null);
var mouse = (IMouseDevice)e.Device;
var mouse = (MouseDevice)e.Device;
if(mouse._disposed)
return;
Position = e.Root.PointToScreen(e.Position);
var props = CreateProperties(e);
@ -441,5 +444,11 @@ namespace Avalonia.Input
el = (IInputElement)el.VisualParent;
}
}
public void Dispose()
{
_disposed = true;
_pointer?.Dispose();
}
}
}

9
src/Avalonia.Input/PointerPoint.cs

@ -1,3 +1,6 @@
// 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.Input
{
public sealed class PointerPoint
@ -27,9 +30,9 @@ namespace Avalonia.Input
public PointerPointProperties(RawInputModifiers modifiers, PointerUpdateKind kind)
{
PointerUpdateKind = kind;
IsLeftButtonPressed = modifiers.HasFlag(RawInputModifiers.LeftMouseButton);
IsMiddleButtonPressed = modifiers.HasFlag(RawInputModifiers.MiddleMouseButton);
IsRightButtonPressed = modifiers.HasFlag(RawInputModifiers.RightMouseButton);
IsLeftButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.LeftMouseButton);
IsMiddleButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.MiddleMouseButton);
IsRightButtonPressed = modifiers.HasFlagCustom(RawInputModifiers.RightMouseButton);
// The underlying input source might be reporting the previous state,
// so make sure that we reflect the current state

21
src/Avalonia.Input/TouchDevice.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Input.Raw;
@ -11,10 +12,11 @@ namespace Avalonia.Input
/// This class is supposed to be used on per-toplevel basis, don't use a shared one
/// </remarks>
/// </summary>
public class TouchDevice : IInputDevice
public class TouchDevice : IInputDevice, IDisposable
{
Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>();
private readonly Dictionary<long, Pointer> _pointers = new Dictionary<long, Pointer>();
private bool _disposed;
KeyModifiers GetKeyModifiers(RawInputModifiers modifiers) =>
(KeyModifiers)(modifiers & RawInputModifiers.KeyboardMask);
@ -28,6 +30,8 @@ namespace Avalonia.Input
public void ProcessRawEvent(RawInputEventArgs ev)
{
if(_disposed)
return;
var args = (RawTouchEventArgs)ev;
if (!_pointers.TryGetValue(args.TouchPointId, out var pointer))
{
@ -82,6 +86,17 @@ namespace Avalonia.Input
}
public void Dispose()
{
if(_disposed)
return;
var values = _pointers.Values.ToList();
_pointers.Clear();
_disposed = true;
foreach (var p in values)
p.Dispose();
}
}
}

1
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -95,7 +95,6 @@ namespace Avalonia.Native
.Bind<IStandardCursorFactory>().ToConstant(new CursorFactory(_factory.CreateCursorFactory()))
.Bind<IPlatformIconLoader>().ToSingleton<IconLoader>()
.Bind<IKeyboardDevice>().ToConstant(KeyboardDevice)
.Bind<IMouseDevice>().ToConstant(MouseDevice)
.Bind<IPlatformSettings>().ToConstant(this)
.Bind<IWindowingPlatform>().ToConstant(this)
.Bind<IClipboard>().ToConstant(new ClipboardImpl(_factory.CreateClipboard()))

5
src/Avalonia.Native/WindowImplBase.cs

@ -24,7 +24,7 @@ namespace Avalonia.Native
private object _syncRoot = new object();
private bool _deferredRendering = false;
private bool _gpu = false;
private readonly IMouseDevice _mouse;
private readonly MouseDevice _mouse;
private readonly IKeyboardDevice _keyboard;
private readonly IStandardCursorFactory _cursorFactory;
private Size _savedLogicalSize;
@ -38,7 +38,7 @@ namespace Avalonia.Native
_deferredRendering = opts.UseDeferredRendering;
_keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
_mouse = AvaloniaLocator.Current.GetService<IMouseDevice>();
_mouse = new MouseDevice();
_cursorFactory = AvaloniaLocator.Current.GetService<IStandardCursorFactory>();
}
@ -142,6 +142,7 @@ namespace Avalonia.Native
{
n?.Dispose();
}
_parent._mouse.Dispose();
}
void IAvnWindowBaseEvents.Activated() => _parent.Activated?.Invoke();

2
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@ -51,7 +51,7 @@ namespace Avalonia.Rendering.SceneGraph
UpdateSize(scene);
}
if (visual.VisualRoot != null)
if (visual.VisualRoot == scene.Root.Visual)
{
if (node?.Parent != null &&
visual.VisualParent != null &&

2
src/Avalonia.X11/X11Platform.cs

@ -19,9 +19,7 @@ namespace Avalonia.X11
class AvaloniaX11Platform : IWindowingPlatform
{
private Lazy<KeyboardDevice> _keyboardDevice = new Lazy<KeyboardDevice>(() => new KeyboardDevice());
private Lazy<MouseDevice> _mouseDevice = new Lazy<MouseDevice>(() => new MouseDevice());
public KeyboardDevice KeyboardDevice => _keyboardDevice.Value;
public MouseDevice MouseDevice => _mouseDevice.Value;
public Dictionary<IntPtr, Action<XEvent>> Windows = new Dictionary<IntPtr, Action<XEvent>>();
public XI2Manager XI2;
public X11Info Info { get; private set; }

10
src/Avalonia.X11/X11Window.cs

@ -32,7 +32,8 @@ namespace Avalonia.X11
private PixelPoint? _configurePoint;
private bool _triggeredExpose;
private IInputRoot _inputRoot;
private readonly IMouseDevice _mouse;
private readonly MouseDevice _mouse;
private readonly TouchDevice _touch;
private readonly IKeyboardDevice _keyboard;
private PixelPoint? _position;
private PixelSize _realSize;
@ -57,7 +58,8 @@ namespace Avalonia.X11
_platform = platform;
_popup = popupParent != null;
_x11 = platform.Info;
_mouse = platform.MouseDevice;
_mouse = new MouseDevice();
_touch = new TouchDevice();
_keyboard = platform.KeyboardDevice;
var glfeature = AvaloniaLocator.Current.GetService<IWindowingPlatformGlFeature>();
@ -702,6 +704,8 @@ namespace Avalonia.X11
_platform.XI2?.OnWindowDestroyed(_handle);
_handle = IntPtr.Zero;
Closed?.Invoke();
_mouse.Dispose();
_touch.Dispose();
}
if (_useRenderWindow && _renderHandle != IntPtr.Zero)
@ -830,6 +834,8 @@ namespace Avalonia.X11
}
public IMouseDevice MouseDevice => _mouse;
public TouchDevice TouchDevice => _touch;
public IPopupImpl CreatePopup()
=> _platform.Options.OverlayPopups ? null : new X11Window(_platform, this);

12
src/Avalonia.X11/XI2Manager.cs

@ -92,8 +92,6 @@ namespace Avalonia.X11
private PointerDeviceInfo _pointerDevice;
private AvaloniaX11Platform _platform;
private readonly TouchDevice _touchDevice = new TouchDevice();
public bool Init(AvaloniaX11Platform platform)
{
@ -198,7 +196,7 @@ namespace Avalonia.X11
(ev.Type == XiEventType.XI_TouchUpdate ?
RawPointerEventType.TouchUpdate :
RawPointerEventType.TouchEnd);
client.ScheduleInput(new RawTouchEventArgs(_touchDevice,
client.ScheduleInput(new RawTouchEventArgs(client.TouchDevice,
ev.Timestamp, client.InputRoot, type, ev.Position, ev.Modifiers, ev.Detail));
return;
}
@ -232,10 +230,10 @@ namespace Avalonia.X11
}
if (scrollDelta != default)
client.ScheduleInput(new RawMouseWheelEventArgs(_platform.MouseDevice, ev.Timestamp,
client.ScheduleInput(new RawMouseWheelEventArgs(client.MouseDevice, ev.Timestamp,
client.InputRoot, ev.Position, scrollDelta, ev.Modifiers));
if (_pointerDevice.HasMotion(ev))
client.ScheduleInput(new RawPointerEventArgs(_platform.MouseDevice, ev.Timestamp, client.InputRoot,
client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
RawPointerEventType.Move, ev.Position, ev.Modifiers));
}
@ -248,7 +246,7 @@ namespace Avalonia.X11
: ev.Button == 3 ? (down ? RawPointerEventType.RightButtonDown : RawPointerEventType.RightButtonUp)
: (RawPointerEventType?)null;
if (type.HasValue)
client.ScheduleInput(new RawPointerEventArgs(_platform.MouseDevice, ev.Timestamp, client.InputRoot,
client.ScheduleInput(new RawPointerEventArgs(client.MouseDevice, ev.Timestamp, client.InputRoot,
type.Value, ev.Position, ev.Modifiers));
}
@ -310,5 +308,7 @@ namespace Avalonia.X11
{
IInputRoot InputRoot { get; }
void ScheduleInput(RawInputEventArgs args);
IMouseDevice MouseDevice { get; }
TouchDevice TouchDevice { get; }
}
}

34
src/Skia/Avalonia.Skia/FormattedTextImpl.cs

@ -99,9 +99,26 @@ namespace Avalonia.Skia
public TextHitTestResult HitTestPoint(Point point)
{
float y = (float)point.Y;
var line = _skiaLines.Find(l => l.Top <= y && (l.Top + l.Height) > y);
if (!line.Equals(default(AvaloniaFormattedTextLine)))
AvaloniaFormattedTextLine line = default;
float nextTop = 0;
foreach(var currentLine in _skiaLines)
{
if(currentLine.Top <= y)
{
line = currentLine;
nextTop = currentLine.Top + currentLine.Height;
}
else
{
nextTop = currentLine.Top;
break;
}
}
if (!line.Equals(default))
{
var rects = GetRects();
@ -127,12 +144,15 @@ namespace Avalonia.Skia
line.Length : (line.Length - 1);
}
return new TextHitTestResult
if (y < nextTop)
{
IsInside = false,
TextPosition = line.Start + offset,
IsTrailing = Text.Length == (line.Start + offset + 1)
};
return new TextHitTestResult
{
IsInside = false,
TextPosition = line.Start + offset,
IsTrailing = Text.Length == (line.Start + offset + 1)
};
}
}
bool end = point.X > _bounds.Width || point.Y > _lines.Sum(l => l.Height);

8
src/Windows/Avalonia.Win32/Input/WindowsMouseDevice.cs

@ -11,19 +11,11 @@ namespace Avalonia.Win32.Input
{
class WindowsMouseDevice : MouseDevice
{
public static WindowsMouseDevice Instance { get; } = new WindowsMouseDevice();
public WindowsMouseDevice() : base(new WindowsMousePointer())
{
}
public WindowImpl CurrentWindow
{
get;
set;
}
class WindowsMousePointer : Pointer
{
public WindowsMousePointer() : base(Pointer.GetNextFreeId(),PointerType.Mouse, true)

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

@ -30,6 +30,7 @@ namespace Avalonia.Win32
private IntPtr _hwnd;
private bool _multitouch;
private TouchDevice _touchDevice = new TouchDevice();
private MouseDevice _mouseDevice = new WindowsMouseDevice();
private IInputRoot _owner;
private ManagedDeferredRendererLock _rendererLock = new ManagedDeferredRendererLock();
private bool _trackingMouse;
@ -205,7 +206,7 @@ namespace Avalonia.Win32
}
}
public IMouseDevice MouseDevice => WindowsMouseDevice.Instance;
public IMouseDevice MouseDevice => _mouseDevice;
public WindowState WindowState
{
@ -333,7 +334,7 @@ namespace Avalonia.Win32
public void BeginMoveDrag(PointerPressedEventArgs e)
{
WindowsMouseDevice.Instance.Capture(null);
_mouseDevice.Capture(null);
UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
new IntPtr((int)UnmanagedMethods.HitTestValues.HTCAPTION), IntPtr.Zero);
e.Pointer.Capture(null);
@ -356,7 +357,7 @@ namespace Avalonia.Win32
#if USE_MANAGED_DRAG
_managedDrag.BeginResizeDrag(edge, ScreenToClient(MouseDevice.Position));
#else
WindowsMouseDevice.Instance.Capture(null);
_mouseDevice.Capture(null);
UnmanagedMethods.DefWindowProc(_hwnd, (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN,
new IntPtr((int)EdgeDic[edge]), IntPtr.Zero);
#endif
@ -437,9 +438,7 @@ namespace Avalonia.Win32
uint timestamp = unchecked((uint)UnmanagedMethods.GetMessageTime());
RawInputEventArgs e = null;
WindowsMouseDevice.Instance.CurrentWindow = this;
switch ((UnmanagedMethods.WindowsMessage)msg)
{
case UnmanagedMethods.WindowsMessage.WM_ACTIVATE:
@ -485,6 +484,8 @@ namespace Avalonia.Win32
_parent._disabledBy.Remove(this);
_parent.UpdateEnabled();
}
_mouseDevice.Dispose();
_touchDevice?.Dispose();
//Free other resources
Dispose();
return IntPtr.Zero;
@ -542,7 +543,7 @@ namespace Avalonia.Win32
if(ShouldIgnoreTouchEmulatedMessage())
break;
e = new RawPointerEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN
@ -559,7 +560,7 @@ namespace Avalonia.Win32
if(ShouldIgnoreTouchEmulatedMessage())
break;
e = new RawPointerEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_LBUTTONUP
@ -587,7 +588,7 @@ namespace Avalonia.Win32
}
e = new RawPointerEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
RawPointerEventType.Move,
@ -597,7 +598,7 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_MOUSEWHEEL:
e = new RawMouseWheelEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
PointToClient(PointFromLParam(lParam)),
@ -606,7 +607,7 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_MOUSEHWHEEL:
e = new RawMouseWheelEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
PointToClient(PointFromLParam(lParam)),
@ -616,7 +617,7 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_MOUSELEAVE:
_trackingMouse = false;
e = new RawPointerEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
RawPointerEventType.LeaveWindow,
@ -627,7 +628,7 @@ namespace Avalonia.Win32
case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN:
case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN:
e = new RawPointerEventArgs(
WindowsMouseDevice.Instance,
_mouseDevice,
timestamp,
_owner,
msg == (int)UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN
@ -649,9 +650,9 @@ namespace Avalonia.Win32
{
Input?.Invoke(new RawTouchEventArgs(_touchDevice, touchInput.Time,
_owner,
touchInput.Flags.HasFlag(TouchInputFlags.TOUCHEVENTF_UP) ?
touchInput.Flags.HasFlagCustom(TouchInputFlags.TOUCHEVENTF_UP) ?
RawPointerEventType.TouchEnd :
touchInput.Flags.HasFlag(TouchInputFlags.TOUCHEVENTF_DOWN) ?
touchInput.Flags.HasFlagCustom(TouchInputFlags.TOUCHEVENTF_DOWN) ?
RawPointerEventType.TouchBegin :
RawPointerEventType.TouchUpdate,
PointToClient(new PixelPoint(touchInput.X / 100, touchInput.Y / 100)),
@ -771,11 +772,11 @@ namespace Avalonia.Win32
{
var keys = (UnmanagedMethods.ModifierKeys)ToInt32(wParam);
var modifiers = WindowsKeyboardDevice.Instance.Modifiers;
if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_LBUTTON))
modifiers |= RawInputModifiers.LeftMouseButton;
if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_RBUTTON))
modifiers |= RawInputModifiers.RightMouseButton;
if (keys.HasFlag(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
if (keys.HasFlagCustom(UnmanagedMethods.ModifierKeys.MK_MBUTTON))
modifiers |= RawInputModifiers.MiddleMouseButton;
return modifiers;
}
@ -785,7 +786,7 @@ namespace Avalonia.Win32
// Ensure that the delegate doesn't get garbage collected by storing it as a field.
_wndProcDelegate = new UnmanagedMethods.WndProc(WndProc);
_className = "Avalonia-" + Guid.NewGuid();
_className = $"Avalonia-{Guid.NewGuid().ToString()}";
UnmanagedMethods.WNDCLASSEX wndClassEx = new UnmanagedMethods.WNDCLASSEX
{

78
tests/Avalonia.Controls.UnitTests/WrapPanelTests.cs

@ -12,14 +12,14 @@ namespace Avalonia.Controls.UnitTests
public void Lays_Out_Horizontally_On_Separate_Lines()
{
var target = new WrapPanel()
{
Width = 100,
Children =
{
Width = 100,
Children =
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
@ -33,14 +33,14 @@ namespace Avalonia.Controls.UnitTests
public void Lays_Out_Horizontally_On_A_Single_Line()
{
var target = new WrapPanel()
{
Width = 200,
Children =
{
Width = 200,
Children =
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
@ -54,15 +54,15 @@ namespace Avalonia.Controls.UnitTests
public void Lays_Out_Vertically_Children_On_A_Single_Line()
{
var target = new WrapPanel()
{
Orientation = Orientation.Vertical,
Height = 120,
Children =
{
Orientation = Orientation.Vertical,
Height = 120,
Children =
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
@ -76,15 +76,15 @@ namespace Avalonia.Controls.UnitTests
public void Lays_Out_Vertically_On_Separate_Lines()
{
var target = new WrapPanel()
{
Orientation = Orientation.Vertical,
Height = 60,
Children =
{
Orientation = Orientation.Vertical,
Height = 60,
Children =
{
new Border { Height = 50, Width = 100 },
new Border { Height = 50, Width = 100 },
}
};
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
@ -98,17 +98,17 @@ namespace Avalonia.Controls.UnitTests
public void Applies_ItemWidth_And_ItemHeight_Properties()
{
var target = new WrapPanel()
{
Orientation = Orientation.Horizontal,
Width = 50,
ItemWidth = 20,
ItemHeight = 15,
Children =
{
Orientation = Orientation.Horizontal,
Width = 50,
ItemWidth = 20,
ItemHeight = 15,
Children =
{
new Border(),
new Border(),
}
};
};
target.Measure(Size.Infinity);
target.Arrange(new Rect(target.DesiredSize));
@ -117,5 +117,33 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new Rect(0, 0, 20, 15), target.Children[0].Bounds);
Assert.Equal(new Rect(20, 0, 20, 15), target.Children[1].Bounds);
}
[Fact]
void ItemWidth_Trigger_InvalidateMeasure()
{
var target = new WrapPanel();
target.Measure(new Size(10, 10));
Assert.True(target.IsMeasureValid);
target.ItemWidth = 1;
Assert.False(target.IsMeasureValid);
}
[Fact]
void ItemHeight_Trigger_InvalidateMeasure()
{
var target = new WrapPanel();
target.Measure(new Size(10, 10));
Assert.True(target.IsMeasureValid);
target.ItemHeight = 1;
Assert.False(target.IsMeasureValid);
}
}
}

75
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -369,6 +369,81 @@ namespace Avalonia.Visuals.UnitTests.Rendering
}
[Fact]
public void Should_Update_VisualNodes_When_Child_Moved_To_New_Parent_And_New_Root()
{
var dispatcher = new ImmediateDispatcher();
var loop = new Mock<IRenderLoop>();
Decorator moveFrom;
Decorator moveTo;
Canvas moveMe;
var root = new TestRoot
{
Child = new StackPanel
{
Children =
{
(moveFrom = new Decorator
{
Child = moveMe = new Canvas(),
})
}
}
};
var otherRoot = new TestRoot
{
Child = new StackPanel
{
Children =
{
(moveTo = new Decorator())
}
}
};
var sceneBuilder = new SceneBuilder();
var target = new DeferredRenderer(
root,
loop.Object,
sceneBuilder: sceneBuilder,
dispatcher: dispatcher);
var otherSceneBuilder = new SceneBuilder();
var otherTarget = new DeferredRenderer(
otherRoot,
loop.Object,
sceneBuilder: otherSceneBuilder,
dispatcher: dispatcher);
root.Renderer = target;
otherRoot.Renderer = otherTarget;
target.Start();
otherTarget.Start();
RunFrame(target);
RunFrame(otherTarget);
moveFrom.Child = null;
moveTo.Child = moveMe;
RunFrame(target);
RunFrame(otherTarget);
var scene = target.UnitTestScene();
var otherScene = otherTarget.UnitTestScene();
var moveFromNode = (VisualNode)scene.FindNode(moveFrom);
var moveToNode = (VisualNode)otherScene.FindNode(moveTo);
Assert.Empty(moveFromNode.Children);
Assert.Equal(1, moveToNode.Children.Count);
Assert.Same(moveMe, moveToNode.Children[0].Visual);
}
[Fact]
public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity()
{

Loading…
Cancel
Save