Browse Source

Merge branch 'master' into calendar-control

pull/1244/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
2bc31fce7d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build/MonoMac.props
  2. 10
      samples/ControlCatalog/Pages/CarouselPage.xaml
  3. 5
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  4. 25
      samples/ControlCatalog/Pages/CheckBoxPage.xaml
  5. 2
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  6. 24
      samples/ControlCatalog/Pages/RadioButtonPage.xaml
  7. 2
      src/Avalonia.Controls/Classes.cs
  8. 36
      src/Avalonia.Controls/Primitives/ToggleButton.cs
  9. 9
      src/Avalonia.Controls/RadioButton.cs
  10. 3
      src/Avalonia.Remote.Protocol/BsonStreamTransport.cs
  11. 2
      src/Avalonia.Styling/Styling/Setter.cs
  12. 31
      src/Avalonia.Themes.Default/CheckBox.xaml
  13. 14
      src/Avalonia.Themes.Default/RadioButton.xaml
  14. 25
      src/Avalonia.Visuals/Animation/PageSlide.cs
  15. 10
      tests/Avalonia.Controls.UnitTests/ClassesTests.cs
  16. 57
      tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
  17. 36
      tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs
  18. 71
      tests/Avalonia.Styling.UnitTests/SetterTests.cs

2
build/MonoMac.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="MonoMac.NetStandard" Version="0.0.3" />
<PackageReference Include="MonoMac.NetStandard" Version="0.0.4" />
</ItemGroup>
</Project>

10
samples/ControlCatalog/Pages/CarouselPage.xaml

@ -9,7 +9,7 @@
</Button>
<Carousel Name="carousel">
<Carousel.Transition>
<PageSlide Duration="0.25"/>
<PageSlide Duration="0.25" Orientation="Vertical" />
</Carousel.Transition>
<Image Source="resm:ControlCatalog.Assets.delicate-arch-896885_640.jpg"/>
<Image Source="resm:ControlCatalog.Assets.hirsch-899118_640.jpg"/>
@ -28,6 +28,14 @@
<DropDownItem>Crossfade</DropDownItem>
</DropDown>
</StackPanel>
<StackPanel Orientation="Horizontal" Gap="4">
<TextBlock VerticalAlignment="Center">Orientation</TextBlock>
<DropDown Name="orientation" SelectedIndex="1" VerticalAlignment="Center">
<DropDownItem>Horizontal</DropDownItem>
<DropDownItem>Vertical</DropDownItem>
</DropDown>
</StackPanel>
</StackPanel>
</UserControl>

5
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@ -11,6 +11,7 @@ namespace ControlCatalog.Pages
private Button _left;
private Button _right;
private DropDown _transition;
private DropDown _orientation;
public CarouselPage()
{
@ -18,6 +19,7 @@ namespace ControlCatalog.Pages
_left.Click += (s, e) => _carousel.Previous();
_right.Click += (s, e) => _carousel.Next();
_transition.SelectionChanged += TransitionChanged;
_orientation.SelectionChanged += TransitionChanged;
}
private void InitializeComponent()
@ -27,6 +29,7 @@ namespace ControlCatalog.Pages
_left = this.FindControl<Button>("left");
_right = this.FindControl<Button>("right");
_transition = this.FindControl<DropDown>("transition");
_orientation = this.FindControl<DropDown>("orientation");
}
private void TransitionChanged(object sender, SelectionChangedEventArgs e)
@ -37,7 +40,7 @@ namespace ControlCatalog.Pages
_carousel.Transition = null;
break;
case 1:
_carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25));
_carousel.Transition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
break;
case 2:
_carousel.Transition = new CrossFade(TimeSpan.FromSeconds(0.25));

25
samples/ControlCatalog/Pages/CheckBoxPage.xaml

@ -1,15 +1,28 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">CheckBox</TextBlock>
<TextBlock Classes="h2">A check box control</TextBlock>
<StackPanel Orientation="Vertical"
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<CheckBox>Unchecked</CheckBox>
<CheckBox IsChecked="True">Checked</CheckBox>
<CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<CheckBox>Unchecked</CheckBox>
<CheckBox IsChecked="True">Checked</CheckBox>
<CheckBox IsChecked="{x:Null}">Indeterminate</CheckBox>
<CheckBox IsChecked="True" IsEnabled="False">Disabled</CheckBox>
</StackPanel>
<StackPanel Orientation="Vertical"
HorizontalAlignment="Center"
Gap="16">
<CheckBox IsChecked="False" IsThreeState="True">Three State: Unchecked</CheckBox>
<CheckBox IsChecked="True" IsThreeState="True">Three State: Checked</CheckBox>
<CheckBox IsChecked="{x:Null}" IsThreeState="True">Three State: Indeterminate</CheckBox>
<CheckBox IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Three State: Disabled</CheckBox>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

