Browse Source

Merge remote-tracking branch 'origin/master' into features/managed-notifications

pull/2453/head
Dan Walmsley 7 years ago
parent
commit
f222830bdc
  1. 4
      samples/ControlCatalog/SideBar.xaml
  2. 10
      src/Avalonia.Themes.Default/TabItem.xaml
  3. 110
      src/Avalonia.Visuals/Media/FontFamily.cs
  4. 35
      src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs
  5. 2
      src/Avalonia.Visuals/Media/Fonts/FontFamilyKey.cs
  6. 48
      tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs
  7. 11
      tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs

4
samples/ControlCatalog/SideBar.xaml

@ -63,13 +63,13 @@
<Style Selector="TabControl.sidebar > TabItem:pointerover"> <Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabControl.sidebar > TabItem:pointerover">
<Setter Property="Background" Value="Transparent"/> <Setter Property="Background" Value="Transparent"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:selected"> <Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Opacity" Value="1"/> <Setter Property="Opacity" Value="1"/>
</Style> </Style>
<Style Selector="TabControl.sidebar > TabItem:selected /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabControl.sidebar > TabItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/> <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style> </Style>
</Styles> </Styles>

10
src/Avalonia.Themes.Default/TabItem.xaml

@ -24,19 +24,19 @@
<Style Selector="TabItem:disabled"> <Style Selector="TabItem:disabled">
<Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/> <Setter Property="Opacity" Value="{DynamicResource ThemeDisabledOpacity}"/>
</Style> </Style>
<Style Selector="TabItem:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabItem:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/> <Setter Property="Background" Value="{DynamicResource ThemeControlHighlightMidBrush}"/>
</Style> </Style>
<Style Selector="TabItem:selected /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabItem:selected">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}"/> <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush4}"/>
</Style> </Style>
<Style Selector="TabItem:selected:focus /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabItem:selected:focus">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/> <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style> </Style>
<Style Selector="TabItem:selected:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabItem:selected:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/> <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush3}"/>
</Style> </Style>
<Style Selector="TabItem:selected:focus:pointerover /template/ ContentPresenter#PART_ContentPresenter"> <Style Selector="TabItem:selected:focus:pointerover">
<Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/> <Setter Property="Background" Value="{DynamicResource ThemeAccentBrush2}"/>
</Style> </Style>
<Style Selector="TabItem[TabStripPlacement=Right]"> <Style Selector="TabItem[TabStripPlacement=Right]">

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

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

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

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

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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Media.Fonts; using Avalonia.Media.Fonts;
@ -12,18 +11,6 @@ namespace Avalonia.Visuals.UnitTests.Media
{ {
public class FontFamilyTests 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] [Fact]
public void Should_Implicitly_Convert_String_To_FontFamily() public void Should_Implicitly_Convert_String_To_FontFamily()
{ {
@ -41,7 +28,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Name() public void Should_Parse_FontFamily_With_SystemFont_Name()
{ {
var fontFamily = FontFamily.Parse("Courier New"); var fontFamily = FontFamily.Parse("Courier New");
@ -49,7 +36,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [Fact]
public void Parse_Parses_FontFamily_With_Names() public void Should_Parse_FontFamily_With_Fallbacks()
{ {
var fontFamily = FontFamily.Parse("Courier New, Times New Roman"); var fontFamily = FontFamily.Parse("Courier New, Times New Roman");
@ -61,7 +48,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [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"); var source = new Uri("resm:Avalonia.Visuals.UnitTests#MyFont");
@ -75,7 +62,7 @@ namespace Avalonia.Visuals.UnitTests.Media
} }
[Fact] [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"); 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); 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. // Licensed under the MIT license. See licence.md file in the project root for full license information.
using System; using System;
using System.Linq;
using Avalonia.Media.Fonts; using Avalonia.Media.Fonts;
using Xunit; using Xunit;
@ -16,18 +15,12 @@ namespace Avalonia.Visuals.UnitTests.Media.Fonts
Assert.Throws<ArgumentNullException>(() => new FamilyNameCollection(null)); 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] [Fact]
public void Should_Be_Equal() 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