Browse Source

Merge branch 'master' into visual-tree-traversal-v2

pull/3253/head
Dariusz Komosiński 6 years ago
committed by GitHub
parent
commit
fee12901b3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      azure-pipelines.yml
  2. 2
      build/SharedVersion.props
  3. 4
      native/Avalonia.Native/inc/avalonia-native.h
  4. 14
      native/Avalonia.Native/src/OSX/app.mm
  5. 48
      native/Avalonia.Native/src/OSX/window.mm
  6. 4
      samples/ControlCatalog/App.xaml.cs
  7. 15
      src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs
  8. 21
      src/Avalonia.Controls/Application.cs
  9. 5
      src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs
  10. 49
      src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs
  11. 25
      src/Avalonia.Controls/Primitives/RangeBase.cs
  12. 6
      src/Avalonia.Controls/TopLevel.cs
  13. 3
      src/Avalonia.Controls/TreeView.cs
  14. 9
      src/Avalonia.Controls/TreeViewItem.cs
  15. 1
      src/Avalonia.Controls/Window.cs
  16. 4
      src/Avalonia.Input/InputElement.cs
  17. 33
      src/Avalonia.Native/WindowImplBase.cs
  18. 13
      src/Avalonia.Styling/IDataContextProvider.cs
  19. 8
      src/Avalonia.Styling/IStyledElement.cs
  20. 4
      src/Avalonia.Styling/StyledElement.cs
  21. 2
      src/Avalonia.Visuals/Visual.cs
  22. 7
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs
  23. 4
      src/Markup/Avalonia.Markup/Data/Binding.cs
  24. 16
      tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs
  25. 38
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  26. 3
      tests/Avalonia.Controls.UnitTests/WindowTests.cs
  27. 4
      tests/Avalonia.UnitTests/MockWindowingPlatform.cs

12
azure-pipelines.yml

@ -34,9 +34,17 @@ jobs:
pool:
vmImage: 'macOS-10.14'
steps:
- task: DotNetCoreInstaller@0
- task: UseDotNet@2
displayName: 'Use .NET Core SDK 3.0.x'
inputs:
version: '2.1.403'
packageType: sdk
version: 3.0.x
- task: UseDotNet@2
displayName: 'Use .NET Core Runtime 2.1.x'
inputs:
packageType: runtime
version: 2.1.x
- task: CmdLine@2
displayName: 'Install Mono 5.18'

2
build/SharedVersion.props

@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Product>Avalonia</Product>
<Version>0.8.999</Version>
<Version>0.9.999</Version>
<Copyright>Copyright 2019 &#169; The AvaloniaUI Project</Copyright>
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>

4
native/Avalonia.Native/inc/avalonia-native.h

@ -212,6 +212,10 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0;
virtual HRESULT SetMainMenu(IAvnAppMenu* menu) = 0;
virtual HRESULT ObtainMainMenu(IAvnAppMenu** retOut) = 0;
virtual HRESULT ObtainNSWindowHandle(void** retOut) = 0;
virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandle(void** retOut) = 0;
virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
virtual bool TryLock() = 0;
virtual void Unlock() = 0;
};

14
native/Avalonia.Native/src/OSX/app.mm

@ -1,16 +1,25 @@
#include "common.h"
@interface AvnAppDelegate : NSObject<NSApplicationDelegate>
@end
extern NSApplicationActivationPolicy AvnDesiredActivationPolicy = NSApplicationActivationPolicyRegular;
@implementation AvnAppDelegate
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
[[NSApplication sharedApplication] setActivationPolicy: AvnDesiredActivationPolicy];
if([[NSApplication sharedApplication] activationPolicy] != AvnDesiredActivationPolicy)
{
for (NSRunningApplication * app in [NSRunningApplication runningApplicationsWithBundleIdentifier:@"com.apple.dock"]) {
[app activateWithOptions:NSApplicationActivateIgnoringOtherApps];
break;
}
[[NSApplication sharedApplication] setActivationPolicy: AvnDesiredActivationPolicy];
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
[NSApp activateIgnoringOtherApps:true];
[[NSRunningApplication currentApplication] activateWithOptions:NSApplicationActivateIgnoringOtherApps];
}
@end
@ -20,5 +29,4 @@ extern void InitializeAvnApp()
NSApplication* app = [NSApplication sharedApplication];
id delegate = [AvnAppDelegate new];
[app setDelegate:delegate];
}

