From 1cc4661c145f8ab66f87a8f671757bbe6d4eb166 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sat, 4 May 2019 19:17:48 +0200 Subject: [PATCH 1/3] Simplified creation of FontFamily instances --- src/Avalonia.Visuals/Media/FontFamily.cs | 110 ++++++++++-------- .../Media/Fonts/FamilyNameCollection.cs | 35 +++--- .../Media/FontFamilyTests.cs | 13 --- .../Media/Fonts/FamilyNameCollectionTests.cs | 11 +- 4 files changed, 84 insertions(+), 85 deletions(-) diff --git a/src/Avalonia.Visuals/Media/FontFamily.cs b/src/Avalonia.Visuals/Media/FontFamily.cs index 5c152cd8a0..a486723d86 100644 --- a/src/Avalonia.Visuals/Media/FontFamily.cs +++ b/src/Avalonia.Visuals/Media/FontFamily.cs @@ -11,46 +11,49 @@ namespace Avalonia.Media { public class FontFamily { + /// /// /// Initializes a new instance of the class. /// - /// The name of the . - /// name - public FontFamily(string name) + /// The name of the . + public FontFamily(string name) : this(null, name) { - Contract.Requires(name != null); - - FamilyNames = new FamilyNameCollection(new[] { name }); } /// /// Initializes a new instance of the class. /// - /// The names of the . - /// name - public FontFamily(IEnumerable names) + /// Specifies the base uri that is used to resolve font family assets. + /// The name of the . + /// Base uri must be an absolute uri. + public FontFamily(Uri baseUri, string name) { - Contract.Requires(names != null); + if (string.IsNullOrEmpty(name)) + { + FamilyNames = new FamilyNameCollection(string.Empty); - FamilyNames = new FamilyNameCollection(names); - } + return; + } - /// - /// - /// Initializes a new instance of the class. - /// - /// The name of the . - /// The source of font resources. - /// - 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); } /// /// Represents the default font family /// - public static FontFamily Default => new FontFamily(String.Empty); + public static FontFamily Default => new FontFamily(string.Empty); /// /// Represents all font families in the system. This can be an expensive call depending on platform implementation. @@ -88,46 +91,40 @@ namespace Avalonia.Media /// public static implicit operator FontFamily(string s) { - return Parse(s); + return new FontFamily(s); } - /// - /// Parses a string. - /// - /// The string. - /// - /// - /// - /// Specified family is not supported. - /// - 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 } } + /// + /// Parses a string. + /// + /// The string. + /// Specifies the base uri that is used to resolve font family assets. + /// + /// + /// Specified family is not supported. + /// + 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); + } + /// /// Returns a that represents this instance. /// diff --git a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs b/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs index 50511d2fb7..acf0bbdb11 100644 --- a/src/Avalonia.Visuals/Media/Fonts/FamilyNameCollection.cs +++ b/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 - { + { /// /// Initializes a new instance of the class. /// /// The family names. /// familyNames - public FamilyNameCollection(IEnumerable familyNames) + public FamilyNameCollection(string familyNames) { - Contract.Requires(familyNames != null); - - var names = new List(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(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 /// /// The names. /// - internal ReadOnlyCollection Names { get; } + internal IReadOnlyList Names { get; } /// /// @@ -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 /// 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()); } } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs index 4c7a89cbda..1cd1742f32 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/FontFamilyTests.cs +++ b/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(() => new FontFamily((string)null)); - } - - [Fact] - public void Exception_Should_Be_Thrown_If_Names_Is_Null() - { - Assert.Throws(() => new FontFamily((IEnumerable)null)); - } - [Fact] public void Should_Implicitly_Convert_String_To_FontFamily() { diff --git a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs b/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs index 14daf5bd8b..e7aa9daab3 100644 --- a/tests/Avalonia.Visuals.UnitTests/Media/Fonts/FamilyNameCollectionTests.cs +++ b/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(() => new FamilyNameCollection(null)); } - [Fact] - public void Exception_Should_Be_Thrown_If_Names_Is_Empty() - { - Assert.Throws(() => new FamilyNameCollection(Enumerable.Empty())); - } - [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); } } } From 8a179816eda1d054d57fa949118157c074c2ad3c Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Mon, 6 May 2019 12:28:19 +0200 Subject: [PATCH 2/3] Target TabItem itself instead of ContentPresenter for default selectors --- samples/ControlCatalog/SideBar.xaml | 4 ++-- src/Avalonia.Themes.Default/TabItem.xaml | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index fea55bcb07..0eae348717 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -63,13 +63,13 @@ - - diff --git a/src/Avalonia.Themes.Default/TabItem.xaml b/src/Avalonia.Themes.Default/TabItem.xaml index fcdb76524e..92482a564c 100644 --- a/src/Avalonia.Themes.Default/TabItem.xaml +++ b/src/Avalonia.Themes.Default/TabItem.xaml @@ -24,19 +24,19 @@ - - - - -