Browse Source

Merge branch 'master' into fixes/5027-avaloniaobject-batching

pull/5070/head
Dariusz Komosiński 6 years ago
committed by GitHub
parent
commit
38996cea70
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
  2. 67
      src/Avalonia.Controls/Control.cs
  3. 45
      src/Avalonia.Controls/Window.cs
  4. 2
      src/Avalonia.Layout/ElementManager.cs
  5. 14
      src/Avalonia.Visuals/Visual.cs
  6. 124
      tests/Avalonia.Controls.UnitTests/WindowTests.cs

11
src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs

@ -3275,7 +3275,7 @@ namespace Avalonia.Collections
addIndex);
// next check if we need to add an item into the current group
// bool needsGrouping = false;
bool needsGrouping = false;
if (Count == 1 && GroupDescriptions.Count > 0)
{
// if this is the first item being added
@ -3302,7 +3302,7 @@ namespace Avalonia.Collections
// otherwise, we need to validate that it is within the current page.
if (PageSize == 0 || (PageIndex + 1) * PageSize > leafIndex)
{
//needsGrouping = true;
needsGrouping = true;
int pageStartIndex = PageIndex * PageSize;
@ -3340,6 +3340,13 @@ namespace Avalonia.Collections
}
}
// if we need to add the item into the current group
// that will be displayed
if (needsGrouping)
{
this._group.AddToSubgroups(addedItem, false /*loading*/);
}
int addedIndex = IndexOf(addedItem);
// if the item is within the current page

67
src/Avalonia.Controls/Control.cs

@ -8,6 +8,8 @@ using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Controls
{
/// <summary>
@ -23,20 +25,20 @@ namespace Avalonia.Controls
/// <summary>
/// Defines the <see cref="FocusAdorner"/> property.
/// </summary>
public static readonly StyledProperty<ITemplate<IControl>> FocusAdornerProperty =
AvaloniaProperty.Register<Control, ITemplate<IControl>>(nameof(FocusAdorner));
public static readonly StyledProperty<ITemplate<IControl>?> FocusAdornerProperty =
AvaloniaProperty.Register<Control, ITemplate<IControl>?>(nameof(FocusAdorner));
/// <summary>
/// Defines the <see cref="Tag"/> property.
/// </summary>
public static readonly StyledProperty<object> TagProperty =
AvaloniaProperty.Register<Control, object>(nameof(Tag));
public static readonly StyledProperty<object?> TagProperty =
AvaloniaProperty.Register<Control, object?>(nameof(Tag));
/// <summary>
/// Defines the <see cref="ContextMenu"/> property.
/// </summary>
public static readonly StyledProperty<ContextMenu> ContextMenuProperty =
AvaloniaProperty.Register<Control, ContextMenu>(nameof(ContextMenu));
public static readonly StyledProperty<ContextMenu?> ContextMenuProperty =
AvaloniaProperty.Register<Control, ContextMenu?>(nameof(ContextMenu));
/// <summary>
/// Event raised when an element wishes to be scrolled into view.
@ -44,16 +46,16 @@ namespace Avalonia.Controls
public static readonly RoutedEvent<RequestBringIntoViewEventArgs> RequestBringIntoViewEvent =
RoutedEvent.Register<Control, RequestBringIntoViewEventArgs>("RequestBringIntoView", RoutingStrategies.Bubble);
private DataTemplates _dataTemplates;
private IControl _focusAdorner;
private DataTemplates? _dataTemplates;
private IControl? _focusAdorner;
/// <summary>
/// Gets or sets the control's focus adorner.
/// </summary>
public ITemplate<IControl> FocusAdorner
public ITemplate<IControl>? FocusAdorner
{
get { return GetValue(FocusAdornerProperty); }
set { SetValue(FocusAdornerProperty, value); }
get => GetValue(FocusAdornerProperty);
set => SetValue(FocusAdornerProperty, value);
}
/// <summary>
@ -63,27 +65,27 @@ namespace Avalonia.Controls
/// Each control may define data templates which are applied to the control itself and its
/// children.
/// </remarks>
public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
public DataTemplates DataTemplates => _dataTemplates ??= new DataTemplates();
/// <summary>
/// Gets or sets a context menu to the control.
/// </summary>
public ContextMenu ContextMenu
public ContextMenu? ContextMenu
{
get { return GetValue(ContextMenuProperty); }
set { SetValue(ContextMenuProperty, value); }
get => GetValue(ContextMenuProperty);
set => SetValue(ContextMenuProperty, value);
}
/// <summary>
/// Gets or sets a user-defined object attached to the control.
/// </summary>
public object Tag
public object? Tag
{
get { return GetValue(TagProperty); }
set { SetValue(TagProperty, value); }
get => GetValue(TagProperty);
set => SetValue(TagProperty, value);
}
public new IControl Parent => (IControl)base.Parent;
public new IControl? Parent => (IControl?)base.Parent;
/// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
@ -106,15 +108,10 @@ namespace Avalonia.Controls
{
var c = i as IControl;
if (c?.IsInitialized == false)
if (c?.IsInitialized == false && c is ISupportInitialize init)
{
var init = c as ISupportInitialize;
if (init != null)
{
init.BeginInit();
init.EndInit();
}
init.BeginInit();
init.EndInit();
}
}
}
@ -131,10 +128,7 @@ namespace Avalonia.Controls
/// Gets the element that receives the focus adorner.
/// </summary>
/// <returns>The control that receives the focus adorner.</returns>
protected virtual IControl GetTemplateFocusTarget()
{
return this;
}
protected virtual IControl? GetTemplateFocusTarget() => this;
/// <inheritdoc/>
protected sealed override void OnAttachedToVisualTreeCore(VisualTreeAttachmentEventArgs e)
@ -173,15 +167,10 @@ namespace Avalonia.Controls
}
}
if (_focusAdorner != null)
if (_focusAdorner != null && GetTemplateFocusTarget() is Visual target)
{
var target = (Visual)GetTemplateFocusTarget();
if (target != null)
{
AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
adornerLayer.Children.Add(_focusAdorner);
}
AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target);
adornerLayer.Children.Add(_focusAdorner);
}
}
}

45
src/Avalonia.Controls/Window.cs

@ -4,10 +4,7 @@ using System.ComponentModel;
using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Chrome;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Layout;
@ -482,10 +479,9 @@ namespace Avalonia.Controls
try
{
if (!ignoreCancel && HandleClosing())
if (!ignoreCancel && ShouldCancelClose())
{
close = false;
return;
}
}
finally
@ -497,11 +493,25 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
/// <returns>true if closing is cancelled. Otherwise false.</returns>
/// </summary>
protected virtual bool HandleClosing()
{
if (!ShouldCancelClose())
{
CloseInternal();
return false;
}
return true;
}
private void CloseInternal()
{
foreach (var (child, _) in _children.ToList())
{
// if we HandleClosing() before then there will be no children.
child.CloseInternal();
}
@ -515,20 +525,18 @@ namespace Avalonia.Controls
PlatformImpl?.Dispose();
}
/// <summary>
/// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
/// </summary>
protected virtual bool HandleClosing()
private bool ShouldCancelClose(CancelEventArgs args = null)
{
if (args is null)
{
args = new CancelEventArgs();
}
bool canClose = true;
foreach (var (child, _) in _children.ToList())
{
if (!child.HandleClosing())
{
child.CloseInternal();
}
else
if (child.ShouldCancelClose(args))
{
canClose = false;
}
@ -536,15 +544,12 @@ namespace Avalonia.Controls
if (canClose)
{
var args = new CancelEventArgs();
OnClosing(args);
return args.Cancel;
}
else
{
return !canClose;
}
return true;
}
protected virtual void HandleWindowStateChanged(WindowState state)

2
src/Avalonia.Layout/ElementManager.cs

@ -207,7 +207,7 @@ namespace Avalonia.Layout
}
}
public bool IsIndexValidInData(int currentIndex) => currentIndex >= 0 && currentIndex < _context.ItemCount;
public bool IsIndexValidInData(int currentIndex) => (uint)currentIndex < _context.ItemCount;
public ILayoutable GetRealizedElement(int dataIndex)
{

14
src/Avalonia.Visuals/Visual.cs

@ -150,7 +150,7 @@ namespace Avalonia
public TransformedBounds? TransformedBounds => _transformedBounds;
/// <summary>
/// Gets a value indicating whether the control should be clipped to its bounds.
/// Gets or sets a value indicating whether the control should be clipped to its bounds.
/// </summary>
public bool ClipToBounds
{
@ -191,7 +191,7 @@ namespace Avalonia
}
/// <summary>
/// Gets a value indicating whether this control is visible.
/// Gets or sets a value indicating whether this control is visible.
/// </summary>
public bool IsVisible
{
@ -200,7 +200,7 @@ namespace Avalonia
}
/// <summary>
/// Gets the opacity of the control.
/// Gets or sets the opacity of the control.
/// </summary>
public double Opacity
{
@ -209,7 +209,7 @@ namespace Avalonia
}
/// <summary>
/// Gets the opacity mask of the control.
/// Gets or sets the opacity mask of the control.
/// </summary>
public IBrush OpacityMask
{
@ -218,7 +218,7 @@ namespace Avalonia
}
/// <summary>
/// Gets the render transform of the control.
/// Gets or sets the render transform of the control.
/// </summary>
public ITransform RenderTransform
{
@ -227,7 +227,7 @@ namespace Avalonia
}
/// <summary>
/// Gets the transform origin of the control.
/// Gets or sets the transform origin of the control.
/// </summary>
public RelativePoint RenderTransformOrigin
{
@ -236,7 +236,7 @@ namespace Avalonia
}
/// <summary>
/// Gets the Z index of the control.
/// Gets or sets the Z index of the control.
/// </summary>
/// <remarks>
/// Controls with a higher <see cref="ZIndex"/> will appear in front of controls with

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

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Layout;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.UnitTests;
@ -137,6 +136,129 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, count);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Child_windows_should_be_closed_before_parent(bool programaticClose)
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var child = new Window();
int count = 0;
int windowClosing = 0;
int childClosing = 0;
int windowClosed = 0;
int childClosed = 0;
window.Closing += (sender, e) =>
{
count++;
windowClosing = count;
};
child.Closing += (sender, e) =>
{
count++;
childClosing = count;
};
window.Closed += (sender, e) =>
{
count++;
windowClosed = count;
};
child.Closed += (sender, e) =>
{
count++;
childClosed = count;
};
window.Show();
child.Show(window);
if (programaticClose)
{
window.Close();
}
else
{
var cancel = window.PlatformImpl.Closing();
Assert.Equal(false, cancel);
}
Assert.Equal(2, windowClosing);
Assert.Equal(1, childClosing);
Assert.Equal(4, windowClosed);
Assert.Equal(3, childClosed);
}
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void Child_windows_must_not_close_before_parent_has_chance_to_Cancel_OSCloseButton(bool programaticClose)
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var window = new Window();
var child = new Window();
int count = 0;
int windowClosing = 0;
int childClosing = 0;
int windowClosed = 0;
int childClosed = 0;
window.Closing += (sender, e) =>
{
count++;
windowClosing = count;
e.Cancel = true;
};
child.Closing += (sender, e) =>
{
count++;
childClosing = count;
};
window.Closed += (sender, e) =>
{
count++;
windowClosed = count;
};
child.Closed += (sender, e) =>
{
count++;
childClosed = count;
};
window.Show();
child.Show(window);
if (programaticClose)
{
window.Close();
}
else
{
var cancel = window.PlatformImpl.Closing();
Assert.Equal(true, cancel);
}
Assert.Equal(2, windowClosing);
Assert.Equal(1, childClosing);
Assert.Equal(0, windowClosed);
Assert.Equal(0, childClosed);
}
}
[Fact]
public void Showing_Should_Start_Renderer()

Loading…
Cancel
Save