45 changed files with 1521 additions and 276 deletions
@ -0,0 +1,34 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System; |
||||
|
|
||||
|
namespace Perspex.Controls |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Defines the selection mode for a control which can select multiple items.
|
||||
|
/// </summary>
|
||||
|
[Flags] |
||||
|
public enum SelectionMode |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// One item can be selected.
|
||||
|
/// </summary>
|
||||
|
Single = 0x00, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Multiple items can be selected.
|
||||
|
/// </summary>
|
||||
|
Multiple = 0x01, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Item selection can be toggled by tapping/spacebar.
|
||||
|
/// </summary>
|
||||
|
Toggle = 0x02, |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// An item will always be selected as long as there are items to select.
|
||||
|
/// </summary>
|
||||
|
AlwaysSelected = 0x04, |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,232 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using System.Linq; |
||||
|
using Perspex.Controls.Presenters; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.LogicalTree; |
||||
|
using Perspex.Styling; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Controls.UnitTests |
||||
|
{ |
||||
|
public class ListBoxTests_Single |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Focusing_Item_With_Tab_Should_Not_Select_It() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.GotFocusEvent, |
||||
|
NavigationMethod = NavigationMethod.Tab, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(-1, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Focusing_Item_With_Arrow_Key_Should_Select_It() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.GotFocusEvent, |
||||
|
NavigationMethod = NavigationMethod.Directional, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Item_Should_Select_It() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Selected_Item_Should_Not_Deselect_It() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 0; |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
SelectionMode = SelectionMode.Single | SelectionMode.Toggle, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
SelectionMode = SelectionMode.Toggle, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 0; |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(-1, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 0; |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
SelectionMode = SelectionMode.Single | SelectionMode.Toggle, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 1; |
||||
|
|
||||
|
target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressEventArgs |
||||
|
{ |
||||
|
RoutedEvent = InputElement.PointerPressedEvent, |
||||
|
MouseButton = MouseButton.Left, |
||||
|
}); |
||||
|
|
||||
|
Assert.Equal(0, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Setting_Item_IsSelected_Sets_ListBox_Selection() |
||||
|
{ |
||||
|
var target = new ListBox |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateListBoxTemplate), |
||||
|
Items = new[] { "Foo", "Bar", "Baz " }, |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
|
||||
|
((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true; |
||||
|
|
||||
|
Assert.Equal("Bar", target.SelectedItem); |
||||
|
Assert.Equal(1, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
private Control CreateListBoxTemplate(ITemplatedControl parent) |
||||
|
{ |
||||
|
return new ScrollViewer |
||||
|
{ |
||||
|
Template = new ControlTemplate(CreateScrollViewerTemplate), |
||||
|
Content = new ItemsPresenter |
||||
|
{ |
||||
|
Name = "itemsPresenter", |
||||
|
[~ItemsPresenter.ItemsProperty] = parent.GetObservable(ItemsControl.ItemsProperty), |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private Control CreateScrollViewerTemplate(ITemplatedControl parent) |
||||
|
{ |
||||
|
return new ScrollContentPresenter |
||||
|
{ |
||||
|
[~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
private class Item |
||||
|
{ |
||||
|
public Item(string value) |
||||
|
{ |
||||
|
Value = value; |
||||
|
} |
||||
|
|
||||
|
public string Value { get; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,336 @@ |
|||||
|
// Copyright (c) The Perspex Project. All rights reserved.
|
||||
|
// Licensed under the MIT license. See licence.md file in the project root for full license information.
|
||||
|
|
||||
|
using Perspex.Collections; |
||||
|
using Perspex.Controls.Presenters; |
||||
|
using Perspex.Controls.Primitives; |
||||
|
using Perspex.Controls.Templates; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Perspex.Controls.UnitTests.Primitives |
||||
|
{ |
||||
|
public class SelectingItemsControlTests_Multiple |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Setting_SelectedIndex_Should_Add_To_SelectedIndexes() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 1; |
||||
|
|
||||
|
Assert.Equal(new[] { 1 }, target.SelectedIndexes); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_SelectedIndexes_Should_Set_SelectedIndex() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(1); |
||||
|
|
||||
|
Assert.Equal(1, target.SelectedIndex); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_First_SelectedIndex_Should_Raise_SelectedIndex_SelectedItem_Changed() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
bool indexRaised = false; |
||||
|
bool itemRaised = false; |
||||
|
target.PropertyChanged += (s, e) => |
||||
|
{ |
||||
|
indexRaised |= e.Property.Name == "SelectedIndex" && |
||||
|
(int)e.OldValue == -1 && |
||||
|
(int)e.NewValue == 1; |
||||
|
itemRaised |= e.Property.Name == "SelectedItem" && |
||||
|
(string)e.OldValue == null && |
||||
|
(string)e.NewValue == "bar"; |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(1); |
||||
|
|
||||
|
Assert.True(indexRaised); |
||||
|
Assert.True(itemRaised); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_Subsequent_SelectedIndexes_Should_Not_Raise_SelectedIndex_SelectedItem_Changed() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(0); |
||||
|
|
||||
|
bool raised = false; |
||||
|
target.PropertyChanged += (s, e) => |
||||
|
raised |= e.Property.Name == "SelectedIndex" || |
||||
|
e.Property.Name == "SelectedItem"; |
||||
|
|
||||
|
target.SelectedIndexes.Add(1); |
||||
|
|
||||
|
Assert.False(raised); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_First_SelectedItem_Should_Raise_SelectedIndex_SelectedItem_Changed() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
bool indexRaised = false; |
||||
|
bool itemRaised = false; |
||||
|
target.PropertyChanged += (s, e) => |
||||
|
{ |
||||
|
indexRaised |= e.Property.Name == "SelectedIndex" && |
||||
|
(int)e.OldValue == -1 && |
||||
|
(int)e.NewValue == 1; |
||||
|
itemRaised |= e.Property.Name == "SelectedItem" && |
||||
|
(string)e.OldValue == null && |
||||
|
(string)e.NewValue == "bar"; |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedItems.Add("bar"); |
||||
|
|
||||
|
Assert.True(indexRaised); |
||||
|
Assert.True(itemRaised); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Removing_Last_SelectedIndex_Should_Raise_SelectedIndex_Changed() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] { "foo", "bar" }, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(0); |
||||
|
|
||||
|
bool raised = false; |
||||
|
target.PropertyChanged += (s, e) => |
||||
|
raised = e.Property.Name == "SelectedIndex" && |
||||
|
(int)e.OldValue == 0 && |
||||
|
(int)e.NewValue == -1; |
||||
|
|
||||
|
target.SelectedIndexes.RemoveAt(0); |
||||
|
|
||||
|
Assert.True(raised); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_To_SelectedIndexes_Should_Add_To_SelectedItems() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
"foo", |
||||
|
"bar", |
||||
|
}, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(1); |
||||
|
|
||||
|
Assert.Equal(new[] { "bar" }, target.SelectedItems); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_To_SelectedItems_Should_Add_To_SelectedIndexes() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
"foo", |
||||
|
"bar", |
||||
|
}, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedItems.Add("bar"); |
||||
|
|
||||
|
Assert.Equal(new[] { 1 }, target.SelectedIndexes); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Adding_SelectedIndexes_Should_Set_Item_IsSelected() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
new ListBoxItem(), |
||||
|
new ListBoxItem(), |
||||
|
}, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(1); |
||||
|
|
||||
|
Assert.True(((ListBoxItem)target.Presenter.Panel.Children[1]).IsSelected); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Removing_SelectedIndexes_Should_Clear_Item_IsSelected() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
new ListBoxItem(), |
||||
|
new ListBoxItem(), |
||||
|
}, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndexes.Add(1); |
||||
|
target.SelectedIndexes.Remove(1); |
||||
|
|
||||
|
Assert.False(((ListBoxItem)target.Presenter.Panel.Children[1]).IsSelected); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Range_Select_Should_Select_Range() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
"foo", |
||||
|
"bar", |
||||
|
"baz", |
||||
|
"qux", |
||||
|
"qiz", |
||||
|
"lol", |
||||
|
}, |
||||
|
SelectionMode = SelectionMode.Multiple, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 1; |
||||
|
target.SelectRange(3); |
||||
|
|
||||
|
Assert.Equal(new[] { 1, 2, 3 }, target.SelectedIndexes); |
||||
|
Assert.Equal(new[] { "bar", "baz", "qux" }, target.SelectedItems); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Range_Select_Backwards_Should_Select_Range() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
"foo", |
||||
|
"bar", |
||||
|
"baz", |
||||
|
"qux", |
||||
|
"qiz", |
||||
|
"lol", |
||||
|
}, |
||||
|
SelectionMode = SelectionMode.Multiple, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 3; |
||||
|
target.SelectRange(1); |
||||
|
|
||||
|
Assert.Equal(new[] { 3, 2, 1 }, target.SelectedIndexes); |
||||
|
Assert.Equal(new[] { "qux", "baz", "bar" }, target.SelectedItems); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Second_Range_Select_Backwards_Should_Select_From_Original_Selection() |
||||
|
{ |
||||
|
var target = new TestSelector |
||||
|
{ |
||||
|
Items = new[] |
||||
|
{ |
||||
|
"foo", |
||||
|
"bar", |
||||
|
"baz", |
||||
|
"qux", |
||||
|
"qiz", |
||||
|
"lol", |
||||
|
}, |
||||
|
SelectionMode = SelectionMode.Multiple, |
||||
|
Template = Template(), |
||||
|
}; |
||||
|
|
||||
|
target.ApplyTemplate(); |
||||
|
target.SelectedIndex = 2; |
||||
|
target.SelectRange(5); |
||||
|
target.SelectRange(4); |
||||
|
|
||||
|
Assert.Equal(new[] { 2, 3, 4 }, target.SelectedIndexes); |
||||
|
Assert.Equal(new[] { "baz", "qux", "qiz" }, target.SelectedItems); |
||||
|
} |
||||
|
|
||||
|
private class TestSelector : SelectingItemsControl |
||||
|
{ |
||||
|
public new IPerspexList<int> SelectedIndexes |
||||
|
{ |
||||
|
get { return base.SelectedIndexes; } |
||||
|
} |
||||
|
|
||||
|
public new IPerspexList<object> SelectedItems |
||||
|
{ |
||||
|
get { return base.SelectedItems; } |
||||
|
} |
||||
|
|
||||
|
public new SelectionMode SelectionMode |
||||
|
{ |
||||
|
get { return base.SelectionMode; } |
||||
|
set { base.SelectionMode = value; } |
||||
|
} |
||||
|
|
||||
|
public void SelectRange(int index) |
||||
|
{ |
||||
|
UpdateSelection(index, true, true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ControlTemplate Template() |
||||
|
{ |
||||
|
return new ControlTemplate<SelectingItemsControl>(control => |
||||
|
new ItemsPresenter |
||||
|
{ |
||||
|
Name = "itemsPresenter", |
||||
|
[~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty], |
||||
|
[~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty], |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue