diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 7956ee6169..af2d093bc7 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -29,7 +29,8 @@
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
-
+
+
+ x:Class="ControlCatalog.Pages.CalendarDatePickerPage">
- DatePicker
+ CalendarDatePicker
A control for selecting dates with a calendar drop-down
-
-
-
-
-
-
-
+
diff --git a/samples/ControlCatalog/Pages/DatePickerPage.xaml.cs b/samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
similarity index 57%
rename from samples/ControlCatalog/Pages/DatePickerPage.xaml.cs
rename to samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
index ef01887c9e..95bdeb363a 100644
--- a/samples/ControlCatalog/Pages/DatePickerPage.xaml.cs
+++ b/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("DatePicker1");
- var dp2 = this.FindControl("DatePicker2");
- var dp3 = this.FindControl("DatePicker3");
- var dp4 = this.FindControl("DatePicker4");
- var dp5 = this.FindControl("DatePicker5");
+ var dp1 = this.FindControl("DatePicker1");
+ var dp2 = this.FindControl("DatePicker2");
+ var dp3 = this.FindControl("DatePicker3");
+ var dp4 = this.FindControl("DatePicker4");
+ var dp5 = this.FindControl("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);
diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs
index 75d58f45d5..0664f22dcb 100644
--- a/src/Avalonia.Base/Properties/AssemblyInfo.cs
+++ b/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")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
diff --git a/src/Avalonia.Base/Utilities/MathUtilities.cs b/src/Avalonia.Base/Utilities/MathUtilities.cs
index a1a2d6c3d0..7949a62949 100644
--- a/src/Avalonia.Base/Utilities/MathUtilities.cs
+++ b/src/Avalonia.Base/Utilities/MathUtilities.cs
@@ -8,6 +8,11 @@ namespace Avalonia.Utilities
///
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;
+
///
/// 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);
}
+ ///
+ /// AreClose - Returns whether or not two floats are "close". That is, whether or
+ /// not they are within epsilon of each other.
+ ///
+ /// The first float to compare.
+ /// The second float to compare.
+ 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);
+ }
+
///
/// 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);
}
+ ///
+ /// 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.
+ ///
+ /// The first single float to compare.
+ /// The second single float to compare.
+ public static bool LessThan(float value1, float value2)
+ {
+ return (value1 < value2) && !AreClose(value1, value2);
+ }
+
///
/// 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);
}
+ ///
+ /// 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.
+ ///
+ /// The first float to compare.
+ /// The second float to compare.
+ public static bool GreaterThan(float value1, float value2)
+ {
+ return (value1 > value2) && !AreClose(value1, value2);
+ }
+
///
/// 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);
}
+ ///
+ /// 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.
+ ///
+ /// The first float to compare.
+ /// The second float to compare.
+ public static bool LessThanOrClose(float value1, float value2)
+ {
+ return (value1 < value2) || AreClose(value1, value2);
+ }
+
///
/// 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);
}
+ ///
+ /// 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.
+ ///
+ /// The first float to compare.
+ /// The second float to compare.
+ public static bool GreaterThanOrClose(float value1, float value2)
+ {
+ return (value1 > value2) || AreClose(value1, value2);
+ }
+
///
/// 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
/// The double to compare to 1.
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;
+ }
+
+ ///
+ /// IsOne - Returns whether or not the float is "close" to 1. Same as AreClose(float, 1),
+ /// but this is faster.
+ ///
+ /// The float to compare to 1.
+ public static bool IsOne(float value)
+ {
+ return Math.Abs(value - 1.0f) < 10.0f * FloatEpsilon;
}
///
@@ -88,7 +166,17 @@ namespace Avalonia.Utilities
/// The double to compare to 0.
public static bool IsZero(double value)
{
- return Math.Abs(value) < 10.0 * double.Epsilon;
+ return Math.Abs(value) < 10.0 * DoubleEpsilon;
+ }
+
+ ///
+ /// IsZero - Returns whether or not the float is "close" to 0. Same as AreClose(float, 0),
+ /// but this is faster.
+ ///
+ /// The float to compare to 0.
+ public static bool IsZero(float value)
+ {
+ return Math.Abs(value) < 10.0f * FloatEpsilon;
}
///
diff --git a/src/Avalonia.Controls/AutoCompleteBox.cs b/src/Avalonia.Controls/AutoCompleteBox.cs
index b38cc56a17..31101dc0f1 100644
--- a/src/Avalonia.Controls/AutoCompleteBox.cs
+++ b/src/Avalonia.Controls/AutoCompleteBox.cs
@@ -468,10 +468,11 @@ namespace Avalonia.Controls
///
/// dependency property.
public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
- nameof(Text),
+ TextBlock.TextProperty.AddOwnerWithDataValidation(
o => o.Text,
- (o, v) => o.Text = v);
+ (o, v) => o.Text = v,
+ defaultBindingMode: BindingMode.TwoWay,
+ enableDataValidation: true);
///
/// Identifies the
@@ -1244,6 +1245,20 @@ namespace Avalonia.Controls
base.OnApplyTemplate(e);
}
+
+ ///
+ /// Called to update the validation state for properties for which data validation is
+ /// enabled.
+ ///
+ /// The property.
+ /// The new binding value for the property.
+ protected override void UpdateDataValidation(AvaloniaProperty property, BindingValue value)
+ {
+ if (property == TextProperty)
+ {
+ DataValidationErrors.SetError(this, value.Error);
+ }
+ }
///
/// Provides handling for the
diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs
index 53c6a54b4d..4cf7db74d9 100644
--- a/src/Avalonia.Controls/Calendar/Calendar.cs
+++ b/src/Avalonia.Controls/Calendar/Calendar.cs
@@ -998,10 +998,10 @@ namespace Avalonia.Controls
///
- /// 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.
///
- internal bool DatePickerDisplayDateFlag { get; set; }
+ internal bool CalendarDatePickerDisplayDateFlag { get; set; }
internal CalendarDayButton FindDayButtonFromDay(DateTime day)
{
diff --git a/src/Avalonia.Controls/Calendar/DatePicker.cs b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
similarity index 87%
rename from src/Avalonia.Controls/Calendar/DatePicker.cs
rename to src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
index 0f53dc1364..b987f065be 100644
--- a/src/Avalonia.Controls/Calendar/DatePicker.cs
+++ b/src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
@@ -16,29 +16,29 @@ namespace Avalonia.Controls
{
///
/// Provides data for the
- ///
+ ///
/// event.
///
- public class DatePickerDateValidationErrorEventArgs : EventArgs
+ public class CalendarDatePickerDateValidationErrorEventArgs : EventArgs
{
private bool _throwException;
///
/// Initializes a new instance of the
- ///
+ ///
/// class.
///
///
/// The initial exception from the
- ///
+ ///
/// event.
///
///
/// The text that caused the
- ///
+ ///
/// event.
///
- 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
///
/// Gets the initial exception associated with the
- ///
+ ///
/// event.
///
///
@@ -56,7 +56,7 @@ namespace Avalonia.Controls
///
/// Gets the text that caused the
- ///
+ ///
/// event.
///
///
@@ -66,7 +66,7 @@ namespace Avalonia.Controls
///
/// Gets or sets a value indicating whether
- ///
+ ///
/// should be thrown.
///
///
@@ -74,7 +74,7 @@ namespace Avalonia.Controls
///
///
/// If set to true and
- ///
+ ///
/// is null.
///
public bool ThrowException
@@ -93,9 +93,9 @@ namespace Avalonia.Controls
///
/// Specifies date formats for a
- /// .
+ /// .
///
- public enum DatePickerFormat
+ public enum CalendarDatePickerFormat
{
///
/// 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
///
public CalendarBlackoutDatesCollection BlackoutDates { get; private set; }
- public static readonly DirectProperty DisplayDateProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty DisplayDateProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(DisplayDate),
o => o.DisplayDate,
(o, v) => o.DisplayDate = v);
- public static readonly DirectProperty DisplayDateStartProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty DisplayDateStartProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(DisplayDateStart),
o => o.DisplayDateStart,
(o, v) => o.DisplayDateStart = v);
- public static readonly DirectProperty DisplayDateEndProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty DisplayDateEndProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(DisplayDateEnd),
o => o.DisplayDateEnd,
(o, v) => o.DisplayDateEnd = v);
public static readonly StyledProperty FirstDayOfWeekProperty =
- AvaloniaProperty.Register(nameof(FirstDayOfWeek));
+ AvaloniaProperty.Register(nameof(FirstDayOfWeek));
- public static readonly DirectProperty IsDropDownOpenProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty IsDropDownOpenProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(IsDropDownOpen),
o => o.IsDropDownOpen,
(o, v) => o.IsDropDownOpen = v);
public static readonly StyledProperty IsTodayHighlightedProperty =
- AvaloniaProperty.Register(nameof(IsTodayHighlighted));
- public static readonly DirectProperty SelectedDateProperty =
- AvaloniaProperty.RegisterDirect(
+ AvaloniaProperty.Register(nameof(IsTodayHighlighted));
+ public static readonly DirectProperty SelectedDateProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v);
- public static readonly StyledProperty SelectedDateFormatProperty =
- AvaloniaProperty.Register(
+ public static readonly StyledProperty SelectedDateFormatProperty =
+ AvaloniaProperty.Register(
nameof(SelectedDateFormat),
- defaultValue: DatePickerFormat.Short,
+ defaultValue: CalendarDatePickerFormat.Short,
validate: IsValidSelectedDateFormat);
public static readonly StyledProperty CustomDateFormatStringProperty =
- AvaloniaProperty.Register(
+ AvaloniaProperty.Register(
nameof(CustomDateFormatString),
defaultValue: "d",
validate: IsValidDateFormatString);
- public static readonly DirectProperty TextProperty =
- AvaloniaProperty.RegisterDirect(
+ public static readonly DirectProperty TextProperty =
+ AvaloniaProperty.RegisterDirect(
nameof(Text),
o => o.Text,
(o, v) => o.Text = v);
public static readonly StyledProperty WatermarkProperty =
- TextBox.WatermarkProperty.AddOwner();
+ TextBox.WatermarkProperty.AddOwner();
public static readonly StyledProperty UseFloatingWatermarkProperty =
- TextBox.UseFloatingWatermarkProperty.AddOwner();
+ TextBox.UseFloatingWatermarkProperty.AddOwner();
///
@@ -218,9 +218,9 @@ namespace Avalonia.Controls
///
///
/// The specified date is not in the range defined by
- ///
+ ///
/// and
- /// .
+ /// .
///
public DateTime DisplayDate
{
@@ -320,7 +320,7 @@ namespace Avalonia.Controls
///
/// An specified format is not valid.
///
- public DatePickerFormat SelectedDateFormat
+ public CalendarDatePickerFormat SelectedDateFormat
{
get { return GetValue(SelectedDateFormatProperty); }
set { SetValue(SelectedDateFormatProperty, value); }
@@ -380,33 +380,33 @@ namespace Avalonia.Controls
/// Occurs when
/// is assigned a value that cannot be interpreted as a date.
///
- public event EventHandler DateValidationError;
+ public event EventHandler DateValidationError;
///
/// Occurs when the
- ///
+ ///
/// property is changed.
///
public event EventHandler SelectedDateChanged;
- static DatePicker()
+ static CalendarDatePicker()
{
- FocusableProperty.OverrideDefaultValue(true);
-
- DisplayDateProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateChanged(e));
- DisplayDateStartProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateStartChanged(e));
- DisplayDateEndProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateEndChanged(e));
- IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
- SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
- SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e));
- CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e));
- TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e));
+ FocusableProperty.OverrideDefaultValue(true);
+
+ DisplayDateProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateChanged(e));
+ DisplayDateStartProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateStartChanged(e));
+ DisplayDateEndProperty.Changed.AddClassHandler((x,e) => x.OnDisplayDateEndChanged(e));
+ IsDropDownOpenProperty.Changed.AddClassHandler((x,e) => x.OnIsDropDownOpenChanged(e));
+ SelectedDateProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateChanged(e));
+ SelectedDateFormatProperty.Changed.AddClassHandler((x,e) => x.OnSelectedDateFormatChanged(e));
+ CustomDateFormatStringProperty.Changed.AddClassHandler((x,e) => x.OnCustomDateFormatStringChanged(e));
+ TextProperty.Changed.AddClassHandler((x,e) => x.OnTextChanged(e));
}
///
/// Initializes a new instance of the
/// class.
///
- 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
///
/// Raises the
- ///
+ ///
/// event.
///
///
/// A
- ///
+ ///
/// that contains the event data.
///
- 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)
{
diff --git a/src/Avalonia.Controls/Calendar/CalendarItem.cs b/src/Avalonia.Controls/Calendar/CalendarItem.cs
index ece0ef97d9..0be7c4f67e 100644
--- a/src/Avalonia.Controls/Calendar/CalendarItem.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Grid.cs b/src/Avalonia.Controls/Grid.cs
index 1781067abb..e10d78917e 100644
--- a/src/Avalonia.Controls/Grid.cs
+++ b/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); }
}
- ///
- /// fp version of d == 0.
- ///
- /// Value to check.
- /// true if d == 0.
- private static bool _IsZero(double d)
- {
- return (Math.Abs(d) < double.Epsilon);
- }
-
- ///
- /// fp version of d1 == d2
- ///
- /// First value to compare
- /// Second value to compare
- /// true if d1 == d2
- private static bool _AreClose(double d1, double d2)
- {
- return (Math.Abs(d1 - d2) < double.Epsilon);
- }
-
///
/// Returns reference to extended data bag.
///
diff --git a/src/Avalonia.Controls/Slider.cs b/src/Avalonia.Controls/Slider.cs
index ec23bfa396..64378a4eb2 100644
--- a/src/Avalonia.Controls/Slider.cs
+++ b/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);
diff --git a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs b/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
index 51eb6edbea..438cbc8b27 100644
--- a/src/Avalonia.Controls/Utils/BorderRenderHelper.cs
+++ b/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);
diff --git a/src/Avalonia.Themes.Fluent/DatePicker.xaml b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml
similarity index 97%
rename from src/Avalonia.Themes.Fluent/DatePicker.xaml
rename to src/Avalonia.Themes.Default/CalendarDatePicker.xaml
index 7adb1c2d5f..bc1aba1a03 100644
--- a/src/Avalonia.Themes.Fluent/DatePicker.xaml
+++ b/src/Avalonia.Themes.Default/CalendarDatePicker.xaml
@@ -8,7 +8,7 @@
-
-
diff --git a/src/Avalonia.Themes.Default/DefaultTheme.xaml b/src/Avalonia.Themes.Default/DefaultTheme.xaml
index 67279fca99..83da5d3142 100644
--- a/src/Avalonia.Themes.Default/DefaultTheme.xaml
+++ b/src/Avalonia.Themes.Default/DefaultTheme.xaml
@@ -45,7 +45,7 @@
-
+
diff --git a/src/Avalonia.Themes.Default/DatePicker.xaml b/src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml
similarity index 97%
rename from src/Avalonia.Themes.Default/DatePicker.xaml
rename to src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml
index 7adb1c2d5f..bc1aba1a03 100644
--- a/src/Avalonia.Themes.Default/DatePicker.xaml
+++ b/src/Avalonia.Themes.Fluent/CalendarDatePicker.xaml
@@ -8,7 +8,7 @@
-
-
diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
index a20f075e21..143b952163 100644
--- a/src/Avalonia.Themes.Fluent/FluentTheme.xaml
+++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml
@@ -44,7 +44,7 @@
-
+
diff --git a/src/Avalonia.Visuals/Media/DrawingContext.cs b/src/Avalonia.Visuals/Media/DrawingContext.cs
index 6fdcd9631b..b1cf1aecc9 100644
--- a/src/Avalonia.Visuals/Media/DrawingContext.cs
+++ b/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);
}
diff --git a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs b/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
index 720185a3ad..0292398782 100644
--- a/src/Avalonia.Visuals/Media/TextFormatting/TextLayout.cs
+++ b/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
///
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);
diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
index bbb45cf64c..9b7ba4844a 100644
--- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs
+++ b/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)
{
diff --git a/tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs b/tests/Avalonia.Base.UnitTests/Utilities/MathUtilitiesTests.cs
new file mode 100644
index 0000000000..0378a5b017
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs b/tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
similarity index 90%
rename from tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
rename to tests/Avalonia.Controls.UnitTests/CalendarDatePickerTests.cs
index 3d396a9726..f41a3e7581 100644
--- a/tests/Avalonia.Controls.UnitTests/DatePickerTests.cs
+++ b/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(
@@ -76,10 +76,10 @@ namespace Avalonia.Controls.UnitTests
private static TestServices Services => TestServices.MockThreadingInterface.With(
standardCursorFactory: Mock.Of());
- 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((control, scope) =>
+ return new FuncControlTemplate((control, scope) =>
{
var textBox =
new TextBox