Browse Source

Merge branch 'master' into feature/fluent-progress-bar

# Conflicts:
#	samples/ControlCatalog/App.xaml
#	samples/ControlCatalog/MainView.xaml.cs
#	samples/ControlCatalog/MainWindow.xaml
pull/4074/head
Dan Walmsley 6 years ago
parent
commit
698b64d911
  1. 5
      samples/ControlCatalog/App.xaml
  2. 53
      samples/ControlCatalog/App.xaml.cs
  3. 9
      samples/ControlCatalog/MainView.xaml
  4. 20
      samples/ControlCatalog/MainView.xaml.cs
  5. 2
      samples/ControlCatalog/MainWindow.xaml
  6. 6
      samples/ControlCatalog/Pages/ButtonPage.xaml
  7. 20
      samples/ControlCatalog/Pages/ButtonPage.xaml.cs
  8. 18
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml
  9. 14
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  10. 1
      samples/ControlCatalog/Pages/TreeViewPage.xaml
  11. 11
      samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs
  12. 15
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  13. 9
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  14. 2
      src/Avalonia.Base/Properties/AssemblyInfo.cs
  15. 94
      src/Avalonia.Base/Utilities/MathUtilities.cs
  16. 21
      src/Avalonia.Controls/AutoCompleteBox.cs
  17. 4
      src/Avalonia.Controls/Calendar/Calendar.cs
  18. 134
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  19. 4
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  20. 35
      src/Avalonia.Controls/Grid.cs
  21. 20
      src/Avalonia.Controls/IndexPath.cs
  22. 47
      src/Avalonia.Controls/IndexRange.cs
  23. 76
      src/Avalonia.Controls/SelectionModel.cs
  24. 2
      src/Avalonia.Controls/SelectionModelChangeSet.cs
  25. 22
      src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs
  26. 27
      src/Avalonia.Controls/SelectionNode.cs
  27. 3
      src/Avalonia.Controls/Slider.cs
  28. 18
      src/Avalonia.Controls/TreeView.cs
  29. 3
      src/Avalonia.Controls/Utils/BorderRenderHelper.cs
  30. 8
      src/Avalonia.Controls/Utils/SelectionTreeHelper.cs
  31. 17
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs
  32. 38
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs
  33. 24
      src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs
  34. 2
      src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml
  35. 4
      src/Avalonia.Themes.Default/CalendarDatePicker.xaml
  36. 2
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  37. 51
      src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml
  38. 52
      src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml
  39. 81
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  40. 80
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
  41. 1
      src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml
  42. 1
      src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml
  43. 36
      src/Avalonia.Themes.Fluent/Button.xaml
  44. 4
      src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml
  45. 2
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  46. 100
      src/Avalonia.Themes.Fluent/RepeatButton.xaml
  47. 8
      src/Avalonia.Themes.Fluent/Slider.xaml
  48. 113
      src/Avalonia.Themes.Fluent/ToggleButton.xaml
  49. 5
      src/Avalonia.Visuals/Media/DrawingContext.cs
  50. 3
      src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
  51. 8
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  52. 2
      src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
  53. 2
      tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs
  54. 119
      tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs
  55. 47
      tests/Avalonia.Benchmarks/Data/AccessorTestObject.cs
  56. 54
      tests/Avalonia.Benchmarks/Data/PropertyAccessorBenchmarks.cs
  57. 60
      tests/Avalonia.Benchmarks/Data/PropertyAccessorPluginBenchmarks.cs
  58. 14
      tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
  59. 82
      tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs
  60. 146
      tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs
  61. 24
      tests/Avalonia.Controls.UnitTests/TabControlTests.cs
  62. 548
      tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
  63. 44
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs
  64. 2
      tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs
  65. 53
      tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

5
samples/ControlCatalog/App.xaml

@ -1,10 +1,7 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.App">
<Application.Styles>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentDark.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/FluentTheme.xaml"/>
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="{DynamicResource FontSizeLarge}"/>

53
samples/ControlCatalog/App.xaml.cs

@ -1,14 +1,67 @@
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.Styling;
using Avalonia.Styling;
namespace ControlCatalog
{
public class App : Application
{
public static Styles FluentDark = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Fluent.Accents.FluentDark.xaml?assembly=Avalonia.Themes.Fluent")
},
};
public static Styles FluentLight = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Fluent.Accents.FluentLight.xaml?assembly=Avalonia.Themes.Fluent")
},
};
public static Styles DefaultLight = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseLight.xaml?assembly=Avalonia.Themes.Default")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
},
};
public static Styles DefaultDark = new Styles
{
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default")
},
new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
{
Source = new Uri("resm:Avalonia.Themes.Default.DefaultTheme.xaml?assembly=Avalonia.Themes.Default")
},
};
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
Styles.Insert(0, FluentDark);
}
public override void OnFrameworkInitializationCompleted()

9
samples/ControlCatalog/MainView.xaml

@ -29,7 +29,8 @@
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<pages:DataGridPage/>
</TabItem>
<TabItem Header="DatePicker"><pages:DatePickerPage/></TabItem>
<TabItem Header="CalendarDatePicker">
<pages:CalendarDatePickerPage/></TabItem>
<TabItem Header="Drag+Drop"><pages:DragAndDropPage/></TabItem>
<TabItem Header="Expander"><pages:ExpanderPage/></TabItem>
<TabItem Header="Image"
@ -67,8 +68,10 @@
<ComboBoxItem>Full Decorations</ComboBoxItem>
</ComboBox>
<ComboBox x:Name="Themes" SelectedIndex="0">
<ComboBoxItem>Light</ComboBoxItem>
<ComboBoxItem>Dark</ComboBoxItem>
<ComboBoxItem>Fluent - Dark</ComboBoxItem>
<ComboBoxItem>Fluent - Light</ComboBoxItem>
<ComboBoxItem>Simple - Light</ComboBoxItem>
<ComboBoxItem>Simple - Dark</ComboBoxItem>
</ComboBox>
<ComboBox x:Name="TransparencyLevels" SelectedIndex="0">
<ComboBoxItem>None</ComboBoxItem>

20
samples/ControlCatalog/MainView.xaml.cs

@ -32,6 +32,26 @@ namespace ControlCatalog
}
var themes = this.Find<ComboBox>("Themes");
themes.SelectionChanged += (sender, e) =>
{
switch (themes.SelectedIndex)
{
case 0:
Application.Current.Styles[0] = App.FluentDark;
break;
case 1:
Application.Current.Styles[0] = App.FluentLight;
break;
case 2:
Application.Current.Styles[0] = App.DefaultLight;
break;
case 3:
Application.Current.Styles[0] = App.DefaultDark;
break;
}
};
var decorations = this.Find<ComboBox>("Decorations");
decorations.SelectionChanged += (sender, e) =>
{

2
samples/ControlCatalog/MainWindow.xaml

@ -7,7 +7,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:ControlCatalog.ViewModels"
xmlns:v="clr-namespace:ControlCatalog.Views"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}">
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}" Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}">
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="File">

6
samples/ControlCatalog/Pages/ButtonPage.xaml

@ -24,7 +24,11 @@
</Style>
</Button.Styles>
</Button>
</StackPanel>
<RepeatButton Name="RepeatButton">
<TextBlock Name="RepeatButtonTextBlock" Text="Repeat Button: 0" />
</RepeatButton>
<ToggleButton Content="Toggle Button"/>
</StackPanel>
<StackPanel Orientation="Vertical" Spacing="8" Width="150">
<Button BorderThickness="0">No Border</Button>

20
samples/ControlCatalog/Pages/ButtonPage.xaml.cs

