Browse Source

Merge branch 'master' into port-relative-panel-fixes

pull/5783/head
Dariusz Komosiński 6 years ago
committed by GitHub
parent
commit
3a6b54d07e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      samples/ControlCatalog/Pages/TextBoxPage.xaml
  2. 6
      samples/ControlCatalog/Pages/TextBoxPage.xaml.cs
  3. 96
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  4. 4
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  5. 12
      src/Android/Avalonia.Android/IInitEditorInfo.cs
  6. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs
  7. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs
  8. 46
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  9. 83
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  10. 42
      src/Android/Avalonia.Android/SoftKeyboardListner.cs
  11. 28
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  12. 5
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  13. 13
      src/Avalonia.Controls/Button.cs
  14. 50
      src/Avalonia.Controls/Selection/InternalSelectionModel.cs
  15. 33
      tests/Avalonia.Controls.UnitTests/ButtonTests.cs
  16. 7
      tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs

2
samples/ControlCatalog/Pages/TextBoxPage.xaml

@ -13,7 +13,7 @@
<StackPanel Orientation="Vertical" Spacing="8">
<TextBox Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit." Width="200" />
<TextBox Width="200" Watermark="ReadOnly" IsReadOnly="True" Text="This is read only"/>
<TextBox Width="200" Watermark="Watermark" />
<TextBox Width="200" Watermark="Numeric Watermark" x:Name="numericWatermark"/>
<TextBox Width="200"
Watermark="Floating Watermark"
UseFloatingWatermark="True"

6
samples/ControlCatalog/Pages/TextBoxPage.xaml.cs

@ -13,6 +13,12 @@ namespace ControlCatalog.Pages
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.Get<TextBox>("numericWatermark")
.TextInputOptionsQuery += (s, a) =>
{
a.ContentType = Avalonia.Input.TextInput.TextInputContentType.Number;
};
}
}
}

96
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -0,0 +1,96 @@
using System;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Input;
using Avalonia.Input.TextInput;
namespace Avalonia.Android
{
class AndroidInputMethod<TView> : ITextInputMethodImpl
where TView: View, IInitEditorInfo
{
private readonly TView _host;
private readonly InputMethodManager _imm;
private IInputElement _inputElement;
public AndroidInputMethod(TView host)
{
if (host.OnCheckIsTextEditor() == false)
throw new InvalidOperationException("Host should return true from OnCheckIsTextEditor()");
_host = host;
_imm = host.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
_host.Focusable = true;
_host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
}
public void Reset()
{
_imm.RestartInput(_host);
}
public void SetActive(bool active)
{
if (active)
{
_host.RequestFocus();
Reset();
_imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
else
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
public void SetCursorRect(Rect rect)
{
}
public void SetOptions(TextInputOptionsQueryEventArgs options)
{
if (_inputElement != null)
{
_inputElement.PointerReleased -= RestoreSoftKeyboard;
}
_inputElement = options.Source as InputElement;
if (_inputElement == null)
{
_imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.None);
}
_host.InitEditorInfo((outAttrs) =>
{
outAttrs.InputType = options.ContentType switch
{
TextInputContentType.Email => global::Android.Text.InputTypes.TextVariationEmailAddress,
TextInputContentType.Number => global::Android.Text.InputTypes.ClassNumber,
TextInputContentType.Password => global::Android.Text.InputTypes.TextVariationPassword,
TextInputContentType.Phone => global::Android.Text.InputTypes.ClassPhone,
TextInputContentType.Url => global::Android.Text.InputTypes.TextVariationUri,
_ => global::Android.Text.InputTypes.ClassText
};
if (options.AutoCapitalization)
{
outAttrs.InitialCapsMode = global::Android.Text.CapitalizationMode.Sentences;
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagCapSentences;
}
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
});
//_inputElement.PointerReleased += RestoreSoftKeyboard;
}
private void RestoreSoftKeyboard(object sender, PointerReleasedEventArgs e)
{
_imm.ShowSoftInput(_host, ShowFlags.Implicit);
}
}
}

4
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -15,7 +15,6 @@ namespace Avalonia.Android
if (_content != null)
View.Content = _content;
SetContentView(View);
TakeKeyEvents(true);
base.OnCreate(savedInstanceState);
}
@ -32,8 +31,5 @@ namespace Avalonia.Android
View.Content = value;
}
}
public override bool DispatchKeyEvent(KeyEvent e) =>
View.DispatchKeyEvent(e) ? true : base.DispatchKeyEvent(e);
}
}