48
native/Avalonia.Native/src/OSX/window.mm

@ -83,6 +83,54 @@ public:
[Window setContentView: View];
}
virtual HRESULT ObtainNSWindowHandle(void** ret) override
{
if (ret == nullptr)
{
return E_POINTER;
}
*ret = (__bridge void*)Window;
return S_OK;
}
virtual HRESULT ObtainNSWindowHandleRetained(void** ret) override
{
if (ret == nullptr)
{
return E_POINTER;
}
*ret = (__bridge_retained void*)Window;
return S_OK;
}
virtual HRESULT ObtainNSViewHandle(void** ret) override
{
if (ret == nullptr)
{
return E_POINTER;
}
*ret = (__bridge void*)View;
return S_OK;
}
virtual HRESULT ObtainNSViewHandleRetained(void** ret) override
{
if (ret == nullptr)
{
return E_POINTER;
}
*ret = (__bridge_retained void*)View;
return S_OK;
}
virtual AvnWindow* GetNSWindow() override
{
return Window;

4
samples/ControlCatalog/App.xaml.cs

@ -1,6 +1,4 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
@ -19,7 +17,7 @@ namespace ControlCatalog
desktopLifetime.MainWindow = new MainWindow();
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
singleViewLifetime.MainView = new MainView();
base.OnFrameworkInitializationCompleted();
}
}

15
src/Avalonia.Base/Platform/IMacOSTopLevelPlatformHandle.cs

@ -0,0 +1,15 @@
// 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.Platform
{
public interface IMacOSTopLevelPlatformHandle
{
IntPtr NSView { get; }
IntPtr GetNSViewRetained();
IntPtr NSWindow { get; }
IntPtr GetNSWindowRetained();
}
}

21
src/Avalonia.Controls/Application.cs

@ -32,7 +32,7 @@ namespace Avalonia
/// method.
/// - Tracks the lifetime of the application.
/// </remarks>
public class Application : AvaloniaObject, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IStyleRoot, IResourceNode
{
/// <summary>
/// The application-global data templates.
@ -45,6 +45,12 @@ namespace Avalonia
private Styles _styles;
private IResourceDictionary _resources;
/// <summary>
/// Defines the <see cref="DataContext"/> property.
/// </summary>
public static readonly StyledProperty<object> DataContextProperty =
StyledElement.DataContextProperty.AddOwner<Application>();
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs> ResourcesChanged;
@ -56,6 +62,19 @@ namespace Avalonia
Name = "Avalonia Application";
}
/// <summary>
/// Gets or sets the Applications's data context.
/// </summary>
/// <remarks>
/// The data context property specifies the default object that will
/// be used for data binding.
/// </remarks>
public object DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
/// <summary>
/// Gets the current instance of the <see cref="Application"/> class.
/// </summary>

5
src/Avalonia.Controls/Generators/ITreeItemContainerGenerator.cs

@ -12,5 +12,10 @@ namespace Avalonia.Controls.Generators
/// Gets the container index for the tree.
/// </summary>
TreeContainerIndex Index { get; }
/// <summary>
/// Updates the index based on the parent <see cref="TreeView"/>.
/// </summary>
void UpdateIndex();
}
}

49
src/Avalonia.Controls/Generators/TreeItemContainerGenerator.cs