@ -5,5 +5,25 @@ namespace ControlCatalog.Pages
{
public class ButtonPage : UserControl
{
private int repeatButtonClickCount = 0;
public ButtonPage()
{
InitializeComponent();
this.FindControl<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void OnRepeatButtonClick(object sender, object args)
{
repeatButtonClickCount++;
var textBlock = this.FindControl<TextBlock>("RepeatButtonTextBlock");
textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
}
}
}

18
samples/ControlCatalog/Pages/DatePickerPage.xaml → samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml

@ -1,8 +1,8 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DatePickerPage">
x:Class="ControlCatalog.Pages.CalendarDatePickerPage">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h1">DatePicker</TextBlock>
<TextBlock Classes="h1">CalendarDatePicker</TextBlock>
<TextBlock Classes="h2">A control for selecting dates with a calendar drop-down</TextBlock>
<StackPanel Orientation="Horizontal"
@ -12,34 +12,34 @@
<StackPanel Orientation="Vertical"
Width="200">
<TextBlock Text="SelectedDateFormat: Short"/>
<DatePicker Name="DatePicker1"
<CalendarDatePicker Name="DatePicker1"
SelectedDateFormat="Short"
Margin="0,0,0,8"/>
<TextBlock Text="SelectedDateFormat: Long"/>
<DatePicker Name="DatePicker2"
<CalendarDatePicker Name="DatePicker2"
SelectedDateFormat="Long"
Margin="0,0,0,8"/>
<TextBlock Text="SelectedDateFormat: Custom"/>
<DatePicker Name="DatePicker3"
<CalendarDatePicker Name="DatePicker3"
SelectedDateFormat="Custom"
CustomDateFormatString="ddd, MMM d"
Margin="0,0,0,8"/>
<TextBlock Text="Blackout Dates"/>
<DatePicker Name="DatePicker4"
<CalendarDatePicker Name="DatePicker4"
Margin="0,0,0,8"/>
<DatePicker Margin="0,0,0,8"
<CalendarDatePicker Margin="0,0,0,8"
Watermark="Watermark"/>
<DatePicker Margin="0,0,0,8"
<CalendarDatePicker Margin="0,0,0,8"
Name="DatePicker5"
Watermark="Floating Watermark"
UseFloatingWatermark="True"/>
<TextBlock Text="Disabled"/>
<DatePicker IsEnabled="False"/>
<CalendarDatePicker IsEnabled="False"/>
</StackPanel>
</StackPanel>

14
samples/ControlCatalog/Pages/DatePickerPage.xaml.cs → samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@ -4,17 +4,17 @@ using System;
namespace ControlCatalog.Pages
{
public class DatePickerPage : UserControl
public class CalendarDatePickerPage : UserControl
{
public DatePickerPage()
public CalendarDatePickerPage()
{
InitializeComponent();
var dp1 = this.FindControl<DatePicker>("DatePicker1");
var dp2 = this.FindControl<DatePicker>("DatePicker2");
var dp3 = this.FindControl<DatePicker>("DatePicker3");
var dp4 = this.FindControl<DatePicker>("DatePicker4");
var dp5 = this.FindControl<DatePicker>("DatePicker5");
var dp1 = this.FindControl<CalendarDatePicker>("DatePicker1");
var dp2 = this.FindControl<CalendarDatePicker>("DatePicker2");
var dp3 = this.FindControl<CalendarDatePicker>("DatePicker3");
var dp4 = this.FindControl<CalendarDatePicker>("DatePicker4");
var dp5 = this.FindControl<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);

1
samples/ControlCatalog/Pages/TreeViewPage.xaml

@ -20,6 +20,7 @@
<Button Command="{Binding AddItemCommand}">Add</Button>
<Button Command="{Binding RemoveItemCommand}">Remove</Button>
<Button Command="{Binding SelectRandomItemCommand}">Select Random</Button>
<ComboBox SelectedIndex="{Binding SelectionMode, Mode=TwoWay}">
<ComboBoxItem>Single</ComboBoxItem>

11
samples/ControlCatalog/ViewModels/TreeViewPageViewModel.cs

@ -23,12 +23,14 @@ namespace ControlCatalog.ViewModels
AddItemCommand = ReactiveCommand.Create(AddItem);
RemoveItemCommand = ReactiveCommand.Create(RemoveItem);
SelectRandomItemCommand = ReactiveCommand.Create(SelectRandomItem);
}
public ObservableCollection<Node> Items { get; }
public SelectionModel Selection { get; }
public ReactiveCommand<Unit, Unit> AddItemCommand { get; }
public ReactiveCommand<Unit, Unit> RemoveItemCommand { get; }
public ReactiveCommand<Unit, Unit> SelectRandomItemCommand { get; }
public SelectionMode SelectionMode
{
@ -74,6 +76,15 @@ namespace ControlCatalog.ViewModels
}
}
private void SelectRandomItem()
{
var random = new Random();
var depth = random.Next(4);
var indexes = Enumerable.Range(0, 4).Select(x => random.Next(10));
var path = new IndexPath(indexes);
Selection.SelectedIndex = path;
}
private void SelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
{
var selected = string.Join(",", e.SelectedIndices);

15
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -12,7 +12,7 @@ namespace Avalonia.Data.Core.Plugins
public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin
{
/// <inheritdoc/>
public bool Match(object obj, string propertyName) => true;
public bool Match(object obj, string propertyName) => GetPropertyWithName(obj.GetType(), propertyName) != null;
/// <summary>
/// Starts monitoring the value of a property on an object.
@ -30,10 +30,7 @@ namespace Avalonia.Data.Core.Plugins
reference.TryGetTarget(out object instance);
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance;
var p = instance.GetType().GetProperty(propertyName, bindingFlags);
var p = GetPropertyWithName(instance.GetType(), propertyName);
if (p != null)
{
@ -47,6 +44,14 @@ namespace Avalonia.Data.Core.Plugins
}
}
private static PropertyInfo GetPropertyWithName(Type type, string propertyName)
{
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance;
return type.GetProperty(propertyName, bindingFlags);
}
private class Accessor : PropertyAccessorBase, IWeakSubscriber<PropertyChangedEventArgs>
{
private readonly WeakReference<object> _reference;

9
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -62,8 +62,13 @@ namespace Avalonia.Data.Core
if (accessor == null)
{
throw new NotSupportedException(
$"Could not find a matching property accessor for {PropertyName}.");
reference.TryGetTarget(out object instance);
var message = $"Could not find a matching property accessor for '{PropertyName}' on '{instance}'";
var exception = new MissingMemberException(message);
accessor = new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
_accessor = accessor;

2
src/Avalonia.Base/Properties/AssemblyInfo.cs

@ -7,4 +7,4 @@ using Avalonia.Metadata;
[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Data.Converters")]
[assembly: InternalsVisibleTo("Avalonia.Base.UnitTests")]
[assembly: InternalsVisibleTo("Avalonia.UnitTests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

94
src/Avalonia.Base/Utilities/MathUtilities.cs

@ -8,6 +8,11 @@ namespace Avalonia.Utilities
/// </summary>
public static class MathUtilities
{
// smallest such that 1.0+DoubleEpsilon != 1.0
private const double DoubleEpsilon = 2.2204460492503131e-016;
private const float FloatEpsilon = 1.192092896e-07F;
/// <summary>
/// AreClose - Returns whether or not two doubles are "close". That is, whether or
/// not they are within epsilon of each other.
@ -18,11 +23,26 @@ namespace Avalonia.Utilities
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * double.Epsilon;
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DoubleEpsilon;
double delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// AreClose - Returns whether or not two floats are "close". That is, whether or
/// not they are within epsilon of each other.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool AreClose(float value1, float value2)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
float eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0f) * FloatEpsilon;
float delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
/// <summary>
/// LessThan - Returns whether or not the first double is less than the second double.
/// That is, whether or not the first is strictly less than *and* not within epsilon of
@ -35,6 +55,18 @@ namespace Avalonia.Utilities
return (value1 < value2) && !AreClose(value1, value2);
}
/// <summary>
/// LessThan - Returns whether or not the first float is less than the second float.
/// That is, whether or not the first is strictly less than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first single float to compare. </param>
/// <param name="value2"> The second single float to compare. </param>
public static bool LessThan(float value1, float value2)
{
return (value1 < value2) && !AreClose(value1, value2);
}
/// <summary>
/// GreaterThan - Returns whether or not the first double is greater than the second double.
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
@ -47,6 +79,18 @@ namespace Avalonia.Utilities
return (value1 > value2) && !AreClose(value1, value2);
}
/// <summary>
/// GreaterThan - Returns whether or not the first float is greater than the second float.
/// That is, whether or not the first is strictly greater than *and* not within epsilon of
/// the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool GreaterThan(float value1, float value2)
{
return (value1 > value2) && !AreClose(value1, value2);
}
/// <summary>
/// LessThanOrClose - Returns whether or not the first double is less than or close to
/// the second double. That is, whether or not the first is strictly less than or within
@ -59,6 +103,18 @@ namespace Avalonia.Utilities
return (value1 < value2) || AreClose(value1, value2);
}
/// <summary>
/// LessThanOrClose - Returns whether or not the first float is less than or close to
/// the second float. That is, whether or not the first is strictly less than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool LessThanOrClose(float value1, float value2)
{
return (value1 < value2) || AreClose(value1, value2);
}
/// <summary>
/// GreaterThanOrClose - Returns whether or not the first double is greater than or close to
/// the second double. That is, whether or not the first is strictly greater than or within
@ -71,6 +127,18 @@ namespace Avalonia.Utilities
return (value1 > value2) || AreClose(value1, value2);
}
/// <summary>
/// GreaterThanOrClose - Returns whether or not the first float is greater than or close to
/// the second float. That is, whether or not the first is strictly greater than or within
/// epsilon of the other number.
/// </summary>
/// <param name="value1"> The first float to compare. </param>
/// <param name="value2"> The second float to compare. </param>
public static bool GreaterThanOrClose(float value1, float value2)
{
return (value1 > value2) || AreClose(value1, value2);
}
/// <summary>
/// IsOne - Returns whether or not the double is "close" to 1. Same as AreClose(double, 1),
/// but this is faster.
@ -78,7 +146,17 @@ namespace Avalonia.Utilities
/// <param name="value"> The double to compare to 1. </param>
public static bool IsOne(double value)
{
return Math.Abs(value - 1.0) < 10.0 * double.Epsilon;
return Math.Abs(value - 1.0) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// IsOne - Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 1. </param>
public static bool IsOne(float value)
{
return Math.Abs(value - 1.0f) < 10.0f * FloatEpsilon;
}
/// <summary>
@ -88,7 +166,17 @@ namespace Avalonia.Utilities
/// <param name="value"> The double to compare to 0. </param>
public static bool IsZero(double value)
{
return Math.Abs(value) < 10.0 * double.Epsilon;
return Math.Abs(value) < 10.0 * DoubleEpsilon;
}
/// <summary>
/// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
/// but this is faster.
/// </summary>
/// <param name="value"> The float to compare to 0. </param>
public static bool IsZero(float value)
{
return Math.Abs(value) < 10.0f * FloatEpsilon;
}
/// <summary>

21
src/Avalonia.Controls/AutoCompleteBox.cs

@ -468,10 +468,11 @@ namespace Avalonia.Controls
/// <see cref="P:Avalonia.Controls.AutoCompleteBox.Text" />
/// dependency property.</value>
public static readonly DirectProperty<AutoCompleteBox, string> TextProperty =
AvaloniaProperty.RegisterDirect<AutoCompleteBox, string>(
nameof(Text),
TextBlock.TextProperty.AddOwnerWithDataValidation<AutoCompleteBox>(
o => o.Text,
(o, v) => o.Text = v);
(o, v) => o.Text = v,
defaultBindingMode: BindingMode.TwoWay,
enableDataValidation: true);
/// <summary>
/// Identifies the
@ -1244,6 +1245,20 @@ namespace Avalonia.Controls
base.OnApplyTemplate(e);
}
/// <summary>
/// Called to update the validation state for properties for which data validation is
/// enabled.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The new binding value for the property.</param>
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{
if (property == TextProperty)
{
DataValidationErrors.SetError(this, value.Error);
}
}
/// <summary>
/// Provides handling for the

4
src/Avalonia.Controls/Calendar/Calendar.cs

@ -998,10 +998,10 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets a value indicating whether DatePicker should change its
/// Gets or sets a value indicating whether CalendarDatePicker should change its
/// DisplayDate because of a SelectedDate change on its Calendar.
/// </summary>
internal bool DatePickerDisplayDateFlag { get; set; }
internal bool CalendarDatePickerDisplayDateFlag { get; set; }
internal CalendarDayButton FindDayButtonFromDay(DateTime day)
{

134
src/Avalonia.Controls/Calendar/DatePicker.cs → src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -16,29 +16,29 @@ namespace Avalonia.Controls
{
/// <summary>
/// Provides data for the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </summary>
public class DatePickerDateValidationErrorEventArgs : EventArgs
public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
{
private bool _throwException;
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.DatePickerDateValidationErrorEventArgs" />
/// <see cref="T:Avalonia.Controls.CalendarDatePickerDateValidationErrorEventArgs" />
/// class.
/// </summary>
/// <param name="exception">
/// The initial exception from the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </param>
/// <param name="text">
/// The text that caused the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </param>
public DatePickerDateValidationErrorEventArgs(Exception exception, string text)
public CalendarDatePickerDateValidationErrorEventArgs(Exception exception, string text)
{
this.Text = text;
this.Exception = exception;
@ -46,7 +46,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the initial exception associated with the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </summary>
/// <value>
@ -56,7 +56,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets the text that caused the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </summary>
/// <value>
@ -66,7 +66,7 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets a value indicating whether
/// <see cref="P:Avalonia.Controls.DatePickerDateValidationErrorEventArgs.Exception" />
/// <see cref="P:Avalonia.Controls.CalendarDatePickerDateValidationErrorEventArgs.Exception" />
/// should be thrown.
/// </summary>
/// <value>
@ -74,7 +74,7 @@ namespace Avalonia.Controls
/// </value>
/// <exception cref="T:System.ArgumentException">
/// If set to true and
/// <see cref="P:Avalonia.Controls.DatePickerDateValidationErrorEventArgs.Exception" />
/// <see cref="P:Avalonia.Controls.CalendarDatePickerDateValidationErrorEventArgs.Exception" />
/// is null.
/// </exception>
public bool ThrowException
@ -93,9 +93,9 @@ namespace Avalonia.Controls
/// <summary>
/// Specifies date formats for a
/// <see cref="T:Avalonia.Controls.DatePicker" />.
/// <see cref="T:Avalonia.Controls.CalendarDatePicker" />.
/// </summary>
public enum DatePickerFormat
public enum CalendarDatePickerFormat
{
/// <summary>
/// Specifies that the date should be displayed using unabbreviated days
@ -115,7 +115,7 @@ namespace Avalonia.Controls
Custom = 2
}
public class DatePicker : TemplatedControl
public class CalendarDatePicker : TemplatedControl
{
private const string ElementTextBox = "PART_TextBox";
private const string ElementButton = "PART_Button";
@ -154,59 +154,59 @@ namespace Avalonia.Controls
/// </value>
public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }
public static readonly DirectProperty<DatePicker, DateTime> DisplayDateProperty =
AvaloniaProperty.RegisterDirect<DatePicker, DateTime>(
public static readonly DirectProperty<CalendarDatePicker, DateTime> DisplayDateProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime>(
nameof(DisplayDate),
o => o.DisplayDate,
(o, v) => o.DisplayDate = v);
public static readonly DirectProperty<DatePicker, DateTime?> DisplayDateStartProperty =
AvaloniaProperty.RegisterDirect<DatePicker, DateTime?>(
public static readonly DirectProperty<CalendarDatePicker, DateTime?> DisplayDateStartProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime?>(
nameof(DisplayDateStart),
o => o.DisplayDateStart,
(o, v) => o.DisplayDateStart = v);
public static readonly DirectProperty<DatePicker, DateTime?> DisplayDateEndProperty =
AvaloniaProperty.RegisterDirect<DatePicker, DateTime?>(
public static readonly DirectProperty<CalendarDatePicker, DateTime?> DisplayDateEndProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime?>(
nameof(DisplayDateEnd),
o => o.DisplayDateEnd,
(o, v) => o.DisplayDateEnd = v);
public static readonly StyledProperty<DayOfWeek> FirstDayOfWeekProperty =
AvaloniaProperty.Register<DatePicker, DayOfWeek>(nameof(FirstDayOfWeek));
AvaloniaProperty.Register<CalendarDatePicker, DayOfWeek>(nameof(FirstDayOfWeek));
public static readonly DirectProperty<DatePicker, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<DatePicker, bool>(
public static readonly DirectProperty<CalendarDatePicker, bool> IsDropDownOpenProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, bool>(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
public static readonly StyledProperty<bool> IsTodayHighlightedProperty =
AvaloniaProperty.Register<DatePicker, bool>(nameof(IsTodayHighlighted));
public static readonly DirectProperty<DatePicker, DateTime?> SelectedDateProperty =
AvaloniaProperty.RegisterDirect<DatePicker, DateTime?>(
AvaloniaProperty.Register<CalendarDatePicker, bool>(nameof(IsTodayHighlighted));
public static readonly DirectProperty<CalendarDatePicker, DateTime?> SelectedDateProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, DateTime?>(
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v);
public static readonly StyledProperty<DatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<DatePicker, DatePickerFormat>(
public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>(
nameof(SelectedDateFormat),
defaultValue: DatePickerFormat.Short,
defaultValue: CalendarDatePickerFormat.Short,
validate: IsValidSelectedDateFormat);
public static readonly StyledProperty<string> CustomDateFormatStringProperty =
AvaloniaProperty.Register<DatePicker, string>(
AvaloniaProperty.Register<CalendarDatePicker, string>(
nameof(CustomDateFormatString),
defaultValue: "d",
validate: IsValidDateFormatString);
public static readonly DirectProperty<DatePicker, string> TextProperty =
AvaloniaProperty.RegisterDirect<DatePicker, string>(
public static readonly DirectProperty<CalendarDatePicker, string> TextProperty =
AvaloniaProperty.RegisterDirect<CalendarDatePicker, string>(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
public static readonly StyledProperty<string> WatermarkProperty =
TextBox.WatermarkProperty.AddOwner<DatePicker>();
TextBox.WatermarkProperty.AddOwner<CalendarDatePicker>();
public static readonly StyledProperty<bool> UseFloatingWatermarkProperty =
TextBox.UseFloatingWatermarkProperty.AddOwner<DatePicker>();
TextBox.UseFloatingWatermarkProperty.AddOwner<CalendarDatePicker>();
/// <summary>
@ -218,9 +218,9 @@ namespace Avalonia.Controls
/// </value>
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// The specified date is not in the range defined by
/// <see cref="P:Avalonia.Controls.DatePicker.DisplayDateStart" />
/// <see cref="P:Avalonia.Controls.CalendarDatePicker.DisplayDateStart" />
/// and
/// <see cref="P:Avalonia.Controls.DatePicker.DisplayDateEnd" />.
/// <see cref="P:Avalonia.Controls.CalendarDatePicker.DisplayDateEnd" />.
/// </exception>
public DateTime DisplayDate
{
@ -320,7 +320,7 @@ namespace Avalonia.Controls
/// <exception cref="T:System.ArgumentOutOfRangeException">
/// An specified format is not valid.
/// </exception>
public DatePickerFormat SelectedDateFormat
public CalendarDatePickerFormat SelectedDateFormat
{
get { return GetValue(SelectedDateFormatProperty); }
set { SetValue(SelectedDateFormatProperty, value); }
@ -380,33 +380,33 @@ namespace Avalonia.Controls
/// Occurs when <see cref="P:Avalonia.Controls.DatePicker.Text" />
/// is assigned a value that cannot be interpreted as a date.
/// </summary>
public event EventHandler<DatePickerDateValidationErrorEventArgs> DateValidationError;
public event EventHandler<CalendarDatePickerDateValidationErrorEventArgs> DateValidationError;
/// <summary>
/// Occurs when the
/// <see cref="P:Avalonia.Controls.DatePicker.SelectedDate" />
/// <see cref="P:Avalonia.Controls.CalendarDatePicker.SelectedDate" />
/// property is changed.
/// </summary>
public event EventHandler<SelectionChangedEventArgs> SelectedDateChanged;
static DatePicker()
static CalendarDatePicker()
{
FocusableProperty.OverrideDefaultValue<DatePicker>(true);
DisplayDateProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnDisplayDateEndChanged(e));
IsDropDownOpenProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnIsDropDownOpenChanged(e));
SelectedDateProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnSelectedDateChanged(e));
SelectedDateFormatProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnSelectedDateFormatChanged(e));
CustomDateFormatStringProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnCustomDateFormatStringChanged(e));
TextProperty.Changed.AddClassHandler<DatePicker>((x,e) => x.OnTextChanged(e));
FocusableProperty.OverrideDefaultValue<CalendarDatePicker>(true);
DisplayDateProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateChanged(e));
DisplayDateStartProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateStartChanged(e));
DisplayDateEndProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnDisplayDateEndChanged(e));
IsDropDownOpenProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnIsDropDownOpenChanged(e));
SelectedDateProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateChanged(e));
SelectedDateFormatProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnSelectedDateFormatChanged(e));
CustomDateFormatStringProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnCustomDateFormatStringChanged(e));
TextProperty.Changed.AddClassHandler<CalendarDatePicker>((x,e) => x.OnTextChanged(e));
}
/// <summary>
/// Initializes a new instance of the
/// <see cref="T:Avalonia.Controls.DatePicker" /> class.
/// </summary>
public DatePicker()
public CalendarDatePicker()
{
FirstDayOfWeek = DateTimeHelper.GetCurrentDateFormat().FirstDayOfWeek;
_defaultText = string.Empty;
@ -662,12 +662,12 @@ namespace Avalonia.Controls
// change is coming from the Calendar UI itself, so, we
// shouldn't change the DisplayDate since it will automatically
// be changed by the Calendar
if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.DatePickerDisplayDateFlag))
if ((day.Month != DisplayDate.Month || day.Year != DisplayDate.Year) && (_calendar == null || !_calendar.CalendarDatePickerDisplayDateFlag))
{
DisplayDate = day;
}
if(_calendar != null)
_calendar.DatePickerDisplayDateFlag = false;
_calendar.CalendarDatePickerDisplayDateFlag = false;
}
else
{
@ -707,7 +707,7 @@ namespace Avalonia.Controls
}
private void OnCustomDateFormatStringChanged(AvaloniaPropertyChangedEventArgs e)
{
if(SelectedDateFormat == DatePickerFormat.Custom)
if(SelectedDateFormat == CalendarDatePickerFormat.Custom)
{
OnDateFormatChanged();
}
@ -752,15 +752,15 @@ namespace Avalonia.Controls
/// <summary>
/// Raises the
/// <see cref="E:Avalonia.Controls.DatePicker.DateValidationError" />
/// <see cref="E:Avalonia.Controls.CalendarDatePicker.DateValidationError" />
/// event.
/// </summary>
/// <param name="e">
/// A
/// <see cref="T:Avalonia.Controls.DatePickerDateValidationErrorEventArgs" />
/// <see cref="T:Avalonia.Controls.CalendarDatePickerDateValidationErrorEventArgs" />
/// that contains the event data.
/// </param>
protected virtual void OnDateValidationError(DatePickerDateValidationErrorEventArgs e)
protected virtual void OnDateValidationError(CalendarDatePickerDateValidationErrorEventArgs e)
{
DateValidationError?.Invoke(this, e);
}
@ -959,7 +959,7 @@ namespace Avalonia.Controls
}
else
{
var dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
var dateValidationError = new CalendarDatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException(nameof(text), "SelectedDate value is not valid."), text);
OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException)
@ -970,7 +970,7 @@ namespace Avalonia.Controls
}
catch (FormatException ex)
{
DatePickerDateValidationErrorEventArgs textParseError = new DatePickerDateValidationErrorEventArgs(ex, text);
CalendarDatePickerDateValidationErrorEventArgs textParseError = new CalendarDatePickerDateValidationErrorEventArgs(ex, text);
OnDateValidationError(textParseError);
if (textParseError.ThrowException)
@ -986,11 +986,11 @@ namespace Avalonia.Controls
switch (SelectedDateFormat)
{
case DatePickerFormat.Short:
case CalendarDatePickerFormat.Short:
return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.ShortDatePattern, dtfi));
case DatePickerFormat.Long:
case CalendarDatePickerFormat.Long:
return string.Format(CultureInfo.CurrentCulture, d.ToString(dtfi.LongDatePattern, dtfi));
case DatePickerFormat.Custom:
case CalendarDatePickerFormat.Custom:
return string.Format(CultureInfo.CurrentCulture, d.ToString(CustomDateFormatString, dtfi));
}
return null;
@ -1118,12 +1118,12 @@ namespace Avalonia.Controls
switch (SelectedDateFormat)
{
case DatePickerFormat.Long:
case CalendarDatePickerFormat.Long:
{
watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.LongDatePattern.ToString());
break;
}
case DatePickerFormat.Short:
case CalendarDatePickerFormat.Short:
default:
{
watermarkText = string.Format(CultureInfo.CurrentCulture, watermarkFormat, dtfi.ShortDatePattern.ToString());
@ -1139,11 +1139,11 @@ namespace Avalonia.Controls
}
}
private static bool IsValidSelectedDateFormat(DatePickerFormat value)
private static bool IsValidSelectedDateFormat(CalendarDatePickerFormat value)
{
return value == DatePickerFormat.Long
|| value == DatePickerFormat.Short
|| value == DatePickerFormat.Custom;
return value == CalendarDatePickerFormat.Long
|| value == CalendarDatePickerFormat.Short
|| value == CalendarDatePickerFormat.Custom;
}
private static bool IsValidDateFormatString(string formatString)
{

4
src/Avalonia.Controls/Calendar/CalendarItem.cs

@ -909,7 +909,7 @@ namespace Avalonia.Controls.Primitives
case CalendarSelectionMode.SingleDate:
{
DateTime selectedDate = (DateTime)b.DataContext;
Owner.DatePickerDisplayDateFlag = true;
Owner.CalendarDatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0)
{
Owner.SelectedDates.Add(selectedDate);
@ -981,7 +981,7 @@ namespace Avalonia.Controls.Primitives
}
case CalendarSelectionMode.SingleDate:
{
Owner.DatePickerDisplayDateFlag = true;
Owner.CalendarDatePickerDisplayDateFlag = true;
if (Owner.SelectedDates.Count == 0)
{
Owner.SelectedDates.Add(selectedDate);

35
src/Avalonia.Controls/Grid.cs

@ -1228,7 +1228,7 @@ namespace Avalonia.Controls
Debug.Assert(1 < count && 0 <= start && (start + count) <= definitions.Count);
// avoid processing when asked to distribute "0"
if (!_IsZero(requestedSize))
if (!MathUtilities.IsZero(requestedSize))
{
DefinitionBase[] tempDefinitions = TempDefinitions; // temp array used to remember definitions for sorting
int end = start + count;
@ -1306,7 +1306,7 @@ namespace Avalonia.Controls
}
// sanity check: requested size must all be distributed
Debug.Assert(_IsZero(sizeToDistribute));
Debug.Assert(MathUtilities.IsZero(sizeToDistribute));
}
else if (requestedSize <= rangeMaxSize)
{
@ -1346,7 +1346,7 @@ namespace Avalonia.Controls
}
// sanity check: requested size must all be distributed
Debug.Assert(_IsZero(sizeToDistribute));
Debug.Assert(MathUtilities.IsZero(sizeToDistribute));
}
else
{
@ -1358,7 +1358,7 @@ namespace Avalonia.Controls
double equalSize = requestedSize / count;
if (equalSize < maxMaxSize
&& !_AreClose(equalSize, maxMaxSize))
&& !MathUtilities.AreClose(equalSize, maxMaxSize))
{
// equi-size is less than maximum of maxSizes.
// in this case distribute so that smaller definitions grow faster than
@ -2151,7 +2151,7 @@ namespace Avalonia.Controls
// and precision of floating-point computation. (However, the resulting
// display is subject to anti-aliasing problems. TANSTAAFL.)
if (!_AreClose(roundedTakenSize, finalSize))
if (!MathUtilities.AreClose(roundedTakenSize, finalSize))
{
// Compute deltas
for (int i = 0; i < definitions.Count; ++i)
@ -2168,7 +2168,7 @@ namespace Avalonia.Controls
if (roundedTakenSize > finalSize)
{
int i = definitions.Count - 1;
while ((adjustedSize > finalSize && !_AreClose(adjustedSize, finalSize)) && i >= 0)
while ((adjustedSize > finalSize && !MathUtilities.AreClose(adjustedSize, finalSize)) && i >= 0)
{
DefinitionBase definition = definitions[definitionIndices[i]];
double final = definition.SizeCache - dpiIncrement;
@ -2184,7 +2184,7 @@ namespace Avalonia.Controls
else if (roundedTakenSize < finalSize)
{
int i = 0;
while ((adjustedSize < finalSize && !_AreClose(adjustedSize, finalSize)) && i < definitions.Count)
while ((adjustedSize < finalSize && !MathUtilities.AreClose(adjustedSize, finalSize)) && i < definitions.Count)
{
DefinitionBase definition = definitions[definitionIndices[i]];
double final = definition.SizeCache + dpiIncrement;
@ -2595,27 +2595,6 @@ namespace Avalonia.Controls
set { SetFlags(value, Flags.HasGroup3CellsInAutoRows); }
}
/// <summary>
/// fp version of <c>d == 0</c>.
/// </summary>
/// <param name="d">Value to check.</param>
/// <returns><c>true</c> if d == 0.</returns>
private static bool _IsZero(double d)
{
return (Math.Abs(d) < double.Epsilon);
}
/// <summary>
/// fp version of <c>d1 == d2</c>
/// </summary>
/// <param name="d1">First value to compare</param>
/// <param name="d2">Second value to compare</param>
/// <returns><c>true</c> if d1 == d2</returns>
private static bool _AreClose(double d1, double d2)
{
return (Math.Abs(d1 - d2) < double.Epsilon);
}
/// <summary>
/// Returns reference to extended data bag.
/// </summary>

20
src/Avalonia.Controls/IndexPath.cs

@ -123,6 +123,26 @@ namespace Avalonia.Controls
}
}
public bool IsAncestorOf(in IndexPath other)
{
if (other.GetSize() <= GetSize())
{
return false;
}
var size = GetSize();
for (int i = 0; i < size; i++)
{
if (GetAt(i) != other.GetAt(i))
{
return false;
}
}
return true;
}
public override string ToString()
{
if (_path != null)

47
src/Avalonia.Controls/IndexRange.cs

@ -132,6 +132,53 @@ namespace Avalonia.Controls
return result;
}
public static int Intersect(
IList<IndexRange> ranges,
IndexRange range,
IList<IndexRange>? removed = null)
{
var result = 0;
for (var i = 0; i < ranges.Count && range != s_invalid; ++i)
{
var existing = ranges[i];
if (existing.End < range.Begin || existing.Begin > range.End)
{
removed?.Add(existing);
ranges.RemoveAt(i--);
result += existing.Count;
}
else
{
if (existing.Begin < range.Begin)
{
var except = new IndexRange(existing.Begin, range.Begin - 1);
removed?.Add(except);
ranges[i] = existing = new IndexRange(range.Begin, existing.End);
result += except.Count;
}
if (existing.End > range.End)
{
var except = new IndexRange(range.End + 1, existing.End);
removed?.Add(except);
ranges[i] = new IndexRange(existing.Begin, range.End);
result += except.Count;
}
}
}
MergeRanges(ranges);
if (removed is object)
{
MergeRanges(removed);
}
return result;
}
public static int Remove(
IList<IndexRange> ranges,
IndexRange range,

76
src/Avalonia.Controls/SelectionModel.cs

@ -46,17 +46,25 @@ namespace Avalonia.Controls
if (_rootNode.Source != null)
{
if (_rootNode.Source != null)
// Temporarily prevent auto-select when switching source.
var restoreAutoSelect = _autoSelect;
_autoSelect = false;
try
{
using (var operation = new Operation(this))
{
ClearSelection(resetAnchor: true);
}
}
finally
{
_autoSelect = restoreAutoSelect;
}
}
_rootNode.Source = value;
ApplyAutoSelect();
ApplyAutoSelect(true);
RaisePropertyChanged("Source");
@ -114,7 +122,7 @@ namespace Avalonia.Controls
if (_autoSelect != value)
{
_autoSelect = value;
ApplyAutoSelect();
ApplyAutoSelect(true);
}
}
}
@ -133,7 +141,7 @@ namespace Avalonia.Controls
while (current?.AnchorIndex >= 0)
{
path.Add(current.AnchorIndex);
current = current.GetAt(current.AnchorIndex, false);
current = current.GetAt(current.AnchorIndex, false, default);
}
anchor = new IndexPath(path);
@ -188,7 +196,6 @@ namespace Avalonia.Controls
using var operation = new Operation(this);
ClearSelection(resetAnchor: true);
SelectWithPathImpl(value, select: true);
ApplyAutoSelect();
}
}
}
@ -384,21 +391,18 @@ namespace Avalonia.Controls
{
using var operation = new Operation(this);
SelectImpl(index, select: false);
ApplyAutoSelect();
}
public void Deselect(int groupIndex, int itemIndex)
{
using var operation = new Operation(this);
SelectWithGroupImpl(groupIndex, itemIndex, select: false);
ApplyAutoSelect();
}
public void DeselectAt(IndexPath index)
{
using var operation = new Operation(this);
SelectWithPathImpl(index, select: false);
ApplyAutoSelect();
}
public bool IsSelected(int index) => _rootNode.IsSelected(index);
@ -416,7 +420,7 @@ namespace Avalonia.Controls
for (int i = 0; i < path.GetSize() - 1; i++)
{
var childIndex = path.GetAt(i);
node = node.GetAt(childIndex, realizeChild: false);
node = node.GetAt(childIndex, false, default);
if (node == null)
{
@ -451,7 +455,7 @@ namespace Avalonia.Controls
}
var isSelected = (bool?)false;
var childNode = _rootNode.GetAt(groupIndex, realizeChild: false);
var childNode = _rootNode.GetAt(groupIndex, false, default);
if (childNode != null)
{
@ -470,7 +474,7 @@ namespace Avalonia.Controls
for (int i = 0; i < path.GetSize() - 1; i++)
{
var childIndex = path.GetAt(i);
node = node.GetAt(childIndex, realizeChild: false);
node = node.GetAt(childIndex, false, default);
if (node == null)
{
@ -565,7 +569,6 @@ namespace Avalonia.Controls
{
using var operation = new Operation(this);
ClearSelection(resetAnchor: true);
ApplyAutoSelect();
}
public IDisposable Update() => new Operation(this);
@ -592,10 +595,13 @@ namespace Avalonia.Controls
}
OnSelectionChanged(e);
ApplyAutoSelect();
ApplyAutoSelect(true);
}
internal IObservable<object?>? ResolvePath(object data, IndexPath dataIndexPath)
internal IObservable<object?>? ResolvePath(
object data,
IndexPath dataIndexPath,
IndexPath finalIndexPath)
{
IObservable<object?>? resolved = null;
@ -604,18 +610,22 @@ namespace Avalonia.Controls
{
if (_childrenRequestedEventArgs == null)
{
_childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs(data, dataIndexPath, false);
_childrenRequestedEventArgs = new SelectionModelChildrenRequestedEventArgs(
data,
dataIndexPath,
finalIndexPath,
false);
}
else
{
_childrenRequestedEventArgs.Initialize(data, dataIndexPath, false);
_childrenRequestedEventArgs.Initialize(data, dataIndexPath, finalIndexPath, false);
}
ChildrenRequested(this, _childrenRequestedEventArgs);
resolved = _childrenRequestedEventArgs.Children;
// Clear out the values in the args so that it cannot be used after the event handler call.
_childrenRequestedEventArgs.Initialize(null, default, true);
_childrenRequestedEventArgs.Initialize(null, default, default, true);
}
return resolved;
@ -632,6 +642,8 @@ namespace Avalonia.Controls
{
AnchorIndex = default;
}
OnSelectionChanged();
}
private void OnSelectionChanged(SelectionModelSelectionChangedEventArgs? e = null)
@ -667,6 +679,8 @@ namespace Avalonia.Controls
{
AnchorIndex = new IndexPath(index);
}
OnSelectionChanged();
}
private void SelectWithGroupImpl(int groupIndex, int itemIndex, bool select)
@ -676,13 +690,15 @@ namespace Avalonia.Controls
ClearSelection(resetAnchor: true);
}
var childNode = _rootNode.GetAt(groupIndex, realizeChild: true);
var childNode = _rootNode.GetAt(groupIndex, true, new IndexPath(groupIndex, itemIndex));
var selected = childNode!.Select(itemIndex, select);
if (selected)
{
AnchorIndex = new IndexPath(groupIndex, itemIndex);
}
OnSelectionChanged();
}
private void SelectWithPathImpl(IndexPath index, bool select)
@ -711,6 +727,8 @@ namespace Avalonia.Controls
{
AnchorIndex = index;
}
OnSelectionChanged();
}
private void SelectRangeFromAnchorImpl(int index, bool select)
@ -724,6 +742,7 @@ namespace Avalonia.Controls
}
_rootNode.SelectRange(new IndexRange(anchorIndex, index), select);
OnSelectionChanged();
}
private void SelectRangeFromAnchorWithGroupImpl(int endGroupIndex, int endItemIndex, bool select)
@ -752,11 +771,13 @@ namespace Avalonia.Controls
for (int groupIdx = startGroupIndex; groupIdx <= endGroupIndex; groupIdx++)
{
var groupNode = _rootNode.GetAt(groupIdx, realizeChild: true)!;
var groupNode = _rootNode.GetAt(groupIdx, true, new IndexPath(endGroupIndex, endItemIndex))!;
int startIndex = groupIdx == startGroupIndex ? startItemIndex : 0;
int endIndex = groupIdx == endGroupIndex ? endItemIndex : groupNode.DataCount - 1;
groupNode.SelectRange(new IndexRange(startIndex, endIndex), select);
}
OnSelectionChanged();
}
private void SelectRangeImpl(IndexPath start, IndexPath end, bool select)
@ -784,6 +805,8 @@ namespace Avalonia.Controls
info.ParentNode!.Select(info.Path.GetAt(info.Path.GetSize() - 1), select);
}
});
OnSelectionChanged();
}
private void BeginOperation()
@ -806,6 +829,8 @@ namespace Avalonia.Controls
if (--_operationCount == 0)
{
ApplyAutoSelect(false);
var changes = new List<SelectionNodeOperation>();
_rootNode.EndOperation(changes);
@ -827,7 +852,7 @@ namespace Avalonia.Controls
}
}
private void ApplyAutoSelect()
private void ApplyAutoSelect(bool createOperation)
{
if (AutoSelect)
{
@ -835,8 +860,15 @@ namespace Avalonia.Controls
if (SelectedIndex == default && _rootNode.ItemsSourceView?.Count > 0)
{
using var operation = new Operation(this);
SelectImpl(0, true);
if (createOperation)
{
using var operation = new Operation(this);
SelectImpl(0, true);
}
else
{
SelectImpl(0, true);
}
}
}
}

2
src/Avalonia.Controls/SelectionModelChangeSet.cs

@ -135,7 +135,7 @@ namespace Avalonia.Controls
if (index >= currentIndex && index < currentIndex + currentCount)
{
int targetIndex = GetIndexAt(getRanges(info), index - currentIndex);
item = info.Items?.GetAt(targetIndex);
item = info.Items?.Count > targetIndex ? info.Items?.GetAt(targetIndex) : null;
break;
}

22
src/Avalonia.Controls/SelectionModelChildrenRequestedEventArgs.cs

@ -16,15 +16,17 @@ namespace Avalonia.Controls
{
private object? _source;
private IndexPath _sourceIndexPath;
private IndexPath _finalIndexPath;
private bool _throwOnAccess;
internal SelectionModelChildrenRequestedEventArgs(
object source,
IndexPath sourceIndexPath,
IndexPath finalIndexPath,
bool throwOnAccess)
{
source = source ?? throw new ArgumentNullException(nameof(source));
Initialize(source, sourceIndexPath, throwOnAccess);
Initialize(source, sourceIndexPath, finalIndexPath, throwOnAccess);
}
/// <summary>
@ -65,9 +67,26 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Gets the index of the final object which is being attempted to be retrieved.
/// </summary>
public IndexPath FinalIndex
{
get
{
if (_throwOnAccess)
{
throw new ObjectDisposedException(nameof(SelectionModelChildrenRequestedEventArgs));
}
return _finalIndexPath;
}
}
internal void Initialize(
object? source,
IndexPath sourceIndexPath,
IndexPath finalIndexPath,
bool throwOnAccess)
{
if (!throwOnAccess && source == null)
@ -77,6 +96,7 @@ namespace Avalonia.Controls
_source = source;
_sourceIndexPath = sourceIndexPath;
_finalIndexPath = finalIndexPath;
_throwOnAccess = throwOnAccess;
}
}

27
src/Avalonia.Controls/SelectionNode.cs

@ -101,6 +101,7 @@ namespace Avalonia.Controls
ItemsSourceView = newDataSource;
TrimInvalidSelections();
PopulateSelectedItemsFromSelectedIndices();
HookupCollectionChangedHandler();
OnSelectionChanged();
@ -108,6 +109,26 @@ namespace Avalonia.Controls
}
}
private void TrimInvalidSelections()
{
if (_selected == null || ItemsSourceView == null)
{
return;
}
var validRange = ItemsSourceView.Count > 0 ? new IndexRange(0, ItemsSourceView.Count - 1) : new IndexRange(-1, -1);
var removed = new List<IndexRange>();
var removedCount = IndexRange.Intersect(_selected, validRange, removed);
if (removedCount > 0)
{
using var operation = _manager.Update();
SelectedCount -= removedCount;
OnSelectionChanged();
_operation!.Deselected(removed);
}
}
public ItemsSourceView? ItemsSourceView { get; private set; }
public int DataCount => ItemsSourceView?.Count ?? 0;
public int ChildrenNodeCount => _childrenNodes.Count;
@ -141,7 +162,7 @@ namespace Avalonia.Controls
// create a bunch of leaf node instances - instead i use the same instance m_leafNode to avoid
// an explosion of node objects. However, I'm still creating the m_childrenNodes
// collection unfortunately.
public SelectionNode? GetAt(int index, bool realizeChild)
public SelectionNode? GetAt(int index, bool realizeChild, IndexPath finalIndexPath)
{
SelectionNode? child = null;
@ -171,7 +192,7 @@ namespace Avalonia.Controls
if (childData != null)
{
var childDataIndexPath = IndexPath.CloneWithChildIndex(index);
resolver = _manager.ResolvePath(childData, childDataIndexPath);
resolver = _manager.ResolvePath(childData, childDataIndexPath, finalIndexPath);
}
if (resolver != null)
@ -843,7 +864,7 @@ namespace Avalonia.Controls
int notSelectedCount = 0;
for (int i = 0; i < ChildrenNodeCount; i++)
{
var child = GetAt(i, realizeChild: false);
var child = GetAt(i, false, default);
if (child != null)
{

3
src/Avalonia.Controls/Slider.cs

@ -193,7 +193,8 @@ namespace Avalonia.Controls
var orient = Orientation == Orientation.Horizontal;
var pointDen = orient ? _track.Bounds.Width : _track.Bounds.Height;
pointDen += double.Epsilon; // Just add epsilon to avoid divide by zero exceptions.
// Just add epsilon to avoid NaN in case 0/0
pointDen += double.Epsilon;
var pointNum = orient ? x.Position.X : x.Position.Y;
var logicalPos = MathUtilities.Clamp(pointNum / pointDen, 0.0d, 1.0d);

18
src/Avalonia.Controls/TreeView.cs

@ -119,9 +119,10 @@ namespace Avalonia.Controls
/// <summary>
/// Gets or sets the selected item.
/// </summary>
/// <summary>
/// Gets or sets the selected item.
/// </summary>
/// <remarks>
/// Note that setting this property only currently works if the item is expanded to be visible.
/// To select non-expanded nodes use `Selection.SelectedIndex`.
/// </remarks>
public object SelectedItem
{
get => Selection.SelectedItem;
@ -346,7 +347,7 @@ namespace Avalonia.Controls
if (container != null)
{
container.BringIntoView();
DispatcherTimer.RunOnce(container.BringIntoView, TimeSpan.Zero);
}
}
}
@ -395,10 +396,17 @@ namespace Avalonia.Controls
private void OnSelectionModelChildrenRequested(object sender, SelectionModelChildrenRequestedEventArgs e)
{
var container = ItemContainerGenerator.Index.ContainerFromItem(e.Source) as ItemsControl;
var container = ItemContainerGenerator.Index.ContainerFromItem(e.Source) as TreeViewItem;
if (container is object)
{
if (e.SourceIndex.IsAncestorOf(e.FinalIndex))
{
container.IsExpanded = true;
container.ApplyTemplate();
container.Presenter?.ApplyTemplate();
}
e.Children = Observable.CombineLatest(
container.GetObservable(TreeViewItem.IsExpandedProperty),
container.GetObservable(ItemsProperty),

3
src/Avalonia.Controls/Utils/BorderRenderHelper.cs

@ -1,6 +1,7 @@
using System;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Utilities;
namespace Avalonia.Controls.Utils
{
@ -119,7 +120,7 @@ namespace Avalonia.Controls.Utils
}
var rect = new Rect(_size);
if (Math.Abs(borderThickness) > double.Epsilon)
if (!MathUtilities.IsZero(borderThickness))
rect = rect.Deflate(borderThickness * 0.5);
var rrect = new RoundedRect(rect, _cornerRadius.TopLeft, _cornerRadius.TopRight,
_cornerRadius.BottomRight, _cornerRadius.BottomLeft);

8
src/Avalonia.Controls/Utils/SelectionTreeHelper.cs

@ -28,7 +28,7 @@ namespace Avalonia.Controls.Utils
if (depth < path.GetSize() - 1)
{
node = node.GetAt(childIndex, realizeChildren)!;
node = node.GetAt(childIndex, realizeChildren, path)!;
}
}
}
@ -50,7 +50,7 @@ namespace Avalonia.Controls.Utils
int count = realizeChildren ? nextNode.Node.DataCount : nextNode.Node.ChildrenNodeCount;
for (int i = count - 1; i >= 0; i--)
{
var child = nextNode.Node.GetAt(i, realizeChildren);
var child = nextNode.Node.GetAt(i, realizeChildren, nextNode.Path);
var childPath = nextNode.Path.CloneWithChildIndex(i);
if (child != null)
{
@ -90,7 +90,7 @@ namespace Avalonia.Controls.Utils
for (int i = endIndex; i >= startIndex; i--)
{
var child = node.GetAt(i, realizeChild: true);
var child = node.GetAt(i, true, end);
if (child != null)
{
var childPath = currentPath.CloneWithChildIndex(i);
@ -112,7 +112,7 @@ namespace Avalonia.Controls.Utils
int endIndex = depth < end.GetSize() && isEndPath ? end.GetAt(depth) : info.Node.DataCount - 1;
for (int i = endIndex; i >= startIndex; i--)
{
var child = info.Node.GetAt(i, realizeChild: true);
var child = info.Node.GetAt(i, true, end);
if (child != null)
{
var childPath = info.Path.CloneWithChildIndex(i);

17
src/Avalonia.Diagnostics/Diagnostics/ViewModels/MainViewModel.cs

@ -2,6 +2,7 @@
using Avalonia.Controls;
using Avalonia.Diagnostics.Models;
using Avalonia.Input;
using Avalonia.Threading;
namespace Avalonia.Diagnostics.ViewModels
{
@ -49,7 +50,21 @@ namespace Avalonia.Diagnostics.ViewModels
value is TreePageViewModel newTree &&
oldTree?.SelectedNode?.Visual is IControl control)
{
newTree.SelectControl(control);
// HACK: We want to select the currently selected control in the new tree, but
// to select nested nodes in TreeView, currently the TreeView has to be able to
// expand the parent nodes. Because at this point the TreeView isn't visible,
// this will fail unless we schedule the selection to run after layout.
DispatcherTimer.RunOnce(
() =>
{
try
{
newTree.SelectControl(control);
}
catch { }
},
TimeSpan.FromMilliseconds(0),
DispatcherPriority.ApplicationIdle);
}
RaiseAndSetIfChanged(ref _content, value);

38
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreeNode.cs

@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reactive;
using System.Reactive.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.VisualTree;
namespace Avalonia.Diagnostics.ViewModels
@ -82,5 +82,41 @@ namespace Avalonia.Diagnostics.ViewModels
get;
private set;
}
public IndexPath Index
{
get
{
var indices = new List<int>();
var child = this;
var parent = Parent;
while (parent is object)
{
indices.Add(IndexOf(parent.Children, child));
child = child.Parent;
parent = parent.Parent;
}
indices.Add(0);
indices.Reverse();
return new IndexPath(indices);
}
}
private static int IndexOf(IReadOnlyList<TreeNode> collection, TreeNode item)
{
var count = collection.Count;
for (var i = 0; i < count; ++i)
{
if (collection[i] == item)
{
return i;
}
}
throw new AvaloniaInternalException("TreeNode was not present in parent Children collection.");
}
}
}

24
src/Avalonia.Diagnostics/Diagnostics/ViewModels/TreePageViewModel.cs

@ -6,28 +6,40 @@ namespace Avalonia.Diagnostics.ViewModels
{
internal class TreePageViewModel : ViewModelBase, IDisposable
{
private TreeNode _selected;
private TreeNode _selectedNode;
private ControlDetailsViewModel _details;
private string _propertyFilter;
public TreePageViewModel(TreeNode[] nodes)
{
Nodes = nodes;
}
Selection = new SelectionModel
{
SingleSelect = true,
Source = Nodes
};
Selection.SelectionChanged += (s, e) =>
{
SelectedNode = (TreeNode)Selection.SelectedItem;
};
}
public TreeNode[] Nodes { get; protected set; }
public SelectionModel Selection { get; }
public TreeNode SelectedNode
{
get => _selected;
set
get => _selectedNode;
private set
{
if (Details != null)
{
_propertyFilter = Details.PropertyFilter;
}
if (RaiseAndSetIfChanged(ref _selected, value))
if (RaiseAndSetIfChanged(ref _selectedNode, value))
{
Details = value != null ?
new ControlDetailsViewModel(value.Visual, _propertyFilter) :
@ -83,8 +95,8 @@ namespace Avalonia.Diagnostics.ViewModels
if (node != null)
{
SelectedNode = node;
ExpandNode(node.Parent);
Selection.SelectedIndex = node.Index;
}
}

2
src/Avalonia.Diagnostics/Diagnostics/Views/TreePageView.xaml

@ -6,7 +6,7 @@
<TreeView Name="tree"
BorderThickness="0"
Items="{Binding Nodes}"
SelectedItem="{Binding SelectedNode, Mode=TwoWay}">
Selection="{Binding Selection}">
<TreeView.DataTemplates>
<TreeDataTemplate DataType="vm:TreeNode"
ItemsSource="{Binding Children}">

4
src/Avalonia.Themes.Fluent/DatePicker.xaml → src/Avalonia.Themes.Default/CalendarDatePicker.xaml

@ -8,7 +8,7 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style Selector="DatePicker">
<Style Selector="CalendarDatePicker">
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
@ -119,7 +119,7 @@
</Setter>
</Style>
<Style Selector="DatePicker:focus /template/ TextBox#PART_TextBox">
<Style Selector="CalendarDatePicker:focus /template/ TextBox#PART_TextBox">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>

2
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -45,7 +45,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarDayButton.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarItem.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Calendar.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.DatePicker.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.CalendarDatePicker.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.ButtonSpinner.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.NumericUpDown.xaml?assembly=Avalonia.Themes.Default"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Default"/>

51
src/Avalonia.Themes.Fluent/Accents/FluentBaseDark.xaml

@ -92,6 +92,57 @@
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for RepeatButton.xaml -->
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for Windows.UI.Xaml.Controls.Primitives.ToggleButton -->
<StaticResource x:Key="ToggleButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminate" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminateDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminate" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminateDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminate" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- BaseResources for ComboBox.xaml -->
<StaticResource x:Key="ComboBoxItemForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ComboBoxItemForegroundPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />

52
src/Avalonia.Themes.Fluent/Accents/FluentBaseLight.xaml

@ -93,6 +93,58 @@
<StaticResource x:Key="ButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for RepeatButton.xaml -->
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- Resources for ToggleButton.xaml -->
<StaticResource x:Key="ToggleButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundPointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminate" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminateDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminate" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminateDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminate" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<!-- BaseResources for ComboBox.xaml -->
<StaticResource x:Key="ComboBoxItemForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ComboBoxItemForegroundPressed" ResourceKey="SystemControlHighlightAltBaseHighBrush" />

81
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@ -39,6 +39,87 @@
<SolidColorBrush x:Key="ButtonPressedBackgroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ButtonPressedForegroundThemeBrush" Color="#FF000000" />
<!-- Resources for RepeatButton.xaml -->
<Thickness x:Key="RepeatButtonBorderThemeThickness">1</Thickness>
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="RepeatButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="RepeatButtonBorderThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="RepeatButtonDisabledBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="RepeatButtonDisabledBorderThemeBrush" Color="#66FFFFFF" />
<SolidColorBrush x:Key="RepeatButtonDisabledForegroundThemeBrush" Color="#66FFFFFF" />
<SolidColorBrush x:Key="RepeatButtonForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="RepeatButtonPointerOverBackgroundThemeBrush" Color="#21FFFFFF" />
<SolidColorBrush x:Key="RepeatButtonPointerOverForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="RepeatButtonPressedBackgroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="RepeatButtonPressedForegroundThemeBrush" Color="#FF000000" />
<!-- Resources for ToggleButton.xaml -->
<Thickness x:Key="ToggleButtonBorderThemeThickness">1</Thickness>
<StaticResource x:Key="ToggleButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorDark1" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminate" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminateDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminate" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminateDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminate" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ToggleButtonBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ToggleButtonBorderThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedBackgroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedBorderThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedDisabledBackgroundThemeBrush" Color="#66FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedDisabledForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedPointerOverBackgroundThemeBrush" Color="#99FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedPointerOverBorderThemeBrush" Color="#99FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedBorderThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonDisabledBorderThemeBrush" Color="#66FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonDisabledForegroundThemeBrush" Color="#66FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonPointerOverBackgroundThemeBrush" Color="#21FFFFFF" />
<SolidColorBrush x:Key="ToggleButtonPressedBackgroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonPressedForegroundThemeBrush" Color="#FF000000" />
<!-- Resources for ComboBox.xaml -->
<x:Double x:Key="ComboBoxArrowThemeFontSize">21</x:Double>
<x:Double x:Key="ComboBoxThemeMinWidth">64</x:Double>

80
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml

@ -39,6 +39,86 @@
<SolidColorBrush x:Key="ButtonPressedBackgroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- Resources for RepeatButton.xaml -->
<Thickness x:Key="RepeatButtonBorderThemeThickness">1</Thickness>
<StaticResource x:Key="RepeatButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="RepeatButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="RepeatButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="RepeatButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="RepeatButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="RepeatButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="RepeatButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="RepeatButtonBorderThemeBrush" Color="#33000000" />
<SolidColorBrush x:Key="RepeatButtonDisabledBackgroundThemeBrush" Color="#66CACACA" />
<SolidColorBrush x:Key="RepeatButtonDisabledBorderThemeBrush" Color="#1A000000" />
<SolidColorBrush x:Key="RepeatButtonDisabledForegroundThemeBrush" Color="#66000000" />
<SolidColorBrush x:Key="RepeatButtonForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="RepeatButtonPointerOverBackgroundThemeBrush" Color="#D1CDCDCD" />
<SolidColorBrush x:Key="RepeatButtonPointerOverForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="RepeatButtonPressedBackgroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="RepeatButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- Resources for ToggleButton.xaml -->
<Thickness x:Key="ToggleButtonBorderThemeThickness">1</Thickness>
<StaticResource x:Key="ToggleButtonBackground" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<SolidColorBrush x:Key="ToggleButtonBackgroundPointerOver" Color="{StaticResource SystemBaseHighColor}" Opacity="0.1" />
<StaticResource x:Key="ToggleButtonBackgroundPressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="SystemControlHighlightAccentBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="SystemAccentColorLight1" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminate" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePointerOver" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminatePressed" ResourceKey="SystemControlBackgroundBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBackgroundIndeterminateDisabled" ResourceKey="SystemControlBackgroundBaseLowBrush" />
<StaticResource x:Key="ToggleButtonForeground" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundPressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundChecked" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPointerOver" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedPressed" ResourceKey="SystemControlHighlightAltChromeWhiteBrush" />
<StaticResource x:Key="ToggleButtonForegroundCheckedDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminate" ResourceKey="SystemControlForegroundBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminatePressed" ResourceKey="SystemControlHighlightBaseHighBrush" />
<StaticResource x:Key="ToggleButtonForegroundIndeterminateDisabled" ResourceKey="SystemControlDisabledBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrush" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPointerOver" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushPressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushChecked" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPointerOver" ResourceKey="SystemControlHighlightAltTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedPressed" ResourceKey="SystemControlTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushCheckedDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminate" ResourceKey="SystemControlForegroundTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePointerOver" ResourceKey="SystemControlHighlightBaseMediumLowBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminatePressed" ResourceKey="SystemControlHighlightTransparentBrush" />
<StaticResource x:Key="ToggleButtonBorderBrushIndeterminateDisabled" ResourceKey="SystemControlDisabledTransparentBrush" />
<SolidColorBrush x:Key="ToggleButtonBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ToggleButtonBorderThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedBackgroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedBorderThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedDisabledBackgroundThemeBrush" Color="#66000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedDisabledForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedForegroundThemeBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="ToggleButtonCheckedPointerOverBackgroundThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedPointerOverBorderThemeBrush" Color="#99000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedBackgroundThemeBrush" Color="Transparent" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedBorderThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonCheckedPressedForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonDisabledBorderThemeBrush" Color="#66000000" />
<SolidColorBrush x:Key="ToggleButtonDisabledForegroundThemeBrush" Color="#66000000" />
<SolidColorBrush x:Key="ToggleButtonForegroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonPointerOverBackgroundThemeBrush" Color="#21000000" />
<SolidColorBrush x:Key="ToggleButtonPressedBackgroundThemeBrush" Color="#FF000000" />
<SolidColorBrush x:Key="ToggleButtonPressedForegroundThemeBrush" Color="#FFFFFFFF" />
<!-- Resources for ComboBox.xaml -->
<x:Double x:Key="ComboBoxArrowThemeFontSize">21</x:Double>
<x:Double x:Key="ComboBoxThemeMinWidth">64</x:Double>

1
src/Avalonia.Themes.Fluent/Accents/FluentDark.xaml

@ -5,4 +5,5 @@
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.Base.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.FluentBaseDark.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.FluentControlResourcesDark.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.FluentTheme.xaml?assembly=Avalonia.Themes.Fluent" />
</Styles>

1
src/Avalonia.Themes.Fluent/Accents/FluentLight.xaml

@ -5,4 +5,5 @@
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.Base.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.FluentBaseLight.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Accents.FluentControlResourcesLight.xaml?assembly=Avalonia.Themes.Fluent" />
<StyleInclude Source="resm:Avalonia.Themes.Fluent.FluentTheme.xaml?assembly=Avalonia.Themes.Fluent" />
</Styles>

36
src/Avalonia.Themes.Fluent/Button.xaml

@ -24,21 +24,19 @@
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter
x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
</ContentPresenter>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
@ -46,19 +44,19 @@
<!-- PointerOverState -->
<Style Selector="Button:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundPointerOver}" />
</Style>
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundPressed}" />
</Style>
<Style Selector="Button:disabled">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Style>
@ -70,19 +68,19 @@
<Style Selector="Button.accent:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPointerOver}" />
</Style>
<Style Selector="Button.accent:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundPressed}" />
</Style>
<Style Selector="Button.accent:disabled">
<Setter Property="Background" Value="{DynamicResource AccentButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource AccentButtonForegroundDisabled}" />
</Style>
</Styles>

4
src/Avalonia.Themes.Default/DatePicker.xaml → src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml

@ -8,7 +8,7 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=netstandard">
<Style Selector="DatePicker">
<Style Selector="CalendarDatePicker">
<Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
@ -119,7 +119,7 @@
</Setter>
</Style>
<Style Selector="DatePicker:focus /template/ TextBox#PART_TextBox">
<Style Selector="CalendarDatePicker:focus /template/ TextBox#PART_TextBox">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderHighBrush}"/>
</Style>

2
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@ -45,7 +45,7 @@
<StyleInclude Source="resm:Avalonia.Themes.Fluent.CalendarDayButton.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.CalendarItem.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.Calendar.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.DatePicker.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.CalendarDatePicker.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.ButtonSpinner.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.NumericUpDown.xaml?assembly=Avalonia.Themes.Fluent"/>
<StyleInclude Source="resm:Avalonia.Themes.Fluent.AutoCompleteBox.xaml?assembly=Avalonia.Themes.Fluent"/>

100
src/Avalonia.Themes.Fluent/RepeatButton.xaml

@ -1,40 +1,62 @@
<Styles xmlns="https://github.com/avaloniaui">
<Style Selector="RepeatButton">
<Setter Property="Background"
Value="{DynamicResource ThemeControlMidBrush}" />
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeBorderLowBrush}" />
<Setter Property="BorderThickness"
Value="{DynamicResource ThemeBorderThickness}" />
<Setter Property="Foreground"
Value="{DynamicResource ThemeForegroundBrush}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Padding"
Value="4" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding Content}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush"
Value="{DynamicResource ThemeBorderMidBrush}" />
</Style>
<Style Selector="RepeatButton:disabled">
<Setter Property="Opacity"
Value="{DynamicResource ThemeDisabledOpacity}" />
</Style>
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Spacing="20">
<RepeatButton Content="Click Me!" />
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
</Styles.Resources>
<Style Selector="RepeatButton">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackground}" />
<!--<Setter Property="BackgroundSizing" Value="OuterBorderEdge" />-->
<Setter Property="Foreground" Value="{DynamicResource RepeatButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{StaticResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<!--<Setter Property="UseSystemFocusVisuals" Value="{StaticResource UseSystemFocusVisuals}" />
<Setter Property="FocusVisualMargin" Value="-3" />-->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="RepeatButton /template/ ContentPresenter">
<Setter Property="CornerRadius" Value="{DynamicResource ControlCornerRadius}" />
</Style>
<Style Selector="RepeatButton:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPointerOver}" />
</Style>
<Style Selector="RepeatButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundPressed}" />
</Style>
<Style Selector="RepeatButton:disabled">
<Setter Property="Background" Value="{DynamicResource RepeatButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource RepeatButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource RepeatButtonForegroundDisabled}" />
</Style>
</Styles>

8
src/Avalonia.Themes.Fluent/Slider.xaml

@ -64,7 +64,7 @@
<TickBar Name="BottomTickBar" Placement="Bottom" Height="{DynamicResource SliderOutsideTickBarThemeHeight}" VerticalAlignment="Top" Margin="0,4,0,0" Grid.Row="2" Grid.ColumnSpan="3" />
<Track Name="PART_Track" Grid.Row="1" Grid.ColumnSpan="3" Orientation="Horizontal">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}">
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -76,7 +76,7 @@
</RepeatButton>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}">
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -125,7 +125,7 @@
<TickBar Name="RightTickBar" Placement="Right" Width="{DynamicResource SliderOutsideTickBarThemeHeight}" HorizontalAlignment="Left" Margin="4,0,0,0" Grid.Column="2" Grid.RowSpan="3" />
<Track Name="PART_Track" Grid.Column="1" Grid.ColumnSpan="1" Grid.RowSpan="3" Orientation="Vertical">
<Track.DecreaseButton>
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}">
<RepeatButton Name="PART_DecreaseButton" Background="{TemplateBinding Foreground}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>
@ -137,7 +137,7 @@
</RepeatButton>
</Track.DecreaseButton>
<Track.IncreaseButton>
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}">
<RepeatButton Name="PART_IncreaseButton" Background="{TemplateBinding Background}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<RepeatButton.Template>
<ControlTemplate>
<Grid>

