Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve design type converters #9447

Merged
merged 2 commits into from
Aug 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Microsoft.Maui.sln
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Graphics.Skia.WPF", "src\Gr
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GraphicsTester.Skia.Gtk", "src\Graphics\samples\GraphicsTester.Skia.Gtk\GraphicsTester.Skia.Gtk.csproj", "{F351A992-18E4-473C-8ADD-2BA0BAA7B5A2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Controls.Core.Design.UnitTests", "src\Controls\tests\Core.Design.UnitTests\Controls.Core.Design.UnitTests.csproj", "{F68932B0-81A2-4CC3-A4F7-28091EE91B23}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -624,6 +626,10 @@ Global
{F351A992-18E4-473C-8ADD-2BA0BAA7B5A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F351A992-18E4-473C-8ADD-2BA0BAA7B5A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F351A992-18E4-473C-8ADD-2BA0BAA7B5A2}.Release|Any CPU.Build.0 = Release|Any CPU
{F68932B0-81A2-4CC3-A4F7-28091EE91B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F68932B0-81A2-4CC3-A4F7-28091EE91B23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F68932B0-81A2-4CC3-A4F7-28091EE91B23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F68932B0-81A2-4CC3-A4F7-28091EE91B23}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -740,6 +746,7 @@ Global
{AFD9D653-08C4-456C-AA1B-F5C9F621D655} = {42AB9AE1-631D-4AD4-85B7-910FF0940BDB}
{F26D31D3-CE4C-4F32-A77F-E2905C948674} = {42AB9AE1-631D-4AD4-85B7-910FF0940BDB}
{F351A992-18E4-473C-8ADD-2BA0BAA7B5A2} = {1BA0121E-0B83-4C8F-81BE-C293E7E35DCE}
{F68932B0-81A2-4CC3-A4F7-28091EE91B23} = {25D0D27A-C5FE-443D-8B65-D6C987F4A80E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0B8ABEAD-D2B5-4370-A187-62B5ABE4EE50}
Expand Down
1 change: 1 addition & 0 deletions eng/cake/dotnet.cake
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ Task("dotnet-test")
var tests = new []
{
"**/Controls.Core.UnitTests.csproj",
"**/Controls.Core.Design.UnitTests.csproj",
"**/Controls.Xaml.UnitTests.csproj",
"**/Core.UnitTests.csproj",
"**/Essentials.UnitTests.csproj",
Expand Down
4 changes: 4 additions & 0 deletions src/Controls/src/Core.Design/AttributeTableBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public AttributeTableBuilder()
);

AddTypeAttributes("Microsoft.Maui.Graphics.Color", new TypeConverterAttribute(typeof(ColorDesignTypeConverter)));
AddTypeAttributes("Microsoft.Maui.GridLength", new TypeConverterAttribute(typeof(GridLengthDesignTypeConverter)));

AddTypeAttributes("Microsoft.Maui.Controls.ConstraintExpression", new MarkupExtensionReturnTypeAttribute());
AddTypeAttributes("Microsoft.Maui.Controls.LayoutOptions", new TypeConverterAttribute(typeof(LayoutOptionsDesignTypeConverter)));
Expand All @@ -80,6 +81,9 @@ public AttributeTableBuilder()
AddMemberAttributes("Microsoft.Maui.Controls.SearchHandler", "FontSize", new TypeConverterAttribute(typeof(FontSizeDesignTypeConverter)));
AddMemberAttributes("Microsoft.Maui.Controls.Span", "FontSize", new TypeConverterAttribute(typeof(FontSizeDesignTypeConverter)));

AddMemberAttributes("Microsoft.Maui.Controls.Grid", "RowDefinitions", new TypeConverterAttribute(typeof(GridLengthCollectionDesignTypeConverter)));
AddMemberAttributes("Microsoft.Maui.Controls.Grid", "ColumnDefinitions", new TypeConverterAttribute(typeof(GridLengthCollectionDesignTypeConverter)));

AddTypeAttributes("Microsoft.Maui.Layouts.FlexJustify", new TypeConverterAttribute(typeof(FlexJustifyDesignTypeConverter)));
AddTypeAttributes("Microsoft.Maui.Layouts.FlexDirection", new TypeConverterAttribute(typeof(FlexDirectionDesignTypeConverter)));
AddTypeAttributes("Microsoft.Maui.Layouts.FlexAlignContent", new TypeConverterAttribute(typeof(FlexAlignContentDesignTypeConverter)));
Expand Down
26 changes: 16 additions & 10 deletions src/Controls/src/Core.Design/ColorDesignTypeConverter.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Microsoft.Maui.Controls.Design
{
Expand All @@ -16,7 +10,7 @@ public class ColorDesignTypeConverter : KnownValuesDesignTypeConverter
public ColorDesignTypeConverter()
{ }

protected override string[] KnownValues =>
private static readonly string[] knownValues =
new[]
{
"AliceBlue",
Expand Down Expand Up @@ -169,6 +163,18 @@ public ColorDesignTypeConverter()
"YellowGreen"
};

private static readonly HashSet<string> knowValuesSet;

static ColorDesignTypeConverter()
{
knowValuesSet = new HashSet<string>(knownValues, StringComparer.OrdinalIgnoreCase);

// Color.TryParse supports "default" (see ..\Graphics\Color.cs) as well as XAML parses used during build.
knowValuesSet.Add("Default");
}

protected override string[] KnownValues => knownValues;

// #rgb, #rrggbb, #aarrggbb are all valid
const string RxColorHexPattern = @"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}([0-9a-fA-F]{2})?)$";
static readonly Lazy<Regex> RxColorHex = new(() => new Regex(RxColorHexPattern, RegexOptions.Compiled | RegexOptions.Singleline));
Expand All @@ -189,12 +195,12 @@ public override bool IsValid(ITypeDescriptorContext context, object value)
if (string.IsNullOrWhiteSpace(str))
return false;

// Any named colors are ok
if (KnownValues.Any(v => str?.ToString()?.Equals(v, StringComparison.Ordinal) ?? false))
// Any named colors are ok. Surrounding white spaces are ok.
if (knowValuesSet.Contains(str.Trim()))
return true;

// Check for HEX Color string
if (RxColorHex.Value.IsMatch(value?.ToString()))
if (RxColorHex.Value.IsMatch(str))
return true;

var match = RxFuncExpr.Value.Match(str);
Expand Down
3 changes: 2 additions & 1 deletion src/Controls/src/Core.Design/Controls.Core.Design.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
<Compile Include="KeyboardDesignTypeConverter.cs" />
<Compile Include="EasingDesignTypeConverter.cs" />
<Compile Include="FlowDirectionDesignTypeConverter.cs" />
<Compile Include="GridLengthCollectionDesignTypeConverter.cs" />
<Compile Include="GridLengthDesignTypeConverter.cs" />
<Compile Include="KnownValuesDesignTypeConverter.cs" />
<Compile Include="LayoutOptionsDesignTypeConverter.cs" />
<Compile Include="LinearItemsLayoutDesignTypeConverter.cs" />
<Compile Include="FontSizeDesignTypeConverter.cs" />
<Compile Include="FlexEnumDesignTypeConverters.cs" />
<PackageReference Include="Microsoft.VisualStudio.DesignTools.Extensibility" Version="17.3.32804.24" />
</ItemGroup>

<!-- The IDE will look for a top level assembly resource called 'Microsoft.Maui.toolbox.xml' to -->
<!-- load the toolbox metadata from. -->
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.ComponentModel;
using System.Linq;

namespace Microsoft.Maui.Controls.Design
{
public class GridLengthCollectionDesignTypeConverter : TypeConverter
{
public override bool IsValid(ITypeDescriptorContext context, object value)
{
if (value?.ToString() is string strValue)
{
string[] lengths = strValue.Split(',');
return lengths.All(GridLengthDesignTypeConverter.IsValid);
}

return false;
}
}
}
33 changes: 33 additions & 0 deletions src/Controls/src/Core.Design/GridLengthDesignTypeConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.ComponentModel;
using System.Globalization;

namespace Microsoft.Maui.Controls.Design
{
public class GridLengthDesignTypeConverter : TypeConverter
{

public override bool IsValid(ITypeDescriptorContext context, object value)
{
return IsValid(value?.ToString());
}

internal static bool IsValid(string value)
{
value = value?.Trim();
if (string.IsNullOrEmpty(value))
return false;

if (string.Compare(value, "auto", StringComparison.OrdinalIgnoreCase) == 0)
return true;
if (string.Compare(value, "*", StringComparison.OrdinalIgnoreCase) == 0)
return true;
if (value.EndsWith("*", StringComparison.Ordinal) && double.TryParse(value.Substring(0, value.Length - 1), NumberStyles.Number, CultureInfo.InvariantCulture, out var len))
return !(len < 0 || double.IsNaN(len));
if (double.TryParse(value, NumberStyles.Number, CultureInfo.InvariantCulture, out len))
return !(len < 0 || double.IsNaN(len));

return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Microsoft.Maui.Controls.Design;
using Xunit;

namespace Microsoft.Maui.Controls.Core.UnitTests
{
public class ColorDesignTypeConverterTests
{
[Theory]
[InlineData("red")]
[InlineData("Red")]
[InlineData("RED")]
[InlineData("DEFAULT")]
[InlineData("default")]
public void ColorConverter_ColorName_Valid_CaseInsensitive(string value)
{
ColorDesignTypeConverter converter = new ColorDesignTypeConverter();
bool actual = converter.IsValid(value);
Assert.True(actual);
}

[Theory]
[InlineData("redd")]
[InlineData("Redd")]
public void ColorConverter_ColorName_Invalid(string value)
{
ColorDesignTypeConverter converter = new ColorDesignTypeConverter();
bool actual = converter.IsValid(value);
Assert.False(actual);
}

[Theory]
[InlineData("default")]
[InlineData(" Default ")]
public void ColorConverter_ColorName_Default(string value)
{
ColorDesignTypeConverter converter = new ColorDesignTypeConverter();
bool actual = converter.IsValid(value);

// Example:
// <Grid BackgroundColor="Default"/>
// The "Default" value is okay to use in XAML. It builds and has default(Color)
// value at runtime. Color.TryParse recognizes it but returns false.
Assert.True(actual);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net472</TargetFrameworks>
<AssemblyName>Microsoft.Maui.Controls.Core.Design.UnitTests</AssemblyName>
<EnableDefaultCompileItems>False</EnableDefaultCompileItems>
<IsPackable>false</IsPackable>
<RootNamespace>Microsoft.Maui.Controls.Core.UnitTests</RootNamespace>
</PropertyGroup>
<ItemGroup>
<Reference Include="System.Xaml" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.DesignTools.Extensibility" Version="17.3.32804.24" />
<ProjectReference Include="..\..\src\Core.Design\Controls.Core.Design.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="ColorDesignTypeConverterTests.cs" />
<Compile Include="GridLengthCollectionDesignTypeConverterTests.cs" />
<Compile Include="GridLengthDesignTypeConverterTests.cs" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.Maui.Controls.Design;
using Xunit;

namespace Microsoft.Maui.Controls.Core.UnitTests
{
public class GridLengthCollectionDesignTypeConverterTests
{
[Theory]
[InlineData("*,auto,123")]
[InlineData(" *, AUTO , 123 ")]
public void GridLengthCollection_Valid(string value)
{
GridLengthCollectionDesignTypeConverter converter = new GridLengthCollectionDesignTypeConverter();
bool actual = converter.IsValid(value);
Assert.True(actual);
}

[Theory]
[InlineData(",1")]
[InlineData("1,")]
[InlineData("1,,2")]
[InlineData("1,-2,3")]
public void GridLengthCollection_Invalid(string value)
{
GridLengthCollectionDesignTypeConverter converter = new GridLengthCollectionDesignTypeConverter();
bool actual = converter.IsValid(value);
Assert.False(actual);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using Microsoft.Maui.Controls.Design;
using Xunit;

namespace Microsoft.Maui.Controls.Core.UnitTests
{
public class GridLengthDesignTypeConverterTests
{
[Theory]
[InlineData("auto")]
[InlineData("Auto ")]
[InlineData(" AUTO")]
[InlineData(" AutO ")]
public void GridLength_Valid_Auto(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.True(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.True(actual);
}

[Theory]
[InlineData("*")]
[InlineData(" * ")]
[InlineData(" 0*")]
[InlineData("2* ")]
[InlineData("1.234567*")]
public void GridLength_Valid_Start(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.True(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.True(actual);
}

[Theory]
[InlineData("-0.00001*")]
[InlineData("-2*")]
[InlineData("NaN*")]
public void GridLength_Invalid_Start(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.False(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.False(actual);
}

[Theory]
[InlineData("0")]
[InlineData(" 2 ")]
[InlineData("1.234")]
public void GridLength_Valid_Absolute(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.True(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.True(actual);
}

[Theory]
[InlineData("-0.00001")]
[InlineData("-2")]
[InlineData("NaN")]
public void GridLength_Invalid_Absolute(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.False(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.False(actual);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void GridLength_Invalid_Empty(string value)
{
bool actual = (new GridLengthDesignTypeConverter()).IsValid(value);
Assert.False(actual);

actual = (new GridLengthCollectionDesignTypeConverter()).IsValid(value);
Assert.False(actual);
}
}
}