12
src/Android/Avalonia.Android/IInitEditorInfo.cs

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.Views.InputMethods;
namespace Avalonia.Android
{
interface IInitEditorInfo
{
void InitEditorInfo(Action<EditorInfo> init);
}
}

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/AndroidFramebuffer.cs

@ -32,7 +32,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
RowBytes = buffer.stride * (Format == PixelFormat.Rgb565 ? 2 : 4);
Address = buffer.bits;
Dpi = scaling * new Vector(96, 96);
Dpi = new Vector(96, 96) * scaling;
}
public void Dispose()

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/InvalidationAwareSurfaceView.cs

@ -13,7 +13,7 @@ namespace Avalonia.Android
bool _invalidateQueued;
readonly object _lock = new object();
private readonly Handler _handler;
public InvalidationAwareSurfaceView(Context context) : base(context)
{

46
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -4,14 +4,16 @@ using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Android.OpenGL;
using Avalonia.Android.Platform.Specific;
using Avalonia.Android.Platform.Specific.Helpers;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Controls.Platform.Surfaces;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.Input.TextInput;
using Avalonia.OpenGL.Egl;
using Avalonia.OpenGL.Surfaces;
using Avalonia.Platform;
@ -19,19 +21,20 @@ using Avalonia.Rendering;
namespace Avalonia.Android.Platform.SkiaPlatform
{
class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo
class TopLevelImpl : IAndroidView, ITopLevelImpl, EglGlPlatformSurfaceBase.IEglWindowGlPlatformSurfaceInfo, ITopLevelImplWithTextInputMethod
{
private readonly IGlPlatformSurface _gl;
private readonly IFramebufferPlatformSurface _framebuffer;
private readonly AndroidKeyboardEventsHelper<TopLevelImpl> _keyboardHelper;
private readonly AndroidTouchEventsHelper<TopLevelImpl> _touchHelper;
private readonly ITextInputMethodImpl _textInputMethod;
private ViewImpl _view;
public TopLevelImpl(Context context, bool placeOnTop = false)
{
_view = new ViewImpl(context, this, placeOnTop);
_textInputMethod = new AndroidInputMethod<ViewImpl>(_view);
_keyboardHelper = new AndroidKeyboardEventsHelper<TopLevelImpl>(this);
_touchHelper = new AndroidTouchEventsHelper<TopLevelImpl>(this, () => InputRoot,
GetAvaloniaPointFromEvent);
@ -45,18 +48,6 @@ namespace Avalonia.Android.Platform.SkiaPlatform
_view.Resources.DisplayMetrics.HeightPixels).ToSize(RenderScaling);
}
private bool _handleEvents;
public bool HandleEvents
{
get { return _handleEvents; }
set
{
_handleEvents = value;
_keyboardHelper.HandleEvents = _handleEvents;
}
}
public virtual Point GetAvaloniaPointFromEvent(MotionEvent e, int pointerIndex) =>
new Point(e.GetX(pointerIndex), e.GetY(pointerIndex)) / RenderScaling;
@ -144,7 +135,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
Resized?.Invoke(size);
}
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback
class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo
{
private readonly TopLevelImpl _tl;
private Size _oldSize;
@ -191,6 +182,27 @@ namespace Avalonia.Android.Platform.SkiaPlatform
base.SurfaceChanged(holder, format, width, height);
}
public sealed override bool OnCheckIsTextEditor()
{
return true;
}
private Action<EditorInfo> _initEditorInfo;
public void InitEditorInfo(Action<EditorInfo> init)
{
_initEditorInfo = init;
}
public sealed override IInputConnection OnCreateInputConnection(EditorInfo outAttrs)
{
if (_initEditorInfo != null)
_initEditorInfo(outAttrs);
return base.OnCreateInputConnection(outAttrs);
}
}
public IPopupImpl CreatePopup() => null;
@ -209,6 +221,8 @@ namespace Avalonia.Android.Platform.SkiaPlatform
public double Scaling => RenderScaling;
public ITextInputMethodImpl TextInputMethod => _textInputMethod;
public void SetTransparencyLevelHint(WindowTransparencyLevel transparencyLevel)
{
throw new NotImplementedException();

83
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -1,12 +1,7 @@
using System;
using System.ComponentModel;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Views.InputMethods;
using Avalonia.Android.Platform.Input;
using Avalonia.Android.Platform.SkiaPlatform;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -14,14 +9,13 @@ namespace Avalonia.Android.Platform.Specific.Helpers
{
internal class AndroidKeyboardEventsHelper<TView> : IDisposable where TView : TopLevelImpl, IAndroidView
{
private TView _view;
private IInputElement _lastFocusedElement;
private readonly TView _view;
public bool HandleEvents { get; set; }
public AndroidKeyboardEventsHelper(TView view)
{
this._view = view;
_view = view;
HandleEvents = true;
}
@ -36,9 +30,20 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return DispatchKeyEventInternal(e, out callBase);
}
string? UnicodeTextInput(KeyEvent keyEvent)
{
return keyEvent.Action == KeyEventActions.Multiple
&& keyEvent.RepeatCount == 0
&& !string.IsNullOrEmpty(keyEvent?.Characters)
? keyEvent.Characters
: null;
}
private bool? DispatchKeyEventInternal(KeyEvent e, out bool callBase)
{
if (e.Action == KeyEventActions.Multiple)
var unicodeTextInput = UnicodeTextInput(e);
if (e.Action == KeyEventActions.Multiple && unicodeTextInput == null)
{
callBase = true;
return null;
@ -53,13 +58,14 @@ namespace Avalonia.Android.Platform.Specific.Helpers
_view.Input(rawKeyEvent);
if (e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
if ((e.Action == KeyEventActions.Down && e.UnicodeChar >= 32)
|| unicodeTextInput != null)
{
var rawTextEvent = new RawTextInputEventArgs(
AndroidKeyboardDevice.Instance,
Convert.ToUInt32(e.EventTime),
_view.InputRoot,
Convert.ToChar(e.UnicodeChar).ToString()
unicodeTextInput ?? Convert.ToChar(e.UnicodeChar).ToString()
);
_view.Input(rawTextEvent);
}
@ -85,61 +91,6 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return rv;
}
private bool NeedsKeyboard(IInputElement element)
{
//may be some other elements
return element is TextBox;
}
private void TryShowHideKeyboard(IInputElement element, bool value)
{
var input = _view.View.Context.GetSystemService(Context.InputMethodService).JavaCast<InputMethodManager>();
if (value)
{
//show keyboard
//may be in the future different keyboards support e.g. normal, only digits etc.
//Android.Text.InputTypes
input.ToggleSoftInput(ShowFlags.Forced, HideSoftInputFlags.ImplicitOnly);
}
else
{
//hide keyboard
input.HideSoftInputFromWindow(_view.View.WindowToken, HideSoftInputFlags.None);
}
}
public void UpdateKeyboardState(IInputElement element)
{
var focusedElement = element;
bool oldValue = NeedsKeyboard(_lastFocusedElement);
bool newValue = NeedsKeyboard(focusedElement);
if (newValue != oldValue || newValue)
{
TryShowHideKeyboard(focusedElement, newValue);
}
_lastFocusedElement = element;
}
public void ActivateAutoShowKeyboard()
{
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged);
//just in case we've called more than once the method
kbDevice.PropertyChanged -= KeyboardDevice_PropertyChanged;
kbDevice.PropertyChanged += KeyboardDevice_PropertyChanged;
}
private void KeyboardDevice_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(KeyboardDevice.FocusedElement))
{
UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement);
}
}
public void Dispose()
{
HandleEvents = false;

42
src/Android/Avalonia.Android/SoftKeyboardListner.cs

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
using Android.Content;
using Android.OS;
using Android.Util;
using Android.Views;
using Avalonia.Input;
namespace Avalonia.Android
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
private readonly View _host;
private bool _wasKeyboard;
public SoftKeyboardListner(View view)
{
_host = view;
}
public void OnGlobalLayout()
{
int estimatedKeyboardHeight = (int)TypedValue.ApplyDimension(ComplexUnitType.Dip,
EstimatedKeyboardDP, _host.Resources.DisplayMetrics);
var rect = new global::Android.Graphics.Rect();
_host.GetWindowVisibleDisplayFrame(rect);
int heightDiff = _host.RootView.Height - (rect.Bottom - rect.Top);
var isKeyboard = heightDiff >= estimatedKeyboardHeight;
if (_wasKeyboard && !isKeyboard)
KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, KeyModifiers.None);
_wasKeyboard = isKeyboard;
}
}
}

