mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-29 15:19:12 +08:00
335 lines
9.8 KiB
C#
335 lines
9.8 KiB
C#
using System.ComponentModel;
|
|
using System.Reflection;
|
|
using AtomUI.Controls;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Data;
|
|
using Avalonia.Media;
|
|
using Avalonia.Threading;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using AtomColorPicker = AtomUI.Desktop.Controls.ColorPicker;
|
|
using AtomGradientColorPicker = AtomUI.Desktop.Controls.GradientColorPicker;
|
|
using AtomMentions = AtomUI.Desktop.Controls.Mentions;
|
|
using AtomSlider = AtomUI.Desktop.Controls.Slider;
|
|
using AvaloniaWindow = Avalonia.Controls.Window;
|
|
|
|
namespace AtomUI.Desktop.Controls.Tests.SelectionSemantics;
|
|
|
|
public class RemainingFormValueBindingTests
|
|
{
|
|
static RemainingFormValueBindingTests()
|
|
{
|
|
AvaloniaTestApp.EnsureInitialized();
|
|
}
|
|
|
|
[Fact]
|
|
public void Remaining_Form_Value_Properties_Are_TwoWay_And_DataValidation_Enabled()
|
|
{
|
|
AssertTwoWayAndDataValidation(AtomSlider.RangeValuesProperty, typeof(AtomSlider));
|
|
AssertTwoWayAndDataValidation(AtomColorPicker.ValueProperty, typeof(AtomColorPicker));
|
|
AssertTwoWayAndDataValidation(AtomGradientColorPicker.ValueProperty, typeof(AtomGradientColorPicker));
|
|
AssertTwoWayAndDataValidation(AtomMentions.ValueProperty, typeof(AtomMentions));
|
|
}
|
|
|
|
[Fact]
|
|
public void Slider_RangeValues_DefaultBindingMode_Updates_ViewModel()
|
|
{
|
|
var viewModel = new RemainingFormValueBindingViewModel
|
|
{
|
|
RangeValues = [10, 30]
|
|
};
|
|
var slider = new AtomSlider
|
|
{
|
|
IsRangeMode = true
|
|
};
|
|
slider.Bind(
|
|
AtomSlider.RangeValuesProperty,
|
|
new Binding(nameof(RemainingFormValueBindingViewModel.RangeValues))
|
|
{
|
|
Source = viewModel
|
|
});
|
|
|
|
ShowInWindow(slider, () =>
|
|
{
|
|
slider.RangeValues.ShouldBe(viewModel.RangeValues);
|
|
|
|
IReadOnlyList<double> updated = [20, 80];
|
|
slider.RangeValues = updated;
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
viewModel.RangeValues.ShouldBe(updated);
|
|
});
|
|
}
|
|
|
|
[Fact]
|
|
public void Slider_RangeValues_DataValidationError_Is_Written_To_DataValidationErrors()
|
|
{
|
|
var slider = new DataValidationProbeSlider
|
|
{
|
|
IsRangeMode = true
|
|
};
|
|
var validationError = new InvalidOperationException("Range is required");
|
|
|
|
slider.ApplyDataValidation(AtomSlider.RangeValuesProperty, validationError);
|
|
|
|
DataValidationErrors.GetHasErrors(slider).ShouldBeTrue();
|
|
DataValidationErrors.GetErrors(slider).ShouldBe([validationError]);
|
|
|
|
slider.ApplyDataValidation(AtomSlider.RangeValuesProperty, null);
|
|
|
|
DataValidationErrors.GetHasErrors(slider).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void ColorPicker_Value_DefaultBindingMode_Updates_ViewModel()
|
|
{
|
|
var viewModel = new RemainingFormValueBindingViewModel
|
|
{
|
|
ColorValue = Colors.Red
|
|
};
|
|
var colorPicker = new AtomColorPicker();
|
|
colorPicker.Bind(
|
|
AtomColorPicker.ValueProperty,
|
|
new Binding(nameof(RemainingFormValueBindingViewModel.ColorValue))
|
|
{
|
|
Source = viewModel
|
|
});
|
|
|
|
Dispatcher.UIThread.RunJobs();
|
|
colorPicker.Value.ShouldBe(Colors.Red);
|
|
|
|
colorPicker.SetCurrentValue(AtomColorPicker.ValueProperty, Colors.Blue);
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
viewModel.ColorValue.ShouldBe(Colors.Blue);
|
|
}
|
|
|
|
[Fact]
|
|
public void ColorPicker_Value_Clr_Wrappers_Are_Publicly_Settable()
|
|
{
|
|
AssertPublicSetter(typeof(AtomColorPicker), nameof(AtomColorPicker.Value));
|
|
AssertPublicSetter(typeof(AtomGradientColorPicker), nameof(AtomGradientColorPicker.Value));
|
|
}
|
|
|
|
[Fact]
|
|
public void ColorPicker_FormValue_Clear_Sets_Value_To_Null()
|
|
{
|
|
var colorPicker = new AtomColorPicker();
|
|
var formItemAware = (IFormItemAware)colorPicker;
|
|
|
|
formItemAware.SetFormValue(Colors.Red);
|
|
formItemAware.GetFormValue().ShouldBe(Colors.Red);
|
|
|
|
formItemAware.ClearFormValue();
|
|
|
|
colorPicker.Value.ShouldBeNull();
|
|
formItemAware.GetFormValue().ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void GradientColorPicker_Value_DefaultBindingMode_Updates_ViewModel()
|
|
{
|
|
var initial = CreateGradient(Colors.Red, Colors.Blue);
|
|
var viewModel = new RemainingFormValueBindingViewModel
|
|
{
|
|
GradientValue = initial
|
|
};
|
|
var colorPicker = new AtomGradientColorPicker();
|
|
colorPicker.Bind(
|
|
AtomGradientColorPicker.ValueProperty,
|
|
new Binding(nameof(RemainingFormValueBindingViewModel.GradientValue))
|
|
{
|
|
Source = viewModel
|
|
});
|
|
|
|
Dispatcher.UIThread.RunJobs();
|
|
colorPicker.Value.ShouldBeSameAs(initial);
|
|
|
|
var updated = CreateGradient(Colors.Green, Colors.Yellow);
|
|
colorPicker.SetCurrentValue(AtomGradientColorPicker.ValueProperty, updated);
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
viewModel.GradientValue.ShouldBeSameAs(updated);
|
|
}
|
|
|
|
[Fact]
|
|
public void GradientColorPicker_FormValue_Clear_Sets_Value_To_Null()
|
|
{
|
|
var initial = CreateGradient(Colors.Red, Colors.Blue);
|
|
var colorPicker = new AtomGradientColorPicker();
|
|
var formItemAware = (IFormItemAware)colorPicker;
|
|
|
|
formItemAware.SetFormValue(initial);
|
|
formItemAware.GetFormValue().ShouldBeSameAs(initial);
|
|
|
|
formItemAware.ClearFormValue();
|
|
|
|
colorPicker.Value.ShouldBeNull();
|
|
formItemAware.GetFormValue().ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Mentions_Value_DefaultBindingMode_Updates_ViewModel()
|
|
{
|
|
var viewModel = new RemainingFormValueBindingViewModel
|
|
{
|
|
MentionValue = "@atom"
|
|
};
|
|
var mentions = new AtomMentions();
|
|
mentions.Bind(
|
|
AtomMentions.ValueProperty,
|
|
new Binding(nameof(RemainingFormValueBindingViewModel.MentionValue))
|
|
{
|
|
Source = viewModel
|
|
});
|
|
|
|
ShowInWindow(mentions, () =>
|
|
{
|
|
mentions.Value.ShouldBe("@atom");
|
|
|
|
mentions.Value = "@atomui";
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
viewModel.MentionValue.ShouldBe("@atomui");
|
|
});
|
|
}
|
|
|
|
private static void AssertTwoWayAndDataValidation<TValue>(
|
|
StyledProperty<TValue> property,
|
|
Type ownerType)
|
|
{
|
|
var metadata = property.GetMetadata(ownerType);
|
|
|
|
metadata.DefaultBindingMode.ShouldBe(BindingMode.TwoWay);
|
|
metadata.EnableDataValidation.ShouldBe(true);
|
|
}
|
|
|
|
private static LinearGradientBrush CreateGradient(Color startColor, Color endColor)
|
|
{
|
|
return new LinearGradientBrush
|
|
{
|
|
StartPoint = new RelativePoint(0, 0.5, RelativeUnit.Relative),
|
|
EndPoint = new RelativePoint(1, 0.5, RelativeUnit.Relative),
|
|
GradientStops =
|
|
[
|
|
new GradientStop(startColor, 0),
|
|
new GradientStop(endColor, 1)
|
|
]
|
|
};
|
|
}
|
|
|
|
private static void AssertPublicSetter(Type ownerType, string propertyName)
|
|
{
|
|
var property = ownerType.GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
|
|
|
|
property.ShouldNotBeNull();
|
|
property.SetMethod.ShouldNotBeNull();
|
|
property.SetMethod!.IsPublic.ShouldBeTrue();
|
|
}
|
|
|
|
private static void ShowInWindow(Control content, Action assertion)
|
|
{
|
|
var window = new AvaloniaWindow
|
|
{
|
|
Width = 360,
|
|
Height = 240,
|
|
Content = content
|
|
};
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
assertion();
|
|
}
|
|
finally
|
|
{
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
private sealed class RemainingFormValueBindingViewModel : INotifyPropertyChanged
|
|
{
|
|
private IReadOnlyList<double>? _rangeValues;
|
|
private Color? _colorValue;
|
|
private LinearGradientBrush? _gradientValue;
|
|
private string? _mentionValue;
|
|
|
|
public IReadOnlyList<double>? RangeValues
|
|
{
|
|
get => _rangeValues;
|
|
set
|
|
{
|
|
if (ReferenceEquals(_rangeValues, value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_rangeValues = value;
|
|
RaisePropertyChanged(nameof(RangeValues));
|
|
}
|
|
}
|
|
|
|
public Color? ColorValue
|
|
{
|
|
get => _colorValue;
|
|
set
|
|
{
|
|
if (_colorValue == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_colorValue = value;
|
|
RaisePropertyChanged(nameof(ColorValue));
|
|
}
|
|
}
|
|
|
|
public LinearGradientBrush? GradientValue
|
|
{
|
|
get => _gradientValue;
|
|
set
|
|
{
|
|
if (ReferenceEquals(_gradientValue, value))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_gradientValue = value;
|
|
RaisePropertyChanged(nameof(GradientValue));
|
|
}
|
|
}
|
|
|
|
public string? MentionValue
|
|
{
|
|
get => _mentionValue;
|
|
set
|
|
{
|
|
if (_mentionValue == value)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_mentionValue = value;
|
|
RaisePropertyChanged(nameof(MentionValue));
|
|
}
|
|
}
|
|
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|
|
|
private void RaisePropertyChanged(string propertyName)
|
|
{
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
}
|
|
|
|
private sealed class DataValidationProbeSlider : AtomSlider
|
|
{
|
|
public void ApplyDataValidation(AvaloniaProperty property, Exception? error)
|
|
{
|
|
UpdateDataValidation(property, BindingValueType.BindingError, error);
|
|
}
|
|
}
|
|
}
|