@ -3,8 +3,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.LogicalTree;
namespace Avalonia.Controls.Generators
{
@ -15,6 +17,8 @@ namespace Avalonia.Controls.Generators
public class TreeItemContainerGenerator<T> : ItemContainerGenerator<T>, ITreeItemContainerGenerator
where T : class, IControl, new()
{
private TreeView _treeView;
/// <summary>
/// Initializes a new instance of the <see cref="TreeItemContainerGenerator{T}"/> class.
/// </summary>
@ -23,31 +27,28 @@ namespace Avalonia.Controls.Generators
/// <param name="contentTemplateProperty">The container's ContentTemplate property.</param>
/// <param name="itemsProperty">The container's Items property.</param>
/// <param name="isExpandedProperty">The container's IsExpanded property.</param>
/// <param name="index">The container index for the tree</param>
public TreeItemContainerGenerator(
IControl owner,
AvaloniaProperty contentProperty,
AvaloniaProperty contentTemplateProperty,
AvaloniaProperty itemsProperty,
AvaloniaProperty isExpandedProperty,
TreeContainerIndex index)
AvaloniaProperty isExpandedProperty)
: base(owner, contentProperty, contentTemplateProperty)
{
Contract.Requires<ArgumentNullException>(owner != null);
Contract.Requires<ArgumentNullException>(contentProperty != null);
Contract.Requires<ArgumentNullException>(itemsProperty != null);
Contract.Requires<ArgumentNullException>(isExpandedProperty != null);
Contract.Requires<ArgumentNullException>(index != null);
ItemsProperty = itemsProperty;
IsExpandedProperty = isExpandedProperty;
Index = index;
UpdateIndex();
}
/// <summary>
/// Gets the container index for the tree.
/// </summary>
public TreeContainerIndex Index { get; }
public TreeContainerIndex Index { get; private set; }
/// <summary>
/// Gets the item container's Items property.
@ -70,7 +71,7 @@ namespace Avalonia.Controls.Generators
}
else if (container != null)
{
Index.Add(item, container);
Index?.Add(item, container);
return container;
}
else
@ -92,7 +93,7 @@ namespace Avalonia.Controls.Generators
result.DataContext = item;
}
Index.Add(item, result);
Index?.Add(item, result);
return result;
}
@ -101,24 +102,50 @@ namespace Avalonia.Controls.Generators
public override IEnumerable<ItemContainerInfo> Clear()
{
var items = base.Clear();
Index.Remove(0, items);
Index?.Remove(0, items);
return items;
}
public override IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count)
{
Index.Remove(startingIndex, GetContainerRange(startingIndex, count));
Index?.Remove(startingIndex, GetContainerRange(startingIndex, count));
return base.Dematerialize(startingIndex, count);
}
public override IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count)
{
Index.Remove(startingIndex, GetContainerRange(startingIndex, count));
Index?.Remove(startingIndex, GetContainerRange(startingIndex, count));
return base.RemoveRange(startingIndex, count);
}
public override bool TryRecycle(int oldIndex, int newIndex, object item) => false;
public void UpdateIndex()
{
if (Owner is TreeView treeViewOwner && Index == null)
{
Index = new TreeContainerIndex();
_treeView = treeViewOwner;
}
else if (Owner.IsAttachedToLogicalTree)
{
var treeView = Owner.GetSelfAndLogicalAncestors().OfType<TreeView>().FirstOrDefault();
if (treeView != _treeView)
{
Clear();
Index = treeView?.ItemContainerGenerator?.Index;
_treeView = treeView;
}
}
else
{
Clear();
Index = null;
_treeView = null;
}
}
class WrapperTreeDataTemplate : ITreeDataTemplate
{
private readonly IDataTemplate _inner;

25
src/Avalonia.Controls/Primitives/RangeBase.cs

@ -75,7 +75,10 @@ namespace Avalonia.Controls.Primitives
set
{
ValidateDouble(value, "Minimum");
if (!ValidateDouble(value))
{
return;
}
if (IsInitialized)
{
@ -102,7 +105,10 @@ namespace Avalonia.Controls.Primitives
set
{
ValidateDouble(value, "Maximum");
if (!ValidateDouble(value))
{
return;
}
if (IsInitialized)
{
@ -129,7 +135,10 @@ namespace Avalonia.Controls.Primitives
set
{
ValidateDouble(value, "Value");
if (!ValidateDouble(value))
{
return;
}
if (IsInitialized)
{
@ -164,16 +173,12 @@ namespace Avalonia.Controls.Primitives
}
/// <summary>
/// Throws an exception if the double value is NaN or Inf.
/// Checks if the double value is not inifinity nor NaN.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="property">The name of the property being set.</param>
private static void ValidateDouble(double value, string property)
private static bool ValidateDouble(double value)
{
if (double.IsInfinity(value) || double.IsNaN(value))
{
throw new ArgumentException($"{value} is not a valid value for {property}.");
}
return !double.IsInfinity(value) || !double.IsNaN(value);
}
/// <summary>

6
src/Avalonia.Controls/TopLevel.cs

@ -266,6 +266,12 @@ namespace Avalonia.Controls
/// </summary>
protected virtual void HandleClosed()
{
var logicalArgs = new LogicalTreeAttachmentEventArgs(this);
((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs);
var visualArgs = new VisualTreeAttachmentEventArgs(this, this);
OnDetachedFromVisualTreeCore(visualArgs);
(this as IInputRoot).MouseDevice?.TopLevelClosed(this);
PlatformImpl = null;
OnClosed(EventArgs.Empty);

3
src/Avalonia.Controls/TreeView.cs

@ -393,8 +393,7 @@ namespace Avalonia.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemTemplateProperty,
TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty,
new TreeContainerIndex());
TreeViewItem.IsExpandedProperty);
result.Index.Materialized += ContainerMaterialized;
return result;
}

9
src/Avalonia.Controls/TreeViewItem.cs

@ -98,17 +98,18 @@ namespace Avalonia.Controls
TreeViewItem.HeaderProperty,
TreeViewItem.ItemTemplateProperty,
TreeViewItem.ItemsProperty,
TreeViewItem.IsExpandedProperty,
_treeView?.ItemContainerGenerator.Index ?? new TreeContainerIndex());
TreeViewItem.IsExpandedProperty);
}
/// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnAttachedToLogicalTree(e);
_treeView = this.GetLogicalAncestors().OfType<TreeView>().FirstOrDefault();
Level = CalculateDistanceFromLogicalParent<TreeView>(this) - 1;
ItemContainerGenerator.UpdateIndex();
if (ItemTemplate == null && _treeView?.ItemTemplate != null)
{
@ -119,7 +120,7 @@ namespace Avalonia.Controls
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
base.OnDetachedFromLogicalTree(e);
ItemContainerGenerator.Clear();
ItemContainerGenerator.UpdateIndex();
}
protected virtual void OnRequestBringIntoView(RequestBringIntoViewEventArgs e)

1
src/Avalonia.Controls/Window.cs

@ -336,7 +336,6 @@ namespace Avalonia.Controls
if (close)
{
PlatformImpl?.Dispose();
HandleClosed();
}
}
}

