Browse Source

Merge branch 'master' into selecting-items-control-events

pull/3435/head
Dariusz Komosiński 7 years ago
committed by GitHub
parent
commit
3174b0f7b2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      src/Avalonia.Controls/Notifications/WindowNotificationManager.cs
  2. 5
      src/Avalonia.Controls/Primitives/AdornerLayer.cs
  3. 7
      src/Avalonia.Controls/Primitives/OverlayLayer.cs
  4. 38
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  5. 126
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  6. 13
      src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs
  7. 17
      tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs
  8. 54
      tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs

5
src/Avalonia.Controls/Notifications/WindowNotificationManager.cs

@ -7,6 +7,7 @@ using System.Linq;
using System.Reactive.Linq;
using System.Threading.Tasks;
using Avalonia.Controls.Primitives;
using Avalonia.Rendering;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Notifications
@ -14,7 +15,7 @@ namespace Avalonia.Controls.Notifications
/// <summary>
/// An <see cref="INotificationManager"/> that displays notifications in a <see cref="Window"/>.
/// </summary>
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager
public class WindowNotificationManager : TemplatedControl, IManagedNotificationManager, ICustomSimpleHitTest
{
private IList _items;
@ -153,5 +154,7 @@ namespace Avalonia.Controls.Notifications
adornerLayer?.Children.Add(this);
}
public bool HitTest(Point point) => VisualChildren.HitTestCustom(point);
}
}

5
src/Avalonia.Controls/Primitives/AdornerLayer.cs