28
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -16,18 +16,18 @@ namespace Avalonia.AndroidTestApplication
Icon = "@drawable/icon",
LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : Activity
public class MainBaseActivity : AvaloniaActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
if (Avalonia.Application.Current == null)
{
AppBuilder.Configure<App>()
.UseAndroid()
.SetupWithoutStarting();
}
SetContentView(new AvaloniaView(this) { Content = App.CreateSimpleWindow() });
base.OnCreate(savedInstanceState);
Content = App.CreateSimpleWindow();
}
}
@ -72,13 +72,33 @@ namespace Avalonia.AndroidTestApplication
Height = 40,
Background = Brushes.LightGreen,
Foreground = Brushes.Black
}
},
CreateTextBox(Input.TextInput.TextInputContentType.Normal),
CreateTextBox(Input.TextInput.TextInputContentType.Password),
CreateTextBox(Input.TextInput.TextInputContentType.Email),
CreateTextBox(Input.TextInput.TextInputContentType.Url),
CreateTextBox(Input.TextInput.TextInputContentType.Phone),
CreateTextBox(Input.TextInput.TextInputContentType.Number),
}
}
};
return window;
}
private static TextBox CreateTextBox(Input.TextInput.TextInputContentType contentType)
{
var textBox = new TextBox()
{
Margin = new Thickness(20, 10),
Watermark = contentType.ToString(),
BorderThickness = new Thickness(3),
FontSize = 20
};
textBox.TextInputOptionsQuery += (s, e) => { e.ContentType = contentType; };
return textBox;
}
}
}

