Browse Source

Merge branch 'master' into fixes/StylableTabItem

pull/2496/head
Steven Kirk 8 years ago
committed by GitHub
parent
commit
e546124a2d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 111
      src/Avalonia.Controls/Presenters/CarouselPresenter.cs
  2. 125
      src/Avalonia.Controls/Presenters/ItemContainerSync.cs
  3. 48
      src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
  4. 9
      src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs
  5. 11
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  6. 110
      src/Avalonia.Visuals/Media/FontFamily.cs
  7. 35
      src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
  8. 2
      src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
  9. 6
      src/Avalonia.X11/XIStructs.cs
  10. 38
      tests/Avalonia.Controls.UnitTests/CarouselTests.cs
  11. 753
      tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs
  12. 48
      tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs
  13. 11
      tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs

111
src/Avalonia.Controls/Presenters/CarouselPresenter.cs

@ -38,7 +38,6 @@ namespace Avalonia.Controls.Presenters
Carousel.PageTransitionProperty.AddOwner<CarouselPresenter>();
private int _selectedIndex = -1;
// private Task _current;
private Task _currentTransition;
private int _queuedTransitionIndex = -1;
@ -47,6 +46,7 @@ namespace Avalonia.Controls.Presenters
/// </summary>
static CarouselPresenter()
{
IsVirtualizedProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.IsVirtualizedChanged);
SelectedIndexProperty.Changed.AddClassHandler<CarouselPresenter>(x => x.SelectedIndexChanged);
}
@ -94,58 +94,67 @@ namespace Avalonia.Controls.Presenters
set { SetValue(PageTransitionProperty, value); }
}
/// <inheritdoc/>
protected override void PanelCreated(IPanel panel)
{
#pragma warning disable 4014
MoveToPage(-1, SelectedIndex);
#pragma warning restore 4014
}
/// <inheritdoc/>
protected override void ItemsChanged(NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
if (!IsVirtualized)
{
case NotifyCollectionChangedAction.Remove:
if (!IsVirtualized)
{
var generator = ItemContainerGenerator;
var containers = generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count);
Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
base.ItemsChanged(e);
#pragma warning disable 4014
MoveToPage(-1, SelectedIndex);
#pragma warning restore 4014
}
break;
case NotifyCollectionChangedAction.Reset:
{
var generator = ItemContainerGenerator;
var containers = generator.Containers.ToList();
generator.Clear();
Panel.Children.RemoveAll(containers.Select(x => x.ContainerControl));
#pragma warning disable 4014
var newIndex = SelectedIndex;
if (Items == null || SelectedIndex >= Items.Count())
{
SelectedIndex = Items.Count() - 1;
}
if(SelectedIndex < 0)
foreach (var c in ItemContainerGenerator.Containers)
{
c.ContainerControl.IsVisible = c.Index == SelectedIndex;
}
}
else if (SelectedIndex != -1 && Panel != null)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex > SelectedIndex)
{
if(Items != null && Items.Count() > 0)
{
newIndex = 0;
}
else
{
newIndex = -1;
}
return;
}
MoveToPage(-1, newIndex);
#pragma warning restore 4014
}
break;
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldStartingIndex > SelectedIndex)
{
return;
}
break;
case NotifyCollectionChangedAction.Replace:
if (e.OldStartingIndex > SelectedIndex ||
e.OldStartingIndex + e.OldItems.Count - 1 < SelectedIndex)
{
return;
}
break;
case NotifyCollectionChangedAction.Move:
if (e.OldStartingIndex > SelectedIndex &&
e.NewStartingIndex > SelectedIndex)
{
return;
}
break;
}
if (Items == null || SelectedIndex >= Items.Count())
{
SelectedIndex = Items.Count() - 1;
}
Panel.Children.Clear();
ItemContainerGenerator.Clear();
if (SelectedIndex != -1)
{
GetOrCreateContainer(SelectedIndex);
}
}
}
@ -212,6 +221,18 @@ namespace Avalonia.Controls.Presenters
return container;
}
/// <summary>
/// Called when the <see cref="IsVirtualized"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private void IsVirtualizedChanged(AvaloniaPropertyChangedEventArgs e)
{
if (Panel != null)
{
ItemsChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
/// <summary>
/// Called when the <see cref="SelectedIndex"/> property changes.
/// </summary>

125
src/Avalonia.Controls/Presenters/ItemContainerSync.cs

@ -0,0 +1,125 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Utils;
namespace Avalonia.Controls.Presenters
{
internal static class ItemContainerSync
{
public static void ItemsChanged(
ItemsPresenterBase owner,
IEnumerable items,
NotifyCollectionChangedEventArgs e)
{
var generator = owner.ItemContainerGenerator;
var panel = owner.Panel;
if (panel == null)
{
return;
}
void Add()
{
if (e.NewStartingIndex + e.NewItems.Count < items.Count())
{
generator.InsertSpace(e.NewStartingIndex, e.NewItems.Count);
}
AddContainers(owner, e.NewStartingIndex, e.NewItems);
}
void Remove()
{
RemoveContainers(panel, generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count));
}
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Add();
break;
case NotifyCollectionChangedAction.Remove:
Remove();
break;
case NotifyCollectionChangedAction.Replace:
RemoveContainers(panel, generator.Dematerialize(e.OldStartingIndex, e.OldItems.Count));
var containers = AddContainers(owner, e.NewStartingIndex, e.NewItems);
var i = e.NewStartingIndex;
foreach (var container in containers)
{
panel.Children[i++] = container.ContainerControl;
}
break;
case NotifyCollectionChangedAction.Move:
Remove();
Add();
break;
case NotifyCollectionChangedAction.Reset:
RemoveContainers(panel, generator.Clear());
if (items != null)
{
AddContainers(owner, 0, items);
}
break;
}
}
private static IList<ItemContainerInfo> AddContainers(
ItemsPresenterBase owner,
int index,
IEnumerable items)
{
var generator = owner.ItemContainerGenerator;
var result = new List<ItemContainerInfo>();
var panel = owner.Panel;
foreach (var item in items)
{
var i = generator.Materialize(index++, item, owner.MemberSelector);
if (i.ContainerControl != null)
{
if (i.Index < panel.Children.Count)
{
// TODO: This will insert at the wrong place when there are null items.
panel.Children.Insert(i.Index, i.ContainerControl);
}
else
{
panel.Children.Add(i.ContainerControl);
}
}
result.Add(i);
}
return result;
}
private static void RemoveContainers(
IPanel panel,
IEnumerable<ItemContainerInfo> items)
{
foreach (var i in items)
{
if (i.ContainerControl != null)
{
panel.Children.Remove(i.ContainerControl);
}
}
}
}
}