4
src/Avalonia.Input/InputElement.cs

@ -342,7 +342,7 @@ namespace Avalonia.Input
}
/// <summary>
/// Gets or sets a value indicating whether the control is focused.
/// Gets a value indicating whether the control is focused.
/// </summary>
public bool IsFocused
{
@ -360,7 +360,7 @@ namespace Avalonia.Input
}
/// <summary>
/// Gets or sets a value indicating whether the pointer is currently over the control.
/// Gets a value indicating whether the pointer is currently over the control.
/// </summary>
public bool IsPointerOver
{

33
src/Avalonia.Native/WindowImplBase.cs

@ -16,6 +16,34 @@ using Avalonia.Threading;
namespace Avalonia.Native
{
public class MacOSTopLevelWindowHandle : IPlatformHandle, IMacOSTopLevelPlatformHandle
{
IAvnWindowBase _native;
public MacOSTopLevelWindowHandle(IAvnWindowBase native)
{
_native = native;
}
public IntPtr Handle => NSWindow;
public string HandleDescriptor => "NSWindow";
public IntPtr NSView => _native.ObtainNSViewHandle();
public IntPtr NSWindow => _native.ObtainNSWindowHandle();
public IntPtr GetNSViewRetained()
{
return _native.ObtainNSViewHandleRetained();
}
public IntPtr GetNSWindowRetained()
{
return _native.ObtainNSWindowHandleRetained();
}
}
public abstract class WindowBaseImpl : IWindowBaseImpl,
IFramebufferPlatformSurface
{
@ -45,6 +73,9 @@ namespace Avalonia.Native
protected void Init(IAvnWindowBase window, IAvnScreens screens)
{
_native = window;
Handle = new MacOSTopLevelWindowHandle(window);
_glSurface = new GlPlatformSurface(window);
Screen = new ScreenImpl(screens);
_savedLogicalSize = ClientSize;
@ -349,6 +380,6 @@ namespace Avalonia.Native
}
public IPlatformHandle Handle => new PlatformHandle(IntPtr.Zero, "NOT SUPPORTED");
public IPlatformHandle Handle { get; private set; }
}
}

13
src/Avalonia.Styling/IDataContextProvider.cs

@ -0,0 +1,13 @@
namespace Avalonia
{
/// <summary>
/// Defines an element with a data context that can be used for binding.
/// </summary>
public interface IDataContextProvider : IAvaloniaObject
{
/// <summary>
/// Gets or sets the element's data context.
/// </summary>
object DataContext { get; set; }
}
}

8
src/Avalonia.Styling/IStyledElement.cs

@ -10,7 +10,8 @@ namespace Avalonia
IStyleHost,
ILogical,
IResourceProvider,
IResourceNode
IResourceNode,
IDataContextProvider
{
/// <summary>
/// Occurs when the control has finished initialization.
@ -27,11 +28,6 @@ namespace Avalonia
/// </summary>
new Classes Classes { get; set; }
/// <summary>
/// Gets or sets the control's data context.
/// </summary>
object DataContext { get; set; }
/// <summary>
/// Gets the control's logical parent.
/// </summary>

4
src/Avalonia.Styling/StyledElement.cs

@ -24,7 +24,7 @@ namespace Avalonia
/// - Implements <see cref="ILogical"/> to form part of a logical tree.
/// - A collection of class strings for custom styling.
/// </summary>
public class StyledElement : Animatable, IStyledElement, ISetLogicalParent, ISetInheritanceParent
public class StyledElement : Animatable, IDataContextProvider, IStyledElement, ISetLogicalParent, ISetInheritanceParent
{
/// <summary>
/// Defines the <see cref="DataContext"/> property.
@ -288,7 +288,7 @@ namespace Avalonia
var list = new AvaloniaList<ILogical>
{
ResetBehavior = ResetBehavior.Remove,
Validate = ValidateLogicalChild
Validate = logical => ValidateLogicalChild(logical)
};
list.CollectionChanged += LogicalChildrenCollectionChanged;
_logicalChildren = list;

2
src/Avalonia.Visuals/Visual.cs

@ -120,7 +120,7 @@ namespace Avalonia
{
var visualChildren = new AvaloniaList<IVisual>();
visualChildren.ResetBehavior = ResetBehavior.Remove;
visualChildren.Validate = ValidateVisualChild;
visualChildren.Validate = visual => ValidateVisualChild(visual);
visualChildren.CollectionChanged += VisualChildrenChanged;
VisualChildren = visualChildren;
}

7
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/BindingExtension.cs

@ -52,6 +52,13 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
// the context.
object anchor = context.GetFirstParent<IControl>();
if(anchor is null)
{
// Try to find IDataContextProvider, this was added to allow us to find
// a datacontext for Application class when using NativeMenuItems.
anchor = context.GetFirstParent<IDataContextProvider>();
}
// If a control was not found, then try to find the highest-level style as the XAML
// file could be a XAML file containing only styles.
return anchor ??

4
src/Markup/Avalonia.Markup/Data/Binding.cs

@ -231,9 +231,9 @@ namespace Avalonia.Data
{
Contract.Requires<ArgumentNullException>(target != null);
if (!(target is IStyledElement))
if (!(target is IDataContextProvider))
{
target = anchor as IStyledElement;
target = anchor as IDataContextProvider;
if (target == null)
{

16
tests/Avalonia.Controls.UnitTests/Primitives/RangeBaseTests.cs

@ -82,22 +82,6 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(50, target.Value);
}
[Fact]
public void Properties_Should_Not_Accept_Nan_And_Inifinity()
{
var target = new TestRange();
Assert.Throws<ArgumentException>(() => target.Minimum = double.NaN);
Assert.Throws<ArgumentException>(() => target.Minimum = double.PositiveInfinity);
Assert.Throws<ArgumentException>(() => target.Minimum = double.NegativeInfinity);
Assert.Throws<ArgumentException>(() => target.Maximum = double.NaN);
Assert.Throws<ArgumentException>(() => target.Maximum = double.PositiveInfinity);
Assert.Throws<ArgumentException>(() => target.Maximum = double.NegativeInfinity);
Assert.Throws<ArgumentException>(() => target.Value = double.NaN);
Assert.Throws<ArgumentException>(() => target.Value = double.PositiveInfinity);
Assert.Throws<ArgumentException>(() => target.Value = double.NegativeInfinity);
}
[Theory]
[InlineData(true)]
[InlineData(false)]

38
tests/Avalonia.Controls.UnitTests/TreeViewTests.cs

@ -10,6 +10,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Data.Core;
using Avalonia.Diagnostics;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
@ -33,6 +34,8 @@ namespace Avalonia.Controls.UnitTests
Items = CreateTestTreeData(),
};
var root = new TestRoot(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@ -77,6 +80,8 @@ namespace Avalonia.Controls.UnitTests
Items = CreateTestTreeData(),
};
var root = new TestRoot(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@ -527,6 +532,8 @@ namespace Avalonia.Controls.UnitTests
Items = data,
};
var root = new TestRoot(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
@ -893,6 +900,37 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2, GetItem(target, 0, 1, 0).Level);
}
[Fact]
public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash()
{
// Issue #2985
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var parent = tree[0];
var node = parent.Children[1];
parent.Children.Remove(node);
parent.Children.Add(node);
var item = target.ItemContainerGenerator.Index.ContainerFromItem(node);
ApplyTemplates(new[] { item });
// #2985 causes ArgumentException here.
node.Children.Add(new Node());
}
[Fact]
public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection()
{

3
tests/Avalonia.Controls.UnitTests/WindowTests.cs

@ -228,8 +228,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var windowImpl = Mock.Of<IWindowImpl>(x => x.Scaling == 1);
var target = new Window(windowImpl);
var target = new Window();
target.Show();
target.Close();

4
tests/Avalonia.UnitTests/MockWindowingPlatform.cs

@ -28,6 +28,10 @@ namespace Avalonia.UnitTests
return CreatePopupMock().Object;
});
mock.Setup(x => x.Dispose()).Callback(() =>
{
mock.Object.Closed?.Invoke();
});
PixelPoint pos = default;
mock.SetupGet(x => x.Position).Returns(() => pos);
mock.Setup(x => x.Move(It.IsAny<PixelPoint>())).Callback(new Action<PixelPoint>(np => pos = np));

Loading…
Cancel
Save