5
src/Avalonia.Controls.DataGrid/DataGrid.cs

@ -910,6 +910,11 @@ namespace Avalonia.Controls
// Clear all row selections
ClearRowSelection(resetAnchorSlot: true);
if (DataConnection.CollectionView != null)
{
DataConnection.CollectionView.MoveCurrentTo(null);
}
}
else
{

13
src/Avalonia.Controls/Button.cs

@ -96,6 +96,7 @@ namespace Avalonia.Controls
{
FocusableProperty.OverrideDefaultValue(typeof(Button), true);
CommandProperty.Changed.Subscribe(CommandChanged);
CommandParameterProperty.Changed.Subscribe(CommandParameterChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
IsCancelProperty.Changed.Subscribe(IsCancelChanged);
}
@ -418,6 +419,18 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Called when the <see cref="CommandParameter"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void CommandParameterChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is Button button)
{
button.CanExecuteChanged(button, EventArgs.Empty);
}
}
/// <summary>
/// Called when the <see cref="IsDefault"/> property changes.
/// </summary>

50
src/Avalonia.Controls/Selection/InternalSelectionModel.cs

@ -13,8 +13,9 @@ namespace Avalonia.Controls.Selection
internal class InternalSelectionModel : SelectionModel<object?>
{
private IList? _writableSelectedItems;
private bool _ignoreModelChanges;
private int _ignoreModelChanges;
private bool _ignoreSelectedItemsChanges;
private bool _isResetting;
public InternalSelectionModel()
{
@ -130,17 +131,31 @@ namespace Avalonia.Controls.Selection
try
{
_ignoreModelChanges = true;
++_ignoreModelChanges;
using (BatchUpdate())
{
Clear();
Add(_writableSelectedItems);
for (var i = 0; i < _writableSelectedItems.Count; ++i)
{
var index = IndexOf(Source, _writableSelectedItems[i]);
if (index != -1)
{
Select(index);
}
else
{
_writableSelectedItems.RemoveAt(i);
--i;
}
}
}
}
finally
{
_ignoreModelChanges = false;
--_ignoreModelChanges;
}
}
@ -162,7 +177,7 @@ namespace Avalonia.Controls.Selection
private void OnSelectionChanged(object sender, SelectionModelSelectionChangedEventArgs e)
{
if (_ignoreModelChanges)
if (_ignoreModelChanges > 0)
{
return;
}
@ -191,6 +206,27 @@ namespace Avalonia.Controls.Selection
}
}
private protected override void OnSourceCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Reset)
{
++_ignoreModelChanges;
_isResetting = true;
}
base.OnSourceCollectionChanged(e);
}
protected override void OnSourceCollectionChangeFinished()
{
base.OnSourceCollectionChangeFinished();
if (_isResetting)
{
--_ignoreModelChanges;
}
}
private void OnSourceReset(object sender, EventArgs e) => SyncFromSelectedItems();
private void OnSelectedItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
@ -222,7 +258,7 @@ namespace Avalonia.Controls.Selection
{
using var operation = BatchUpdate();
_ignoreModelChanges = true;
++_ignoreModelChanges;
switch (e.Action)
{
@ -244,7 +280,7 @@ namespace Avalonia.Controls.Selection
}
finally
{
_ignoreModelChanges = false;
--_ignoreModelChanges;
}
}