48
src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs

@ -60,53 +60,7 @@ namespace Avalonia.Controls.Presenters
public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
{
base.ItemsChanged(items, e);
var generator = Owner.ItemContainerGenerator;
var panel = Owner.Panel;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex + e.NewItems.Count < Items.Count())
{
generator.InsertSpace(e.NewStartingIndex, e.NewItems.Count);
}
AddContainers(e.NewStartingIndex, e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
RemoveContainers(generator.RemoveRange(e.OldStartingIndex, e.OldItems.Count));
break;
case NotifyCollectionChangedAction.Replace:
RemoveContainers(generator.Dematerialize(e.OldStartingIndex, e.OldItems.Count));
var containers = AddContainers(e.NewStartingIndex, e.NewItems);
var i = e.NewStartingIndex;
foreach (var container in containers)
{
panel.Children[i++] = container.ContainerControl;
}
break;
case NotifyCollectionChangedAction.Move:
// TODO: Handle move in a more efficient manner. At the moment we just
// drop through to Reset to recreate all the containers.
case NotifyCollectionChangedAction.Reset:
RemoveContainers(generator.Clear());
if (Items != null)
{
AddContainers(0, Items);
}
break;
}
ItemContainerSync.ItemsChanged(Owner, items, e);
Owner.InvalidateMeasure();
}

