diff --git a/.gitignore b/.gitignore index e277807e..726249ed 100644 --- a/.gitignore +++ b/.gitignore @@ -329,3 +329,4 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ /clean-all-bin-obj-directories.bat +.DS_Store diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchAndPressEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchAndPressEffect.cs new file mode 100644 index 00000000..59842bf7 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchAndPressEffect.cs @@ -0,0 +1,110 @@ +using Android.Views; +using Maui.FreakyControls.Shared.Enums; +using Maui.FreakyControls.Shared.TouchPress; +using Microsoft.Maui.Controls.Platform; +using View = Android.Views.View; + +namespace Maui.FreakyControls.Platforms.Android; + +internal class TouchAndPressEffect : PlatformEffect +{ + private ITouchPressEffect _touchAndPressEffectConsumer; + private View _view; + private float? firstX; + private float? firstY; + private bool ignored; + + public TouchAndPressEffect() + { + } + + protected override void OnAttached() + { + _view = Control ?? Container; + + if (_view != null && Element is ITouchPressEffect touchAndPressEffectConsumer) + { + _view.Touch += OnViewOnTouch; + _touchAndPressEffectConsumer = touchAndPressEffectConsumer; + } + } + + protected override void OnDetached() + { + if (_view != null) + { + _view.Touch -= OnViewOnTouch; + } + } + + private void OnViewOnTouch(object sender, View.TouchEventArgs e) + { + switch (e.Event.ActionMasked) + { + case MotionEventActions.Cancel: + _touchAndPressEffectConsumer?.ConsumeEvent(EventType.Cancelled); + break; + + case MotionEventActions.Pointer1Down: + case MotionEventActions.ButtonPress: + case MotionEventActions.Down: + _touchAndPressEffectConsumer?.ConsumeEvent(EventType.Pressing); + break; + + case MotionEventActions.Move: + OnPointerMoved(e.Event); + break; + + case MotionEventActions.ButtonRelease: + case MotionEventActions.Up: + case MotionEventActions.Pointer1Up: + _touchAndPressEffectConsumer?.ConsumeEvent(EventType.Released); + break; + + default: + case MotionEventActions.Outside: + case MotionEventActions.HoverEnter: + case MotionEventActions.HoverExit: + case MotionEventActions.HoverMove: + case MotionEventActions.Mask: + case MotionEventActions.Pointer2Down: + case MotionEventActions.Pointer2Up: + case MotionEventActions.Pointer3Down: + case MotionEventActions.Pointer3Up: + case MotionEventActions.PointerIdMask: + case MotionEventActions.PointerIdShift: + break; + } + + if (e.Event.ActionMasked != MotionEventActions.Move) + { + this.ignored = false; + this.firstX = null; + this.firstY = null; + } + } + + private void OnPointerMoved(MotionEvent motionEvent) + { + if (motionEvent != null) + { + var x = motionEvent.GetX(); + var y = motionEvent.GetY(); + + if (!this.firstX.HasValue || !this.firstY.HasValue) + { + this.firstX = x; + this.firstY = y; + } + + var maxDelta = 10; + var deltaX = Math.Abs(x - this.firstX.Value); + var deltaY = Math.Abs(y - this.firstY.Value); + if (!this.ignored && (deltaX > maxDelta || deltaY > maxDelta)) + { + this.ignored = true; + _touchAndPressEffectConsumer?.ConsumeEvent(EventType.Ignored); + } + } + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchReleaseEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchReleaseEffect.cs new file mode 100644 index 00000000..4b167566 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Android/Effects/TouchReleaseEffect.cs @@ -0,0 +1,43 @@ +using Android.Views; +using Microsoft.Maui.Controls.Platform; +using View = Android.Views.View; + +namespace Maui.FreakyControls.Platforms.Android; + +internal class TouchReleaseEffect : PlatformEffect +{ + private Action _onRelease; + private View _view; + + protected override void OnAttached() + { + _view = Control ?? Container; + + if (_view != null) + { + var touchReleaseEffect = (Maui.FreakyControls.Shared.TouchPress.ToouchReleaseRoutingEffect)Element.Effects.FirstOrDefault(x => x is TouchReleaseEffect); + if (touchReleaseEffect != null && touchReleaseEffect.OnRelease != null) + { + _onRelease = touchReleaseEffect.OnRelease; + _view.Touch += OnViewOnTouch; + } + } + } + + protected override void OnDetached() + { + if (_view != null) + _view.Touch -= OnViewOnTouch; + } + + private void OnViewOnTouch(object sender, View.TouchEventArgs e) + { + e.Handled = false; + + if (e.Event.ActionMasked == MotionEventActions.Up) + { + System.Diagnostics.Debug.WriteLine(e.Event.ActionMasked); + _onRelease.Invoke(); + } + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchAndPressEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchAndPressEffect.cs new file mode 100644 index 00000000..3c1228cb --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchAndPressEffect.cs @@ -0,0 +1,14 @@ +using Microsoft.Maui.Controls.Platform; + +namespace Maui.FreakyControls.Platforms.MacCatalyst; + +internal class TouchAndPressEffect : PlatformEffect +{ + protected override void OnAttached() + { + } + + protected override void OnDetached() + { + } +} diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchReleaseEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchReleaseEffect.cs new file mode 100644 index 00000000..7f91268e --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/MacCatalyst/TouchReleaseEffect.cs @@ -0,0 +1,14 @@ +using Microsoft.Maui.Controls.Platform; + +namespace Maui.FreakyControls.Platforms.MacCatalyst; + +internal class TouchReleaseEffect : PlatformEffect +{ + protected override void OnAttached() + { + } + + protected override void OnDetached() + { + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchAndPressEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchAndPressEffect.cs new file mode 100644 index 00000000..9f0256a3 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchAndPressEffect.cs @@ -0,0 +1,13 @@ +using System; +namespace Maui.FreakyControls.Platforms.Windows; + +internal class TouchAndPressEffect : PlatformEffect +{ + protected override void OnAttached() + { + } + + protected override void OnDetached() + { + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchReleaseEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchReleaseEffect.cs new file mode 100644 index 00000000..4cbdfa6c --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/Windows/TouchReleaseEffect.cs @@ -0,0 +1,14 @@ +using Microsoft.Maui.Controls.Platform; + +namespace Maui.FreakyControls.Platforms.Windows; + +internal class TouchReleaseEffect : PlatformEffect +{ + protected override void OnAttached() + { + } + + protected override void OnDetached() + { + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchAndPressEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchAndPressEffect.cs new file mode 100644 index 00000000..46c68c33 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchAndPressEffect.cs @@ -0,0 +1,88 @@ +using Foundation; +using Maui.FreakyControls.Shared.Enums; +using Maui.FreakyControls.Shared.TouchPress; +using Microsoft.Maui.Controls.Platform; +using UIKit; + +namespace Maui.FreakyControls.Platforms.iOS +{ + internal class TouchAndPressEffect : PlatformEffect + { + private UIView _view; + private TouchAndPressGestureRecognizer _touchAndPressGestureRecognizer; + + protected override void OnAttached() + { + _view = Control ?? Container; + + if (Element is ITouchPressEffect touchAndPressEffectConsumer) + { + _view.UserInteractionEnabled = true; + + _touchAndPressGestureRecognizer = new TouchAndPressGestureRecognizer(touchAndPressEffectConsumer); + _view.AddGestureRecognizer(_touchAndPressGestureRecognizer); + } + } + + protected override void OnDetached() + { + if (_view != null && _touchAndPressGestureRecognizer != null) + { + _view.RemoveGestureRecognizer(_touchAndPressGestureRecognizer); + } + } + + private sealed class TouchAndPressGestureRecognizer : UIGestureRecognizer + { + private readonly ITouchPressEffect _touchAndPressEffectConsumer; + + public TouchAndPressGestureRecognizer(ITouchPressEffect touchAndPressEffectConsumer) + { + _touchAndPressEffectConsumer = touchAndPressEffectConsumer; + } + + public override void PressesBegan(NSSet presses, UIPressesEvent evt) + { + base.PressesBegan(presses, evt); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Pressing); + } + + public override void TouchesBegan(NSSet touches, UIEvent evt) + { + base.TouchesBegan(touches, evt); + + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Pressing); + } + + public override void PressesEnded(NSSet presses, UIPressesEvent evt) + { + base.PressesEnded(presses, evt); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Released); + } + + public override void TouchesEnded(NSSet touches, UIEvent evt) + { + base.TouchesEnded(touches, evt); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Released); + } + + public override void PressesCancelled(NSSet presses, UIPressesEvent evt) + { + base.PressesCancelled(presses, evt); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Cancelled); + } + + public override void TouchesCancelled(NSSet touches, UIEvent evt) + { + base.TouchesCancelled(touches, evt); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Cancelled); + } + + public override void IgnoreTouch(UITouch touch, UIEvent forEvent) + { + base.IgnoreTouch(touch, forEvent); + _touchAndPressEffectConsumer.ConsumeEvent(EventType.Ignored); + } + } + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchReleaseEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchReleaseEffect.cs new file mode 100644 index 00000000..6bd2be14 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Platforms/iOS/Effects/TouchReleaseEffect.cs @@ -0,0 +1,15 @@ +using Microsoft.Maui.Controls.Platform; + +namespace Maui.FreakyControls.Platforms.iOS +{ + internal class TouchReleaseEffect : PlatformEffect + { + protected override void OnAttached() + { + } + + protected override void OnDetached() + { + } + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ITouchPressEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ITouchPressEffect.cs new file mode 100644 index 00000000..a3c5d5ba --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ITouchPressEffect.cs @@ -0,0 +1,14 @@ +using Maui.FreakyControls.Shared.Enums; + +namespace Maui.FreakyControls.Shared.TouchPress; + +internal interface ITouchPressEffect +{ + ButtonAnimations Animation { get; set; } + + bool IsEnabled { get; set; } + + void ConsumeEvent(EventType gestureType); + + void ExecuteAction(); +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ToouchReleaseRoutingEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ToouchReleaseRoutingEffect.cs new file mode 100644 index 00000000..d3314e48 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/ToouchReleaseRoutingEffect.cs @@ -0,0 +1,11 @@ +namespace Maui.FreakyControls.Shared.TouchPress; + +internal class ToouchReleaseRoutingEffect : RoutingEffect +{ + public ToouchReleaseRoutingEffect(Action onRelease) + { + OnRelease = onRelease; + } + + public Action OnRelease { get; set; } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressAnimation.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressAnimation.cs new file mode 100644 index 00000000..b6f83c46 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressAnimation.cs @@ -0,0 +1,67 @@ +using Maui.FreakyControls.Shared.Enums; + +namespace Maui.FreakyControls.Shared.TouchPress; + +internal static class TouchAndPressAnimation +{ + public static void Animate(View view, EventType gestureType) + { + var touchAndPressEffectConsumer = view as ITouchPressEffect; + + switch (gestureType) + { + case EventType.Pressing: + SetAnimation(view, touchAndPressEffectConsumer); + break; + + case EventType.Cancelled: + case EventType.Released: + touchAndPressEffectConsumer.ExecuteAction(); + RestoreAnimation(view, touchAndPressEffectConsumer); + break; + + case EventType.Ignored: + RestoreAnimation(view, touchAndPressEffectConsumer); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(gestureType), gestureType, null); + } + } + + private static void RestoreAnimation(View view, ITouchPressEffect touchAndPressEffectConsumer) + { + if (touchAndPressEffectConsumer.Animation != ButtonAnimations.None) + { + Task.Run(async () => + { + if (touchAndPressEffectConsumer.Animation == ButtonAnimations.Fade) + await view.FadeTo(1, 500); + else if (touchAndPressEffectConsumer.Animation == ButtonAnimations.Scale) + await view.ScaleTo(1, 100); + else if (touchAndPressEffectConsumer.Animation == ButtonAnimations.FadeAndScale) + { + await Task.WhenAll(view.ScaleTo(1, 100), view.FadeTo(1, 500)); + } + }); + } + } + + private static void SetAnimation(View view, ITouchPressEffect touchAndPressEffectConsumer) + { + if (touchAndPressEffectConsumer.Animation != ButtonAnimations.None && touchAndPressEffectConsumer.IsEnabled) + { + Task.Run(async () => + { + if (touchAndPressEffectConsumer.Animation == ButtonAnimations.Fade) + await view.FadeTo(0.7, 100); + else if (touchAndPressEffectConsumer.Animation == ButtonAnimations.Scale) + await view.ScaleTo(0.95, 100); + else if (touchAndPressEffectConsumer.Animation == ButtonAnimations.FadeAndScale) + { + await Task.WhenAll(view.ScaleTo(0.95, 100), view.FadeTo(0.7, 100)); + } + }); + } + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressRoutingEffect.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressRoutingEffect.cs new file mode 100644 index 00000000..c7d36ac4 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Effects/TouchAndPressRoutingEffect.cs @@ -0,0 +1,5 @@ +namespace Maui.FreakyControls.Shared.TouchPress; + +internal class TouchAndPressRoutingEffect : RoutingEffect +{ +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/ButtonAnimations.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/ButtonAnimations.cs new file mode 100644 index 00000000..1d10390f --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/ButtonAnimations.cs @@ -0,0 +1,9 @@ +namespace Maui.FreakyControls.Shared.Enums; + +public enum ButtonAnimations +{ + None, + Fade, + Scale, + FadeAndScale +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/EventType.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/EventType.cs new file mode 100644 index 00000000..4c72c02a --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Enums/EventType.cs @@ -0,0 +1,9 @@ +namespace Maui.FreakyControls.Shared.Enums; + +public enum EventType +{ + Pressing, + Released, + Cancelled, + Ignored +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Extensions/Extensions.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Extensions/Extensions.cs index 8da9b626..c5921ea1 100644 --- a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Extensions/Extensions.cs +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/Extensions/Extensions.cs @@ -1,9 +1,18 @@ using SkiaSharp.Views.Maui.Controls.Hosting; using System.Windows.Input; +using TouchAndPressRoutingEffect = Maui.FreakyControls.Shared.TouchPress.TouchAndPressRoutingEffect; +using TouchReleaseRoutingEffect = Maui.FreakyControls.Shared.TouchPress.ToouchReleaseRoutingEffect; +#if MACCATALYST +using Maui.FreakyControls.Platforms.MacCatalyst; +#endif +#if WINDOWS +using Maui.FreakyControls.Platforms.Windows; +#endif #if ANDROID using Microsoft.Maui.Controls.Compatibility.Platform.Android; +using Maui.FreakyControls.Platforms.Android; using static Microsoft.Maui.ApplicationModel.Platform; using NativeImage = Android.Graphics.Bitmap; @@ -28,12 +37,27 @@ public static void ExecuteCommandIfAvailable(this ICommand command, object param } } - public static void AddFreakyHandlers(this IMauiHandlersCollection handlers) + public static void InitializeFreakyControls(this MauiAppBuilder builder, bool useSkiaSharp = true) + { + if (useSkiaSharp) + { + builder.UseSkiaSharp(); + } + builder.ConfigureMauiHandlers(builders => builders.AddHandlers()); + builder.ConfigureEffects(effects =>effects.AddEffects()); + } + + private static void AddEffects(this IEffectsBuilder effects) + { + effects.Add(); + effects.Add(); + } + + private static void AddHandlers(this IMauiHandlersCollection handlers) { handlers.AddHandler(typeof(FreakyEditor), typeof(FreakyEditorHandler)); handlers.AddHandler(typeof(FreakyEntry), typeof(FreakyEntryHandler)); handlers.AddHandler(typeof(FreakyCircularImage), typeof(FreakyCircularImageHandler)); - handlers.AddHandler(typeof(FreakyButton), typeof(FreakyButtonHandler)); handlers.AddHandler(typeof(FreakyDatePicker), typeof(FreakyDatePickerHandler)); handlers.AddHandler(typeof(FreakyTimePicker), typeof(FreakyTimePickerHandler)); handlers.AddHandler(typeof(FreakyPicker), typeof(FreakyPickerHandler)); @@ -41,6 +65,10 @@ public static void AddFreakyHandlers(this IMauiHandlersCollection handlers) handlers.AddHandler(typeof(FreakySignatureCanvasView), typeof(FreakySignatureCanvasViewHandler)); } + [Obsolete("Please use InitializeFreakyControls instead.")] + public static void AddFreakyHandlers(this IMauiHandlersCollection handlers) => handlers.AddHandlers(); + + [Obsolete("Please use InitializeFreakyControls instead.")] public static void InitSkiaSharp(this MauiAppBuilder mauiAppBuilder) { mauiAppBuilder.UseSkiaSharp(); diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.cs deleted file mode 100644 index ff4d063e..00000000 --- a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Maui.FreakyControls; - -public class FreakyButton : Button -{ -} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml new file mode 100644 index 00000000..c5ee8874 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml.cs new file mode 100644 index 00000000..488058a8 --- /dev/null +++ b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButton.xaml.cs @@ -0,0 +1,399 @@ +using Maui.FreakyControls.Extensions; +using Maui.FreakyControls.Shared.Enums; +using Maui.FreakyControls.Shared.TouchPress; +using System.Windows.Input; + +namespace Maui.FreakyControls; + +public partial class FreakyButton : ContentView, ITouchPressEffect +{ + public static readonly string IsBusyVisualState = "Busy"; + + #region Bindable properties + + public static readonly BindableProperty ActivityIndicatorSizeProperty = + BindableProperty.Create(nameof(ActivityIndicatorSize), typeof(double), typeof(FreakyButton), defaultValue: 30.0); + + public static readonly BindableProperty AnimationProperty = + BindableProperty.Create(nameof(Animation), typeof(ButtonAnimations), typeof(FreakyButton), defaultValue: ButtonAnimations.Fade); + + public static readonly BindableProperty AreIconsDistantProperty = + BindableProperty.Create(nameof(AreIconsDistant), typeof(bool), typeof(FreakyButton), defaultValue: true, propertyChanged: OnIconsAreExpandedChanged); + + public new static readonly BindableProperty BackgroundColorProperty = + BindableProperty.Create(nameof(BackgroundColor), typeof(Color), typeof(FreakyButton), defaultValue: Colors.Black); + + public static readonly BindableProperty BorderColorProperty = + BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(FreakyButton), defaultValue: Colors.White); + + public static readonly BindableProperty BorderWidthProperty = + BindableProperty.Create(nameof(BorderWidth), typeof(double), typeof(FreakyButton), defaultValue: Button.BorderWidthProperty.DefaultValue); + + public static readonly BindableProperty BusyColorProperty = + BindableProperty.Create(nameof(BusyColor), typeof(Color), typeof(FreakyButton), defaultValue: Colors.White); + + public static readonly BindableProperty CharacterSpacingProperty = + BindableProperty.Create(nameof(CharacterSpacing), typeof(double), typeof(FreakyButton), Button.CharacterSpacingProperty.DefaultValue); + + public static readonly BindableProperty CommandParameterProperty = + BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(FreakyButton), defaultValue: null); + + public static readonly BindableProperty CommandProperty = + BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(FreakyButton), defaultValue: null); + + public static readonly BindableProperty CornerRadiusProperty = + BindableProperty.Create( + nameof(CornerRadius), + typeof(CornerRadius), + typeof(FreakyButton), + new CornerRadius(10)); + + public static readonly BindableProperty FontAttributesProperty = + BindableProperty.Create(nameof(FontAttributes), typeof(FontAttributes), typeof(FreakyButton), defaultValue: FontAttributes.None); + + public static readonly BindableProperty FontAutoScalingEnabledProperty = + BindableProperty.Create(nameof(FontAutoScalingEnabled), typeof(bool), typeof(FreakyButton), defaultValue: true); + + public static readonly BindableProperty FontFamilyProperty = + BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(FreakyButton), defaultValue: null); + + public static readonly BindableProperty FontSizeProperty = + BindableProperty.Create(nameof(FontSize), typeof(double), typeof(FreakyButton), defaultValue: Microsoft.Maui.Font.Default.Size); + + public static readonly BindableProperty HorizontalTextAlignmentProperty = + BindableProperty.Create( + nameof(HorizontalTextAlignment), + typeof(TextAlignment), + typeof(FreakyButton), + TextAlignment.Center); + + public static readonly BindableProperty IconSizeProperty = + BindableProperty.Create(nameof(IconSize), typeof(double), typeof(FreakyButton), defaultValue: 24.0); + + public static readonly BindableProperty IsBusyProperty = + BindableProperty.Create(nameof(IsBusy), typeof(bool), typeof(FreakyButton), defaultValue: false, propertyChanged: OnIsBusyPropertyChanged); + + public new static readonly BindableProperty IsEnabledProperty = + BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(FreakyButton), defaultValue: true); + + public static readonly BindableProperty LeadingIconProperty = + BindableProperty.Create(nameof(LeadingIcon), typeof(View), typeof(FreakyButton), defaultValue: null, propertyChanged: OnLeadingIconChanged); + + public static readonly BindableProperty LineBreakModeProperty = + BindableProperty.Create(nameof(LineBreakMode), typeof(LineBreakMode), typeof(FreakyButton), defaultValue: LineBreakMode.NoWrap); + + public new static readonly BindableProperty PaddingProperty = + BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(FreakyButton), defaultValue: new Thickness(12, 0)); + + public static readonly BindableProperty SpacingProperty = + BindableProperty.Create(nameof(Spacing), typeof(int), typeof(FreakyButton), defaultValue: 12); + + public static readonly BindableProperty TextColorProperty = + BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(FreakyButton), defaultValue: Colors.White); + + public static readonly BindableProperty TextDecorationsProperty = + BindableProperty.Create(nameof(TextDecorations), typeof(TextDecorations), typeof(FreakyButton), defaultValue: TextDecorations.None); + + public static readonly BindableProperty TextProperty = + BindableProperty.Create(nameof(Text), typeof(string), typeof(FreakyButton), defaultValue: null); + + public static readonly BindableProperty TextTransformProperty = + BindableProperty.Create(nameof(TextTransform), typeof(TextTransform), typeof(FreakyButton), defaultValue: TextTransform.None); + + public static readonly BindableProperty TextTypeProperty = + BindableProperty.Create(nameof(TextType), typeof(TextType), typeof(FreakyButton), defaultValue: TextType.Text); + + public static readonly BindableProperty TrailingIconProperty = + BindableProperty.Create(nameof(TrailingIcon), typeof(View), typeof(FreakyButton), defaultValue: null, propertyChanged: OnTrailingIconChanged); + + public static readonly BindableProperty VerticalTextAlignmentProperty = + BindableProperty.Create( + nameof(VerticalTextAlignment), + typeof(TextAlignment), + typeof(FreakyButton), + TextAlignment.Center); + + public event EventHandler Clicked; + + public double ActivityIndicatorSize + { + get { return (double)GetValue(ActivityIndicatorSizeProperty); } + set { SetValue(ActivityIndicatorSizeProperty, value); } + } + + public ButtonAnimations Animation + { + get => (ButtonAnimations)GetValue(AnimationProperty); + set => SetValue(AnimationProperty, value); + } + + public bool AreIconsDistant + { + get => (bool)GetValue(AreIconsDistantProperty); + set => SetValue(AreIconsDistantProperty, value); + } + + public new Color BackgroundColor + { + get => (Color)GetValue(BackgroundColorProperty); + set => SetValue(BackgroundColorProperty, value); + } + + public Color BorderColor + { + get => (Color)GetValue(BorderColorProperty); + set => SetValue(BorderColorProperty, value); + } + + public double BorderWidth + { + get => (double)GetValue(BorderWidthProperty); + set { SetValue(BorderWidthProperty, value); } + } + + public Color BusyColor + { + get => (Color)GetValue(BusyColorProperty); + set => SetValue(BusyColorProperty, value); + } + + public double CharacterSpacing + { + get { return (double)GetValue(CharacterSpacingProperty); } + set { SetValue(CharacterSpacingProperty, value); } + } + + public ICommand Command + { + get => (ICommand)GetValue(CommandProperty); + set => SetValue(CommandProperty, value); + } + + public object CommandParameter + { + get => GetValue(CommandParameterProperty); + set => SetValue(CommandParameterProperty, value); + } + + public CornerRadius CornerRadius + { + get => (CornerRadius)GetValue(CornerRadiusProperty); + set => SetValue(CornerRadiusProperty, value); + } + + public FontAttributes FontAttributes + { + get => (FontAttributes)GetValue(FontAttributesProperty); + set => SetValue(FontAttributesProperty, value); + } + + public bool FontAutoScalingEnabled + { + get => (bool)GetValue(FontAutoScalingEnabledProperty); + set => SetValue(FontAutoScalingEnabledProperty, value); + } + + public string FontFamily + { + get { return (string)GetValue(FontFamilyProperty); } + set { SetValue(FontFamilyProperty, value); } + } + + public double FontSize + { + get => (double)GetValue(FontSizeProperty); + set => SetValue(FontSizeProperty, value); + } + + public TextAlignment HorizontalTextAlignment + { + get => (TextAlignment)GetValue(HorizontalTextAlignmentProperty); + set => SetValue(HorizontalTextAlignmentProperty, value); + } + + public double IconSize + { + get { return (double)GetValue(IconSizeProperty); } + set { SetValue(IconSizeProperty, value); } + } + + public bool IsBusy + { + get => (bool)GetValue(IsBusyProperty); + set => SetValue(IsBusyProperty, value); + } + + public new bool IsEnabled + { + get => (bool)GetValue(IsEnabledProperty); + set => SetValue(IsEnabledProperty, value); + } + + public View LeadingIcon + { + get { return (View)GetValue(LeadingIconProperty); } + set { SetValue(LeadingIconProperty, value); } + } + + public LineBreakMode LineBreakMode + { + get => (LineBreakMode)GetValue(LineBreakModeProperty); + set => SetValue(LineBreakModeProperty, value); + } + + public new Thickness Padding + { + get { return (Thickness)GetValue(PaddingProperty); } + set { SetValue(PaddingProperty, value); } + } + + public int Spacing + { + get { return (int)GetValue(SpacingProperty); } + set { SetValue(SpacingProperty, value); } + } + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public Color TextColor + { + get => (Color)GetValue(TextColorProperty); + set => SetValue(TextColorProperty, value); + } + + public TextDecorations TextDecorations + { + get => (TextDecorations)GetValue(TextDecorationsProperty); + set => SetValue(TextDecorationsProperty, value); + } + + public TextTransform TextTransform + { + get => (TextTransform)GetValue(TextTransformProperty); + set => SetValue(TextTransformProperty, value); + } + + public TextType TextType + { + get { return (TextType)GetValue(TextTypeProperty); } + set { SetValue(TextTypeProperty, value); } + } + + public View TrailingIcon + { + get { return (View)GetValue(TrailingIconProperty); } + set { SetValue(TrailingIconProperty, value); } + } + + public TextAlignment VerticalTextAlignment + { + get => (TextAlignment)GetValue(VerticalTextAlignmentProperty); + set => SetValue(VerticalTextAlignmentProperty, value); + } + + private static void OnIconsAreExpandedChanged(BindableObject bindable, object oldValue, object newValue) + { + var freakyButton = bindable as FreakyButton; + var areIconsExpanded = (bool)newValue; + freakyButton.mainGrid.HorizontalOptions = areIconsExpanded ? LayoutOptions.Fill : LayoutOptions.Center; + } + + private static async void OnIsBusyPropertyChanged(BindableObject bindable, object oldValue, object newValue) + { + var freakyButton = bindable as FreakyButton; + var isBusy = (bool)newValue; + if (isBusy) + { + await freakyButton.txtLabel.TranslateTo(0, -35, 300, Easing.Linear); + freakyButton.txtLabel.IsVisible = false; + freakyButton.activityIndicator.IsVisible = true; + await freakyButton.activityIndicator.TranslateTo(0, 0, 200, Easing.Linear); + } + else + { + freakyButton.txtLabel.IsVisible = false; + freakyButton.txtLabel.IsVisible = true; + freakyButton.activityIndicator.TranslationY = 35; + freakyButton.txtLabel.TranslationY = 0; + } + } + + private static void OnLeadingIconChanged(BindableObject bindable, object oldValue, object newValue) + { + var freakyButton = bindable as FreakyButton; + if (newValue != null) + { + freakyButton.leadingContentView.IsVisible = true; + freakyButton.leadingContentView.Content = newValue as View; + } + else + { + freakyButton.leadingContentView.IsVisible = true; + } + } + + private static void OnTrailingIconChanged(BindableObject bindable, object oldValue, object newValue) + { + var freakyButton = bindable as FreakyButton; + if (newValue != null) + { + freakyButton.trailingContentView.IsVisible = true; + freakyButton.trailingContentView.Content = newValue as View; + } + else + { + freakyButton.trailingContentView.IsVisible = true; + } + } + + #endregion Bindable properties + + #region Constructors + + public FreakyButton() + { + InitializeComponent(); + this.Effects.Add(new TouchAndPressRoutingEffect()); + } + + #endregion Constructors + + #region Methods + + public void ConsumeEvent(EventType gestureType) + { + if (IsEnabled) + TouchAndPressAnimation.Animate(this, gestureType); + } + + public void ExecuteAction() + { + if (IsEnabled) + Command?.ExecuteCommandIfAvailable(CommandParameter); + + if (IsEnabled && Clicked != null) + Clicked.Invoke(this, EventArgs.Empty); + } + + protected override void ChangeVisualState() + { + if (IsBusy) + { + VisualStateManager.GoToState(this, FreakyButton.IsBusyVisualState); + } + else if (IsEnabled) + { + VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Normal); + } + else + { + VisualStateManager.GoToState(this, VisualStateManager.CommonStates.Disabled); + } + } + + #endregion Methods +} \ No newline at end of file diff --git a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButtonHandler.cs b/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButtonHandler.cs deleted file mode 100644 index 26e4051f..00000000 --- a/MAUI.FreakyControls/MAUI.FreakyControls/Shared/FreakyButton/FreakyButtonHandler.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Microsoft.Maui.Handlers; -namespace Maui.FreakyControls; -public partial class FreakyButtonHandler : ButtonHandler -{ -} \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/AppShell.xaml.cs b/MAUI.FreakyControls/Samples/AppShell.xaml.cs index 4942a034..5a159eab 100644 --- a/MAUI.FreakyControls/Samples/AppShell.xaml.cs +++ b/MAUI.FreakyControls/Samples/AppShell.xaml.cs @@ -2,14 +2,15 @@ public partial class AppShell : Shell { + internal const string buttons = "Buttons"; + internal const string checkboxes = "Checkboxes"; + internal const string imageViews = "ImageViews"; internal const string inputViews = "InputViews"; - internal const string textInputLayout = "TextInputLayouts"; internal const string pickers = "Pickers"; - internal const string imageViews = "ImageViews"; - internal const string signatureView = "SignatureView"; - internal const string signaturePreview = "ImageDisplay"; - internal const string checkboxes = "Checkboxes"; internal const string radioButtons = "RadioButtons"; + internal const string signaturePreview = "ImageDisplay"; + internal const string signatureView = "SignatureView"; + internal const string textInputLayout = "TextInputLayouts"; public AppShell() { @@ -22,5 +23,6 @@ public AppShell() Routing.RegisterRoute(signaturePreview, typeof(SignatureView.ImageDisplay)); Routing.RegisterRoute(checkboxes, typeof(Checkboxes.CheckboxesView)); Routing.RegisterRoute(radioButtons, typeof(RadioButtons.RadioButtonsView)); + Routing.RegisterRoute(buttons, typeof(ButtonsView.ButtonsView)); } } \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml new file mode 100644 index 00000000..0f989ac7 --- /dev/null +++ b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml.cs b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml.cs new file mode 100644 index 00000000..f064aca0 --- /dev/null +++ b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsView.xaml.cs @@ -0,0 +1,9 @@ +namespace Samples.ButtonsView; + +public partial class ButtonsView : ContentPage +{ + public ButtonsView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/ButtonsView/ButtonsViewModel.cs b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsViewModel.cs new file mode 100644 index 00000000..50b414fa --- /dev/null +++ b/MAUI.FreakyControls/Samples/ButtonsView/ButtonsViewModel.cs @@ -0,0 +1,5 @@ +namespace Samples.ButtonsView; + +public class ButtonsViewModel : MainViewModel +{ +} \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/MainViewModel.cs b/MAUI.FreakyControls/Samples/MainViewModel.cs index 780d5a43..90642f92 100644 --- a/MAUI.FreakyControls/Samples/MainViewModel.cs +++ b/MAUI.FreakyControls/Samples/MainViewModel.cs @@ -7,35 +7,9 @@ namespace Samples { public class MainViewModel : BaseViewModel { - public ICommand ImageWasTappedCommand - { - get; set; - } - private ObservableCollection _suggestionItem; - private ObservableCollection items; - - public ObservableCollection SuggestionItem - { - get => _suggestionItem; - set - { - _suggestionItem = value; - OnPropertyChanged(); - } - } - public ObservableCollection Items - { - get => items; - set - { - items = value; - OnPropertyChanged(); - } - } - - public ICommand FreakyLongPressedCommand { get; set; } + private ObservableCollection items; public MainViewModel() { @@ -50,7 +24,8 @@ public MainViewModel() AppShell.imageViews, AppShell.signatureView, AppShell.checkboxes, - AppShell.radioButtons + AppShell.radioButtons, + AppShell.buttons }; var strSuggestionArr = new string[] { @@ -64,9 +39,31 @@ public MainViewModel() SuggestionItem = new ObservableCollection(strSuggestionArr.ToList()); } - private async Task LongPressedAsync(object commandParam) + public ICommand FreakyLongPressedCommand { get; set; } + + public ICommand ImageWasTappedCommand { - await Application.Current.MainPage.DisplayAlert(commandParam?.ToString(), "Long pressed yo :D", "Ok"); + get; set; + } + + public ObservableCollection Items + { + get => items; + set + { + items = value; + OnPropertyChanged(); + } + } + + public ObservableCollection SuggestionItem + { + get => _suggestionItem; + set + { + _suggestionItem = value; + OnPropertyChanged(); + } } private async Task ImageTappedAsync() @@ -74,5 +71,10 @@ private async Task ImageTappedAsync() await MainThread.InvokeOnMainThreadAsync(() => Application.Current.MainPage.DisplayAlert("Title", "The image was clicked on that FreakyEntry", "Ok")); } + + private async Task LongPressedAsync(object commandParam) + { + await Application.Current.MainPage.DisplayAlert(commandParam?.ToString(), "Long pressed yo :D", "Ok"); + } } } \ No newline at end of file diff --git a/MAUI.FreakyControls/Samples/MauiProgram.cs b/MAUI.FreakyControls/Samples/MauiProgram.cs index 97b8986a..ad70a1e9 100644 --- a/MAUI.FreakyControls/Samples/MauiProgram.cs +++ b/MAUI.FreakyControls/Samples/MauiProgram.cs @@ -16,12 +16,8 @@ public static MauiApp CreateMauiApp() { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); - }) - .ConfigureMauiHandlers(handlers => - { - handlers.AddFreakyHandlers(); }); - builder.InitSkiaSharp(); + builder.InitializeFreakyControls(); return builder.Build(); } } \ No newline at end of file diff --git a/README.md b/README.md index 70c7fab6..ece4d5e6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ + @@ -33,6 +34,8 @@ For more details and API documentation check our [Wiki](https://github.com/Freak | | | | | | | | | +| | | + ## License @@ -44,17 +47,17 @@ The license for this project can be found [here](https://github.com/FreakyAli/Ma Add our [NuGet](https://www.nuget.org/packages/FreakyControls) package or -Run the following command to add nuget to your .Net MAUI app: +Run the following command to add our Nuget to your .Net MAUI app: Install-Package FreakyControls -Version xx.xx.xx -Adding FreakyControlsHandlers to your MAUI app: +**Adding FreakyControlsHandlers to your MAUI app**: Add the following using statement and then Init the handlers in your MauiProgram: using MAUI.FreakyControls.Extensions; -And then in your MauiProgram which would be something like below : +### For Version 4.3 and below: namespace Something; @@ -67,15 +70,41 @@ And then in your MauiProgram which would be something like below : { handlers.AddFreakyHandlers(); // To Init your freaky handlers for Entry and Editor }); - // This line is needed for the follow issue: https://github.com/mono/SkiaSharp/issues/1979 + // This line is needed for the following issue: https://github.com/mono/SkiaSharp/issues/1979 builder.InitSkiaSharp(); // Use this if you want to use FreakySvgImageView return builder.Build(); - } - + } + +### Post version 4.3: + + namespace Samples; + + public static class MauiProgram + { + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .ConfigureFonts(fonts => + { + fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); + fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); + }); + //Initialization is now a one-liner and the old methods have been deprecated and will be removed in future updates. + //Takes one argument if you would like to init Skiasharp through FreakyControls or not (Used for RadioButton, Checkbox & SVGImageView) + builder.InitializeFreakyControls(); + return builder.Build(); + } + } Now you can use the controls in your app. ## Activity +Fossa: + +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FFreakyAli%2FMaui.FreakyControls.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FFreakyAli%2FMaui.FreakyControls?ref=badge_large) + Sparkline: [![Sparkline](https://stars.medv.io/FreakyAli/Maui.FreakyControls.svg)](https://stars.medv.io/FreakyAli/Maui.FreakyControls)