2
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -36,7 +36,7 @@ namespace ControlCatalog.Pages
};
}
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked ? (Window)this.VisualRoot : null;
Window GetWindow() => this.FindControl<CheckBox>("IsModal").IsChecked.Value ? (Window)this.VisualRoot : null;
private void InitializeComponent()
{

24
samples/ControlCatalog/Pages/RadioButtonPage.xaml

@ -1,15 +1,27 @@
<UserControl xmlns="https://github.com/avaloniaui">
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Gap="4">
<TextBlock Classes="h1">RadioButton</TextBlock>
<TextBlock Classes="h2">Allows the selection of a single option of many</TextBlock>
<StackPanel Orientation="Vertical"
<StackPanel Orientation="Horizontal"
Margin="0,16,0,0"
HorizontalAlignment="Center"
Gap="16">
<RadioButton IsChecked="True">Option 1</RadioButton>
<RadioButton>Option 2</RadioButton>
<RadioButton IsEnabled="False">Disabled</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<RadioButton IsChecked="True">Option 1</RadioButton>
<RadioButton>Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}">Option 3</RadioButton>
<RadioButton IsEnabled="False">Disabled</RadioButton>
</StackPanel>
<StackPanel Orientation="Vertical"
Gap="16">
<RadioButton IsChecked="True" IsThreeState="True">Three States: Option 1</RadioButton>
<RadioButton IsChecked="False" IsThreeState="True">Three States: Option 2</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True">Three States: Option 3</RadioButton>
<RadioButton IsChecked="{x:Null}" IsThreeState="True" IsEnabled="False">Disabled</RadioButton>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>

2
src/Avalonia.Controls/Classes.cs

@ -179,7 +179,7 @@ namespace Avalonia.Controls
{
ThrowIfPseudoclass(name, "removed");
if (!Contains(name))
if (Contains(name))
{
c.Add(name);
}

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

@ -9,26 +9,37 @@ namespace Avalonia.Controls.Primitives
{
public class ToggleButton : Button
{
public static readonly DirectProperty<ToggleButton, bool> IsCheckedProperty =
AvaloniaProperty.RegisterDirect<ToggleButton, bool>(
"IsChecked",
public static readonly DirectProperty<ToggleButton, bool?> IsCheckedProperty =
AvaloniaProperty.RegisterDirect<ToggleButton, bool?>(
nameof(IsChecked),
o => o.IsChecked,
(o,v) => o.IsChecked = v,
(o, v) => o.IsChecked = v,
defaultBindingMode: BindingMode.TwoWay);
private bool _isChecked;
public static readonly StyledProperty<bool> IsThreeStateProperty =
AvaloniaProperty.Register<ToggleButton, bool>(nameof(IsThreeState));
private bool? _isChecked = false;
static ToggleButton()
{
PseudoClass(IsCheckedProperty, ":checked");
PseudoClass(IsCheckedProperty, c => c == true, ":checked");
PseudoClass(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass(IsCheckedProperty, c => c == null, ":indeterminate");
}
public bool IsChecked
public bool? IsChecked
{
get { return _isChecked; }
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
}
public bool IsThreeState
{
get => GetValue(IsThreeStateProperty);
set => SetValue(IsThreeStateProperty, value);
}
protected override void OnClick()
{
Toggle();
@ -37,7 +48,16 @@ namespace Avalonia.Controls.Primitives
protected virtual void Toggle()
{
IsChecked = !IsChecked;
if (IsChecked.HasValue)
if (IsChecked.Value)
if (IsThreeState)
IsChecked = null;
else
IsChecked = false;
else
IsChecked = true;
else
IsChecked = false;
}
}
}

9
src/Avalonia.Controls/RadioButton.cs

@ -17,17 +17,17 @@ namespace Avalonia.Controls
protected override void Toggle()
{
if (!IsChecked)
if (!IsChecked.GetValueOrDefault())
{
IsChecked = true;
}
}
private void IsCheckedChanged(bool value)
private void IsCheckedChanged(bool? value)
{
var parent = this.GetVisualParent();
if (value && parent != null)
if (value.GetValueOrDefault() && parent != null)
{
var siblings = parent
.GetVisualChildren()
@ -36,7 +36,8 @@ namespace Avalonia.Controls
foreach (var sibling in siblings)
{
sibling.IsChecked = false;
if (sibling.IsChecked.GetValueOrDefault())
sibling.IsChecked = false;
}
}
}

3
src/Avalonia.Remote.Protocol/BsonStreamTransport.cs

@ -64,7 +64,6 @@ namespace Avalonia.Remote.Protocol
async Task Reader()
{
Task.Yield();
try
{
while (true)
@ -147,4 +146,4 @@ namespace Avalonia.Remote.Protocol
public event Action<IAvaloniaRemoteTransportConnection, object> OnMessage;
public event Action<IAvaloniaRemoteTransportConnection, Exception> OnException;
}
}
}

2
src/Avalonia.Styling/Styling/Setter.cs

@ -174,7 +174,7 @@ namespace Avalonia.Styling
}
else
{
return sourceInstance.WithPriority(BindingPriority.StyleTrigger);
return sourceInstance.WithPriority(BindingPriority.Style);
}
}
}