113
src/Avalonia.Themes.Fluent/ToggleButton.xaml

@ -1,38 +1,109 @@
<Styles xmlns="https://github.com/avaloniaui">
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Design.PreviewWith>
<Border Padding="20">
<StackPanel Spacing="20">
<ToggleButton Content="Click Me!" />
<ToggleButton Content="Disabled" IsEnabled="False" />
<ToggleButton Content="Three state" IsThreeState="True" />
</StackPanel>
</Border>
</Design.PreviewWith>
<Styles.Resources>
<Thickness x:Key="ButtonPadding">8,5,8,6</Thickness>
</Styles.Resources>
<Style Selector="ToggleButton">
<Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowBrush}"/>
<Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}"/>
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}"/>
<Setter Property="Padding" Value="4"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackground}" />
<Setter Property="Foreground" Value="{DynamicResource ToggleButtonForeground}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource ToggleButtonBorderThemeThickness}" />
<Setter Property="Padding" Value="{DynamicResource ButtonPadding}" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="FontFamily" Value="{DynamicResource ContentControlThemeFontFamily}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="FontSize" Value="{DynamicResource ControlContentThemeFontSize}" />
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}"
CornerRadius="{DynamicResource ControlCornerRadius}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Padding="{TemplateBinding Padding}"
TextBlock.Foreground="{TemplateBinding Foreground}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}" />
</ControlTemplate>
</Setter>
</Style>
<Style Selector="ToggleButton:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
</Style>
<!-- PointerOverState -->
<Style Selector="ToggleButton:pointerover /template/ ContentPresenter">
<Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderMidBrush}"/>
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPointerOver}" />
</Style>
<Style Selector="ToggleButton:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighBrush}"/>
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundPressed}" />
</Style>
<Style Selector="ToggleButton:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundDisabled}" />
</Style>
<Style Selector="ToggleButton:checked /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundChecked}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushChecked}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundChecked}" />
</Style>
<Style Selector="ToggleButton:checked:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPointerOver}" />
</Style>
<Style Selector="ToggleButton:checked:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedPressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedPressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedPressed}" />
</Style>
<Style Selector="ToggleButton:checked:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundCheckedDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushCheckedDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundCheckedDisabled}" />
</Style>
<Style Selector="ToggleButton:indeterminate /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminate}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminate}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminate}" />
</Style>
<Style Selector="ToggleButton:indeterminate:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePointerOver}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePointerOver}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePointerOver}" />
</Style>
<Style Selector="ToggleButton:indeterminate:pressed /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminatePressed}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminatePressed}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminatePressed}" />
</Style>
<Style Selector="ToggleButton:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
<Style Selector="ToggleButton:indeterminate:disabled /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ToggleButtonBackgroundIndeterminateDisabled}" />
<Setter Property="BorderBrush" Value="{DynamicResource ToggleButtonBorderBrushIndeterminateDisabled}" />
<Setter Property="TextBlock.Foreground" Value="{DynamicResource ToggleButtonForegroundIndeterminateDisabled}" />
</Style>
</Styles>
</Styles>