33
tests/Avalonia.Controls.UnitTests/ButtonTests.cs

@ -269,6 +269,19 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(0, command.SubscriptionCount);
}
[Fact]
public void Button_Invokes_CanExecute_When_CommandParameter_Changed()
{
var command = new TestCommand(p => p is bool value && value);
var target = new Button { Command = command };
target.CommandParameter = true;
Assert.True(target.IsEffectivelyEnabled);
target.CommandParameter = false;
Assert.False(target.IsEffectivelyEnabled);
}
private class TestButton : Button, IRenderRoot
{
public TestButton()
@ -324,12 +337,22 @@ namespace Avalonia.Controls.UnitTests
private class TestCommand : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
private EventHandler _canExecuteChanged;
private bool _enabled;
private bool _enabled = true;
public TestCommand(bool enabled)
public TestCommand(bool enabled = true)
{
_enabled = enabled;
_canExecute = _ => _enabled;
_execute = _ => { };
}
public TestCommand(Func<object, bool> canExecute, Action<object> execute = null)
{
_canExecute = canExecute;
_execute = execute ?? (_ => { });
}
public bool IsEnabled
@ -353,11 +376,9 @@ namespace Avalonia.Controls.UnitTests
remove { _canExecuteChanged -= value; --SubscriptionCount; }
}
public bool CanExecute(object parameter) => _enabled;
public bool CanExecute(object parameter) => _canExecute(parameter);
public void Execute(object parameter)
{
}
public void Execute(object parameter) => _execute(parameter);
}
}
}

7
tests/Avalonia.Controls.UnitTests/Selection/InternalSelectionModelTests.cs

@ -97,6 +97,7 @@ namespace Avalonia.Controls.UnitTests.Selection
target.WritableSelectedItems.Clear();
Assert.Empty(target.SelectedIndexes);
Assert.Empty(target.WritableSelectedItems);
}
[Fact]
@ -123,6 +124,7 @@ namespace Avalonia.Controls.UnitTests.Selection
target.WritableSelectedItems = null;
Assert.Empty(target.SelectedIndexes);
Assert.Empty(target.WritableSelectedItems);
}
[Fact]
@ -182,6 +184,7 @@ namespace Avalonia.Controls.UnitTests.Selection
target.Source = items;
Assert.Equal(1, target.SelectedIndex);
Assert.Equal(new[] { "bar" }, target.WritableSelectedItems);
}
[Fact]
@ -203,6 +206,7 @@ namespace Avalonia.Controls.UnitTests.Selection
items.Reset(new[] { "baz", "foo", "bar" });
Assert.Equal(2, target.SelectedIndex);
Assert.Equal(new[] { "bar" }, target.WritableSelectedItems);
}
[Fact]
@ -227,6 +231,7 @@ namespace Avalonia.Controls.UnitTests.Selection
Assert.Equal(-1, target.SelectedIndex);
Assert.Equal(null, target.SelectedItem);
Assert.Empty(target.WritableSelectedItems);
Assert.Contains(nameof(target.SelectedIndex), changed);
Assert.Contains(nameof(target.SelectedItem), changed);
@ -246,6 +251,7 @@ namespace Avalonia.Controls.UnitTests.Selection
Assert.Equal("foo", target.SelectedItem);
Assert.Equal(1, target.SelectedIndex);
Assert.Equal(new[] { "foo" }, target.WritableSelectedItems);
}
[Fact]
@ -257,6 +263,7 @@ namespace Avalonia.Controls.UnitTests.Selection
target.Source = new[] { "baz", "foo", "bar" };
Assert.Equal(2, target.SelectedIndex);
Assert.Equal(new[] { "bar" }, target.WritableSelectedItems);
}
private static InternalSelectionModel CreateTarget(

Loading…
Cancel
Save