9
src/Avalonia.Controls/Presenters/ItemsPresenterBase.cs

@ -7,6 +7,7 @@ using System.Collections.Specialized;
using Avalonia.Collections;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Templates;
using Avalonia.Controls.Utils;
using Avalonia.Styling;
namespace Avalonia.Controls.Presenters
@ -205,7 +206,13 @@ namespace Avalonia.Controls.Presenters
/// has been set, the items collection has been modified, or the panel has been created.
/// </summary>
/// <param name="e">A description of the change.</param>
protected abstract void ItemsChanged(NotifyCollectionChangedEventArgs e);
/// <remarks>
/// The panel is guaranteed to be created when this method is called.
/// </remarks>
protected virtual void ItemsChanged(NotifyCollectionChangedEventArgs e)
{
ItemContainerSync.ItemsChanged(this, Items, e);
}
/// <summary>
/// Creates the <see cref="Panel"/> when <see cref="ApplyTemplate"/> is called for the first

11
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -325,14 +325,19 @@ namespace Avalonia.Controls.Primitives
if (_updateCount == 0)
{
var newIndex = -1;
if (SelectedIndex != -1)
{
SelectedIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem);
newIndex = IndexOf((IEnumerable)e.NewValue, SelectedItem);
}
else if (AlwaysSelected && Items != null && Items.Cast<object>().Any())
if (AlwaysSelected && Items != null && Items.Cast<object>().Any())
{
SelectedIndex = 0;
newIndex = 0;
}
SelectedIndex = newIndex;
}
}

110
src/Avalonia.Visuals/Media/FontFamily.cs