5
src/Avalonia.Visuals/Media/DrawingContext.cs

@ -4,6 +4,7 @@ using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Rendering.SceneGraph;
using Avalonia.Threading;
using Avalonia.Utilities;
using Avalonia.Visuals.Media.Imaging;
namespace Avalonia.Media
@ -154,12 +155,12 @@ namespace Avalonia.Media
return;
}
if (Math.Abs(radiusX) > double.Epsilon)
if (!MathUtilities.IsZero(radiusX))
{
radiusX = Math.Min(radiusX, rect.Width / 2);
}
if (Math.Abs(radiusY) > double.Epsilon)
if (!MathUtilities.IsZero(radiusY))
{
radiusY = Math.Min(radiusY, rect.Height / 2);
}

3
src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs

@ -4,6 +4,7 @@ using System.Linq;
using Avalonia.Media.Immutable;
using Avalonia.Media.TextFormatting.Unicode;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.Utility;
namespace Avalonia.Media.TextFormatting
@ -184,7 +185,7 @@ namespace Avalonia.Media.TextFormatting
/// </summary>
private void UpdateLayout()
{
if (_text.IsEmpty || Math.Abs(MaxWidth) < double.Epsilon || Math.Abs(MaxHeight) < double.Epsilon)
if (_text.IsEmpty || MathUtilities.IsZero(MaxWidth) || MathUtilities.IsZero(MaxHeight))
{
var textLine = CreateEmptyTextLine(0);

8
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -107,6 +107,14 @@ namespace Avalonia.Data
private object ConvertValue(IList<object> values, Type targetType, IMultiValueConverter converter)
{
for (var i = 0; i < values.Count; ++i)
{
if (values[i] is BindingNotification notification)
{
values[i] = notification.Value;
}
}
var culture = CultureInfo.CurrentCulture;
var converted = converter.Convert(values, targetType, ConverterParameter, culture);

2
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@ -236,7 +236,7 @@ namespace Avalonia.Direct2D1.Media
Math.Max(rrect.RadiiTopRight.X, Math.Max(rrect.RadiiBottomRight.X, rrect.RadiiBottomLeft.X)));
var radiusY = Math.Max(rrect.RadiiTopLeft.Y,
Math.Max(rrect.RadiiTopRight.Y, Math.Max(rrect.RadiiBottomRight.Y, rrect.RadiiBottomLeft.Y)));
var isRounded = Math.Abs(radiusX) > double.Epsilon || Math.Abs(radiusY) > double.Epsilon;
var isRounded = !MathUtilities.IsZero(radiusX) || !MathUtilities.IsZero(radiusY);
if (brush != null)
{

2
tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs

@ -322,7 +322,7 @@ namespace Avalonia.Base.UnitTests.Data.Core
{
"bar",
new BindingNotification(
new MissingMemberException("Could not find CLR property 'Bar' on 'Avalonia.Base.UnitTests.Data.Core.ExpressionObserverTests_Property+WithoutBar'"),
new MissingMemberException("Could not find a matching property accessor for 'Bar' on 'Avalonia.Base.UnitTests.Data.Core.ExpressionObserverTests_Property+WithoutBar'"),
BindingErrorType.Error),
"baz",
},

119
tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs

@ -0,0 +1,119 @@
using System;
using Avalonia.Utilities;
using Xunit;
namespace Avalonia.Base.UnitTests.Utilities
{
public class MathUtilitiesTests
{
private const double AnyValue = 42.42;
private readonly double _calculatedAnyValue;
private readonly double _one;
private readonly double _zero;
public MathUtilitiesTests()
{
_calculatedAnyValue = 0.0;
_one = 0.0;
_zero = 1.0;
const int N = 10;
var dxAny = AnyValue / N;
var dxOne = 1.0 / N;
var dxZero = _zero / N;
for (var i = 0; i < N; ++i)
{
_calculatedAnyValue += dxAny;
_one += dxOne;
_zero -= dxZero;
}
}
[Fact]
public void Two_Equivalent_Double_Values_Are_Close()
{
var actual = MathUtilities.AreClose(AnyValue, _calculatedAnyValue);
Assert.True(actual);
Assert.Equal(AnyValue, Math.Round(_calculatedAnyValue, 14));
}
[Fact]
public void Two_Equivalent_Single_Values_Are_Close()
{
var expectedValue = (float)AnyValue;
var actualValue = (float)_calculatedAnyValue;
var actual = MathUtilities.AreClose(expectedValue, actualValue);
Assert.True(actual);
Assert.Equal((float) Math.Round(expectedValue, 5), (float) Math.Round(actualValue, 4));
}
[Fact]
public void Calculated_Double_One_Is_One()
{
var actual = MathUtilities.IsOne(_one);
Assert.True(actual);
Assert.Equal(1.0, Math.Round(_one, 15));
}
[Fact]
public void Calculated_Single_One_Is_One()
{
var actualValue = (float)_one;
var actual = MathUtilities.IsOne(actualValue);
Assert.True(actual);
Assert.Equal(1.0f, (float) Math.Round(actualValue, 7));
}
[Fact]
public void Calculated_Double_Zero_Is_Zero()
{
var actual = MathUtilities.IsZero(_zero);
Assert.True(actual);
Assert.Equal(0.0, Math.Round(_zero, 15));
}
[Fact]
public void Calculated_Single_Zero_Is_Zero()
{
var actualValue = (float)_zero;
var actual = MathUtilities.IsZero(actualValue);
Assert.True(actual);
Assert.Equal(0.0f, (float) Math.Round(actualValue, 7));
}
[Fact]
public void Clamp_Input_NaN_Return_NaN()
{
var clamp = MathUtilities.Clamp(double.NaN, 0.0, 1.0);
Assert.True(double.IsNaN(clamp));
}
[Fact]
public void Clamp_Input_NegativeInfinity_Return_Min()
{
const double min = 0.0;
const double max = 1.0;
var actual = MathUtilities.Clamp(double.NegativeInfinity, min, max);
Assert.Equal(min, actual);
}
[Fact]
public void Clamp_Input_PositiveInfinity_Return_Max()
{
const double min = 0.0;
const double max = 1.0;
var actual = MathUtilities.Clamp(double.PositiveInfinity, min, max);
Assert.Equal(max, actual);
}
}
}

47
tests/Avalonia.Benchmarks/Data/AccessorTestObject.cs

@ -0,0 +1,47 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
namespace Avalonia.Benchmarks.Data
{
internal class AccessorTestObject : INotifyPropertyChanged
{
private string _test;
public string Test
{
get => _test;
set
{
if (_test == value)
{
return;
}
_test = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void Execute()
{
}
public void Execute(object p0)
{
}
public void Execute(object p0, object p1)
{
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

54
tests/Avalonia.Benchmarks/Data/PropertyAccessorBenchmarks.cs

@ -1,9 +1,6 @@
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Avalonia.Data.Core.Plugins;
using BenchmarkDotNet.Attributes;
using JetBrains.Annotations;
namespace Avalonia.Benchmarks.Data
{
@ -12,7 +9,7 @@ namespace Avalonia.Benchmarks.Data
{
private readonly InpcPropertyAccessorPlugin _inpcPlugin = new InpcPropertyAccessorPlugin();
private readonly MethodAccessorPlugin _methodPlugin = new MethodAccessorPlugin();
private readonly TestObject _targetStrongRef = new TestObject();
private readonly AccessorTestObject _targetStrongRef = new AccessorTestObject();
private readonly WeakReference<object> _targetWeakRef;
public PropertyAccessorBenchmarks()
@ -23,66 +20,25 @@ namespace Avalonia.Benchmarks.Data
[Benchmark]
public void InpcAccessorMatch()
{
_inpcPlugin.Match(_targetWeakRef, nameof(TestObject.Test));
_inpcPlugin.Match(_targetWeakRef, nameof(AccessorTestObject.Test));
}
[Benchmark]
public void InpcAccessorStart()
{
_inpcPlugin.Start(_targetWeakRef, nameof(TestObject.Test));
_inpcPlugin.Start(_targetWeakRef, nameof(AccessorTestObject.Test));
}
[Benchmark]
public void MethodAccessorMatch()
{
_methodPlugin.Match(_targetWeakRef, nameof(TestObject.Execute));
_methodPlugin.Match(_targetWeakRef, nameof(AccessorTestObject.Execute));
}
[Benchmark]
public void MethodAccessorStart()
{
_methodPlugin.Start(_targetWeakRef, nameof(TestObject.Execute));
}
private class TestObject : INotifyPropertyChanged
{
private string _test;
public string Test
{
get => _test;
set
{
if (_test == value)
{
return;
}
_test = value;
OnPropertyChanged();
}
}
public void Execute()
{
}
public void Execute(object p0)
{
}
public void Execute(object p0, object p1)
{
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
_methodPlugin.Start(_targetWeakRef, nameof(AccessorTestObject.Execute));
}
}
}

60
tests/Avalonia.Benchmarks/Data/PropertyAccessorPluginBenchmarks.cs

@ -0,0 +1,60 @@
using System.Collections.Generic;
using Avalonia.Data.Core.Plugins;
using BenchmarkDotNet.Attributes;
namespace Avalonia.Benchmarks.Data
{
[MemoryDiagnoser, InProcess]
public class PropertyAccessorPluginBenchmarks
{
private readonly AccessorTestObject _targetStrongRef = new AccessorTestObject();
private readonly List<IPropertyAccessorPlugin> _oldPlugins;
private readonly List<IPropertyAccessorPlugin> _newPlugins;
public PropertyAccessorPluginBenchmarks()
{
_oldPlugins = new List<IPropertyAccessorPlugin>
{
new AvaloniaPropertyAccessorPlugin(),
new MethodAccessorPlugin(),
new InpcPropertyAccessorPlugin()
};
_newPlugins = new List<IPropertyAccessorPlugin>
{
new AvaloniaPropertyAccessorPlugin(),
new InpcPropertyAccessorPlugin(),
new MethodAccessorPlugin()
};
}
[Benchmark]
public void MatchAccessorOld()
{
var propertyName = nameof(AccessorTestObject.Test);
foreach (IPropertyAccessorPlugin x in _oldPlugins)
{
if (x.Match(_targetStrongRef, propertyName))
{
break;
}
}
}
[Benchmark]
public void MatchAccessorNew()
{
var propertyName = nameof(AccessorTestObject.Test);
foreach (IPropertyAccessorPlugin x in _newPlugins)
{
if (x.Match(_targetStrongRef, propertyName))
{
break;
}
}
}
}
}

14
tests/Avalonia.Controls.UnitTests/DatePickerTests.cs → tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs

@ -15,7 +15,7 @@ using Xunit;
namespace Avalonia.Controls.UnitTests
{
public class DatePickerTests
public class CalendarDatePickerTests
{
private static bool CompareDates(DateTime first, DateTime second)
{
@ -30,7 +30,7 @@ namespace Avalonia.Controls.UnitTests
using (UnitTestApplication.Start(Services))
{
bool handled = false;
DatePicker datePicker = CreateControl();
CalendarDatePicker datePicker = CreateControl();
datePicker.SelectedDateChanged += (s,e) =>
{
handled = true;
@ -47,7 +47,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(Services))
{
DatePicker datePicker = CreateControl();
CalendarDatePicker datePicker = CreateControl();
datePicker.BlackoutDates.AddDatesInPast();
DateTime goodValue = DateTime.Today.AddDays(1);
@ -65,7 +65,7 @@ namespace Avalonia.Controls.UnitTests
{
using (UnitTestApplication.Start(Services))
{
DatePicker datePicker = CreateControl();
CalendarDatePicker datePicker = CreateControl();
datePicker.SelectedDate = DateTime.Today.AddDays(5);
Assert.ThrowsAny<ArgumentOutOfRangeException>(
@ -76,10 +76,10 @@ namespace Avalonia.Controls.UnitTests
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of<IStandardCursorFactory>());
private DatePicker CreateControl()
private CalendarDatePicker CreateControl()
{
var datePicker =
new DatePicker
new CalendarDatePicker
{
Template = CreateTemplate()
};
@ -90,7 +90,7 @@ namespace Avalonia.Controls.UnitTests
private IControlTemplate CreateTemplate()
{
return new FuncControlTemplate<DatePicker>((control, scope) =>
return new FuncControlTemplate<CalendarDatePicker>((control, scope) =>
{
var textBox =
new TextBox

82
tests/Avalonia.Controls.UnitTests/IndexRangeTests.cs

@ -127,6 +127,88 @@ namespace Avalonia.Controls.UnitTests
Assert.Empty(selected);
}
[Fact]
public void Intersect_Should_Remove_Items_From_Beginning()
{
var ranges = new List<IndexRange> { new IndexRange(0, 10) };
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(2, 12), removed);
Assert.Equal(2, result);
Assert.Equal(new[] { new IndexRange(2, 10) }, ranges);
Assert.Equal(new[] { new IndexRange(0, 1) }, removed);
}
[Fact]
public void Intersect_Should_Remove_Items_From_End()
{
var ranges = new List<IndexRange> { new IndexRange(0, 10) };
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(0, 8), removed);
Assert.Equal(2, result);
Assert.Equal(new[] { new IndexRange(0, 8) }, ranges);
Assert.Equal(new[] { new IndexRange(9, 10) }, removed);
}
[Fact]
public void Intersect_Should_Remove_Entire_Range_Start()
{
var ranges = new List<IndexRange> { new IndexRange(0, 5), new IndexRange(6, 10) };
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(6, 10), removed);
Assert.Equal(6, result);
Assert.Equal(new[] { new IndexRange(6, 10) }, ranges);
Assert.Equal(new[] { new IndexRange(0, 5) }, removed);
}
[Fact]
public void Intersect_Should_Remove_Entire_Range_End()
{
var ranges = new List<IndexRange> { new IndexRange(0, 5), new IndexRange(6, 10) };
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(0, 4), removed);
Assert.Equal(6, result);
Assert.Equal(new[] { new IndexRange(0, 4) }, ranges);
Assert.Equal(new[] { new IndexRange(5, 10) }, removed);
}
[Fact]
public void Intersect_Should_Remove_Entire_Range_Start_End()
{
var ranges = new List<IndexRange>
{
new IndexRange(0, 2),
new IndexRange(3, 7),
new IndexRange(8, 10)
};
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(3, 7), removed);
Assert.Equal(6, result);
Assert.Equal(new[] { new IndexRange(3, 7) }, ranges);
Assert.Equal(new[] { new IndexRange(0, 2), new IndexRange(8, 10) }, removed);
}
[Fact]
public void Intersect_Should_Remove_Entire_And_Partial_Range_Start_End()
{
var ranges = new List<IndexRange>
{
new IndexRange(0, 2),
new IndexRange(3, 7),
new IndexRange(8, 10)
};
var removed = new List<IndexRange>();
var result = IndexRange.Intersect(ranges, new IndexRange(4, 6), removed);
Assert.Equal(8, result);
Assert.Equal(new[] { new IndexRange(4, 6) }, ranges);
Assert.Equal(new[] { new IndexRange(0, 3), new IndexRange(7, 10) }, removed);
}
[Fact]
public void Remove_Should_Remove_Entire_Range()
{

146
tests/Avalonia.Controls.UnitTests/SelectionModelTests.cs

@ -1512,6 +1512,47 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(0, raised);
}
[Fact]
public void Batch_Update_Selection_Is_Correct_Throughout()
{
var data = new[] { "foo", "bar", "baz", "qux" };
var target = new SelectionModel { Source = data };
var raised = 0;
using (target.Update())
{
target.Select(1);
Assert.Equal(new IndexPath(1), target.SelectedIndex);
Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
Assert.Equal("bar", target.SelectedItem);
Assert.Equal(new[] { "bar" }, target.SelectedItems);
target.Deselect(1);
Assert.Equal(new IndexPath(), target.SelectedIndex);
Assert.Empty(target.SelectedIndices);
Assert.Null(target.SelectedItem);
Assert.Empty(target.SelectedItems);
target.SelectRange(new IndexPath(1), new IndexPath(1));
Assert.Equal(new IndexPath(1), target.SelectedIndex);
Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
Assert.Equal("bar", target.SelectedItem);
Assert.Equal(new[] { "bar" }, target.SelectedItems);
target.ClearSelection();
Assert.Equal(new IndexPath(), target.SelectedIndex);
Assert.Empty(target.SelectedIndices);
Assert.Null(target.SelectedItem);
Assert.Empty(target.SelectedItems);
}
Assert.Equal(0, raised);
}
[Fact]
public void AutoSelect_Selects_When_Enabled()
{
@ -1713,6 +1754,30 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(0, raised);
}
[Fact]
public void AutoSelect_Is_Applied_At_End_Of_Batch_Update()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel { AutoSelect = true, Source = data };
using (target.Update())
{
target.ClearSelection();
Assert.Equal(new IndexPath(), target.SelectedIndex);
Assert.Empty(target.SelectedIndices);
Assert.Null(target.SelectedItem);
Assert.Empty(target.SelectedItems);
}
Assert.Equal(new IndexPath(0), target.SelectedIndex);
Assert.Equal(new[] { new IndexPath(0) }, target.SelectedIndices);
Assert.Equal("foo", target.SelectedItem);
Assert.Equal(new[] { "foo" }, target.SelectedItems);
Assert.Equal(new IndexPath(0), target.SelectedIndex);
}
[Fact]
public void Can_Replace_Parent_Children_Collection()
{
@ -1806,6 +1871,87 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(0, node.PropertyChangedSubscriptions);
}
[Fact]
public void Setting_SelectedIndex_To_Minus_1_Clears_Selection()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel { Source = data };
target.SelectedIndex = new IndexPath(1);
target.SelectedIndex = new IndexPath(-1);
Assert.Empty(target.SelectedIndices);
}
[Fact]
public void Assigning_Source_With_Less_Items_Than_Previous_Clears_Selection()
{
var data = new[] { "foo", "bar", "baz", "boo", "hoo" };
var smallerData = new[] { "foo", "bar", "baz" };
var target = new SelectionModel { RetainSelectionOnReset = true };
target.Source = data;
target.SelectedIndex = new IndexPath(4);
target.Source = smallerData;
Assert.Empty(target.SelectedIndices);
}
[Fact]
public void Initializing_Source_With_Less_Items_Than_Selection_Trims_Selection()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel();
target.SelectedIndex = new IndexPath(4);
target.Source = data;
Assert.Empty(target.SelectedIndices);
}
[Fact]
public void Initializing_Source_With_Less_Items_Than_Selection_Trims_Selection_RetainSelection()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel { RetainSelectionOnReset = true };
target.SelectedIndex = new IndexPath(4);
target.Source = data;
Assert.Empty(target.SelectedIndices);
}
[Fact]
public void Initializing_Source_With_Less_Items_Than_Multiple_Selection_Trims_Selection()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel { RetainSelectionOnReset = true };
target.Select(4);
target.Select(2);
target.Source = data;
Assert.Equal(1, target.SelectedIndices.Count);
Assert.Equal(new IndexPath(2), target.SelectedIndices.First());
}
[Fact]
public void Initializing_Source_With_Less_Items_Than_Selection_Raises_SelectionChanged()
{
var data = new[] { "foo", "bar", "baz" };
var target = new SelectionModel();
var raised = 0;
target.SelectedIndex = new IndexPath(4);
target.SelectionChanged += (s, e) =>
{
if (raised == 0)
{
Assert.Equal(new[] { Path(4) }, e.DeselectedIndices);
Assert.Equal(new object[] { null }, e.DeselectedItems);
Assert.Empty(e.SelectedIndices);
Assert.Empty(e.SelectedItems);
}
++raised;
};
target.Source = data;
Assert.Equal(2, raised);
}
private int GetSubscriberCount(AvaloniaList<object> list)
{
return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0;

24
tests/Avalonia.Controls.UnitTests/TabControlTests.cs

@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.LogicalTree;
using Avalonia.Styling;
using Avalonia.UnitTests;
@ -325,6 +327,28 @@ namespace Avalonia.Controls.UnitTests
Assert.NotEqual(dataContext, tabItem.Content);
}
[Fact]
public void Can_Have_Empty_Tab_Control()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<TabControl Name='tabs' Items='{Binding Tabs}'/>
</Window>";
var loader = new Markup.Xaml.AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var tabControl = window.FindControl<TabControl>("tabs");
tabControl.DataContext = new { Tabs = new List<string>() };
window.ApplyTemplate();
Assert.Equal(0, tabControl.Items.Count());
}
}
private IControlTemplate TabControlTemplate()
{
return new FuncControlTemplate<TabControl>((parent, scope) =>

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

@ -118,313 +118,340 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Clicking_Item_Should_Select_It()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
Assert.NotNull(container);
Assert.NotNull(container);
_mouse.Click(container);
_mouse.Click(container);
Assert.Equal(item, target.SelectedItem);
Assert.True(container.IsSelected);
Assert.Equal(item, target.SelectedItem);
Assert.True(container.IsSelected);
}
}
[Fact]
public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_It()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
Assert.NotNull(container);
Assert.NotNull(container);
target.SelectedItem = item;
target.SelectedItem = item;
Assert.True(container.IsSelected);
Assert.True(container.IsSelected);
_mouse.Click(container, modifiers: KeyModifiers.Control);
_mouse.Click(container, modifiers: KeyModifiers.Control);
Assert.Null(target.SelectedItem);
Assert.False(container.IsSelected);
Assert.Null(target.SelectedItem);
Assert.False(container.IsSelected);
}
}
[Fact]
public void Clicking_WithControlModifier_Not_Selected_Item_Should_Select_It()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item1 = tree[0].Children[1].Children[0];
var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
var item1 = tree[0].Children[1].Children[0];
var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
var item2 = tree[0].Children[1];
var container2 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
var item2 = tree[0].Children[1];
var container2 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
Assert.NotNull(container1);
Assert.NotNull(container2);
Assert.NotNull(container1);
Assert.NotNull(container2);
target.SelectedItem = item1;
target.SelectedItem = item1;
Assert.True(container1.IsSelected);
Assert.True(container1.IsSelected);
_mouse.Click(container2, modifiers: KeyModifiers.Control);
Assert.Equal(item2, target.SelectedItem);
Assert.False(container1.IsSelected);
Assert.True(container2.IsSelected);
_mouse.Click(container2, modifiers: KeyModifiers.Control);
Assert.Equal(item2, target.SelectedItem);
Assert.False(container1.IsSelected);
Assert.True(container2.IsSelected);
}
}
[Fact]
public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_And_Remove_From_SelectedItems()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
var rootNode = tree[0];
var item1 = rootNode.Children[0];
var item2 = rootNode.Children.Last();
var item1 = rootNode.Children[0];
var item2 = rootNode.Children.Last();
var item1Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
var item2Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
var item1Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
var item2Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
ClickContainer(item1Container, KeyModifiers.Control);
Assert.True(item1Container.IsSelected);
ClickContainer(item1Container, KeyModifiers.Control);
Assert.True(item1Container.IsSelected);
ClickContainer(item2Container, KeyModifiers.Control);
Assert.True(item2Container.IsSelected);
ClickContainer(item2Container, KeyModifiers.Control);
Assert.True(item2Container.IsSelected);
Assert.Equal(new[] {item1, item2}, target.Selection.SelectedItems.OfType<Node>());
Assert.Equal(new[] { item1, item2 }, target.Selection.SelectedItems.OfType<Node>());
ClickContainer(item1Container, KeyModifiers.Control);
Assert.False(item1Container.IsSelected);
ClickContainer(item1Container, KeyModifiers.Control);
Assert.False(item1Container.IsSelected);
Assert.DoesNotContain(item1, target.Selection.SelectedItems.OfType<Node>());
Assert.DoesNotContain(item1, target.Selection.SelectedItems.OfType<Node>());
}
}
[Fact]
public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
var rootNode = tree[0];
var from = rootNode.Children[0];
var to = rootNode.Children.Last();
var from = rootNode.Children[0];
var to = rootNode.Children.Last();
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(fromContainer, KeyModifiers.None);
Assert.True(fromContainer.IsSelected);
Assert.True(fromContainer.IsSelected);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
}
}
[Fact]
public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
var rootNode = tree[0];
var from = rootNode.Children.Last();
var to = rootNode.Children[0];
var from = rootNode.Children.Last();
var to = rootNode.Children[0];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(fromContainer, KeyModifiers.None);
Assert.True(fromContainer.IsSelected);
Assert.True(fromContainer.IsSelected);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
}
}
[Fact]
public void Clicking_First_Item_Of_SelectedItems_Should_Select_Only_It()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var rootNode = tree[0];
var rootNode = tree[0];
var from = rootNode.Children.Last();
var to = rootNode.Children[0];
var from = rootNode.Children.Last();
var to = rootNode.Children[0];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
ClickContainer(toContainer, KeyModifiers.Shift);
AssertChildrenSelected(target, rootNode);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(fromContainer, KeyModifiers.None);
Assert.True(fromContainer.IsSelected);
Assert.True(fromContainer.IsSelected);
foreach (var child in rootNode.Children)
{
if (child == from)
foreach (var child in rootNode.Children)
{
continue;
}
if (child == from)
{
continue;
}
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(child);
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(child);
Assert.False(container.IsSelected);
Assert.False(container.IsSelected);
}
}
}
[Fact]
public void Setting_SelectedItem_Should_Set_Container_Selected()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
var item = tree[0].Children[1].Children[0];
var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
Assert.NotNull(container);
Assert.NotNull(container);
target.SelectedItem = item;
target.SelectedItem = item;
Assert.True(container.IsSelected);
Assert.True(container.IsSelected);
}
}
[Fact]
public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
ExpandAll(target);
var item = tree[0].Children[1].Children[0];
var item = tree[0].Children[1].Children[0];
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Empty(e.RemovedItems);
Assert.Equal(1, e.AddedItems.Count);
Assert.Same(item, e.AddedItems[0]);
called = true;
};
var called = false;
target.SelectionChanged += (s, e) =>
{
Assert.Empty(e.RemovedItems);
Assert.Equal(1, e.AddedItems.Count);
Assert.Same(item, e.AddedItems[0]);
called = true;
};
target.SelectedItem = item;
Assert.True(called);
target.SelectedItem = item;
Assert.True(called);
}
}
[Fact]
@ -564,7 +591,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
{
using (UnitTestApplication.Start(TestServices.RealFocus))
using (Application())
{
var focus = FocusManager.Instance;
var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
@ -647,7 +674,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Pressing_SelectAll_Gesture_With_Downward_Range_Selected_Should_Select_All_Nodes()
{
using (UnitTestApplication.Start())
using (Application())
{
var tree = CreateTestTreeData();
var target = new TreeView
@ -694,7 +721,7 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Pressing_SelectAll_Gesture_With_Upward_Range_Selected_Should_Select_All_Nodes()
{
using (UnitTestApplication.Start())
using (Application())
{
var tree = CreateTestTreeData();
var target = new TreeView
@ -768,97 +795,106 @@ namespace Avalonia.Controls.UnitTests
[Fact]
public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
var rootNode = tree[0];
var to = rootNode.Children[0];
var then = rootNode.Children[1];
var rootNode = tree[0];
var to = rootNode.Children[0];
var then = rootNode.Children[1];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var thenContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(then);
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var thenContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(then);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(toContainer, KeyModifiers.Shift);
ClickContainer(fromContainer, KeyModifiers.None);
ClickContainer(toContainer, KeyModifiers.Shift);
Assert.Equal(2, target.Selection.SelectedItems.Count);
Assert.Equal(2, target.Selection.SelectedItems.Count);
_mouse.Click(thenContainer, MouseButton.Right);
_mouse.Click(thenContainer, MouseButton.Right);
Assert.Equal(1, target.Selection.SelectedItems.Count);
Assert.Equal(1, target.Selection.SelectedItems.Count);
}
}
[Fact]
public void Shift_Right_Click_Should_Not_Select_Multiple()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
var rootNode = tree[0];
var from = rootNode.Children[0];
var to = rootNode.Children[1];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var rootNode = tree[0];
var from = rootNode.Children[0];
var to = rootNode.Children[1];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
_mouse.Click(fromContainer);
_mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift);
_mouse.Click(fromContainer);
_mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift);
Assert.Equal(1, target.Selection.SelectedItems.Count);
Assert.Equal(1, target.Selection.SelectedItems.Count);
}
}
[Fact]
public void Ctrl_Right_Click_Should_Not_Select_Multiple()
{
var tree = CreateTestTreeData();
var target = new TreeView
using (Application())
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var tree = CreateTestTreeData();
var target = new TreeView
{
Template = CreateTreeViewTemplate(),
Items = tree,
SelectionMode = SelectionMode.Multiple,
};
var visualRoot = new TestRoot();
visualRoot.Child = target;
var visualRoot = new TestRoot();
visualRoot.Child = target;
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
CreateNodeDataTemplate(target);
ApplyTemplates(target);
target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
var rootNode = tree[0];
var from = rootNode.Children[0];
var to = rootNode.Children[1];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
var rootNode = tree[0];
var from = rootNode.Children[0];
var to = rootNode.Children[1];
var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
_mouse.Click(fromContainer);
_mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control);
_mouse.Click(fromContainer);
_mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control);
Assert.Equal(1, target.Selection.SelectedItems.Count);
Assert.Equal(1, target.Selection.SelectedItems.Count);
}
}
[Fact]
@ -944,7 +980,7 @@ namespace Avalonia.Controls.UnitTests
public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection()
{
/// Issue #2980.
using (UnitTestApplication.Start(TestServices.RealStyler))
using (Application())
{
var target = new DerivedTreeView
{
@ -1183,12 +1219,12 @@ namespace Avalonia.Controls.UnitTests
}
}
void ClickContainer(IControl container, KeyModifiers modifiers)
private void ClickContainer(IControl container, KeyModifiers modifiers)
{
_mouse.Click(container, modifiers: modifiers);
}
void AssertChildrenSelected(TreeView treeView, Node rootNode)
private void AssertChildrenSelected(TreeView treeView, Node rootNode)
{
foreach (var child in rootNode.Children)
{
@ -1198,6 +1234,16 @@ namespace Avalonia.Controls.UnitTests
}
}
private IDisposable Application()
{
return UnitTestApplication.Start(
TestServices.MockThreadingInterface.With(
focusManager: new FocusManager(),
keyboardDevice: () => new KeyboardDevice(),
keyboardNavigation: new KeyboardNavigationHandler(),
inputManager: new InputManager()));
}
private class Node : NotifyingBase
{
private IAvaloniaList<Node> _children;

44
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

@ -113,6 +113,50 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal("(null)", target.Text);
}
[Fact]
public void Should_Pass_UnsetValue_To_Converter_For_Broken_Binding()
{
var source = new { A = 1, B = 2, C = 3 };
var target = new TextBlock { DataContext = source };
var binding = new MultiBinding
{
Converter = new ConcatConverter(),
Bindings = new[]
{
new Binding { Path = "A" },
new Binding { Path = "B" },
new Binding { Path = "Missing" },
},
};
target.Bind(TextBlock.TextProperty, binding);
Assert.Equal("1,2,(unset)", target.Text);
}
[Fact]
public void Should_Pass_FallbackValue_To_Converter_For_Broken_Binding()
{
var source = new { A = 1, B = 2, C = 3 };
var target = new TextBlock { DataContext = source };
var binding = new MultiBinding
{
Converter = new ConcatConverter(),
Bindings = new[]
{
new Binding { Path = "A" },
new Binding { Path = "B" },
new Binding { Path = "Missing", FallbackValue = "Fallback" },
},
};
target.Bind(TextBlock.TextProperty, binding);
Assert.Equal("1,2,Fallback", target.Text);
}
private class ConcatConverter : IMultiValueConverter
{
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)

2
tests/Avalonia.Markup.UnitTests/Parsers/ExpressionObserverBuilderTests_Property.cs

@ -22,7 +22,7 @@ namespace Avalonia.Markup.UnitTests.Parsers
Assert.Equal(
new BindingNotification(
new MissingMemberException("Could not find CLR property 'Baz' on '1'"), BindingErrorType.Error),
new MissingMemberException("Could not find a matching property accessor for 'Baz' on '1'"), BindingErrorType.Error),
result);
GC.KeepAlive(data);