31
src/Avalonia.Themes.Default/CheckBox.xaml

@ -12,14 +12,23 @@
Width="18"
Height="18"
VerticalAlignment="Center">
<Path Name="checkMark"
Fill="{DynamicResource HighlightBrush}"
Width="11"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"/>
<Panel>
<Path Name="checkMark"
Fill="{DynamicResource HighlightBrush}"
Width="11"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 1145.607177734375,430 C1145.607177734375,430 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1141.449951171875,435.0772705078125 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1139.232177734375,433.0999755859375 1138,434.5538330078125 1138,434.5538330078125 1138,434.5538330078125 1141.482177734375,438 1141.482177734375,438 1141.482177734375,438 1141.96875,437.9375 1141.96875,437.9375 1141.96875,437.9375 1147,431.34619140625 1147,431.34619140625 1147,431.34619140625 1145.607177734375,430 1145.607177734375,430 z"/>
<Rectangle Name="indeterminateMark"
Fill="{DynamicResource HighlightBrush}"
Width="10"
Height="10"
Stretch="Uniform"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Panel>
</Border>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
@ -37,9 +46,15 @@
<Style Selector="CheckBox /template/ Path#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox /template/ Rectangle#indeterminateMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="CheckBox:checked /template/ Path#checkMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CheckBox:indeterminate /template/ Rectangle#indeterminateMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="CheckBox:disabled /template/ Border#border">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>

14
src/Avalonia.Themes.Default/RadioButton.xaml

@ -20,6 +20,14 @@
UseLayoutRounding="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<Ellipse Name="indeterminateMark"
Fill="{DynamicResource ThemeAccentBrush}"
Width="10"
Height="10"
Stretch="Uniform"
UseLayoutRounding="False"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<ContentPresenter Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
@ -36,9 +44,15 @@
<Style Selector="RadioButton /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="RadioButton /template/ Ellipse#indeterminateMark">
<Setter Property="IsVisible" Value="False"/>
</Style>
<Style Selector="RadioButton:checked /template/ Ellipse#checkMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="RadioButton:indeterminate /template/ Ellipse#indeterminateMark">
<Setter Property="IsVisible" Value="True"/>
</Style>
<Style Selector="RadioButton:disabled /template/ Ellipse#border">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style>

25
src/Avalonia.Visuals/Animation/PageSlide.cs

