diff --git a/samples/CommunityToolkit.Maui.Sample/Pages/Views/Popup/PopupsPage.xaml.cs b/samples/CommunityToolkit.Maui.Sample/Pages/Views/Popup/PopupsPage.xaml.cs index 1d6e2198fe..076ef8e8b5 100644 --- a/samples/CommunityToolkit.Maui.Sample/Pages/Views/Popup/PopupsPage.xaml.cs +++ b/samples/CommunityToolkit.Maui.Sample/Pages/Views/Popup/PopupsPage.xaml.cs @@ -149,7 +149,7 @@ async void HandleSelfClosingPopupButtonClicked(object? sender, EventArgs e) await this.ClosePopupAsync(); } - + async void HandleCollectionViewPopupClicked(object? sender, EventArgs e) { var popupResult = await popupService.ShowPopupAsync( diff --git a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/Popup/CollectionViewPopupViewModel.cs b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/Popup/CollectionViewPopupViewModel.cs index fc4137b407..4bdf0b62ed 100644 --- a/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/Popup/CollectionViewPopupViewModel.cs +++ b/samples/CommunityToolkit.Maui.Sample/ViewModels/Views/Popup/CollectionViewPopupViewModel.cs @@ -14,7 +14,7 @@ async Task OnReturnButtonTapped(CancellationToken token) { await popupService.ClosePopupAsync(navigation, SelectedTitle ?? string.Empty, token); } - + [ObservableProperty, NotifyCanExecuteChangedFor(nameof(ReturnButtonTappedCommand))] public partial string? SelectedTitle { get; set; } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs b/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs index 7d31ea60a3..67210168f5 100644 --- a/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs @@ -39,12 +39,12 @@ public event EventHandler MediaCaptured static ICameraProvider CameraProvider => IPlatformApplication.Current?.Services.GetRequiredService() ?? throw new CameraException("Unable to retrieve CameraProvider"); /// - [BindableProperty(DefaultValue = CameraViewDefaults.IsAvailable)] - public partial bool IsAvailable { get; } + [BindableProperty] + public partial bool IsAvailable { get; } = CameraViewDefaults.IsAvailable; /// - [BindableProperty(DefaultValue = CameraViewDefaults.IsCameraBusy)] - public partial bool IsBusy { get; } + [BindableProperty] + public partial bool IsBusy { get; } = CameraViewDefaults.IsCameraBusy; /// /// Gets the that triggers an image capture. @@ -92,24 +92,24 @@ public event EventHandler MediaCaptured public partial Command StopVideoRecordingCommand { get; } /// - [BindableProperty(DefaultValueCreatorMethodName = nameof(CreateCameraFlashMode))] - public partial CameraFlashMode CameraFlashMode { get; set; } + [BindableProperty] + public partial CameraFlashMode CameraFlashMode { get; set; } = CameraViewDefaults.CameraFlashMode; /// [BindableProperty(DefaultBindingMode = BindingMode.TwoWay)] public partial CameraInfo? SelectedCamera { get; set; } /// - [BindableProperty(DefaultValue = CameraViewDefaults.ZoomFactor, DefaultBindingMode = BindingMode.TwoWay, CoerceValueMethodName = nameof(CoerceZoom))] - public partial float ZoomFactor { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.TwoWay, CoerceValueMethodName = nameof(CoerceZoom))] + public partial float ZoomFactor { get; set; } = CameraViewDefaults.ZoomFactor; /// - [BindableProperty(DefaultValueCreatorMethodName = nameof(CreateImageCaptureResolution), DefaultBindingMode = BindingMode.TwoWay)] - public partial Size ImageCaptureResolution { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.TwoWay)] + public partial Size ImageCaptureResolution { get; set; } = CameraViewDefaults.ImageCaptureResolution; /// - [BindableProperty(DefaultValue = CameraViewDefaults.IsTorchOn)] - public partial bool IsTorchOn { get; set; } + [BindableProperty] + public partial bool IsTorchOn { get; set; } = CameraViewDefaults.IsTorchOn; new CameraViewHandler Handler => (CameraViewHandler)(base.Handler ?? throw new InvalidOperationException("Unable to retrieve Handler")); @@ -230,10 +230,6 @@ protected virtual void Dispose(bool disposing) } } - static object CreateImageCaptureResolution(BindableObject bindable) => CameraViewDefaults.ImageCaptureResolution; - - static object CreateCameraFlashMode(BindableObject bindable) => CameraViewDefaults.CameraFlashMode; - static Command CreateCaptureImageCommand(BindableObject bindable) { var cameraView = (CameraView)bindable; diff --git a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/MaxLengthReachedBehaviorDefaults.sharaed.cs b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/MaxLengthReachedBehaviorDefaults.sharaed.cs new file mode 100644 index 0000000000..d1642c93c7 --- /dev/null +++ b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/MaxLengthReachedBehaviorDefaults.sharaed.cs @@ -0,0 +1,9 @@ +using System.Windows.Input; + +namespace CommunityToolkit.Maui.Core; + +static class MaxLengthReachedBehaviorDefaults +{ + public const bool ShouldDismissKeyboardAutomatically = false; + public static ICommand? Command { get; } = null; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/ProgressBarAnimationBehaviorDefaults.cs b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/ProgressBarAnimationBehaviorDefaults.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.Core/Primitives/Defaults/ProgressBarAnimationBehaviorDefaults.cs rename to src/CommunityToolkit.Maui.Core/Primitives/Defaults/ProgressBarAnimationBehaviorDefaults.shared.cs diff --git a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/TouchBehaviorDefaults.shared.cs b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/TouchBehaviorDefaults.shared.cs index f8d832ba46..a328d96779 100644 --- a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/TouchBehaviorDefaults.shared.cs +++ b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/TouchBehaviorDefaults.shared.cs @@ -190,15 +190,15 @@ static class TouchBehaviorDefaults /// /// Default Value for TouchBehavior /// - public const Color? DefaultBackgroundColor = null; + public static Color DefaultBackgroundColor { get; } = Colors.Transparent; /// /// Default Value for TouchBehavior /// - public const Color? HoveredBackgroundColor = null; + public static Color HoveredBackgroundColor { get; } = Colors.Transparent; /// /// Default Value for TouchBehavior /// - public const Color? PressedBackgroundColor = null; + public static Color PressedBackgroundColor { get; } = Colors.Transparent; } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.Core/Primitives/Defaults/UserStoppedTypingBehaviorDefaults.cs b/src/CommunityToolkit.Maui.Core/Primitives/Defaults/UserStoppedTypingBehaviorDefaults.shared.cs similarity index 100% rename from src/CommunityToolkit.Maui.Core/Primitives/Defaults/UserStoppedTypingBehaviorDefaults.cs rename to src/CommunityToolkit.Maui.Core/Primitives/Defaults/UserStoppedTypingBehaviorDefaults.shared.cs diff --git a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs index a3946f62f6..535154f893 100644 --- a/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/MediaElement.shared.cs @@ -102,26 +102,26 @@ internal event EventHandler StopRequested /// /// Gets the in pixels. /// - [BindableProperty(DefaultValue = MediaElementDefaults.MediaHeight)] - public partial int MediaHeight { get; } + [BindableProperty] + public partial int MediaHeight { get; } = MediaElementDefaults.MediaHeight; /// /// Gets the in pixels. /// - [BindableProperty(DefaultValue = MediaElementDefaults.MediaWidth)] - public partial int MediaWidth { get; } + [BindableProperty] + public partial int MediaWidth { get; } = MediaElementDefaults.MediaWidth; /// /// Gets the current of the media playback. /// - [BindableProperty(DefaultValue = MediaElementDefaults.Position)] - public partial TimeSpan Position { get; } + [BindableProperty] + public partial TimeSpan Position { get; } = MediaElementDefaults.Position; /// /// Gets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.Duration)] - public partial TimeSpan Duration { get; } + [BindableProperty] + public partial TimeSpan Duration { get; } = MediaElementDefaults.Duration; /// /// Gets or sets the of the media. @@ -131,38 +131,38 @@ internal event EventHandler StopRequested /// /// Gets or sets the ratio used to display the video content. /// - [BindableProperty(DefaultValue = MediaElementDefaults.Aspect)] - public partial Aspect Aspect { get; set; } + [BindableProperty] + public partial Aspect Aspect { get; set; } = MediaElementDefaults.Aspect; /// /// Gets or sets the indicating whether the media should play automatically. /// - [BindableProperty(DefaultValue = MediaElementDefaults.ShouldAutoPlay)] - public partial bool ShouldAutoPlay { get; set; } + [BindableProperty] + public partial bool ShouldAutoPlay { get; set; } = MediaElementDefaults.ShouldAutoPlay; /// /// Gets or sets the indicating whether the media should loop playback. /// - [BindableProperty(DefaultValue = MediaElementDefaults.ShouldLoopPlayback)] - public partial bool ShouldLoopPlayback { get; set; } + [BindableProperty] + public partial bool ShouldLoopPlayback { get; set; } = MediaElementDefaults.ShouldLoopPlayback; /// /// Gets or sets the indicating whether the screen should be kept on during media playback. /// - [BindableProperty(DefaultValue = MediaElementDefaults.ShouldKeepScreenOn)] - public partial bool ShouldKeepScreenOn { get; set; } + [BindableProperty] + public partial bool ShouldKeepScreenOn { get; set; } = MediaElementDefaults.ShouldKeepScreenOn; /// /// Gets or sets the indicating whether the media should be muted. /// - [BindableProperty(DefaultValue = MediaElementDefaults.ShouldMute)] - public partial bool ShouldMute { get; set; } + [BindableProperty] + public partial bool ShouldMute { get; set; } = MediaElementDefaults.ShouldMute; /// /// Gets or sets the indicating whether playback controls should be shown. /// - [BindableProperty(DefaultValue = MediaElementDefaults.ShouldShowPlaybackControls)] - public partial bool ShouldShowPlaybackControls { get; set; } + [BindableProperty] + public partial bool ShouldShowPlaybackControls { get; set; } = MediaElementDefaults.ShouldShowPlaybackControls; /// /// Gets or sets the of the media. @@ -174,38 +174,38 @@ internal event EventHandler StopRequested /// /// Gets or sets the of the media playback. /// - [BindableProperty(DefaultValue = MediaElementDefaults.Speed)] - public partial double Speed { get; set; } + [BindableProperty] + public partial double Speed { get; set; } = MediaElementDefaults.Speed; /// /// Gets or sets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.MetadataTitle)] - public partial string MetadataTitle { get; set; } + [BindableProperty] + public partial string MetadataTitle { get; set; } = MediaElementDefaults.MetadataTitle; /// /// Gets or sets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.MetadataArtist)] - public partial string MetadataArtist { get; set; } + [BindableProperty] + public partial string MetadataArtist { get; set; } = MediaElementDefaults.MetadataArtist; /// /// Gets or sets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.MetadataArtworkUrl)] - public partial string MetadataArtworkUrl { get; set; } + [BindableProperty] + public partial string MetadataArtworkUrl { get; set; } = MediaElementDefaults.MetadataArtworkUrl; /// /// Gets or sets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.Volume, DefaultBindingMode = BindingMode.TwoWay, PropertyChangingMethodName = nameof(OnVolumeChanging))] - public partial double Volume { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.TwoWay, PropertyChangingMethodName = nameof(OnVolumeChanging))] + public partial double Volume { get; set; } = MediaElementDefaults.Volume; /// /// Gets or sets the of the media. /// - [BindableProperty(DefaultValue = MediaElementDefaults.CurrentState, PropertyChangedMethodName = nameof(OnCurrentStatePropertyChanged))] - public partial MediaElementState CurrentState { get; private set; } + [BindableProperty(PropertyChangedMethodName = nameof(OnCurrentStatePropertyChanged))] + public partial MediaElementState CurrentState { get; private set; } = MediaElementDefaults.CurrentState; /// TaskCompletionSource IAsynchronousMediaElementHandler.SeekCompletedTCS => seekCompletedTaskCompletionSource; diff --git a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementDefaults.shared.cs b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementDefaults.shared.cs index bd32fd22ac..a1105823a8 100644 --- a/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementDefaults.shared.cs +++ b/src/CommunityToolkit.Maui.MediaElement/Primitives/MediaElementDefaults.shared.cs @@ -8,10 +8,6 @@ static class MediaElementDefaults public const int MediaWidth = 0; - public const string Position = "00:00:00"; - - public const string Duration = "00:00:00"; - public const bool ShouldAutoPlay = false; public const bool ShouldLoopPlayback = false; @@ -33,4 +29,8 @@ static class MediaElementDefaults public const string MetadataArtworkUrl = ""; public const MediaElementState CurrentState = MediaElementState.None; + + public static TimeSpan Position { get; } = TimeSpan.Zero; + + public static TimeSpan Duration { get; } = TimeSpan.Zero; } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/BaseBindablePropertyAttributeSourceGeneratorTest.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/BaseBindablePropertyAttributeSourceGeneratorTest.cs index 6faf8ec9c4..5a184f5f6c 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/BaseBindablePropertyAttributeSourceGeneratorTest.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/BaseBindablePropertyAttributeSourceGeneratorTest.cs @@ -23,7 +23,6 @@ sealed partial class BindablePropertyAttribute : global::System.Attribute { public string? PropertyName { get; } public global::System.Type? DeclaringType { get; set; } - public object? DefaultValue { get; set; } public global::Microsoft.Maui.Controls.BindingMode DefaultBindingMode { get; set; } public string ValidateValueMethodName { get; set; } = string.Empty; public string PropertyChangedMethodName { get; set; } = string.Empty; diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs index 593bcabf7f..6edfee7d07 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/CommonUsageTests.cs @@ -45,47 +45,6 @@ public partial class {{defaultTestClassName}} await VerifySourceGeneratorAsync(source, expectedGenerated); } - [Fact] - public async Task GenerateBindableProperty_WithDefaultValue_GeneratesCorrectCode() - { - const string source = - /* language=C#-test */ - //lang=csharp - $$""" - using CommunityToolkit.Maui; - using Microsoft.Maui.Controls; - - namespace {{defaultTestNamespace}}; - - public partial class {{defaultTestClassName}} : View - { - [BindablePropertyAttribute(DefaultValue = "Hello")] - public partial string Text { get; set; } - } - """; - - const string expectedGenerated = - /* language=C#-test */ - //lang=csharp - $$""" - // - // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator - #pragma warning disable - #nullable enable - namespace {{defaultTestNamespace}}; - public partial class {{defaultTestClassName}} - { - /// - /// Backing BindableProperty for the property. - /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty TextProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Text", typeof(string), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (string)"Hello", 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); - } - [Fact] public async Task GenerateBindableProperty_WithNewKeyword_GeneratesCorrectCode() { @@ -233,14 +192,13 @@ namespace {{defaultTestNamespace}}; public partial class {{defaultTestClassName}} : View { [BindablePropertyAttribute( - DefaultValue = 42, DefaultBindingMode = BindingMode.TwoWay, ValidateValueMethodName = "ValidateValue", PropertyChangedMethodName = "OnPropertyChanged", PropertyChangingMethodName = "OnPropertyChanging", CoerceValueMethodName = "CoerceValue", DefaultValueCreatorMethodName = "CreateDefaultValue")] - public partial int Value { get; set; } + public partial int Value { get; set; } = 42; static bool ValidateValue(BindableObject bindable, object value) => true; static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue) { } @@ -264,8 +222,8 @@ public partial class {{defaultTestClassName}} /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(int), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (int)42, (Microsoft.Maui.Controls.BindingMode)1, ValidateValue, OnPropertyChanged, OnPropertyChanging, CoerceValue, CreateDefaultValue); - public partial int Value { get => (int)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty ValueProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Value", typeof(int), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, (Microsoft.Maui.Controls.BindingMode)1, ValidateValue, OnPropertyChanged, OnPropertyChanging, CoerceValue, CreateDefaultValue); + public partial int Value { get => false ? field : (int)GetValue(ValueProperty); set => SetValue(ValueProperty, value); } } """; @@ -602,57 +560,6 @@ public partial class {{defaultTestClassName}} await VerifySourceGeneratorAsync(source, expectedGenerated); } - [Fact] - public async Task GenerateBindableProperty_WithTimeSpanStringDefaultValue_GeneratesCorrectCode() - { - const string source = - /* language=C#-test */ - //lang=csharp - $$""" - using CommunityToolkit.Maui; - using Microsoft.Maui.Controls; - using System; - - namespace {{defaultTestNamespace}}; - - public partial class {{defaultTestClassName}} : View - { - [BindablePropertyAttribute(DefaultValue = "00:00:00")] - public partial TimeSpan Position { get; set; } - - [BindablePropertyAttribute(DefaultValue = "00:01:30")] - public partial TimeSpan CustomDuration { get; set; } - } - """; - - const string expectedGenerated = - /* language=C#-test */ - //lang=csharp - $$""" - // - // See: CommunityToolkit.Maui.SourceGenerators.Internal.BindablePropertyAttributeSourceGenerator - #pragma warning disable - #nullable enable - namespace {{defaultTestNamespace}}; - public partial class {{defaultTestClassName}} - { - /// - /// Backing BindableProperty for the property. - /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty PositionProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Position", typeof(System.TimeSpan), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), global::System.TimeSpan.Zero, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial System.TimeSpan Position { get => (System.TimeSpan)GetValue(PositionProperty); set => SetValue(PositionProperty, value); } - - /// - /// Backing BindableProperty for the property. - /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty CustomDurationProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("CustomDuration", typeof(System.TimeSpan), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), new global::System.TimeSpan(900000000), Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial System.TimeSpan CustomDuration { get => (System.TimeSpan)GetValue(CustomDurationProperty); set => SetValue(CustomDurationProperty, value); } - } - """; - - await VerifySourceGeneratorAsync(source, expectedGenerated); - } - [Fact] public async Task GenerateBindableProperty_WithInitializer_GeneratesCorrectCode() diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs index 2fef4c8764..5883995f53 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyAttributeSourceGeneratorTests/EdgeCaseTests.cs @@ -68,8 +68,8 @@ namespace {{defaultTestNamespace}}; public partial class {{defaultTestClassName}} : View { - [BindableProperty(DefaultValue = Status.Approved)] - public partial Status InvoiceStatus { get; set; } + [BindableProperty] + public partial Status InvoiceStatus { get; set; } = Status.Approved; } public enum Status : byte @@ -94,8 +94,20 @@ public partial class {{defaultTestClassName}} /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (TestNamespace.Status)1, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial TestNamespace.Status InvoiceStatus { get => (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultInvoiceStatus); + public partial TestNamespace.Status InvoiceStatus { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingInvoiceStatus ? field : (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); } + } + + file static class __{{defaultTestClassName}}BindablePropertyInitHelpers + { + public static bool IsInitializingInvoiceStatus = false; + public static object CreateDefaultInvoiceStatus(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingInvoiceStatus = true; + var defaultValue = ((TestView)bindable).InvoiceStatus; + IsInitializingInvoiceStatus = false; + return defaultValue; + } } """; @@ -116,8 +128,8 @@ namespace {{defaultTestNamespace}}; public partial class {{defaultTestClassName}} : View { - [BindableProperty(DefaultValue = Status.Rejected)] - public partial Status InvoiceStatus { get; set; } + [BindableProperty] + public partial Status InvoiceStatus { get; set; } = Status.Rejected; } public enum Status : long @@ -142,8 +154,20 @@ public partial class {{defaultTestClassName}} /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (TestNamespace.Status)9223372036854775807, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial TestNamespace.Status InvoiceStatus { get => (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty InvoiceStatusProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("InvoiceStatus", typeof(TestNamespace.Status), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultInvoiceStatus); + public partial TestNamespace.Status InvoiceStatus { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingInvoiceStatus ? field : (TestNamespace.Status)GetValue(InvoiceStatusProperty); set => SetValue(InvoiceStatusProperty, value); } + } + + file static class __{{defaultTestClassName}}BindablePropertyInitHelpers + { + public static bool IsInitializingInvoiceStatus = false; + public static object CreateDefaultInvoiceStatus(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingInvoiceStatus = true; + var defaultValue = ((TestView)bindable).InvoiceStatus; + IsInitializingInvoiceStatus = false; + return defaultValue; + } } """; @@ -407,19 +431,32 @@ public async Task GenerateBindableProperty_WithComplexDefaultValues_GeneratesCor $$""" using CommunityToolkit.Maui; using Microsoft.Maui.Controls; + using System; namespace {{defaultTestNamespace}}; public partial class {{defaultTestClassName}} : View { - [BindableProperty(DefaultValue = true)] - public partial bool IsEnabled { get; set; } + [BindableProperty] + public partial bool IsEnabled { get; set; } = true; - [BindableProperty(DefaultValue = 3.14)] - public partial double Pi { get; set; } + [BindableProperty] + public partial double Pi { get; set; } = 3.14; - [BindableProperty(DefaultValue = 'A')] - public partial char Letter { get; set; } + [BindableProperty] + public partial char Letter { get; set; } = 'A'; + + [BindableProperty] + public partial TimeSpan TimeSpent { get; set; } = System.TimeSpan.Zero; + + [BindableProperty] + public partial double DoubleEpsilon { get; set; } = double.Epsilon; + + [BindableProperty] + public partial double SingleEpsilon { get; set; } = float.Epsilon; + + [BindableProperty] + public partial DateTimeOffset CurrentTime { get; set; } = DateTimeOffset.UtcNow; } """; @@ -437,20 +474,110 @@ public partial class {{defaultTestClassName}} /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty IsEnabledProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("IsEnabled", typeof(bool), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (bool)true, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial bool IsEnabled { get => (bool)GetValue(IsEnabledProperty); set => SetValue(IsEnabledProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty IsEnabledProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("IsEnabled", typeof(bool), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultIsEnabled); + public partial bool IsEnabled { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingIsEnabled ? field : (bool)GetValue(IsEnabledProperty); set => SetValue(IsEnabledProperty, value); } /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty PiProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Pi", typeof(double), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (double)3.14, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial double Pi { get => (double)GetValue(PiProperty); set => SetValue(PiProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty PiProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Pi", typeof(double), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultPi); + public partial double Pi { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingPi ? field : (double)GetValue(PiProperty); set => SetValue(PiProperty, value); } /// /// Backing BindableProperty for the property. /// - public static readonly global::Microsoft.Maui.Controls.BindableProperty LetterProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Letter", typeof(char), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), (char)'A', Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, null); - public partial char Letter { get => (char)GetValue(LetterProperty); set => SetValue(LetterProperty, value); } + public static readonly global::Microsoft.Maui.Controls.BindableProperty LetterProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("Letter", typeof(char), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultLetter); + public partial char Letter { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingLetter ? field : (char)GetValue(LetterProperty); set => SetValue(LetterProperty, value); } + + /// + /// Backing BindableProperty for the property. + /// + public static readonly global::Microsoft.Maui.Controls.BindableProperty TimeSpentProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("TimeSpent", typeof(System.TimeSpan), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultTimeSpent); + public partial System.TimeSpan TimeSpent { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingTimeSpent ? field : (System.TimeSpan)GetValue(TimeSpentProperty); set => SetValue(TimeSpentProperty, value); } + + /// + /// Backing BindableProperty for the property. + /// + public static readonly global::Microsoft.Maui.Controls.BindableProperty DoubleEpsilonProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("DoubleEpsilon", typeof(double), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultDoubleEpsilon); + public partial double DoubleEpsilon { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingDoubleEpsilon ? field : (double)GetValue(DoubleEpsilonProperty); set => SetValue(DoubleEpsilonProperty, value); } + + /// + /// Backing BindableProperty for the property. + /// + public static readonly global::Microsoft.Maui.Controls.BindableProperty SingleEpsilonProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("SingleEpsilon", typeof(double), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultSingleEpsilon); + public partial double SingleEpsilon { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingSingleEpsilon ? field : (double)GetValue(SingleEpsilonProperty); set => SetValue(SingleEpsilonProperty, value); } + + /// + /// Backing BindableProperty for the property. + /// + public static readonly global::Microsoft.Maui.Controls.BindableProperty CurrentTimeProperty = global::Microsoft.Maui.Controls.BindableProperty.Create("CurrentTime", typeof(System.DateTimeOffset), typeof({{defaultTestNamespace}}.{{defaultTestClassName}}), null, Microsoft.Maui.Controls.BindingMode.OneWay, null, null, null, null, __{{defaultTestClassName}}BindablePropertyInitHelpers.CreateDefaultCurrentTime); + public partial System.DateTimeOffset CurrentTime { get => __{{defaultTestClassName}}BindablePropertyInitHelpers.IsInitializingCurrentTime ? field : (System.DateTimeOffset)GetValue(CurrentTimeProperty); set => SetValue(CurrentTimeProperty, value); } + } + + file static class __{{defaultTestClassName}}BindablePropertyInitHelpers + { + public static bool IsInitializingIsEnabled = false; + public static object CreateDefaultIsEnabled(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingIsEnabled = true; + var defaultValue = ((TestView)bindable).IsEnabled; + IsInitializingIsEnabled = false; + return defaultValue; + } + + public static bool IsInitializingPi = false; + public static object CreateDefaultPi(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingPi = true; + var defaultValue = ((TestView)bindable).Pi; + IsInitializingPi = false; + return defaultValue; + } + + public static bool IsInitializingLetter = false; + public static object CreateDefaultLetter(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingLetter = true; + var defaultValue = ((TestView)bindable).Letter; + IsInitializingLetter = false; + return defaultValue; + } + + public static bool IsInitializingTimeSpent = false; + public static object CreateDefaultTimeSpent(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingTimeSpent = true; + var defaultValue = ((TestView)bindable).TimeSpent; + IsInitializingTimeSpent = false; + return defaultValue; + } + + public static bool IsInitializingDoubleEpsilon = false; + public static object CreateDefaultDoubleEpsilon(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingDoubleEpsilon = true; + var defaultValue = ((TestView)bindable).DoubleEpsilon; + IsInitializingDoubleEpsilon = false; + return defaultValue; + } + + public static bool IsInitializingSingleEpsilon = false; + public static object CreateDefaultSingleEpsilon(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingSingleEpsilon = true; + var defaultValue = ((TestView)bindable).SingleEpsilon; + IsInitializingSingleEpsilon = false; + return defaultValue; + } + + public static bool IsInitializingCurrentTime = false; + public static object CreateDefaultCurrentTime(global::Microsoft.Maui.Controls.BindableObject bindable) + { + IsInitializingCurrentTime = true; + var defaultValue = ((TestView)bindable).CurrentTime; + IsInitializingCurrentTime = false; + return defaultValue; + } } """; diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs index f0df70603e..c1df3de90c 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal.UnitTests/BindablePropertyModelTests.cs @@ -20,7 +20,6 @@ public void BindablePropertyName_ReturnsCorrectPropertyName() "TestProperty", propertySymbol.Type, typeSymbol, - "null", "Microsoft.Maui.Controls.BindingMode.OneWay", "null", "null", @@ -49,7 +48,6 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues() var propertySymbol = typeSymbol.GetMembers("TestProperty").OfType().First(); const string propertyName = "TestProperty"; - const string defaultValue = "\"Hello\""; const string defaultBindingMode = "Microsoft.Maui.Controls.BindingMode.TwoWay"; const string validateValueMethodName = "ValidateValue"; const string propertyChangedMethodName = "OnPropertyChanged"; @@ -64,7 +62,6 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues() propertyName, propertySymbol.Type, typeSymbol, - defaultValue, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, @@ -81,7 +78,6 @@ public void BindablePropertyModel_WithAllParameters_StoresCorrectValues() Assert.Equal(propertyName, model.PropertyName); Assert.Equal(propertySymbol.Type, model.ReturnType); Assert.Equal(typeSymbol, model.DeclaringType); - Assert.Equal(defaultValue, model.DefaultValue); Assert.Equal(defaultBindingMode, model.DefaultBindingMode); Assert.Equal(validateValueMethodName, model.ValidateValueMethodName); Assert.Equal(propertyChangedMethodName, model.PropertyChangedMethodName); @@ -125,7 +121,6 @@ public void SemanticValues_WithClassInfoAndProperties_StoresCorrectValues() "TestProperty", propertySymbol.Type, typeSymbol, - "null", "Microsoft.Maui.Controls.BindingMode.OneWay", "null", "null", @@ -182,7 +177,6 @@ public class TestClass { public string TestProperty { get; set; } = "Initial Val propertyName, propertySymbol.Type, typeSymbol, - "null", "Microsoft.Maui.Controls.BindingMode.OneWay", "null", "null", @@ -220,7 +214,6 @@ public class TestClass { public string TestProperty { get; set; } = "Initial Val propertyName, propertySymbol.Type, typeSymbol, - "null", "Microsoft.Maui.Controls.BindingMode.OneWay", "null", "null", diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs index b38a572090..85fc81820d 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Generators/BindablePropertyAttributeSourceGenerator.cs @@ -38,7 +38,6 @@ sealed partial class BindablePropertyAttribute : global::System.Attribute { public string? PropertyName { get; } public global::System.Type? DeclaringType { get; set; } - public object? DefaultValue { get; set; } public global::Microsoft.Maui.Controls.BindingMode DefaultBindingMode { get; set; } public string ValidateValueMethodName { get; set; } = string.Empty; public string PropertyChangedMethodName { get; set; } = string.Empty; @@ -182,7 +181,7 @@ static string GenerateSource(SemanticValues value) var fullDeclaringType = string.IsNullOrEmpty(value.ClassInformation.ContainingTypes) ? classNameWithGenerics : string.Concat(value.ClassInformation.ContainingTypes, ".", classNameWithGenerics); - + var fileStaticClassName = $"__{classNameWithGenerics}BindablePropertyInitHelpers"; foreach (var info in value.BindableProperties) @@ -258,9 +257,7 @@ static void GenerateReadOnlyBindableProperty(StringBuilder sb, in BindableProper .Append(GetFormattedReturnType(nonNullableReturnType)) .Append("), typeof(") .Append(info.DeclaringType) - .Append("), ") - .Append(info.DefaultValue) - .Append(", ") + .Append("), null, ") .Append(info.DefaultBindingMode) .Append(", ") .Append(info.ValidateValueMethodName) @@ -321,9 +318,7 @@ static void GenerateBindableProperty(StringBuilder sb, in BindablePropertyModel .Append(GetFormattedReturnType(nonNullableReturnType)) .Append("), typeof(") .Append(info.DeclaringType) - .Append("), ") - .Append(info.DefaultValue) - .Append(", ") + .Append("), null, ") .Append(info.DefaultBindingMode) .Append(", ") .Append(info.ValidateValueMethodName) @@ -529,7 +524,6 @@ static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attrib throw new ArgumentException($"{nameof(attributeData)}.{nameof(attributeData.AttributeClass)} Cannot Be Null", nameof(attributeData)); } - var defaultValue = attributeData.GetNamedTypeArgumentsAttributeValueByNameAsCastedString(nameof(BindablePropertyModel.DefaultValue), returnType); var coerceValueMethodName = attributeData.GetNamedMethodGroupArgumentsAttributeValueByNameAsString(nameof(BindablePropertyModel.CoerceValueMethodName)); var defaultBindingMode = attributeData.GetNamedTypeArgumentsAttributeValueForDefaultBindingMode(nameof(BindablePropertyModel.DefaultBindingMode), "Microsoft.Maui.Controls.BindingMode.OneWay"); var defaultValueCreatorMethodName = attributeData.GetNamedMethodGroupArgumentsAttributeValueByNameAsString(nameof(BindablePropertyModel.DefaultValueCreatorMethodName)); @@ -538,7 +532,7 @@ static BindablePropertyModel CreateBindablePropertyModel(in AttributeData attrib var validateValueMethodName = attributeData.GetNamedMethodGroupArgumentsAttributeValueByNameAsString(nameof(BindablePropertyModel.ValidateValueMethodName)); var newKeywordText = doesContainNewKeyword ? "new " : string.Empty; - return new BindablePropertyModel(propertyName, returnType, declaringType, defaultValue, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility, hasInitializer); + return new BindablePropertyModel(propertyName, returnType, declaringType, defaultBindingMode, validateValueMethodName, propertyChangedMethodName, propertyChangingMethodName, coerceValueMethodName, defaultValueCreatorMethodName, newKeywordText, isReadOnly, setterAccessibility, hasInitializer); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Helpers/AttributeExtensions.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Helpers/AttributeExtensions.cs index a3dd55c558..524aba5199 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Helpers/AttributeExtensions.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Helpers/AttributeExtensions.cs @@ -18,63 +18,10 @@ public static string GetNamedTypeArgumentsAttributeValueForDefaultBindingMode(th return data.Value is null ? placeholder : $"({data.Type}){data.Value}"; } - public static string GetNamedTypeArgumentsAttributeValueByNameAsCastedString(this AttributeData attribute, string name, ITypeSymbol propertyType, string placeholder = "null") - { - var data = attribute.NamedArguments.SingleOrDefault(kvp => kvp.Key == name).Value; - - // true.ToString() => "True" and false.ToString() => "False", but we want "true" and "false" - if (data.Kind is TypedConstantKind.Primitive && data.Type?.SpecialType is SpecialType.System_Boolean) - { - return data.Value is null ? placeholder : $"({data.Type}){data.Value.ToString().ToLowerInvariant()}"; - } - - if (data.Kind is TypedConstantKind.Enum && data.Type is not null && data.Value is not null) - { - return $"({data.Type}){data.Value}"; - } - - if (data.Type?.SpecialType is SpecialType.System_String) - { - // Special handling for TimeSpan string representations - only when property type is TimeSpan - if (data.Value is string stringValue && IsTimeSpanType(propertyType) && TimeSpan.TryParse(stringValue, CultureInfo.InvariantCulture, out var timeSpanValue)) - { - // Check if it's TimeSpan.Zero - if (timeSpanValue == TimeSpan.Zero) - { - return "global::System.TimeSpan.Zero"; - } - - // For other TimeSpan values, use the ticks constructor - return $"new global::System.TimeSpan({timeSpanValue.Ticks})"; - } - - return data.Value is null ? $"\"{placeholder}\"" : $"({data.Type})\"{data.Value}\""; - } - - if (data.Type?.SpecialType is SpecialType.System_Char) - { - return data.Value is null ? $"\"{placeholder}\"" : $"({data.Type})\'{data.Value}\'"; - } - - return data.Value is null ? placeholder : $"({data.Type}){data.Value}"; - } - public static string GetNamedMethodGroupArgumentsAttributeValueByNameAsString(this AttributeData attribute, string name, string placeholder = "null") { var data = attribute.NamedArguments.SingleOrDefault(kvp => kvp.Key == name).Value; return data.Value is null ? placeholder : data.Value.ToString(); } - - static bool IsTimeSpanType(ITypeSymbol typeSymbol) - { - if (typeSymbol is null) - { - return false; - } - - // Check if it's System.TimeSpan by comparing name - return typeSymbol is { Name: "TimeSpan", ContainingNamespace: not null } - && typeSymbol.ContainingNamespace.ToDisplayString() == "System"; - } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs index ddc946229b..48aca49402 100644 --- a/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs +++ b/src/CommunityToolkit.Maui.SourceGenerators.Internal/Models/Records.cs @@ -3,7 +3,7 @@ namespace CommunityToolkit.Maui.SourceGenerators.Internal.Models; -public record BindablePropertyModel(string PropertyName, ITypeSymbol ReturnType, ITypeSymbol DeclaringType, string DefaultValue, string DefaultBindingMode, string ValidateValueMethodName, string PropertyChangedMethodName, string PropertyChangingMethodName, string CoerceValueMethodName, string DefaultValueCreatorMethodName, string NewKeywordText, bool IsReadOnlyBindableProperty, string? SetterAccessibility, bool HasInitializer) +public record BindablePropertyModel(string PropertyName, ITypeSymbol ReturnType, ITypeSymbol DeclaringType, string DefaultBindingMode, string ValidateValueMethodName, string PropertyChangedMethodName, string PropertyChangingMethodName, string CoerceValueMethodName, string DefaultValueCreatorMethodName, string NewKeywordText, bool IsReadOnlyBindableProperty, string? SetterAccessibility, bool HasInitializer) { // When both a DefaultValueCreatorMethodName and an initializer are provided, we implement the DefaultValueCreator method and the ignore the partial Property initializer public bool ShouldUsePropertyInitializer => HasInitializer && DefaultValueCreatorMethodName is "null"; diff --git a/src/CommunityToolkit.Maui.UnitTests/Behaviors/ImageTouchBehaviorTests.cs b/src/CommunityToolkit.Maui.UnitTests/Behaviors/ImageTouchBehaviorTests.cs index 9a761b5ff4..d36c5baab6 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Behaviors/ImageTouchBehaviorTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Behaviors/ImageTouchBehaviorTests.cs @@ -24,13 +24,13 @@ protected override void Dispose(bool isDisposing) [Fact] public void VerifyDefaults() { - Assert.Equal(ImageTouchBehaviorDefaults.DefaultBackgroundImageSource, imageTouchBehavior.DefaultImageSource); - Assert.Equal(ImageTouchBehaviorDefaults.HoveredBackgroundImageSource, imageTouchBehavior.HoveredImageSource); - Assert.Equal(ImageTouchBehaviorDefaults.PressedBackgroundImageSource, imageTouchBehavior.PressedImageSource); + Assert.Null(imageTouchBehavior.DefaultImageSource); + Assert.Null(imageTouchBehavior.HoveredImageSource); + Assert.Null(imageTouchBehavior.PressedImageSource); - Assert.Equal(ImageTouchBehaviorDefaults.DefaultBackgroundImageAspect, imageTouchBehavior.DefaultImageAspect); - Assert.Equal(ImageTouchBehaviorDefaults.HoveredBackgroundImageAspect, imageTouchBehavior.HoveredImageAspect); - Assert.Equal(ImageTouchBehaviorDefaults.PressedBackgroundImageAspect, imageTouchBehavior.PressedImageAspect); + Assert.Null(imageTouchBehavior.DefaultImageAspect); + Assert.Null(imageTouchBehavior.HoveredImageAspect); + Assert.Null(imageTouchBehavior.PressedImageAspect); Assert.Equal(ImageTouchBehaviorDefaults.ShouldSetImageOnAnimationEnd, imageTouchBehavior.ShouldSetImageOnAnimationEnd); } @@ -75,8 +75,6 @@ public async Task VerifyPressedBackgroundImageChange() imageTouchBehavior.DefaultImageSource = normalImageSource; imageTouchBehavior.PressedImageSource = pressedImageSource; - Assert.Null(view.Source); - await imageTouchBehavior.ForceUpdateState(TestContext.Current.CancellationToken, false); Assert.Equal(normalImageSource, view.Source); Assert.Same(normalImageSource, view.Source); @@ -179,6 +177,54 @@ public void SetShouldSetImageOnAnimationEndTest() Assert.Equal(shouldSetImageOnAnimationEnd, viewModel.ShouldSetImageOnAnimationEnd); } + [Fact] + public void VerifyImageSourceStateMachineWhenImageSourceSetToNullWhilstActive() + { + var image = new Image(); + AttachTouchBehaviorToVisualElement(image); + + // Verify Default Source appears when Hover Active but not set + imageTouchBehavior.HandleHover(HoverStatus.Entered); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.DefaultImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png")); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.DefaultImageSource = null; + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + // Verify Pressed Source appears when Hover + Press simultaneously active + imageTouchBehavior.HandleTouch(TouchStatus.Started); + imageTouchBehavior.HandleHover(HoverStatus.Entered); + Assert.Equal(imageTouchBehavior.PressedImageSource, image.Source); + + imageTouchBehavior.PressedImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/2x/googlelogo_light_color_272x92dp.png")); + Assert.Equal(imageTouchBehavior.PressedImageSource, image.Source); + + imageTouchBehavior.PressedImageSource = null; + Assert.Equal(imageTouchBehavior.PressedImageSource, image.Source); + + // Verify Hovered Source appears when Hover active + imageTouchBehavior.HandleTouch(TouchStatus.Completed); + Assert.Equal(imageTouchBehavior.HoveredImageSource, image.Source); + + imageTouchBehavior.HoveredImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/1x/googlelogo_dark_color_272x92dp.png")); + Assert.Equal(imageTouchBehavior.HoveredImageSource, image.Source); + + imageTouchBehavior.HoveredImageSource = null; + Assert.Equal(imageTouchBehavior.HoveredImageSource, image.Source); + + // Verify Default Source appears when neither active + imageTouchBehavior.HandleHover(HoverStatus.Exited); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.DefaultImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png")); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.DefaultImageSource = null; + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + } + [Fact] public void VerifyImageSourceStateMachine() { @@ -199,7 +245,30 @@ public void VerifyImageSourceStateMachine() imageTouchBehavior.HandleHover(HoverStatus.Entered); Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + imageTouchBehavior.HoveredImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/1x/googlelogo_light_color_272x92dp.png")); + + // Verify Pressed Source appears when Hover + Press simultaneously active + imageTouchBehavior.HandleTouch(TouchStatus.Started); + imageTouchBehavior.HandleHover(HoverStatus.Entered); + Assert.Equal(imageTouchBehavior.PressedImageSource, image.Source); + + // Verify Hovered Source appears when Hover active + imageTouchBehavior.HandleTouch(TouchStatus.Completed); + Assert.Equal(imageTouchBehavior.HoveredImageSource, image.Source); + + // Verify Default Source appears when neither active + imageTouchBehavior.HandleHover(HoverStatus.Exited); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.DefaultImageSource = null; imageTouchBehavior.HoveredImageSource = null; + imageTouchBehavior.PressedImageSource = null; + + // Verify Default Source appears when Hover Active but not set + imageTouchBehavior.HandleHover(HoverStatus.Entered); + Assert.Equal(imageTouchBehavior.DefaultImageSource, image.Source); + + imageTouchBehavior.HoveredImageSource = ImageSource.FromUri(new Uri("https://www.google.com/images/branding/googlelogo/2x/googlelogo_dark_color_272x92dp.png")); // Verify Pressed Source appears when Hover + Press simultaneously active imageTouchBehavior.HandleTouch(TouchStatus.Started); diff --git a/src/CommunityToolkit.Maui.UnitTests/Behaviors/MaxLengthReachedBehaviorTests.cs b/src/CommunityToolkit.Maui.UnitTests/Behaviors/MaxLengthReachedBehaviorTests.cs index 2f42c2ae91..e82e0d4904 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Behaviors/MaxLengthReachedBehaviorTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Behaviors/MaxLengthReachedBehaviorTests.cs @@ -1,5 +1,6 @@ using System.Windows.Input; using CommunityToolkit.Maui.Behaviors; +using CommunityToolkit.Maui.Core; using Xunit; namespace CommunityToolkit.Maui.UnitTests.Behaviors; @@ -174,4 +175,15 @@ static Entry CreateEntry(int? maxLength = 2, return entry; } + + [Fact] + public void VerifyDefaults() + { + // Arrange + var behavior = new MaxLengthReachedBehavior(); + + // Act Assert + Assert.Equal(MaxLengthReachedBehaviorDefaults.Command, behavior.Command); + Assert.Equal(MaxLengthReachedBehaviorDefaults.ShouldDismissKeyboardAutomatically, behavior.ShouldDismissKeyboardAutomatically); + } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui.UnitTests/Behaviors/ProgressBarAnimationBehaviorTests.cs b/src/CommunityToolkit.Maui.UnitTests/Behaviors/ProgressBarAnimationBehaviorTests.cs index 7db7e75ab3..7eb4034d54 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Behaviors/ProgressBarAnimationBehaviorTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Behaviors/ProgressBarAnimationBehaviorTests.cs @@ -31,8 +31,7 @@ public async Task ValidPropertiesTests(double progress, uint length, Easing easi progressBar.Behaviors.Add(progressBarAnimationBehavior); Assert.Equal(0.0d, progressBar.Progress); - Assert.Equal(0.0d, ProgressBarAnimationBehavior.ProgressProperty.DefaultValue); - Assert.Equal((uint)500, ProgressBarAnimationBehavior.LengthProperty.DefaultValue); + Assert.Equal((uint)500, progressBarAnimationBehavior.Length); Assert.Equal(Easing.Linear, progressBarAnimationBehavior.Easing); progressBarAnimationBehavior.Length = length; diff --git a/src/CommunityToolkit.Maui.UnitTests/Behaviors/StatusBarBehaviorTests.cs b/src/CommunityToolkit.Maui.UnitTests/Behaviors/StatusBarBehaviorTests.cs index 602db8a0f3..22ef8991cc 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Behaviors/StatusBarBehaviorTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Behaviors/StatusBarBehaviorTests.cs @@ -12,8 +12,9 @@ public void VerifyDefaults() { var statusBarBehavior = new StatusBarBehavior(); - Assert.Equal(Colors.Transparent, statusBarBehavior.StatusBarColor); - Assert.Equal(StatusBarStyle.Default, statusBarBehavior.StatusBarStyle); + Assert.Equal(StatusBarBehaviorDefaults.StatusBarColor, statusBarBehavior.StatusBarColor); + Assert.Equal(StatusBarBehaviorDefaults.StatusBarStyle, statusBarBehavior.StatusBarStyle); + Assert.Equal(StatusBarBehaviorDefaults.ApplyOn, statusBarBehavior.ApplyOn); } [Fact] diff --git a/src/CommunityToolkit.Maui.UnitTests/Behaviors/TouchBehaviorTests.cs b/src/CommunityToolkit.Maui.UnitTests/Behaviors/TouchBehaviorTests.cs index 8a3af263e3..25ded1f20b 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Behaviors/TouchBehaviorTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Behaviors/TouchBehaviorTests.cs @@ -34,41 +34,41 @@ public void VerifyAttachToViewSucceeds() [Fact] public void VerifyDefaults() { - Assert.Equal(TouchBehaviorDefaults.HoveredOpacity, touchBehavior.HoveredOpacity); - Assert.Equal(TouchBehaviorDefaults.PressedOpacity, touchBehavior.PressedOpacity); - Assert.Equal(TouchBehaviorDefaults.DefaultOpacity, touchBehavior.DefaultOpacity); + Assert.Null(touchBehavior.HoveredOpacity); + Assert.Null(touchBehavior.PressedOpacity); + Assert.Null(touchBehavior.DefaultOpacity); - Assert.Equal(TouchBehaviorDefaults.HoveredScale, touchBehavior.HoveredScale); - Assert.Equal(TouchBehaviorDefaults.PressedScale, touchBehavior.PressedScale); - Assert.Equal(TouchBehaviorDefaults.DefaultScale, touchBehavior.DefaultScale); + Assert.Null(touchBehavior.HoveredScale); + Assert.Null(touchBehavior.PressedScale); + Assert.Null(touchBehavior.DefaultScale); - Assert.Equal(TouchBehaviorDefaults.HoveredTranslationX, touchBehavior.HoveredTranslationX); - Assert.Equal(TouchBehaviorDefaults.PressedTranslationX, touchBehavior.PressedTranslationX); - Assert.Equal(TouchBehaviorDefaults.DefaultTranslationX, touchBehavior.DefaultTranslationX); + Assert.Null(touchBehavior.HoveredTranslationX); + Assert.Null(touchBehavior.PressedTranslationX); + Assert.Null(touchBehavior.DefaultTranslationX); - Assert.Equal(TouchBehaviorDefaults.HoveredTranslationY, touchBehavior.HoveredTranslationY); - Assert.Equal(TouchBehaviorDefaults.PressedTranslationY, touchBehavior.PressedTranslationY); - Assert.Equal(TouchBehaviorDefaults.DefaultTranslationY, touchBehavior.DefaultTranslationY); + Assert.Null(touchBehavior.HoveredTranslationY); + Assert.Null(touchBehavior.PressedTranslationY); + Assert.Null(touchBehavior.DefaultTranslationY); - Assert.Equal(TouchBehaviorDefaults.HoveredRotation, touchBehavior.HoveredRotation); - Assert.Equal(TouchBehaviorDefaults.PressedRotation, touchBehavior.PressedRotation); - Assert.Equal(TouchBehaviorDefaults.DefaultRotation, touchBehavior.DefaultRotation); + Assert.Null(touchBehavior.HoveredRotation); + Assert.Null(touchBehavior.PressedRotation); + Assert.Null(touchBehavior.DefaultRotation); - Assert.Equal(TouchBehaviorDefaults.HoveredRotationX, touchBehavior.HoveredRotationX); - Assert.Equal(TouchBehaviorDefaults.PressedRotationX, touchBehavior.PressedRotationX); - Assert.Equal(TouchBehaviorDefaults.DefaultRotationX, touchBehavior.DefaultRotationX); + Assert.Null(touchBehavior.HoveredRotationX); + Assert.Null(touchBehavior.PressedRotationX); + Assert.Null(touchBehavior.DefaultRotationX); - Assert.Equal(TouchBehaviorDefaults.HoveredRotationY, touchBehavior.HoveredRotationY); - Assert.Equal(TouchBehaviorDefaults.PressedRotationY, touchBehavior.PressedRotationY); - Assert.Equal(TouchBehaviorDefaults.DefaultRotationY, touchBehavior.DefaultRotationY); + Assert.Null(touchBehavior.HoveredRotationY); + Assert.Null(touchBehavior.PressedRotationY); + Assert.Null(touchBehavior.DefaultRotationY); - Assert.Equal(TouchBehaviorDefaults.DefaultAnimationDuration, touchBehavior.DefaultAnimationDuration); - Assert.Equal(TouchBehaviorDefaults.HoveredAnimationDuration, touchBehavior.HoveredAnimationDuration); - Assert.Equal(TouchBehaviorDefaults.PressedAnimationDuration, touchBehavior.PressedAnimationDuration); + Assert.Null(touchBehavior.DefaultAnimationDuration); + Assert.Null(touchBehavior.HoveredAnimationDuration); + Assert.Null(touchBehavior.PressedAnimationDuration); - Assert.Equal(TouchBehaviorDefaults.DefaultAnimationEasing, touchBehavior.DefaultAnimationEasing); - Assert.Equal(TouchBehaviorDefaults.HoveredAnimationEasing, touchBehavior.HoveredAnimationEasing); - Assert.Equal(TouchBehaviorDefaults.PressedAnimationEasing, touchBehavior.PressedAnimationEasing); + Assert.Null(touchBehavior.DefaultAnimationEasing); + Assert.Null(touchBehavior.HoveredAnimationEasing); + Assert.Null(touchBehavior.PressedAnimationEasing); Assert.False(touchBehavior.CanExecute); @@ -93,9 +93,9 @@ public void VerifyDefaults() Assert.Equal(TouchBehaviorDefaults.CurrentInteractionStatus, touchBehavior.CurrentInteractionStatus); - Assert.Equal(TouchBehaviorDefaults.DefaultBackgroundColor, touchBehavior.DefaultBackgroundColor); - Assert.Equal(TouchBehaviorDefaults.HoveredBackgroundColor, touchBehavior.HoveredBackgroundColor); - Assert.Equal(TouchBehaviorDefaults.PressedBackgroundColor, touchBehavior.PressedBackgroundColor); + Assert.Null(touchBehavior.DefaultBackgroundColor); + Assert.Null(touchBehavior.HoveredBackgroundColor); + Assert.Null(touchBehavior.PressedBackgroundColor); } [Fact] @@ -106,8 +106,8 @@ public async Task VerifyHoverOpacityChange() var view = new View(); AttachTouchBehaviorToVisualElement(view); - Assert.Equal(TouchBehaviorDefaults.DefaultOpacity, touchBehavior.DefaultOpacity); - Assert.Equal(TouchBehaviorDefaults.HoveredOpacity, touchBehavior.HoveredOpacity); + Assert.Null(touchBehavior.DefaultOpacity); + Assert.Null(touchBehavior.HoveredOpacity); touchBehavior.DefaultOpacity = updatedDefaultOpacity; touchBehavior.HoveredOpacity = updatedHoveredOpacity; @@ -148,8 +148,8 @@ public async Task VerifyPressedOpacityChange() var view = new View(); AttachTouchBehaviorToVisualElement(view); - Assert.Equal(TouchBehaviorDefaults.DefaultOpacity, touchBehavior.DefaultOpacity); - Assert.Equal(TouchBehaviorDefaults.PressedOpacity, touchBehavior.HoveredOpacity); + Assert.Null(touchBehavior.DefaultOpacity); + Assert.Null(touchBehavior.HoveredOpacity); touchBehavior.DefaultOpacity = updatedDefaultOpacity; touchBehavior.PressedOpacity = updatedHoveredOpacity; @@ -270,6 +270,7 @@ public async Task VerifyPressedScaleChange() touchBehavior.HandleTouch(TouchStatus.Canceled); Assert.Equal(updatedDefaultScale, view.Scale); } + [Fact] public async Task VerifyHoverRotationChange() { diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/MediaElement/MediaElementTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/MediaElement/MediaElementTests.cs index 284dcc637f..93b6f2e1ff 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Views/MediaElement/MediaElementTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Views/MediaElement/MediaElementTests.cs @@ -24,11 +24,11 @@ public void VerifyDefaults() Assert.Equal(MediaElementDefaults.MediaHeight, mediaElement.MediaHeight); Assert.Equal(MediaElementDefaults.Aspect, mediaElement.Aspect); Assert.Equal(MediaElementDefaults.CurrentState, mediaElement.CurrentState); - Assert.Equal(TimeSpan.Parse(MediaElementDefaults.Duration), mediaElement.Duration); + Assert.Equal(MediaElementDefaults.Duration, mediaElement.Duration); Assert.Equal(MediaElementDefaults.MediaWidth, mediaElement.MediaWidth); Assert.Equal(MediaElementDefaults.MetadataArtist, mediaElement.MetadataArtist); Assert.Equal(MediaElementDefaults.MetadataArtworkUrl, mediaElement.MetadataArtworkUrl); - Assert.Equal(TimeSpan.Parse(MediaElementDefaults.Position), mediaElement.Position); + Assert.Equal(MediaElementDefaults.Position, mediaElement.Position); Assert.Equal(MediaElementDefaults.ShouldAutoPlay, mediaElement.ShouldAutoPlay); Assert.Equal(MediaElementDefaults.ShouldKeepScreenOn, mediaElement.ShouldKeepScreenOn); Assert.Equal(MediaElementDefaults.ShouldLoopPlayback, mediaElement.ShouldLoopPlayback); diff --git a/src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs index de62dff003..cf436199da 100644 --- a/src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/AnimationBehavior.shared.cs @@ -31,7 +31,7 @@ public partial class AnimationBehavior : EventToCommandBehavior /// /// has a of Command<CancellationToken> which requires a as a CommandParameter. See and for more information on passing a into as a CommandParameter" /// - [BindableProperty(DefaultValue = null, DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangingMethodName = nameof(OnAnimateCommandChanging), DefaultValueCreatorMethodName = nameof(CreateAnimateCommand))] + [BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangingMethodName = nameof(OnAnimateCommandChanging), DefaultValueCreatorMethodName = nameof(CreateAnimateCommand))] public partial Command AnimateCommand { get; diff --git a/src/CommunityToolkit.Maui/Behaviors/MaxLengthReachedBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/MaxLengthReachedBehavior.shared.cs index c2465a28f0..655283ad0c 100644 --- a/src/CommunityToolkit.Maui/Behaviors/MaxLengthReachedBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/MaxLengthReachedBehavior.shared.cs @@ -1,5 +1,6 @@ using System.ComponentModel; using System.Windows.Input; +using CommunityToolkit.Maui.Core; namespace CommunityToolkit.Maui.Behaviors; @@ -14,13 +15,13 @@ public partial class MaxLengthReachedBehavior : BaseBehavior /// Command that is triggered when the value configured in is reached. Both the event and this command are triggered. This is a bindable property. /// [BindableProperty] - public partial ICommand? Command { get; set; } + public partial ICommand? Command { get; set; } = MaxLengthReachedBehaviorDefaults.Command; /// /// Indicates whether the keyboard should be dismissed automatically after the maximum length is reached. This is a bindable property. /// - [BindableProperty(DefaultValue = false)] - public partial bool ShouldDismissKeyboardAutomatically { get; set; } + [BindableProperty] + public partial bool ShouldDismissKeyboardAutomatically { get; set; } = MaxLengthReachedBehaviorDefaults.ShouldDismissKeyboardAutomatically; /// /// Event that is triggered when the value configured in is reached. Both the and this event are triggered. This is a bindable property. diff --git a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs index c41225af79..fbbf37ed52 100644 --- a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/ImageTouch/ImageTouchBehavior.shared.cs @@ -1,4 +1,3 @@ -using System.ComponentModel; using CommunityToolkit.Maui.Core; namespace CommunityToolkit.Maui.Behaviors; @@ -11,42 +10,100 @@ public partial class ImageTouchBehavior : TouchBehavior /// /// Gets or sets the when is . /// - [BindableProperty] - public partial ImageSource? DefaultImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.DefaultBackgroundImageSource; + [BindableProperty(PropertyChangedMethodName = nameof(HandleDefaultImageSourceChanged))] + public partial ImageSource? DefaultImageSource { get; set; } /// /// Gets or sets the when the is /// - [BindableProperty] - public partial ImageSource? HoveredImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.HoveredBackgroundImageSource; + [BindableProperty(PropertyChangedMethodName = nameof(HandleHoveredImageSourceChanged))] + public partial ImageSource? HoveredImageSource { get; set; } /// /// Gets or sets the when the is /// - [BindableProperty] - public partial ImageSource? PressedImageSource { get; set; } = (ImageSource?)ImageTouchBehaviorDefaults.PressedBackgroundImageSource; + [BindableProperty(PropertyChangedMethodName = nameof(HandlePressedImageSourceChanged))] + public partial ImageSource? PressedImageSource { get; set; } /// /// Gets or sets the when is . /// [BindableProperty] - public partial Aspect DefaultImageAspect { get; set; } = ImageTouchBehaviorDefaults.DefaultBackgroundImageAspect; + public partial Aspect? DefaultImageAspect { get; set; } /// /// Gets or sets the when is . /// [BindableProperty] - public partial Aspect HoveredImageAspect { get; set; } = ImageTouchBehaviorDefaults.HoveredBackgroundImageAspect; + public partial Aspect? HoveredImageAspect { get; set; } /// /// Gets or sets the when the is /// [BindableProperty] - public partial Aspect PressedImageAspect { get; set; } = ImageTouchBehaviorDefaults.PressedBackgroundImageAspect; + public partial Aspect? PressedImageAspect { get; set; } /// /// Gets or sets a value indicating whether the image should be set when the animation ends. /// [BindableProperty] public partial bool ShouldSetImageOnAnimationEnd { get; set; } = ImageTouchBehaviorDefaults.ShouldSetImageOnAnimationEnd; + + static void HandleDefaultImageSourceChanged(BindableObject bindable, object? oldValue, object? newValue) + { + var imageTouchBehavior = (ImageTouchBehavior)bindable; + var updatedImageSource = (ImageSource?)newValue; + + if (!GestureManager.TryGetBindableImageTouchBehaviorElement(imageTouchBehavior, out var imageElement)) + { + return; + } + + // GestureManager does not automatically toggle ImageElement.SourceProperty when currently being displayed + switch (imageTouchBehavior.CurrentTouchState, imageTouchBehavior.CurrentHoverState) + { + case (TouchState.Default, HoverState.Hovered) when imageTouchBehavior.HoveredImageSource is null: + case (TouchState.Default, HoverState.Default): + imageElement.SetValue(ImageElement.SourceProperty, updatedImageSource); + break; + } + } + + static void HandlePressedImageSourceChanged(BindableObject bindable, object? oldValue, object? newValue) + { + var imageTouchBehavior = (ImageTouchBehavior)bindable; + var updatedImageSource = (ImageSource?)newValue; + + if (!GestureManager.TryGetBindableImageTouchBehaviorElement(imageTouchBehavior, out var imageElement)) + { + return; + } + + // GestureManager does not automatically toggle ImageElement.SourceProperty when currently being displayed + switch (imageTouchBehavior.CurrentTouchState, imageTouchBehavior.CurrentHoverState) + { + case (TouchState.Pressed, _): + imageElement.SetValue(ImageElement.SourceProperty, updatedImageSource); + break; + } + } + + static void HandleHoveredImageSourceChanged(BindableObject bindable, object? oldValue, object? newValue) + { + var imageTouchBehavior = (ImageTouchBehavior)bindable; + var updatedImageSource = (ImageSource?)newValue; + + if (!GestureManager.TryGetBindableImageTouchBehaviorElement(imageTouchBehavior, out var imageElement)) + { + return; + } + + // GestureManager does not automatically toggle ImageElement.SourceProperty when currently being displayed + switch (imageTouchBehavior.CurrentTouchState, imageTouchBehavior.CurrentHoverState) + { + case (TouchState.Default, HoverState.Hovered): + imageElement.SetValue(ImageElement.SourceProperty, updatedImageSource); + break; + } + } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/StatusBar/StatusBarBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/StatusBar/StatusBarBehavior.shared.cs index c6ed7b7daf..2211ede8b6 100644 --- a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/StatusBar/StatusBarBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/StatusBar/StatusBarBehavior.shared.cs @@ -31,20 +31,20 @@ public partial class StatusBarBehavior : BasePlatformBehavior /// /// Property that holds the value of the Status bar color. /// - [BindableProperty(DefaultValueCreatorMethodName = nameof(StatusBarColorValueCreator))] - public partial Color StatusBarColor { get; set; } + [BindableProperty] + public partial Color StatusBarColor { get; set; } = StatusBarBehaviorDefaults.StatusBarColor; /// /// Property that holds the value of the Status bar color. /// - [BindableProperty(DefaultValue = StatusBarStyle.Default)] - public partial StatusBarStyle StatusBarStyle { get; set; } + [BindableProperty] + public partial StatusBarStyle StatusBarStyle { get; set; } = StatusBarBehaviorDefaults.StatusBarStyle; /// /// When the status bar color and style should be applied. /// - [BindableProperty(DefaultValue = StatusBarApplyOn.OnBehaviorAttachedTo)] - public partial StatusBarApplyOn ApplyOn { get; set; } + [BindableProperty] + public partial StatusBarApplyOn ApplyOn { get; set; } = StatusBarBehaviorDefaults.ApplyOn; #if !(WINDOWS || MACCATALYST || TIZEN) @@ -137,6 +137,4 @@ protected override void OnPropertyChanged([CallerMemberName] string? propertyNam } } #endif - - static Color StatusBarColorValueCreator(BindableObject bindable) => Colors.Transparent; } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/GestureManager.shared.cs b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/GestureManager.shared.cs index 59d9155f78..4f66656c37 100644 --- a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/GestureManager.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/GestureManager.shared.cs @@ -34,6 +34,19 @@ public async ValueTask DisposeAsync() longPressTokenSource = animationTokenSource = null; } + internal static bool TryGetBindableImageTouchBehaviorElement(ImageTouchBehavior imageTouchBehavior, [NotNullWhen(true)] out BindableObject? bindable) + { + // Validate the ImageTouchBehaviorElement is both a BindableObject and IImage + if (imageTouchBehavior.Element is not (BindableObject and Microsoft.Maui.IImage)) + { + bindable = null; + return false; + } + + bindable = imageTouchBehavior.Element; + return true; + } + internal static void HandleUserInteraction(in TouchBehavior touchBehavior, in TouchInteractionStatus interactionStatus) { touchBehavior.CurrentInteractionStatus = interactionStatus; @@ -261,7 +274,7 @@ static async Task SetImageSource(TouchBehavior touchBehavior, TouchState touchSt return; } - if (touchBehavior.Element is not (BindableObject bindable and Microsoft.Maui.IImage)) + if (!TryGetBindableImageTouchBehaviorElement(imageTouchBehavior, out var bindable)) { throw new InvalidOperationException($"{nameof(ImageTouchBehavior)} can only be attached to an {nameof(Microsoft.Maui.IImage)}"); } @@ -278,56 +291,64 @@ static async Task SetImageSource(TouchBehavior touchBehavior, TouchState touchSt return; } + ImageSource? updatedImageSource = imageTouchBehavior.DefaultImageSource ?? (ImageSource?)ImageTouchBehaviorDefaults.DefaultBackgroundImageSource; + Aspect? updatedImageAspect = imageTouchBehavior.DefaultImageAspect ?? ImageTouchBehaviorDefaults.DefaultBackgroundImageAspect; + switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (imageTouchBehavior.IsSet(ImageTouchBehavior.PressedImageAspectProperty)) + if (imageTouchBehavior.PressedImageAspect is not null) { - bindable.SetValue(ImageElement.AspectProperty, imageTouchBehavior.PressedImageAspect); + updatedImageAspect = imageTouchBehavior.PressedImageAspect; } - if (imageTouchBehavior.IsSet(ImageTouchBehavior.PressedImageSourceProperty)) + if (imageTouchBehavior.PressedImageSource is not null) { - bindable.SetValue(ImageElement.SourceProperty, imageTouchBehavior.PressedImageSource); + updatedImageSource = imageTouchBehavior.PressedImageSource; } + break; case (TouchState.Default, HoverState.Hovered): - if (imageTouchBehavior.IsSet(ImageTouchBehavior.HoveredImageAspectProperty)) + if (imageTouchBehavior.HoveredImageAspect is not null) { - bindable.SetValue(ImageElement.AspectProperty, imageTouchBehavior.HoveredImageAspect); + updatedImageAspect = imageTouchBehavior.HoveredImageAspect; } - else if (imageTouchBehavior.IsSet(ImageTouchBehavior.DefaultImageAspectProperty)) + else if (imageTouchBehavior.DefaultImageAspect is not null) { - bindable.SetValue(ImageElement.AspectProperty, imageTouchBehavior.DefaultImageAspect); + updatedImageAspect = imageTouchBehavior.DefaultImageAspect; } - if (imageTouchBehavior.IsSet(ImageTouchBehavior.HoveredImageSourceProperty)) + if (imageTouchBehavior.HoveredImageSource is not null) { - bindable.SetValue(ImageElement.SourceProperty, imageTouchBehavior.HoveredImageSource); + updatedImageSource = imageTouchBehavior.HoveredImageSource; } - else if (imageTouchBehavior.IsSet(ImageTouchBehavior.DefaultImageSourceProperty)) + else if (imageTouchBehavior.DefaultImageSource is not null) { - bindable.SetValue(ImageElement.SourceProperty, imageTouchBehavior.DefaultImageSource); + updatedImageSource = imageTouchBehavior.DefaultImageSource; } break; case (TouchState.Default, HoverState.Default): - if (imageTouchBehavior.IsSet(ImageTouchBehavior.DefaultImageAspectProperty)) + if (imageTouchBehavior.DefaultImageAspect is not null) { - bindable.SetValue(ImageElement.AspectProperty, imageTouchBehavior.DefaultImageAspect); + updatedImageAspect = imageTouchBehavior.DefaultImageAspect; } - if (imageTouchBehavior.IsSet(ImageTouchBehavior.DefaultImageSourceProperty)) + if (imageTouchBehavior.DefaultImageSource is not null) { - bindable.SetValue(ImageElement.SourceProperty, imageTouchBehavior.DefaultImageSource); + updatedImageSource = imageTouchBehavior.DefaultImageSource; } + break; default: throw new NotSupportedException($"The combination of {nameof(TouchState)} {touchState} and {nameof(HoverState)} {hoverState} is not yet supported"); } + + bindable.SetValue(ImageElement.SourceProperty, updatedImageSource); + bindable.SetValue(ImageElement.AspectProperty, updatedImageAspect); } static void UpdateStatusAndState(in TouchBehavior touchBehavior, in TouchStatus status, in TouchState state) @@ -351,9 +372,9 @@ static void UpdateVisualState(in VisualElement visualElement, in TouchState touc static async Task SetOpacity(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultOpacity - 1) <= double.Epsilon && - Abs(touchBehavior.PressedOpacity - 1) <= double.Epsilon && - Abs(touchBehavior.HoveredOpacity - 1) <= double.Epsilon) + if (Abs(touchBehavior.DefaultOpacity ?? TouchBehaviorDefaults.DefaultOpacity - 1) <= double.Epsilon + && Abs(touchBehavior.PressedOpacity ?? TouchBehaviorDefaults.PressedOpacity - 1) <= double.Epsilon + && Abs(touchBehavior.HoveredOpacity ?? TouchBehaviorDefaults.HoveredOpacity - 1) <= double.Epsilon) { return false; } @@ -363,33 +384,36 @@ static async Task SetOpacity(TouchBehavior touchBehavior, TouchState touch return false; } - var updatedOpacity = touchBehavior.DefaultOpacity; + double? updatedOpacity = touchBehavior.DefaultOpacity ?? TouchBehaviorDefaults.DefaultOpacity; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedOpacityProperty)) + if (touchBehavior.PressedOpacity is not null) { updatedOpacity = touchBehavior.PressedOpacity; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredOpacityProperty)) + if (touchBehavior.HoveredOpacity is not null) { updatedOpacity = touchBehavior.HoveredOpacity; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultOpacityProperty)) + else if (touchBehavior.DefaultOpacity is not null) { updatedOpacity = touchBehavior.DefaultOpacity; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultOpacityProperty)) + if (touchBehavior.DefaultOpacity is not null) { updatedOpacity = touchBehavior.DefaultOpacity; } + break; default: @@ -399,18 +423,18 @@ static async Task SetOpacity(TouchBehavior touchBehavior, TouchState touch if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.Opacity = updatedOpacity; + element.Opacity = updatedOpacity.Value; return true; } - return await element.FadeToAsync(updatedOpacity, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); + return await element.FadeToAsync(updatedOpacity.Value, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); } static async Task SetScale(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultScale - 1) <= double.Epsilon && - Abs(touchBehavior.PressedScale - 1) <= double.Epsilon && - Abs(touchBehavior.HoveredScale - 1) <= double.Epsilon) + if (Abs(touchBehavior.DefaultScale ?? TouchBehaviorDefaults.DefaultScale - 1) <= double.Epsilon + && Abs(touchBehavior.PressedScale ?? TouchBehaviorDefaults.PressedScale - 1) <= double.Epsilon + && Abs(touchBehavior.HoveredScale ?? TouchBehaviorDefaults.HoveredScale - 1) <= double.Epsilon) { return false; } @@ -420,33 +444,36 @@ static async Task SetScale(TouchBehavior touchBehavior, TouchState touchSt return false; } - var updatedScale = touchBehavior.DefaultScale; + double? updatedScale = touchBehavior.DefaultScale ?? TouchBehaviorDefaults.DefaultScale; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedScaleProperty)) + if (touchBehavior.PressedScale is not null) { updatedScale = touchBehavior.PressedScale; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredScaleProperty)) + if (touchBehavior.HoveredScale is not null) { updatedScale = touchBehavior.HoveredScale; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultScaleProperty)) + else if (touchBehavior.DefaultScale is not null) { updatedScale = touchBehavior.DefaultScale; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultScaleProperty)) + if (touchBehavior.DefaultScale is not null) { updatedScale = touchBehavior.DefaultScale; } + break; default: @@ -456,7 +483,7 @@ static async Task SetScale(TouchBehavior touchBehavior, TouchState touchSt if (duration <= TimeSpan.Zero) { element.AbortAnimations(nameof(SetScale)); - element.Scale = updatedScale; + element.Scale = updatedScale.Value; return false; } @@ -470,19 +497,19 @@ static async Task SetScale(TouchBehavior touchBehavior, TouchState touchSt } element.Scale = v; - }, element.Scale, updatedScale, 16, (uint)Abs(duration.TotalMilliseconds), easing, (v, b) => animationCompletionSource.SetResult(b)); + }, element.Scale, (double)updatedScale, 16, (uint)Abs(duration.TotalMilliseconds), easing, (v, b) => animationCompletionSource.SetResult(b)); return await animationCompletionSource.Task.WaitAsync(token); } static async Task SetTranslation(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultTranslationX) <= double.Epsilon - && Abs(touchBehavior.PressedTranslationX) <= double.Epsilon - && Abs(touchBehavior.HoveredTranslationX) <= double.Epsilon - && Abs(touchBehavior.DefaultTranslationY) <= double.Epsilon - && Abs(touchBehavior.PressedTranslationY) <= double.Epsilon - && Abs(touchBehavior.HoveredTranslationY) <= double.Epsilon) + if (Abs(touchBehavior.DefaultTranslationX ?? TouchBehaviorDefaults.DefaultTranslationX) <= double.Epsilon + && Abs(touchBehavior.PressedTranslationX ?? TouchBehaviorDefaults.PressedTranslationX) <= double.Epsilon + && Abs(touchBehavior.HoveredTranslationX ?? TouchBehaviorDefaults.HoveredTranslationX) <= double.Epsilon + && Abs(touchBehavior.DefaultTranslationY ?? TouchBehaviorDefaults.DefaultTranslationY) <= double.Epsilon + && Abs(touchBehavior.PressedTranslationY ?? TouchBehaviorDefaults.PressedTranslationY) <= double.Epsilon + && Abs(touchBehavior.HoveredTranslationY ?? TouchBehaviorDefaults.HoveredTranslationY) <= double.Epsilon) { return false; } @@ -492,53 +519,56 @@ static async Task SetTranslation(TouchBehavior touchBehavior, TouchState t return false; } - var updatedTranslationX = touchBehavior.DefaultTranslationY; - var updatedTranslationY = touchBehavior.DefaultTranslationX; + double? updatedTranslationX = touchBehavior.DefaultTranslationY ?? TouchBehaviorDefaults.DefaultTranslationY; + double? updatedTranslationY = touchBehavior.DefaultTranslationX ?? TouchBehaviorDefaults.DefaultTranslationX; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedTranslationXProperty)) + if (touchBehavior.PressedTranslationX is not null) { updatedTranslationX = touchBehavior.PressedTranslationX; } - if (touchBehavior.IsSet(TouchBehavior.PressedTranslationYProperty)) + if (touchBehavior.PressedTranslationY is not null) { updatedTranslationY = touchBehavior.PressedTranslationY; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredTranslationXProperty)) + if (touchBehavior.HoveredTranslationX is not null) { updatedTranslationX = touchBehavior.HoveredTranslationX; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultTranslationXProperty)) + else if (touchBehavior.DefaultTranslationX is not null) { updatedTranslationX = touchBehavior.DefaultTranslationX; } - if (touchBehavior.IsSet(TouchBehavior.HoveredTranslationYProperty)) + if (touchBehavior.HoveredTranslationY is not null) { updatedTranslationY = touchBehavior.HoveredTranslationY; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultTranslationYProperty)) + else if (touchBehavior.DefaultTranslationY is not null) { updatedTranslationY = touchBehavior.DefaultTranslationY; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultTranslationXProperty)) + if (touchBehavior.DefaultTranslationX is not null) { updatedTranslationX = touchBehavior.DefaultTranslationX; } - if (touchBehavior.IsSet(TouchBehavior.DefaultTranslationYProperty)) + if (touchBehavior.DefaultTranslationY is not null) { updatedTranslationY = touchBehavior.DefaultTranslationY; } + break; default: @@ -548,19 +578,20 @@ static async Task SetTranslation(TouchBehavior touchBehavior, TouchState t if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.TranslationX = updatedTranslationX; - element.TranslationY = updatedTranslationY; + element.TranslationX = updatedTranslationX.Value; + element.TranslationY = updatedTranslationY.Value; + return true; } - return await element.TranslateToAsync(updatedTranslationX, updatedTranslationY, (uint)Abs(duration.Milliseconds), easing).WaitAsync(token); + return await element.TranslateToAsync(updatedTranslationX.Value, updatedTranslationY.Value, (uint)Abs(duration.Milliseconds), easing).WaitAsync(token); } static async Task SetRotation(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultRotation) <= double.Epsilon - && Abs(touchBehavior.PressedRotation) <= double.Epsilon - && Abs(touchBehavior.HoveredRotation) <= double.Epsilon) + if (Abs(touchBehavior.DefaultRotation ?? TouchBehaviorDefaults.DefaultRotation) <= double.Epsilon + && Abs(touchBehavior.PressedRotation ?? TouchBehaviorDefaults.PressedRotation) <= double.Epsilon + && Abs(touchBehavior.HoveredRotation ?? TouchBehaviorDefaults.HoveredRotation) <= double.Epsilon) { return false; } @@ -570,33 +601,36 @@ static async Task SetRotation(TouchBehavior touchBehavior, TouchState touc return false; } - var updatedRotation = touchBehavior.DefaultRotation; + double? updatedRotation = touchBehavior.DefaultRotation ?? TouchBehaviorDefaults.DefaultRotation; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedRotationProperty)) + if (touchBehavior.PressedRotation is not null) { updatedRotation = touchBehavior.PressedRotation; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredRotationProperty)) + if (touchBehavior.HoveredRotation is not null) { updatedRotation = touchBehavior.HoveredRotation; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultRotationProperty)) + else if (touchBehavior.DefaultRotation is not null) { updatedRotation = touchBehavior.DefaultRotation; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultRotationProperty)) + if (touchBehavior.DefaultRotation is not null) { updatedRotation = touchBehavior.DefaultRotation; } + break; default: @@ -606,18 +640,18 @@ static async Task SetRotation(TouchBehavior touchBehavior, TouchState touc if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.Rotation = updatedRotation; + element.Rotation = updatedRotation.Value; return true; } - return await element.RotateToAsync(updatedRotation, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); + return await element.RotateToAsync(updatedRotation.Value, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); } static async Task SetRotationX(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultRotationX) <= double.Epsilon && - Abs(touchBehavior.PressedRotationX) <= double.Epsilon && - Abs(touchBehavior.HoveredRotationX) <= double.Epsilon) + if (Abs(touchBehavior.DefaultRotationX ?? TouchBehaviorDefaults.DefaultRotationX) <= double.Epsilon && + Abs(touchBehavior.PressedRotationX ?? TouchBehaviorDefaults.PressedRotationX) <= double.Epsilon && + Abs(touchBehavior.HoveredRotationX ?? TouchBehaviorDefaults.HoveredRotationX) <= double.Epsilon) { return false; } @@ -627,33 +661,36 @@ static async Task SetRotationX(TouchBehavior touchBehavior, TouchState tou return false; } - var updatedRotationX = touchBehavior.DefaultRotationX; + double? updatedRotationX = touchBehavior.DefaultRotationX ?? TouchBehaviorDefaults.DefaultRotationX; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedRotationXProperty)) + if (touchBehavior.PressedRotationX is not null) { updatedRotationX = touchBehavior.PressedRotationX; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredRotationXProperty)) + if (touchBehavior.HoveredRotationX is not null) { updatedRotationX = touchBehavior.HoveredRotationX; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultRotationXProperty)) + else if (touchBehavior.DefaultRotationX is not null) { updatedRotationX = touchBehavior.DefaultRotationX; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultRotationXProperty)) + if (touchBehavior.DefaultRotationX is not null) { updatedRotationX = touchBehavior.DefaultRotationX; } + break; default: @@ -663,18 +700,18 @@ static async Task SetRotationX(TouchBehavior touchBehavior, TouchState tou if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.RotationX = updatedRotationX; + element.RotationX = updatedRotationX.Value; return true; } - return await element.RotateXToAsync(updatedRotationX, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); + return await element.RotateXToAsync(updatedRotationX.Value, (uint)Abs(duration.TotalMilliseconds), easing).WaitAsync(token); } static async Task SetRotationY(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) { - if (Abs(touchBehavior.DefaultRotationY) <= double.Epsilon && - Abs(touchBehavior.PressedRotationY) <= double.Epsilon && - Abs(touchBehavior.HoveredRotationY) <= double.Epsilon) + if (Abs(touchBehavior.DefaultRotationY ?? TouchBehaviorDefaults.DefaultRotationY) <= double.Epsilon && + Abs(touchBehavior.PressedRotationY ?? TouchBehaviorDefaults.PressedRotationY) <= double.Epsilon && + Abs(touchBehavior.HoveredRotationY ?? TouchBehaviorDefaults.HoveredRotationY) <= double.Epsilon) { return false; } @@ -684,33 +721,36 @@ static async Task SetRotationY(TouchBehavior touchBehavior, TouchState tou return false; } - double updatedRotationY = touchBehavior.DefaultRotationY; + double? updatedRotationY = touchBehavior.DefaultRotationY ?? TouchBehaviorDefaults.DefaultRotationY; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedRotationYProperty)) + if (touchBehavior.PressedRotationY is not null) { updatedRotationY = touchBehavior.PressedRotationY; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredRotationYProperty)) + if (touchBehavior.HoveredRotationY is not null) { updatedRotationY = touchBehavior.HoveredRotationY; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultRotationYProperty)) + else if (touchBehavior.DefaultRotationY is not null) { updatedRotationY = touchBehavior.DefaultRotationY; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultRotationYProperty)) + if (touchBehavior.DefaultRotationY is not null) { updatedRotationY = touchBehavior.DefaultRotationY; } + break; default: @@ -720,11 +760,11 @@ static async Task SetRotationY(TouchBehavior touchBehavior, TouchState tou if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.RotationY = updatedRotationY; + element.RotationY = updatedRotationY.Value; return true; } - return await element.RotateYToAsync(updatedRotationY, (uint)Abs(duration.Milliseconds), easing).WaitAsync(token); + return await element.RotateYToAsync(updatedRotationY.Value, (uint)Abs(duration.Milliseconds), easing).WaitAsync(token); } async Task SetBackgroundColor(TouchBehavior touchBehavior, TouchState touchState, HoverState hoverState, TimeSpan duration, Easing? easing, CancellationToken token) @@ -736,33 +776,36 @@ async Task SetBackgroundColor(TouchBehavior touchBehavior, TouchState touc defaultBackgroundColor ??= element.BackgroundColor; - var updatedBackgroundColor = defaultBackgroundColor; + var updatedBackgroundColor = defaultBackgroundColor ?? TouchBehaviorDefaults.DefaultBackgroundColor; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedBackgroundColorProperty)) + if (touchBehavior.PressedBackgroundColor is not null) { updatedBackgroundColor = touchBehavior.PressedBackgroundColor; } + break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredBackgroundColorProperty)) + if (touchBehavior.HoveredBackgroundColor is not null) { updatedBackgroundColor = touchBehavior.HoveredBackgroundColor; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultBackgroundColorProperty)) + else if (touchBehavior.DefaultBackgroundColor is not null) { updatedBackgroundColor = touchBehavior.DefaultBackgroundColor; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultBackgroundColorProperty)) + if (touchBehavior.DefaultBackgroundColor is not null) { updatedBackgroundColor = touchBehavior.DefaultBackgroundColor; } + break; default: @@ -772,7 +815,7 @@ async Task SetBackgroundColor(TouchBehavior touchBehavior, TouchState touc if (duration <= TimeSpan.Zero) { element.AbortAnimations(); - element.BackgroundColor = updatedBackgroundColor; + element.BackgroundColor = updatedBackgroundColor ?? Colors.Transparent; return true; } @@ -811,18 +854,18 @@ await Task.WhenAll( static (Easing? Easing, int Duration) GetEasingAndDurationForCurrentState(in TouchState touchState, in HoverState hoverState, in TouchBehavior touchBehavior) { - Easing? easing = touchBehavior.DefaultAnimationEasing; - int duration = touchBehavior.DefaultAnimationDuration; + Easing? easing = touchBehavior.DefaultAnimationEasing ?? TouchBehaviorDefaults.DefaultAnimationEasing; + int? duration = touchBehavior.DefaultAnimationDuration ?? TouchBehaviorDefaults.DefaultAnimationDuration; switch (touchState, hoverState) { case (TouchState.Pressed, _): - if (touchBehavior.IsSet(TouchBehavior.PressedAnimationDurationProperty)) + if (touchBehavior.PressedAnimationDuration is not null) { duration = touchBehavior.PressedAnimationDuration; } - if (touchBehavior.IsSet(TouchBehavior.PressedAnimationEasingProperty)) + if (touchBehavior.PressedAnimationEasing is not null) { easing = touchBehavior.PressedAnimationEasing; } @@ -830,42 +873,44 @@ await Task.WhenAll( break; case (TouchState.Default, HoverState.Hovered): - if (touchBehavior.IsSet(TouchBehavior.HoveredAnimationDurationProperty)) + if (touchBehavior.HoveredAnimationDuration is not null) { duration = touchBehavior.HoveredAnimationDuration; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultAnimationDurationProperty)) + else if (touchBehavior.DefaultAnimationDuration is not null) { duration = touchBehavior.DefaultAnimationDuration; } - if (touchBehavior.IsSet(TouchBehavior.HoveredAnimationEasingProperty)) + if (touchBehavior.HoveredAnimationEasing is not null) { easing = touchBehavior.HoveredAnimationEasing; } - else if (touchBehavior.IsSet(TouchBehavior.DefaultAnimationEasingProperty)) + else if (touchBehavior.DefaultAnimationEasing is not null) { easing = touchBehavior.DefaultAnimationEasing; } + break; case (TouchState.Default, HoverState.Default): - if (touchBehavior.IsSet(TouchBehavior.DefaultAnimationDurationProperty)) + if (touchBehavior.DefaultAnimationDuration is not null) { duration = touchBehavior.DefaultAnimationDuration; } - if (touchBehavior.IsSet(TouchBehavior.DefaultAnimationEasingProperty)) + if (touchBehavior.DefaultAnimationEasing is not null) { easing = touchBehavior.DefaultAnimationEasing; } + break; default: throw new NotSupportedException($"The combination of {nameof(TouchState)} {touchState} and {nameof(HoverState)} {hoverState} is not yet supported"); } - return (easing, duration); + return (easing, duration.Value); } } } \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/TouchBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/TouchBehavior.shared.cs index a68fb859d4..6c4b25499e 100644 --- a/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/TouchBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/PlatformBehaviors/Touch/TouchBehavior.shared.cs @@ -95,261 +95,261 @@ public event EventHandler LongPressCompleted /// /// Gets or sets a value indicating whether the behavior is enabled. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.IsEnabled)] - public partial bool IsEnabled { get; set; } + [BindableProperty] + public partial bool IsEnabled { get; set; } = TouchBehaviorDefaults.IsEnabled; /// /// Gets or sets a value indicating whether the children of the element should be made input transparent. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.ShouldMakeChildrenInputTransparent)] - public partial bool ShouldMakeChildrenInputTransparent { get; set; } + [BindableProperty] + public partial bool ShouldMakeChildrenInputTransparent { get; set; } = TouchBehaviorDefaults.ShouldMakeChildrenInputTransparent; /// /// Gets or sets the to invoke when the user has completed a touch gesture. /// - [BindableProperty(DefaultValue = null)] + [BindableProperty] public partial ICommand? Command { get; set; } /// /// Gets or sets the parameter to pass to the property. /// - [BindableProperty(DefaultValue = null)] + [BindableProperty] public partial object? CommandParameter { get; set; } /// /// Gets or sets the to invoke when the user has completed a long press. /// - [BindableProperty(DefaultValue = null)] + [BindableProperty] public partial ICommand? LongPressCommand { get; set; } /// /// Gets or sets the parameter to pass to the property. /// - [BindableProperty(DefaultValue = null)] + [BindableProperty] public partial object? LongPressCommandParameter { get; set; } /// /// Gets or sets the duration required to trigger the long press gesture. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.LongPressDuration)] - public partial int LongPressDuration { get; set; } + [BindableProperty] + public partial int LongPressDuration { get; set; } = TouchBehaviorDefaults.LongPressDuration; /// /// Gets the current of the behavior. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.CurrentTouchStatus, PropertyChangedMethodName = nameof(RaiseCurrentTouchStatusChanged), DefaultBindingMode = BindingMode.OneWayToSource)] - public partial TouchStatus CurrentTouchStatus { get; set; } + [BindableProperty(PropertyChangedMethodName = nameof(RaiseCurrentTouchStatusChanged), DefaultBindingMode = BindingMode.OneWayToSource)] + public partial TouchStatus CurrentTouchStatus { get; set; } = TouchBehaviorDefaults.CurrentTouchStatus; /// /// Gets the current of the behavior. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.CurrentTouchState, DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseCurrentTouchStateChanged))] - public partial TouchState CurrentTouchState { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseCurrentTouchStateChanged))] + public partial TouchState CurrentTouchState { get; set; } = TouchBehaviorDefaults.CurrentTouchState; /// /// Gets the current of the behavior. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.CurrentInteractionStatus, DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseInteractionStatusChanged))] - public partial TouchInteractionStatus CurrentInteractionStatus { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseInteractionStatusChanged))] + public partial TouchInteractionStatus CurrentInteractionStatus { get; set; } = TouchBehaviorDefaults.CurrentInteractionStatus; /// /// Gets the current of the behavior. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.CurrentHoverStatus, DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseHoverStatusChanged))] - public partial HoverStatus CurrentHoverStatus { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseHoverStatusChanged))] + public partial HoverStatus CurrentHoverStatus { get; set; } = TouchBehaviorDefaults.CurrentHoverStatus; /// /// Gets the current of the behavior. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.CurrentHoverState, DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseHoverStateChanged))] - public partial HoverState CurrentHoverState { get; set; } + [BindableProperty(DefaultBindingMode = BindingMode.OneWayToSource, PropertyChangedMethodName = nameof(RaiseHoverStateChanged))] + public partial HoverState CurrentHoverState { get; set; } = TouchBehaviorDefaults.CurrentHoverState; /// /// Gets or sets the background color of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultBackgroundColor)] + [BindableProperty] public partial Color? DefaultBackgroundColor { get; set; } /// /// Gets or sets the background color of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredBackgroundColor)] + [BindableProperty] public partial Color? HoveredBackgroundColor { get; set; } /// /// Gets or sets the background color of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedBackgroundColor)] + [BindableProperty] public partial Color? PressedBackgroundColor { get; set; } /// /// Gets or sets the opacity of the element when the is . /// /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultOpacity, PropertyChangingMethodName = nameof(HandleDefaultOpacityChanging))] - public partial double DefaultOpacity { get; set; } + [BindableProperty(PropertyChangingMethodName = nameof(HandleDefaultOpacityChanging))] + public partial double? DefaultOpacity { get; set; } /// /// Gets or sets the opacity of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredOpacity, PropertyChangingMethodName = nameof(HandleHoveredOpacityChanging))] - public partial double HoveredOpacity { get; set; } + [BindableProperty(PropertyChangingMethodName = nameof(HandleHoveredOpacityChanging))] + public partial double? HoveredOpacity { get; set; } /// /// Gets or sets the opacity of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedOpacity, PropertyChangingMethodName = nameof(HandlePressedOpacityChanging))] - public partial double PressedOpacity { get; set; } + [BindableProperty(PropertyChangingMethodName = nameof(HandlePressedOpacityChanging))] + public partial double? PressedOpacity { get; set; } /// /// Gets or sets the scale of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultScale)] - public partial double DefaultScale { get; set; } + [BindableProperty] + public partial double? DefaultScale { get; set; } /// /// Gets or sets the scale of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredScale)] - public partial double HoveredScale { get; set; } + [BindableProperty] + public partial double? HoveredScale { get; set; } /// /// Gets or sets the scale of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedScale)] - public partial double PressedScale { get; set; } + [BindableProperty] + public partial double? PressedScale { get; set; } /// /// Gets or sets the translation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultTranslationX)] - public partial double DefaultTranslationX { get; set; } + [BindableProperty] + public partial double? DefaultTranslationX { get; set; } /// /// Gets or sets the translation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredTranslationX)] - public partial double HoveredTranslationX { get; set; } + [BindableProperty] + public partial double? HoveredTranslationX { get; set; } /// /// Gets or sets the translation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedTranslationX)] - public partial double PressedTranslationX { get; set; } + [BindableProperty] + public partial double? PressedTranslationX { get; set; } /// /// Gets or sets the translation Y of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultTranslationY)] - public partial double DefaultTranslationY { get; set; } + [BindableProperty] + public partial double? DefaultTranslationY { get; set; } /// /// Gets or sets the translation Y of the element when the is . /// - [BindableProperty(DefaultValue = 0.0)] - public partial double HoveredTranslationY { get; set; } + [BindableProperty] + public partial double? HoveredTranslationY { get; set; } /// /// Gets or sets the translation Y of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedTranslationY)] - public partial double PressedTranslationY { get; set; } + [BindableProperty] + public partial double? PressedTranslationY { get; set; } /// /// Gets or sets the rotation of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultRotation)] - public partial double DefaultRotation { get; set; } + [BindableProperty] + public partial double? DefaultRotation { get; set; } /// /// Gets or sets the rotation of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredRotation)] - public partial double HoveredRotation { get; set; } + [BindableProperty] + public partial double? HoveredRotation { get; set; } /// /// Gets or sets the rotation of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedRotation)] - public partial double PressedRotation { get; set; } + [BindableProperty] + public partial double? PressedRotation { get; set; } /// /// Gets or sets the rotation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultRotationX)] - public partial double DefaultRotationX { get; set; } + [BindableProperty] + public partial double? DefaultRotationX { get; set; } /// /// Gets or sets the rotation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredRotationX)] - public partial double HoveredRotationX { get; set; } + [BindableProperty] + public partial double? HoveredRotationX { get; set; } /// /// Gets or sets the rotation X of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedRotationX)] - public partial double PressedRotationX { get; set; } + [BindableProperty] + public partial double? PressedRotationX { get; set; } /// /// Gets or sets the rotation Y of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultRotationY)] - public partial double DefaultRotationY { get; set; } + [BindableProperty] + public partial double? DefaultRotationY { get; set; } /// /// Gets or sets the rotation Y of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredRotationY)] - public partial double HoveredRotationY { get; set; } + [BindableProperty] + public partial double? HoveredRotationY { get; set; } /// /// Gets or sets the rotation Y of the element when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedRotationY)] - public partial double PressedRotationY { get; set; } + [BindableProperty] + public partial double? PressedRotationY { get; set; } /// /// Gets or sets the duration of the animation when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedAnimationDuration)] - public partial int PressedAnimationDuration { get; set; } + [BindableProperty] + public partial int? PressedAnimationDuration { get; set; } /// /// Gets or sets the easing of the animation when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.PressedAnimationEasing)] + [BindableProperty] public partial Easing? PressedAnimationEasing { get; set; } /// /// Gets or sets the duration of the animation when is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultAnimationDuration)] - public partial int DefaultAnimationDuration { get; set; } + [BindableProperty] + public partial int? DefaultAnimationDuration { get; set; } /// /// Gets or sets the of the animation when is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DefaultAnimationEasing)] + [BindableProperty] public partial Easing? DefaultAnimationEasing { get; set; } /// /// Gets or sets the duration of the animation when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredAnimationDuration)] - public partial int HoveredAnimationDuration { get; set; } + [BindableProperty] + public partial int? HoveredAnimationDuration { get; set; } /// /// Gets or sets the easing of the animation when the is . /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.HoveredAnimationEasing)] + [BindableProperty] public partial Easing? HoveredAnimationEasing { get; set; } /// /// Gets or sets the threshold for disallowing touch. /// - [BindableProperty(DefaultValue = TouchBehaviorDefaults.DisallowTouchThreshold)] - public partial int DisallowTouchThreshold { get; set; } + [BindableProperty] + public partial int DisallowTouchThreshold { get; set; } = TouchBehaviorDefaults.DisallowTouchThreshold; internal bool CanExecute => IsEnabled && Element?.IsEnabled is true diff --git a/src/CommunityToolkit.Maui/Behaviors/ProgressBarAnimationBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/ProgressBarAnimationBehavior.shared.cs index b28107491c..c2bccfebff 100644 --- a/src/CommunityToolkit.Maui/Behaviors/ProgressBarAnimationBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/ProgressBarAnimationBehavior.shared.cs @@ -22,20 +22,20 @@ public event EventHandler AnimationCompleted /// /// Value of , clamped to a minimum value of 0 and a maximum value of 1 /// - [BindableProperty(DefaultValue = ProgressBarAnimationBehaviorDefaults.Progress, PropertyChangingMethodName = nameof(OnAnimateProgressPropertyChanging), PropertyChangedMethodName = nameof(OnAnimateProgressPropertyChanged))] - public partial double Progress { get; set; } + [BindableProperty(PropertyChangingMethodName = nameof(OnAnimateProgressPropertyChanging), PropertyChangedMethodName = nameof(OnAnimateProgressPropertyChanged))] + public partial double Progress { get; set; } = ProgressBarAnimationBehaviorDefaults.Progress; /// /// Length in milliseconds of the progress bar animation /// - [BindableProperty(DefaultValue = ProgressBarAnimationBehaviorDefaults.Length)] - public partial uint Length { get; set; } + [BindableProperty] + public partial uint Length { get; set; } = ProgressBarAnimationBehaviorDefaults.Length; /// /// Easing of the progress bar animation /// - [BindableProperty(DefaultValueCreatorMethodName = nameof(EasingValueCreator))] - public partial Easing Easing { get; set; } + [BindableProperty] + public partial Easing Easing { get; set; } = ProgressBarAnimationBehaviorDefaults.Easing; static async void OnAnimateProgressPropertyChanged(BindableObject bindable, object oldValue, object newValue) { @@ -66,8 +66,6 @@ static void OnAnimateProgressPropertyChanging(BindableObject bindable, object ol } - static Easing EasingValueCreator(BindableObject bindable) => ProgressBarAnimationBehaviorDefaults.Easing; - static Task AnimateProgress(in ProgressBar progressBar, in double progress, in uint animationLength, in Easing animationEasing, in CancellationToken token) { return progressBar.ProgressTo(progress, animationLength, animationEasing).WaitAsync(token); diff --git a/src/CommunityToolkit.Maui/Behaviors/UserStoppedTypingBehavior.shared.cs b/src/CommunityToolkit.Maui/Behaviors/UserStoppedTypingBehavior.shared.cs index 0c2075204f..399a65aaa4 100644 --- a/src/CommunityToolkit.Maui/Behaviors/UserStoppedTypingBehavior.shared.cs +++ b/src/CommunityToolkit.Maui/Behaviors/UserStoppedTypingBehavior.shared.cs @@ -31,20 +31,20 @@ public partial class UserStoppedTypingBehavior : BaseBehavior, IDispo /// /// The time of inactivity in milliseconds after which will be executed. If is also set, the condition there also needs to be met. This is a bindable property. /// - [BindableProperty(DefaultValue = UserStoppedTypingBehaviorDefaults.StoppedTypingTimeThreshold)] - public partial int StoppedTypingTimeThreshold { get; set; } + [BindableProperty] + public partial int StoppedTypingTimeThreshold { get; set; } = UserStoppedTypingBehaviorDefaults.StoppedTypingTimeThreshold; /// /// The minimum length of the input value required before will be executed but only after has passed. This is a bindable property. /// - [BindableProperty(DefaultValue = UserStoppedTypingBehaviorDefaults.MinimumLengthThreshold)] - public partial int MinimumLengthThreshold { get; set; } + [BindableProperty] + public partial int MinimumLengthThreshold { get; set; } = UserStoppedTypingBehaviorDefaults.MinimumLengthThreshold; /// /// Indicates whether the keyboard should be dismissed automatically after the user stopped typing. This is a bindable property. /// - [BindableProperty(DefaultValue = UserStoppedTypingBehaviorDefaults.ShouldDismissKeyboardAutomatically)] - public partial bool ShouldDismissKeyboardAutomatically { get; set; } + [BindableProperty] + public partial bool ShouldDismissKeyboardAutomatically { get; set; } = UserStoppedTypingBehaviorDefaults.ShouldDismissKeyboardAutomatically; /// public void Dispose() diff --git a/src/CommunityToolkit.Maui/Primitives/StatusBarBehaviorDefaults.shared.cs b/src/CommunityToolkit.Maui/Primitives/StatusBarBehaviorDefaults.shared.cs new file mode 100644 index 0000000000..c6695601a5 --- /dev/null +++ b/src/CommunityToolkit.Maui/Primitives/StatusBarBehaviorDefaults.shared.cs @@ -0,0 +1,11 @@ +using CommunityToolkit.Maui.Behaviors; +using CommunityToolkit.Maui.Core; + +namespace CommunityToolkit.Maui; + +static class StatusBarBehaviorDefaults +{ + public static Color StatusBarColor { get; } = Colors.Transparent; + public static StatusBarStyle StatusBarStyle { get; } = StatusBarStyle.Default; + public static StatusBarApplyOn ApplyOn { get; } = StatusBarApplyOn.OnBehaviorAttachedTo; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs b/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs index 4c7a8cd2ed..e21ba2c5b7 100644 --- a/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs +++ b/src/CommunityToolkit.Maui/Views/AvatarView.shared.cs @@ -314,15 +314,15 @@ void HandlePropertyChanged(object? sender, PropertyChangedEventArgs e) { // Ensure avatarImage is clipped to the bounds of the AvatarView whenever its Height, Width, CornerRadius and Padding properties change if ((e.PropertyName == HeightProperty.PropertyName - || e.PropertyName == WidthProperty.PropertyName - || e.PropertyName == PaddingProperty.PropertyName - || e.PropertyName == ImageSourceProperty.PropertyName - || e.PropertyName == BorderWidthProperty.PropertyName - || e.PropertyName == CornerRadiusProperty.PropertyName - || e.PropertyName == StrokeThicknessProperty.PropertyName) - && Height >= 0 // The default value of Height (before the view is drawn onto the page) is -1 - && Width >= 0 // The default value of Y (before the view is drawn onto the page) is -1 - && avatarImage.Source is not null) + || e.PropertyName == WidthProperty.PropertyName + || e.PropertyName == PaddingProperty.PropertyName + || e.PropertyName == ImageSourceProperty.PropertyName + || e.PropertyName == BorderWidthProperty.PropertyName + || e.PropertyName == CornerRadiusProperty.PropertyName + || e.PropertyName == StrokeThicknessProperty.PropertyName) + && Height >= 0 // The default value of Height (before the view is drawn onto the page) is -1 + && Width >= 0 // The default value of Y (before the view is drawn onto the page) is -1 + && avatarImage.Source is not null) { Geometry? avatarImageClipGeometry = null; diff --git a/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs b/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs index 4f6293a4f1..8da2c64896 100644 --- a/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs +++ b/src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs @@ -238,15 +238,15 @@ public PopupPageLayout(in Popup popupContent, in IPopupOptions options, in Actio overlayTapGestureRecognizer.Tapped += HandleOverlayTapped; TapGestureGestureOverlay = new PopupGestureOverlay(); TapGestureGestureOverlay.GestureRecognizers.Add(overlayTapGestureRecognizer); - + Children.Add(TapGestureGestureOverlay); Children.Add(PopupBorder); } - + void HandleOverlayTapped(object? sender, TappedEventArgs e) { ArgumentNullException.ThrowIfNull(sender); - + var position = e.GetPosition(this); if (position is null)