53
tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs

@ -1,6 +1,13 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.UnitTests;
using Avalonia.VisualTree;
using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Data
@ -54,5 +61,51 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data
Assert.Equal("bar", textBlock.Text);
}
}
[Fact]
public void MultiBinding_TemplatedParent_Works()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Data;assembly=Avalonia.Markup.Xaml.UnitTests'>
<TextBox Name='textBox' Text='Foo' Watermark='Bar'>
<TextBox.Template>
<ControlTemplate>
<TextPresenter Name='PART_TextPresenter'>
<TextPresenter.Text>
<MultiBinding Converter='{x:Static local:ConcatConverter.Instance}'>
<Binding RelativeSource='{RelativeSource TemplatedParent}' Path='Text'/>
<Binding RelativeSource='{RelativeSource TemplatedParent}' Path='Watermark'/>
</MultiBinding>
</TextPresenter.Text>
</TextPresenter>
</ControlTemplate>
</TextBox.Template>
</TextBox>
</Window>";
var loader = new AvaloniaXamlLoader();
var window = (Window)loader.Load(xaml);
var textBox = window.FindControl<TextBox>("textBox");
window.ApplyTemplate();
textBox.ApplyTemplate();
var target = (TextPresenter)textBox.GetVisualChildren().Single();
Assert.Equal("Foo,Bar", target.Text);
}
}
}
public class ConcatConverter : IMultiValueConverter
{
public static ConcatConverter Instance { get; } = new ConcatConverter();
public object Convert(IList<object> values, Type targetType, object parameter, CultureInfo culture)
{
return string.Join(",", values);
}
}
}

Loading…
Cancel
Save