@ -15,6 +15,15 @@ namespace Avalonia.Animation
/// </summary>
public class PageSlide : IPageTransition
{
/// <summary>
/// The axis on which the PageSlide should occur
/// </summary>
public enum SlideAxis
{
Horizontal,
Vertical
}
/// <summary>
/// Initializes a new instance of the <see cref="PageSlide"/> class.
/// </summary>
@ -26,9 +35,11 @@ namespace Avalonia.Animation
/// Initializes a new instance of the <see cref="PageSlide"/> class.
/// </summary>
/// <param name="duration">The duration of the animation.</param>
public PageSlide(TimeSpan duration)
/// <param name="orientation">The axis on which the animation should occur</param>
public PageSlide(TimeSpan duration, SlideAxis orientation = SlideAxis.Horizontal)
{
Duration = duration;
Orientation = orientation;
}
/// <summary>
@ -36,6 +47,11 @@ namespace Avalonia.Animation
/// </summary>
public TimeSpan Duration { get; set; }
/// <summary>
/// Gets the duration of the animation.
/// </summary>
public SlideAxis Orientation { get; set; }
/// <summary>
/// Starts the animation.
/// </summary>
@ -55,7 +71,8 @@ namespace Avalonia.Animation
{
var tasks = new List<Task>();
var parent = GetVisualParent(from, to);
var distance = parent.Bounds.Width;
var distance = Orientation == SlideAxis.Horizontal ? parent.Bounds.Width : parent.Bounds.Height;
var translateProperty = Orientation == SlideAxis.Horizontal ? TranslateTransform.XProperty : TranslateTransform.YProperty;
if (from != null)
{
@ -63,7 +80,7 @@ namespace Avalonia.Animation
from.RenderTransform = transform;
tasks.Add(Animate.Property(
transform,
TranslateTransform.XProperty,
translateProperty,
0.0,
forward ? -distance : distance,
LinearEasing.For<double>(),
@ -77,7 +94,7 @@ namespace Avalonia.Animation
to.IsVisible = true;
tasks.Add(Animate.Property(
transform,
TranslateTransform.XProperty,
translateProperty,
forward ? distance : -distance,
0.0,
LinearEasing.For<double>(),

10
tests/Avalonia.Controls.UnitTests/ClassesTests.cs

@ -161,5 +161,15 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(new[] { ":baz" }, target);
}
[Fact]
public void RemoveAll_Should_Remove_Classes()
{
var target = new Classes("foo", "bar", "baz");
target.RemoveAll(new[] { "bar", "baz" });
Assert.Equal(new[] { "foo" }, target);
}
}
}

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

@ -0,0 +1,57 @@
using Avalonia.Markup.Xaml.Data;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.Primitives.UnitTests
{
public class ToggleButtonTests
{
private const string uncheckedClass = ":unchecked";
private const string checkedClass = ":checked";
private const string indeterminateClass = ":indeterminate";
[Theory]
[InlineData(false, uncheckedClass, false)]
[InlineData(false, uncheckedClass, true)]
[InlineData(true, checkedClass, false)]
[InlineData(true, checkedClass, true)]
[InlineData(null, indeterminateClass, false)]
[InlineData(null, indeterminateClass, true)]
public void ToggleButton_Has_Correct_Class_According_To_Is_Checked(bool? isChecked, string expectedClass, bool isThreeState)
{
var toggleButton = new ToggleButton();
toggleButton.IsThreeState = isThreeState;
toggleButton.IsChecked = isChecked;
Assert.Contains(expectedClass, toggleButton.Classes);
}
[Fact]
public void ToggleButton_Is_Checked_Binds_To_Bool()
{
var toggleButton = new ToggleButton();
var source = new Class1();
toggleButton.DataContext = source;
toggleButton.Bind(ToggleButton.IsCheckedProperty, new Binding("Foo"));
source.Foo = true;
Assert.True(toggleButton.IsChecked);
source.Foo = false;
Assert.False(toggleButton.IsChecked);
}
private class Class1 : NotifyingBase
{
private bool _foo;
public bool Foo
{
get { return _foo; }
set { _foo = value; RaisePropertyChanged(); }
}
}
}
}

36
tests/Avalonia.Controls.UnitTests/RadioButtonTests.cs

@ -0,0 +1,36 @@
using Avalonia.Markup.Xaml.Data;
using Avalonia.UnitTests;
using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class RadioButtonTests
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Indeterminate_RadioButton_Is_Not_Unchecked_After_Checking_Other_Radio_Button(bool isThreeState)
{
var panel = new Panel();
var radioButton1 = new RadioButton();
radioButton1.IsThreeState = false;
radioButton1.IsChecked = false;
var radioButton2 = new RadioButton();
radioButton2.IsThreeState = isThreeState;
radioButton2.IsChecked = null;
panel.Children.Add(radioButton1);
panel.Children.Add(radioButton2);
Assert.Null(radioButton2.IsChecked);
radioButton1.IsChecked = true;
Assert.True(radioButton1.IsChecked);
Assert.Null(radioButton2.IsChecked);
}
}
}

71
tests/Avalonia.Styling.UnitTests/SetterTests.cs

@ -86,6 +86,77 @@ namespace Avalonia.Styling.UnitTests
Assert.Null(control.Tag);
}
[Fact]
public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, "foo");
setter.Apply(style, control.Object, null);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.Style));
}
[Fact]
public void Setter_Should_Apply_Value_With_Activator_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, "foo");
var activator = new Subject<bool>();
setter.Apply(style, control.Object, activator);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.StyleTrigger));
}
[Fact]
public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, CreateMockBinding(TextBlock.TextProperty));
setter.Apply(style, control.Object, null);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.Style));
}
[Fact]
public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority()
{
var control = new Mock<IStyleable>();
var style = Mock.Of<Style>();
var setter = new Setter(TextBlock.TextProperty, CreateMockBinding(TextBlock.TextProperty));
var activator = new Subject<bool>();
setter.Apply(style, control.Object, activator);
control.Verify(x => x.Bind(
TextBlock.TextProperty,
It.IsAny<IObservable<object>>(),
BindingPriority.StyleTrigger));
}
private IBinding CreateMockBinding(AvaloniaProperty property)
{
var subject = new Subject<object>();
var descriptor = InstancedBinding.OneWay(subject);
var binding = Mock.Of<IBinding>(x =>
x.Initiate(It.IsAny<IAvaloniaObject>(), property, null, false) == descriptor);
return binding;
}
private class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)

Loading…
Cancel
Save