@ -11,46 +11,49 @@ namespace Avalonia.Media
{
public class FontFamily
{
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="name">The name of the <see cref="FontFamily"/>.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception>
public FontFamily(string name)
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
public FontFamily(string name) : this(null, name)
{
Contract.Requires<ArgumentNullException>(name != null);
FamilyNames = new FamilyNameCollection(new[] { name });
}
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="names">The names of the <see cref="FontFamily"/>.</param>
/// <exception cref="T:System.ArgumentNullException">name</exception>
public FontFamily(IEnumerable<string> names)
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <exception cref="T:System.ArgumentException">Base uri must be an absolute uri.</exception>
public FontFamily(Uri baseUri, string name)
{
Contract.Requires<ArgumentNullException>(names != null);
if (string.IsNullOrEmpty(name))
{
FamilyNames = new FamilyNameCollection(string.Empty);
FamilyNames = new FamilyNameCollection(names);
}
return;
}
/// <inheritdoc />
/// <summary>
/// Initializes a new instance of the <see cref="T:Avalonia.Media.FontFamily" /> class.
/// </summary>
/// <param name="name">The name of the <see cref="T:Avalonia.Media.FontFamily" />.</param>
/// <param name="source">The source of font resources.</param>
/// <param name="baseUri"></param>
public FontFamily(string name, Uri source, Uri baseUri = null) : this(name)
{
Key = new FontFamilyKey(source, baseUri);
var fontFamilySegment = GetFontFamilyIdentifier(name);
if (fontFamilySegment.Source != null)
{
if (baseUri != null && !baseUri.IsAbsoluteUri)
{
throw new ArgumentException("Base uri must be an absolute uri.", nameof(baseUri));
}
Key = new FontFamilyKey(fontFamilySegment.Source, baseUri);
}
FamilyNames = new FamilyNameCollection(fontFamilySegment.Name);
}
/// <summary>
/// Represents the default font family
/// </summary>
public static FontFamily Default => new FontFamily(String.Empty);
public static FontFamily Default => new FontFamily(string.Empty);
/// <summary>
/// Represents all font families in the system. This can be an expensive call depending on platform implementation.
@ -88,46 +91,40 @@ namespace Avalonia.Media
/// <param name="s"></param>
public static implicit operator FontFamily(string s)
{
return Parse(s);
return new FontFamily(s);
}
/// <summary>
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri"></param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
private struct FontFamilyIdentifier
{
if (string.IsNullOrEmpty(s))
public FontFamilyIdentifier(string name, Uri source)
{
throw new ArgumentException("Specified family is not supported.");
Name = name;
Source = source;
}
var segments = s.Split('#');
public string Name { get; }
public Uri Source { get; }
}
private static FontFamilyIdentifier GetFontFamilyIdentifier(string name)
{
var segments = name.Split('#');
switch (segments.Length)
{
case 1:
{
var names = segments[0].Split(',')
.Select(x => x.Trim())
.Where(x => !string.IsNullOrWhiteSpace(x));
return new FontFamily(names);
return new FontFamilyIdentifier(segments[0], null);
}
case 2:
{
var uri = segments[0].StartsWith("/")
? new Uri(segments[0], UriKind.Relative)
: new Uri(segments[0], UriKind.RelativeOrAbsolute);
var source = segments[0].StartsWith("/")
? new Uri(segments[0], UriKind.Relative)
: new Uri(segments[0], UriKind.RelativeOrAbsolute);
return uri.IsAbsoluteUri
? new FontFamily(segments[1], uri)
: new FontFamily(segments[1], uri, baseUri);
return new FontFamilyIdentifier(segments[1], source);
}
default:
@ -137,6 +134,25 @@ namespace Avalonia.Media
}
}
/// <summary>
/// Parses a <see cref="T:Avalonia.Media.FontFamily"/> string.
/// </summary>
/// <param name="s">The <see cref="T:Avalonia.Media.FontFamily"/> string.</param>
/// <param name="baseUri">Specifies the base uri that is used to resolve font family assets.</param>
/// <returns></returns>
/// <exception cref="ArgumentException">
/// Specified family is not supported.
/// </exception>
public static FontFamily Parse(string s, Uri baseUri = null)
{
if (string.IsNullOrEmpty(s))
{
throw new ArgumentException("Specified family is not supported.", nameof(s));
}
return new FontFamily(baseUri, s);
}
/// <summary>
/// Returns a <see cref="string" /> that represents this instance.
/// </summary>

35
src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs

@ -4,31 +4,28 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
namespace Avalonia.Media.Fonts
{
using System.Text;
public class FamilyNameCollection : IEnumerable<string>
{
{
/// <summary>
/// Initializes a new instance of the <see cref="FamilyNameCollection"/> class.
/// </summary>
/// <param name="familyNames">The family names.</param>
/// <exception cref="ArgumentException">familyNames</exception>
public FamilyNameCollection(IEnumerable<string> familyNames)
public FamilyNameCollection(string familyNames)
{
Contract.Requires<ArgumentNullException>(familyNames != null);
var names = new List<string>(familyNames);
if (names.Count == 0) throw new ArgumentException($"{nameof(familyNames)} must not be empty.");
if (familyNames == null)
{
throw new ArgumentNullException(nameof(familyNames));
}
Names = new ReadOnlyCollection<string>(names);
Names = familyNames.Split(',').Select(x => x.Trim()).ToArray();
PrimaryFamilyName = Names.First();
PrimaryFamilyName = Names[0];
HasFallbacks = Names.Count > 1;
}
@ -55,7 +52,7 @@ namespace Avalonia.Media.Fonts
/// <value>
/// The names.
/// </value>
internal ReadOnlyCollection<string> Names { get; }
internal IReadOnlyList<string> Names { get; }
/// <inheritdoc />
/// <summary>
@ -95,7 +92,10 @@ namespace Avalonia.Media.Fonts
{
builder.Append(Names[index]);
if (index == Names.Count - 1) break;
if (index == Names.Count - 1)
{
break;
}
builder.Append(", ");
}
@ -123,9 +123,12 @@ namespace Avalonia.Media.Fonts
/// </returns>
public override bool Equals(object obj)
{
if (!(obj is FamilyNameCollection other)) return false;
if (!(obj is FamilyNameCollection other))
{
return false;
}
return other.ToString().Equals(ToString());
}
}
}
}

2
src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs

@ -95,7 +95,7 @@ namespace Avalonia.Media.Fonts
{
if (!Source.IsAbsoluteUri && BaseUri != null)
{
return BaseUri.Authority + Source;
return BaseUri.AbsoluteUri + Source.OriginalString;
}
return Source.ToString();

6
src/Avalonia.X11/XIStructs.cs

@ -197,7 +197,7 @@ namespace Avalonia.X11
unsafe struct XIDeviceChangedEvent
{
public int Type; /* GenericEvent */
public ulong Serial; /* # of last request processed by server */
public UIntPtr Serial; /* # of last request processed by server */
public Bool SendEvent; /* true if this came from a SendEvent request */
public IntPtr Display; /* Display the event was read from */
public int Extension; /* XI extension offset */
@ -214,7 +214,7 @@ namespace Avalonia.X11
struct XIDeviceEvent
{
public XEventName type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */
@ -241,7 +241,7 @@ namespace Avalonia.X11
unsafe struct XIEvent
{
public int type; /* GenericEvent */
public ulong serial; /* # of last request processed by server */
public UIntPtr serial; /* # of last request processed by server */
public Bool send_event; /* true if this came from a SendEvent request */
public IntPtr display; /* Display the event was read from */
public int extension; /* XI extension offset */

38
tests/Avalonia.Controls.UnitTests/CarouselTests.cs

@ -76,25 +76,6 @@ namespace Avalonia.Controls.UnitTests
Assert.Single(target.ItemContainerGenerator.Containers);
}
[Fact]
public void Should_Not_Remove_NonCurrent_Page_When_IsVirtualized_False()
{
var target = new Carousel
{
Template = new FuncControlTemplate<Carousel>(CreateTemplate),
Items = new[] { "foo", "bar" },
IsVirtualized = false,
SelectedIndex = 0,
};
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
}
[Fact]
public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
{
@ -115,9 +96,9 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren());
Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single();
var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text);
@ -127,7 +108,7 @@ namespace Avalonia.Controls.UnitTests
target.Items = newItems;
child = target.GetLogicalChildren().Single();
child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child);
Assert.Equal("Bar", ((TextBlock)child).Text);
@ -146,7 +127,8 @@ namespace Avalonia.Controls.UnitTests
var target = new Carousel
{
Template = new FuncControlTemplate<Carousel>(CreateTemplate),
Items = items
Items = items,
IsVirtualized = true,
};
target.ApplyTemplate();
@ -190,9 +172,9 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren());
Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single();
var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text);
@ -254,16 +236,16 @@ namespace Avalonia.Controls.UnitTests
target.ApplyTemplate();
target.Presenter.ApplyTemplate();
Assert.Single(target.GetLogicalChildren());
Assert.Equal(3, target.GetLogicalChildren().Count());
var child = target.GetLogicalChildren().Single();
var child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child);
Assert.Equal("Foo", ((TextBlock)child).Text);
items.RemoveAt(0);
child = target.GetLogicalChildren().Single();
child = target.GetLogicalChildren().First();
Assert.IsType<TextBlock>(child);
Assert.Equal("Bar", ((TextBlock)child).Text);