@ -138,10 +138,7 @@ namespace Avalonia.Controls.Primitives
}
}
public bool HitTest(Point point)
{
return Children.Any(ctrl => ctrl.TransformedBounds?.Contains(point) == true);
}
public bool HitTest(Point point) => Children.HitTestCustom(point);
private class AdornedElementInfo
{

7
src/Avalonia.Controls/Primitives/OverlayLayer.cs

@ -21,11 +21,8 @@ namespace Avalonia.Controls.Primitives
return null;
}
public bool HitTest(Point point)
{
return Children.Any(ctrl => ctrl.TransformedBounds?.Contains(point) == true);
}
public bool HitTest(Point point) => Children.HitTestCustom(point);
protected override Size ArrangeOverride(Size finalSize)
{

38
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls.Generators;
@ -240,17 +241,14 @@ namespace Avalonia.Controls.Primitives
public override void BeginInit()
{
base.BeginInit();
++_updateCount;
_updateSelectedIndex = int.MinValue;
InternalBeginInit();
}
/// <inheritdoc/>
public override void EndInit()
{
if (--_updateCount == 0)
{
UpdateFinished();
}
InternalEndInit();
base.EndInit();
}
@ -437,7 +435,8 @@ namespace Avalonia.Controls.Primitives
protected override void OnDataContextBeginUpdate()
{
base.OnDataContextBeginUpdate();
++_updateCount;
InternalBeginInit();
}
/// <inheritdoc/>
@ -445,10 +444,7 @@ namespace Avalonia.Controls.Primitives
{
base.OnDataContextEndUpdate();
if (--_updateCount == 0)
{
UpdateFinished();
}
InternalEndInit();
}
protected override void OnKeyDown(KeyEventArgs e)
@ -1118,6 +1114,26 @@ namespace Avalonia.Controls.Primitives
}
}
private void InternalBeginInit()
{
if (_updateCount == 0)
{
_updateSelectedIndex = int.MinValue;
}
++_updateCount;
}
private void InternalEndInit()
{
Debug.Assert(_updateCount > 0);
if (--_updateCount == 0)
{
UpdateFinished();
}
}
private class Selection : IEnumerable<int>
{
private readonly List<int> _list = new List<int>();

126
src/Avalonia.Controls/Primitives/ToggleButton.cs

@ -7,8 +7,14 @@ using Avalonia.Data;
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states.
/// </summary>
public class ToggleButton : Button
{
/// <summary>
/// Defines the <see cref="IsChecked"/> property.
/// </summary>
public static readonly DirectProperty<ToggleButton, bool?> IsCheckedProperty =
AvaloniaProperty.RegisterDirect<ToggleButton, bool?>(
nameof(IsChecked),
@ -17,9 +23,30 @@ namespace Avalonia.Controls.Primitives
unsetValue: null,
defaultBindingMode: BindingMode.TwoWay);
/// <summary>
/// Defines the <see cref="IsThreeState"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsThreeStateProperty =
AvaloniaProperty.Register<ToggleButton, bool>(nameof(IsThreeState));
/// <summary>
/// Defines the <see cref="Checked"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> CheckedEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(nameof(Checked), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Unchecked"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> UncheckedEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(nameof(Unchecked), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="Unchecked"/> event.
/// </summary>
public static readonly RoutedEvent<RoutedEventArgs> IndeterminateEvent =
RoutedEvent.Register<ToggleButton, RoutedEventArgs>(nameof(Indeterminate), RoutingStrategies.Bubble);
private bool? _isChecked = false;
static ToggleButton()
@ -27,14 +54,49 @@ namespace Avalonia.Controls.Primitives
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == true, ":checked");
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass<ToggleButton, bool?>(IsCheckedProperty, c => c == null, ":indeterminate");
IsCheckedProperty.Changed.AddClassHandler<ToggleButton>((x, e) => x.OnIsCheckedChanged(e));
}
/// <summary>
/// Raised when a <see cref="ToggleButton"/> is checked.
/// </summary>
public event EventHandler<RoutedEventArgs> Checked
{
add => AddHandler(CheckedEvent, value);
remove => RemoveHandler(CheckedEvent, value);
}
/// <summary>
/// Raised when a <see cref="ToggleButton"/> is unchecked.
/// </summary>
public event EventHandler<RoutedEventArgs> Unchecked
{
add => AddHandler(UncheckedEvent, value);
remove => RemoveHandler(UncheckedEvent, value);
}
/// <summary>
/// Raised when a <see cref="ToggleButton"/> is neither checked nor unchecked.
/// </summary>
public event EventHandler<RoutedEventArgs> Indeterminate
{
add => AddHandler(IndeterminateEvent, value);
remove => RemoveHandler(IndeterminateEvent, value);
}
/// <summary>
/// Gets or sets whether the <see cref="ToggleButton"/> is checked.
/// </summary>
public bool? IsChecked
{
get { return _isChecked; }
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
get => _isChecked;
set => SetAndRaise(IsCheckedProperty, ref _isChecked, value);
}
/// <summary>
/// Gets or sets a value that indicates whether the control supports three states.
/// </summary>
public bool IsThreeState
{
get => GetValue(IsThreeStateProperty);
@ -47,18 +109,78 @@ namespace Avalonia.Controls.Primitives
base.OnClick();
}
/// <summary>
/// Toggles the <see cref="IsChecked"/> property.
/// </summary>
protected virtual void Toggle()
{
if (IsChecked.HasValue)
{
if (IsChecked.Value)
{
if (IsThreeState)
{
IsChecked = null;
}
else
{
IsChecked = false;
}
}
else
{
IsChecked = true;
}
}
else
{
IsChecked = false;
}
}
/// <summary>
/// Called when <see cref="IsChecked"/> becomes true.
/// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
protected virtual void OnChecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
/// <summary>
/// Called when <see cref="IsChecked"/> becomes false.
/// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
protected virtual void OnUnchecked(RoutedEventArgs e)
{
RaiseEvent(e);
}
/// <summary>
/// Called when <see cref="IsChecked"/> becomes null.
/// </summary>
/// <param name="e">Event arguments for the routed event that is raised by the default implementation of this method.</param>
protected virtual void OnIndeterminate(RoutedEventArgs e)
{
RaiseEvent(e);
}
private void OnIsCheckedChanged(AvaloniaPropertyChangedEventArgs e)
{
var newValue = (bool?)e.NewValue;
switch (newValue)
{
case true:
OnChecked(new RoutedEventArgs(CheckedEvent));
break;
case false:
OnUnchecked(new RoutedEventArgs(UncheckedEvent));
break;
default:
OnIndeterminate(new RoutedEventArgs(IndeterminateEvent));
break;
}
}
}
}

