Skip to content
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CommunityToolkit.Maui.Core;

static class BaseAnimationDefaults
{
public const uint Length = 250u;
public static Easing Easing { get; } = Easing.Linear;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace CommunityToolkit.Maui.Core;

static class FadeAnimationDefaults
{
public const uint Length = 300u;
public const double Opacity = 0.3;
}
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ public partial class {{defaultTestClassName}}

file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static bool IsInitializingText = false;
public static volatile bool IsInitializingText = false;
public static object CreateDefaultText(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingText = true;
Expand All @@ -619,7 +619,7 @@ public static object CreateDefaultText(global::Microsoft.Maui.Controls.BindableO
return defaultValue;
}

public static bool IsInitializingCustomDuration = false;
public static volatile bool IsInitializingCustomDuration = false;
public static object CreateDefaultCustomDuration(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingCustomDuration = true;
Expand All @@ -632,4 +632,45 @@ public static object CreateDefaultCustomDuration(global::Microsoft.Maui.Controls

await VerifySourceGeneratorAsync(source, expectedGenerated);
}

[Fact]
public async Task GenerateBindableProperty_GenericClassExample_GeneratesCorrectCode()
{
const string source =
/* language=C#-test */
//lang=csharp
$$"""
using CommunityToolkit.Maui;
using Microsoft.Maui.Controls;

namespace {{defaultTestNamespace}};

public partial class {{defaultTestClassName}}<T> : View
{
[BindablePropertyAttribute]
public partial string Text { get; set; }
}
""";

const string expectedGenerated =
/* language=C#-test */
//lang=csharp
$$"""
// <auto-generated>
// See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
#pragma warning disable
#nullable enable
namespace {{defaultTestNamespace}};
public partial class {{defaultTestClassName}}<T>
{
/// <summary>
/// Backing BindableProperty for the <see cref = "Text"/> property.
/// </summary>
public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T>), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null);
public partial string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
}
""";

await VerifySourceGeneratorAsync(source, expectedGenerated);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public partial class {{defaultTestClassName}}

file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static bool IsInitializingInvoiceStatus = false;
public static volatile bool IsInitializingInvoiceStatus = false;
public static object CreateDefaultInvoiceStatus(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingInvoiceStatus = true;
Expand Down Expand Up @@ -160,7 +160,7 @@ public partial class {{defaultTestClassName}}

file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static bool IsInitializingInvoiceStatus = false;
public static volatile bool IsInitializingInvoiceStatus = false;
public static object CreateDefaultInvoiceStatus(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingInvoiceStatus = true;
Expand Down Expand Up @@ -516,7 +516,7 @@ public partial class {{defaultTestClassName}}

file static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static bool IsInitializingIsEnabled = false;
public static volatile bool IsInitializingIsEnabled = false;
public static object CreateDefaultIsEnabled(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingIsEnabled = true;
Expand All @@ -525,7 +525,7 @@ public static object CreateDefaultIsEnabled(global::Microsoft.Maui.Controls.Bind
return defaultValue;
}

public static bool IsInitializingPi = false;
public static volatile bool IsInitializingPi = false;
public static object CreateDefaultPi(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingPi = true;
Expand All @@ -534,7 +534,7 @@ public static object CreateDefaultPi(global::Microsoft.Maui.Controls.BindableObj
return defaultValue;
}

public static bool IsInitializingLetter = false;
public static volatile bool IsInitializingLetter = false;
public static object CreateDefaultLetter(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingLetter = true;
Expand All @@ -543,7 +543,7 @@ public static object CreateDefaultLetter(global::Microsoft.Maui.Controls.Bindabl
return defaultValue;
}

public static bool IsInitializingTimeSpent = false;
public static volatile bool IsInitializingTimeSpent = false;
public static object CreateDefaultTimeSpent(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingTimeSpent = true;
Expand All @@ -552,7 +552,7 @@ public static object CreateDefaultTimeSpent(global::Microsoft.Maui.Controls.Bind
return defaultValue;
}

public static bool IsInitializingDoubleEpsilon = false;
public static volatile bool IsInitializingDoubleEpsilon = false;
public static object CreateDefaultDoubleEpsilon(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingDoubleEpsilon = true;
Expand All @@ -561,7 +561,7 @@ public static object CreateDefaultDoubleEpsilon(global::Microsoft.Maui.Controls.
return defaultValue;
}

public static bool IsInitializingSingleEpsilon = false;
public static volatile bool IsInitializingSingleEpsilon = false;
public static object CreateDefaultSingleEpsilon(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingSingleEpsilon = true;
Expand All @@ -570,7 +570,7 @@ public static object CreateDefaultSingleEpsilon(global::Microsoft.Maui.Controls.
return defaultValue;
}

public static bool IsInitializingCurrentTime = false;
public static volatile bool IsInitializingCurrentTime = false;
public static object CreateDefaultCurrentTime(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingCurrentTime = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,60 @@ public partial class {{defaultTestClassName}}<T, U>
await VerifySourceGeneratorAsync(source, expectedGenerated);
}

[Fact]
public async Task GenerateBindableProperty_GenericClass_WithInitializer_GeneratesCorrectCode()
{
const string source =
/* language=C#-test */
//lang=csharp
$$"""
using CommunityToolkit.Maui;
using Microsoft.Maui.Controls;

namespace {{defaultTestNamespace}};

public partial class {{defaultTestClassName}}<T,U> : View where T : class
{
[BindableProperty]
public partial T? Value { get; set; } = default;
}
""";

const string expectedGenerated =
/* language=C#-test */
//lang=csharp
$$"""
// <auto-generated>
// See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator
#pragma warning disable
#nullable enable
namespace {{defaultTestNamespace}};
public partial class {{defaultTestClassName}}<T, U>
{
/// <summary>
/// Backing BindableProperty for the <see cref = "Value"/> property.
/// </summary>
public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(T), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}<T, U>), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultValue);
public partial T? Value { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingValue ? field : (T? )GetValue(ValueProperty); set => SetValue(ValueProperty, value); }

[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
private static class __{{defaultTestClassName}}BindablePropertyInitHelpers
{
public static volatile bool IsInitializingValue = false;
public static object CreateDefaultValue(global::Microsoft.Maui.Controls.BindableObject bindable)
{
IsInitializingValue = true;
var defaultValue = (({{defaultTestClassName}}<T, U>)bindable).Value;
IsInitializingValue = false;
return defaultValue;
}
}
}
""";

await VerifySourceGeneratorAsync(source, expectedGenerated);
}

[Fact]
public async Task GenerateBindableProperty_NestedClass_GeneratesCorrectCode()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,17 +182,17 @@ static string GenerateSource(SemanticValues value)
? classNameWithGenerics
: string.Concat(value.ClassInformation.ContainingTypes, ".", classNameWithGenerics);

var fileStaticClassName = $"__{classNameWithGenerics}BindablePropertyInitHelpers";
var bindablePropertyInitHelpersClassName = $"__{value.ClassInformation.ClassName}BindablePropertyInitHelpers";

foreach (var info in value.BindableProperties)
{
if (info.IsReadOnlyBindableProperty)
{
GenerateReadOnlyBindableProperty(sb, in info, fileStaticClassName);
GenerateReadOnlyBindableProperty(sb, in info, bindablePropertyInitHelpersClassName);
}
else
{
GenerateBindableProperty(sb, in info, fileStaticClassName);
GenerateBindableProperty(sb, in info, bindablePropertyInitHelpersClassName);
}

if (info.ShouldUsePropertyInitializer)
Expand All @@ -209,7 +209,20 @@ static string GenerateSource(SemanticValues value)
}
}

GenerateProperty(sb, in info, fileStaticClassName);
GenerateProperty(sb, in info, bindablePropertyInitHelpersClassName);
}

// If we generated any helper members and the declaring class is generic,
// emit the helper class nested inside the generated partial class so
// generic type parameters are in scope for casts used by the helper.
if (fileStaticClassStringBuilder.Length > 0 && !string.IsNullOrEmpty(value.ClassInformation.GenericTypeParameters))
{
sb.Append("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]");
sb.Append("\n");
sb.Append("private static class ").Append(bindablePropertyInitHelpersClassName).Append("\n{");
sb.Append("\n");
sb.Append(fileStaticClassStringBuilder.ToString());
sb.Append("}\n\n");
}

sb.Append('}');
Expand All @@ -224,10 +237,12 @@ static string GenerateSource(SemanticValues value)
}
}

// If we generated any helper members, emit a file static class with them.
if (fileStaticClassStringBuilder.Length > 0)
// If we generated any helper members and the declaring class is not generic,
// emit a file static class with them. Generic types have their helpers emitted
// nested inside the class above to ensure type parameter scope.
if (fileStaticClassStringBuilder.Length > 0 && string.IsNullOrEmpty(value.ClassInformation.GenericTypeParameters))
{
sb.Append("\n\nfile static class ").Append(fileStaticClassName).Append("\n{\n");
sb.Append("\n\nfile static class ").Append(bindablePropertyInitHelpersClassName).Append("\n{\n");
sb.Append(fileStaticClassStringBuilder.ToString());
sb.Append("}\n");
}
Expand Down Expand Up @@ -598,7 +613,7 @@ static string GetFormattedReturnType(ITypeSymbol typeSymbol)
static void AppendHelperInitializingField(StringBuilder fileStaticClassStringBuilder, in BindablePropertyModel info)
{
// Make the flag public static so it can be referenced from the generated partial class in the same file.
fileStaticClassStringBuilder.Append("public static bool ")
fileStaticClassStringBuilder.Append("public static volatile bool ")
.Append(info.InitializingPropertyName)
.Append(" = false;\n");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using CommunityToolkit.Maui.Animations;
using CommunityToolkit.Maui.Core;
using Xunit;

namespace CommunityToolkit.Maui.UnitTests.Animations;

public class BaseAnimationTests
{
[Fact]
public void BaseAnimationT_EnsureDefaults()
{
// Arrange
BaseAnimation animation = new MockBaseAnimation();

// Act // Assert
Assert.Equal(BaseAnimationDefaults.Easing, animation.Easing);
Assert.Equal(BaseAnimationDefaults.Length, animation.Length);
}

[Fact]
public void BaseAnimation_EnsureDefaults()
{
// Arrange
BaseAnimation<VisualElement> animation = new MockBaseAnimation();

// Act // Assert
Assert.Equal(BaseAnimationDefaults.Easing, animation.Easing);
Assert.Equal(BaseAnimationDefaults.Length, animation.Length);
}

class MockBaseAnimation : BaseAnimation
{
public override Task Animate(VisualElement view, CancellationToken token = default)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CommunityToolkit.Maui.Animations;
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.UnitTests.Mocks;
using FluentAssertions;
using Xunit;
Expand Down Expand Up @@ -67,4 +68,16 @@ public async Task AnimateShouldReturnToOriginalOpacity()

label.Opacity.Should().Be(0.9);
}

[Fact]
public void EnsureDefaults()
{
// Arrange
var animation = new FadeAnimation();

// Act // Assert
Assert.Equal(BaseAnimationDefaults.Easing, animation.Easing);
Assert.Equal(FadeAnimationDefaults.Length, animation.Length);
Assert.Equal(FadeAnimationDefaults.Opacity, animation.Opacity);
}
}
Loading
Loading