753
tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs

@ -8,6 +8,7 @@ using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Xunit;
using System.Collections.ObjectModel;
using System.Collections;
namespace Avalonia.Controls.UnitTests.Presenters
{
@ -49,148 +50,672 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.IsType<ItemContainerGenerator<TestItem>>(target.ItemContainerGenerator);
}
[Fact]
public void Setting_SelectedIndex_Should_Show_Page()
public class Virtualized
{
var target = new CarouselPresenter
[Fact]
public void Should_Initially_Materialize_Selected_Container()
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
};
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
target.ApplyTemplate();
Assert.IsType<ContentPresenter>(target.Panel.Children[0]);
Assert.Equal("foo", ((ContentPresenter)target.Panel.Children[0]).Content);
}
AssertSingle(target);
}
[Fact]
public void Changing_SelectedIndex_Should_Show_Page()
{
var target = new CarouselPresenter
[Fact]
public void Should_Initially_Materialize_Nothing_If_No_Selected_Container()
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
};
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
IsVirtualized = true,
};
target.ApplyTemplate();
target.SelectedIndex = 1;
target.ApplyTemplate();
Assert.IsType<ContentPresenter>(target.Panel.Children[0]);
Assert.Equal("bar", ((ContentPresenter)target.Panel.Children[0]).Content);
}
Assert.Empty(target.Panel.Children);
Assert.Empty(target.ItemContainerGenerator.Containers);
}
[Fact]
public void Should_Remove_NonCurrent_Page_When_IsVirtualized_True()
{
var target = new CarouselPresenter
[Fact]
public void Switching_To_Virtualized_Should_Reset_Containers()
{
Items = new[] { "foo", "bar" },
IsVirtualized = true,
SelectedIndex = 0,
};
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
Assert.Single(target.ItemContainerGenerator.Containers);
target.SelectedIndex = 1;
Assert.Single(target.ItemContainerGenerator.Containers);
}
target.ApplyTemplate();
target.IsVirtualized = true;
[Fact]
public void Should_Not_Remove_NonCurrent_Page_When_IsVirtualized_False()
{
var target = new CarouselPresenter
AssertSingle(target);
}
[Fact]
public void Changing_SelectedIndex_Should_Show_Page()
{
Items = new[] { "foo", "bar" },
IsVirtualized = false,
SelectedIndex = 0,
};
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
AssertSingle(target);
target.SelectedIndex = 1;
AssertSingle(target);
}
target.ApplyTemplate();
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
target.SelectedIndex = 0;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
}
[Fact]
public void Should_Remove_NonCurrent_Page()
{
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
IsVirtualized = true,
SelectedIndex = 0,
};
[Fact]
public void Should_Remove_Controls_When_IsVirtualized_Is_False()
{
ObservableCollection<string> items = new ObservableCollection<string>();
var target = new CarouselPresenter
target.ApplyTemplate();
AssertSingle(target);
target.SelectedIndex = 1;
AssertSingle(target);
}
[Fact]
public void Should_Handle_Inserting_Item_At_SelectedItem()
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertSingle(target);
}
target.ApplyTemplate();
target.SelectedIndex = 0;
items.Add("foo");
target.SelectedIndex = 0;
[Fact]
public void Should_Handle_Inserting_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Inserting_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items.Insert(2, "after");
Assert.Same(child, AssertSingle(target));
}
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
[Fact]
public void Should_Handle_Removing_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(1);
AssertSingle(target);
}
items.Add("bar");
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
[Fact]
public void Should_Handle_Removing_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(0);
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Removing_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items.RemoveAt(2);
Assert.Same(child, AssertSingle(target));
}
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
[Fact]
public void Should_Handle_Removing_SelectedItem_When_Its_Last()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(2);
Assert.Equal(1, target.SelectedIndex);
AssertSingle(target);
}
items.Remove(items[0]);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
[Fact]
public void Should_Handle_Removing_Last_Item()
{
var items = new ObservableCollection<string>
{
"item0",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
items.RemoveAt(0);
Assert.Empty(target.Panel.Children);
Assert.Empty(target.ItemContainerGenerator.Containers);
}
items.Remove(items[0]);
Assert.Empty(target.ItemContainerGenerator.Containers);
Assert.Empty(target.Panel.Children);
[Fact]
public void Should_Handle_Replacing_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items[1] = "replaced";
AssertSingle(target);
}
[Fact]
public void Should_Do_Nothing_When_Replacing_Non_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
var child = AssertSingle(target);
items[0] = "replaced";
Assert.Same(child, AssertSingle(target));
}
[Fact]
public void Should_Handle_Moving_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = true,
};
target.ApplyTemplate();
items.Move(1, 0);
AssertSingle(target);
}
private static IControl AssertSingle(CarouselPresenter target)
{
var items = (IList)target.Items;
var index = target.SelectedIndex;
var content = items[index];
var child = Assert.Single(target.Panel.Children);
var presenter = Assert.IsType<ContentPresenter>(child);
var container = Assert.Single(target.ItemContainerGenerator.Containers);
var visible = Assert.Single(target.Panel.Children.Where(x => x.IsVisible));
Assert.Same(child, container.ContainerControl);
Assert.Same(child, visible);
Assert.Equal(content, presenter.Content);
Assert.Equal(content, container.Item);
Assert.Equal(index, container.Index);
return child;
}
}
[Fact]
public void Should_Have_Correct_ItemsContainer_Index()
public class NonVirtualized
{
ObservableCollection<string> items = new ObservableCollection<string>();
[Fact]
public void Should_Initially_Materialize_All_Containers()
{
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
IsVirtualized = false,
};
target.ApplyTemplate();
AssertAll(target);
}
var target = new CarouselPresenter
[Fact]
public void Should_Initially_Show_Selected_Item()
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
AssertAll(target);
}
target.ApplyTemplate();
target.SelectedIndex = 0;
items.Add("foo");
target.SelectedIndex = 0;
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
items.Add("bar");
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
target.SelectedIndex = 1;
Assert.Equal(2, target.ItemContainerGenerator.Containers.Count());
Assert.Equal(2, target.Panel.Children.Count);
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Single(target.ItemContainerGenerator.Containers);
Assert.Single(target.Panel.Children);
Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index);
items.Remove(items[0]);
Assert.Empty(target.ItemContainerGenerator.Containers);
Assert.Empty(target.Panel.Children);
[Fact]
public void Switching_To_Non_Virtualized_Should_Reset_Containers()
{
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = true,
};
target.ApplyTemplate();
target.IsVirtualized = false;
AssertAll(target);
}
[Fact]
public void Changing_SelectedIndex_Should_Show_Page()
{
var target = new CarouselPresenter
{
Items = new[] { "foo", "bar" },
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
AssertAll(target);
target.SelectedIndex = 1;
AssertAll(target);
}
[Fact]
public void Should_Handle_Inserting_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertAll(target);
}
[Fact]
public void Should_Handle_Inserting_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(1, "item1a");
AssertAll(target);
}
[Fact]
public void Should_Do_Handle_Inserting_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Insert(2, "after");
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_At_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(1);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_Before_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(0);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Item_After_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(2);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_SelectedItem_When_Its_Last()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 2,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(2);
Assert.Equal(1, target.SelectedIndex);
AssertAll(target);
}
[Fact]
public void Should_Handle_Removing_Last_Item()
{
var items = new ObservableCollection<string>
{
"item0",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 0,
IsVirtualized = false,
};
target.ApplyTemplate();
items.RemoveAt(0);
Assert.Empty(target.Panel.Children);
Assert.Empty(target.ItemContainerGenerator.Containers);
}
[Fact]
public void Should_Handle_Replacing_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items[1] = "replaced";
AssertAll(target);
}
[Fact]
public void Should_Handle_Moving_SelectedItem()
{
var items = new ObservableCollection<string>
{
"item0",
"item1",
"item2",
};
var target = new CarouselPresenter
{
Items = items,
SelectedIndex = 1,
IsVirtualized = false,
};
target.ApplyTemplate();
items.Move(1, 0);
AssertAll(target);
}
private static void AssertAll(CarouselPresenter target)
{
var items = (IList)target.Items;
Assert.Equal(items?.Count ?? 0, target.Panel.Children.Count);
Assert.Equal(items?.Count ?? 0, target.ItemContainerGenerator.Containers.Count());
for (var i = 0; i < items?.Count; ++i)
{
var content = items[i];
var child = target.Panel.Children[i];
var presenter = Assert.IsType<ContentPresenter>(child);
var container = target.ItemContainerGenerator.ContainerFromIndex(i);
Assert.Same(child, container);
Assert.Equal(i == target.SelectedIndex, child.IsVisible);
Assert.Equal(content, presenter.Content);
Assert.Equal(i, target.ItemContainerGenerator.IndexFromContainer(container));
}
}
}
private class TestItem : ContentControl