13
src/Avalonia.Visuals/Rendering/ICustomSimpleHitTest.cs

@ -1,3 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Avalonia.VisualTree;
namespace Avalonia.Rendering
{
/// <summary>
@ -9,4 +13,13 @@ namespace Avalonia.Rendering
{
bool HitTest(Point point);
}
public static class CustomSimpleHitTestExtensions
{
public static bool HitTestCustom(this IVisual visual, Point point)
=> (visual as ICustomSimpleHitTest)?.HitTest(point) ?? visual.TransformedBounds?.Contains(point) == true;
public static bool HitTestCustom(this IEnumerable<IVisual> children, Point point)
=> children.Any(ctrl => ctrl.HitTestCustom(point));
}
}

17
tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs

@ -129,6 +129,23 @@ namespace Avalonia.Controls.UnitTests.Primitives
Assert.Equal(-1, target.SelectedIndex);
}
[Fact]
public void SelectedIndex_Should_Be_Minus_1_Without_Initialize()
{
var items = new[]
{
new Item(),
new Item(),
};
var target = new ListBox();
target.Items = items;
target.Template = Template();
target.DataContext = new object();
Assert.Equal(-1, target.SelectedIndex);
}
[Fact]
public void SelectedIndex_Should_Be_0_After_Initialize_With_AlwaysSelected()
{

54
tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs

@ -1,5 +1,4 @@
using Avalonia.Data;
using Avalonia.Markup.Data;
using Avalonia.UnitTests;
using Xunit;
@ -63,6 +62,54 @@ namespace Avalonia.Controls.Primitives.UnitTests
Assert.Null(threeStateButton.IsChecked);
}
[Fact]
public void ToggleButton_Events_Are_Raised_On_Is_Checked_Changes()
{
var threeStateButton = new ToggleButton();
bool checkedRaised = false;
threeStateButton.Checked += (_, __) => checkedRaised = true;
threeStateButton.IsChecked = true;
Assert.True(checkedRaised);
bool uncheckedRaised = false;
threeStateButton.Unchecked += (_, __) => uncheckedRaised = true;
threeStateButton.IsChecked = false;
Assert.True(uncheckedRaised);
bool indeterminateRaised = false;
threeStateButton.Indeterminate += (_, __) => indeterminateRaised = true;
threeStateButton.IsChecked = null;
Assert.True(indeterminateRaised);
}
[Fact]
public void ToggleButton_Events_Are_Raised_When_Toggling()
{
var threeStateButton = new TestToggleButton { IsThreeState = true };
bool checkedRaised = false;
threeStateButton.Checked += (_, __) => checkedRaised = true;
threeStateButton.Toggle();
Assert.True(checkedRaised);
bool indeterminateRaised = false;
threeStateButton.Indeterminate += (_, __) => indeterminateRaised = true;
threeStateButton.Toggle();
Assert.True(indeterminateRaised);
bool uncheckedRaised = false;
threeStateButton.Unchecked += (_, __) => uncheckedRaised = true;
threeStateButton.Toggle();
Assert.True(uncheckedRaised);
}
private class Class1 : NotifyingBase
{
private bool _foo;
@ -80,5 +127,10 @@ namespace Avalonia.Controls.Primitives.UnitTests
set { nullableFoo = value; RaisePropertyChanged(); }
}
}
private class TestToggleButton : ToggleButton
{
public new void Toggle() => base.Toggle();
}
}
}

Loading…
Cancel
Save