48
tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Media;
using Avalonia.Media.Fonts;
@ -12,18 +11,6 @@ namespace Avalonia.Visuals.UnitTests.Media
{
public class FontFamilyTests
{
[Fact]
public void Exception_Should_Be_Thrown_If_Name_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new FontFamily((string)null));
}
[Fact]
public void Exception_Should_Be_Thrown_If_Names_Is_Null()
{
Assert.Throws<ArgumentNullException>(() => new FontFamily((IEnumerable<string>)null));
}
[Fact]
public void Should_Implicitly_Convert_String_To_FontFamily()
{
@ -41,7 +28,7 @@ namespace Avalonia.Visuals.UnitTests.Media
}
[Fact]
public void Parse_Parses_FontFamily_With_Name()
public void Should_Parse_FontFamily_With_SystemFont_Name()
{
var fontFamily = FontFamily.Parse("Courier New");
@ -49,7 +36,7 @@ namespace Avalonia.Visuals.UnitTests.Media
}
[Fact]
public void Parse_Parses_FontFamily_With_Names()
public void Should_Parse_FontFamily_With_Fallbacks()
{
var fontFamily = FontFamily.Parse("Courier New, Times New Roman");
@ -61,7 +48,7 @@ namespace Avalonia.Visuals.UnitTests.Media
}
[Fact]
public void Parse_Parses_FontFamily_With_Resource_Folder()
public void Should_Parse_FontFamily_With_Resource_Folder()
{
var source = new Uri("resm:Avalonia.Visuals.UnitTests#MyFont");
@ -75,7 +62,7 @@ namespace Avalonia.Visuals.UnitTests.Media
}
[Fact]
public void Parse_Parses_FontFamily_With_Resource_Filename()
public void Should_Parse_FontFamily_With_Resource_Filename()
{
var source = new Uri("resm:Avalonia.Visuals.UnitTests.MyFont.ttf#MyFont");
@ -87,5 +74,32 @@ namespace Avalonia.Visuals.UnitTests.Media
Assert.Equal(key, fontFamily.Key);
}
[Theory]
[InlineData("resm:Avalonia.Visuals.UnitTests/Assets/Fonts#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts#MyFont")]
public void Should_Create_FontFamily_From_Uri(string name)
{
var fontFamily = new FontFamily(name);
Assert.Equal("MyFont", fontFamily.Name);
Assert.NotNull(fontFamily.Key);
}
[Theory]
[InlineData("resm:Avalonia.Visuals.UnitTests.Assets.Fonts", "#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests/Assets/Fonts", "#MyFont")]
[InlineData("avares://Avalonia.Visuals.UnitTests", "/Assets/Fonts#MyFont")]
public void Should_Create_FontFamily_From_Uri_With_Base_Uri(string @base, string name)
{
var baseUri = new Uri(@base);
var fontFamily = new FontFamily(baseUri, name);
Assert.Equal("MyFont", fontFamily.Name);
Assert.NotNull(fontFamily.Key);
}
}
}

11
tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs

@ -2,7 +2,6 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
using System.Linq;
using Avalonia.Media.Fonts;
using Xunit;
@ -16,18 +15,12 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts
Assert.Throws<ArgumentNullException>(() => new FamilyNameCollection(null));
}
[Fact]
public void Exception_Should_Be_Thrown_If_Names_Is_Empty()
{
Assert.Throws<ArgumentException>(() => new FamilyNameCollection(Enumerable.Empty<string>()));
}
[Fact]
public void Should_Be_Equal()
{
var familyNames = new FamilyNameCollection(new[] { "Arial", "Times New Roman" });
var familyNames = new FamilyNameCollection("Arial, Times New Roman");
Assert.Equal(new FamilyNameCollection(new[] { "Arial", "Times New Roman" }), familyNames);
Assert.Equal(new FamilyNameCollection("Arial, Times New Roman"), familyNames);
}
}
}

Loading…
Cancel
Save