diff --git a/sandbox/SandboxMAUI/Pages/CheckBoxPage.xaml b/sandbox/SandboxMAUI/Pages/CheckBoxPage.xaml index 971f104..8853f45 100644 --- a/sandbox/SandboxMAUI/Pages/CheckBoxPage.xaml +++ b/sandbox/SandboxMAUI/Pages/CheckBoxPage.xaml @@ -14,12 +14,12 @@ - + - - - - + + + + diff --git a/src/InputKit.Maui/Shared/Controls/CheckBox.cs b/src/InputKit.Maui/Shared/Controls/CheckBox.cs index 8652aeb..e326d63 100644 --- a/src/InputKit.Maui/Shared/Controls/CheckBox.cs +++ b/src/InputKit.Maui/Shared/Controls/CheckBox.cs @@ -2,11 +2,10 @@ using InputKit.Shared.Configuration; using InputKit.Shared.Helpers; using InputKit.Shared.Layouts; -using Microsoft.Maui; -using Microsoft.Maui.Controls; -using Microsoft.Maui.Graphics; -using System; +using Microsoft.Maui.Controls.Shapes; +using System.ComponentModel; using System.Windows.Input; +using Path = Microsoft.Maui.Controls.Shapes.Path; namespace InputKit.Shared.Controls; @@ -26,18 +25,36 @@ public partial class CheckBox : StatefulStackLayout, IValidatable FontSize = 14, LabelPosition = LabelPosition.After }; + protected static PathGeometryConverter PathGeometryConverter { get; } = new PathGeometryConverter(); #region Constants - public const string RESOURCE_CHECK = "InputKit.Shared.Resources.check.png"; - public const string RESOURCE_CROSS = "InputKit.Shared.Resources.cross.png"; - public const string RESOURCE_STAR = "InputKit.Shared.Resources.star.png"; + public const string PATH_CHECK = "M 6.5212 16.4777 l -6.24 -6.24 c -0.3749 -0.3749 -0.3749 -0.9827 0 -1.3577 l 1.3576 -1.3577 c 0.3749 -0.3749 0.9828 -0.3749 1.3577 0 L 7.2 11.7259 L 16.2036 2.7224 c 0.3749 -0.3749 0.9828 -0.3749 1.3577 0 l 1.3576 1.3577 c 0.3749 0.3749 0.3749 0.9827 0 1.3577 l -11.04 11.04 c -0.3749 0.3749 -0.9828 0.3749 -1.3577 -0 z"; + public const string PATH_SQUARE = "M12 12H0V0h12v12z"; + public const string PATH_LINE = "M 17.2026 6.7911 H 0.9875 C 0.4422 6.7911 0 7.2332 0 7.7784 v 2.6331 c 0 0.5453 0.442 0.9873 0.9875 0.9873 h 16.2151 c 0.5453 0 0.9873 -0.442 0.9873 -0.9873 v -2.6331 C 18.1901 7.2332 17.7481 6.7911 17.2026 6.7911 z"; + internal const double CHECK_SIZE_RATIO = .65; #endregion #region Fields - protected internal Frame frmBackground = new Frame { Padding = 0, CornerRadius = GlobalSetting.CornerRadius, InputTransparent = true, HeightRequest = GlobalSetting.Size, WidthRequest = GlobalSetting.Size, BackgroundColor = GlobalSetting.BackgroundColor, BorderColor = GlobalSetting.BorderColor, VerticalOptions = LayoutOptions.CenterAndExpand, HasShadow = false }; - protected internal BoxView boxSelected = new BoxView { IsVisible = false, HeightRequest = GlobalSetting.Size * .60, WidthRequest = GlobalSetting.Size * .60, Color = GlobalSetting.Color, VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.Center }; - protected internal IconView imgSelected = new IconView { Source = ImageSource.FromResource(RESOURCE_CHECK), FillColor = GlobalSetting.Color, VerticalOptions = LayoutOptions.CenterAndExpand, HorizontalOptions = LayoutOptions.Center, IsVisible = false }; - internal Label lblOption = new Label { VerticalOptions = LayoutOptions.CenterAndExpand, FontSize = GlobalSetting.FontSize, TextColor = GlobalSetting.TextColor, FontFamily = GlobalSetting.FontFamily, IsVisible = false }; + protected internal Grid IconLayout; + protected Rectangle outlineBox = new Rectangle + { + Fill = GlobalSetting.BackgroundColor, + Stroke = GlobalSetting.BorderColor, + StrokeThickness = 2, + WidthRequest = GlobalSetting.Size, + HeightRequest = GlobalSetting.Size, + }; + protected Path selectedIcon = new Path + { + Fill = GlobalSetting.Color, + Aspect = Stretch.Uniform, + HeightRequest = GlobalSetting.Size, + WidthRequest = GlobalSetting.Size, + MaximumHeightRequest = GlobalSetting.Size, + MaximumWidthRequest = GlobalSetting.Size, + Scale = 0, + }; + internal Label lblOption = new Label { VerticalOptions = LayoutOptions.Center, HorizontalOptions = LayoutOptions.Start, FontSize = GlobalSetting.FontSize, TextColor = GlobalSetting.TextColor, FontFamily = GlobalSetting.FontFamily, IsVisible = false }; private CheckType _type = CheckType.Box; private bool _isEnabled; #endregion @@ -50,20 +67,26 @@ public CheckBox() { InitVisualStates(); Orientation = StackOrientation.Horizontal; - Padding = new Thickness(0, 10); Spacing = 10; - frmBackground.Content = boxSelected; - ApplyLabelPosition(LabelPosition); + Padding = new Thickness(0, 10); ApplyIsCheckedAction = ApplyIsChecked; ApplyIsPressedAction = ApplyIsPressed; + + IconLayout = new Grid + { + Children = + { + outlineBox, + selectedIcon + } + }; + + ApplyLabelPosition(LabelPosition); + UpdateType(); GestureRecognizers.Add(new TapGestureRecognizer { Command = new Command(() => { if (IsDisabled) return; IsChecked = !IsChecked; ExecuteCommand(); CheckChanged?.Invoke(this, new EventArgs()); ValidationChanged?.Invoke(this, new EventArgs()); }), }); - - imgSelected.BackgroundColor = Colors.Cyan; - - imgSelected.WidthRequest = 15; } /// @@ -163,12 +186,12 @@ public Color Color /// /// Size of Checkbox /// - public double BoxSize { get => frmBackground.Width; } + public double BoxSize { get => outlineBox.Width; } /// /// SizeRequest of CheckBox /// - public double BoxSizeRequest { get => frmBackground.WidthRequest; set => SetBoxSize(value); } + public double BoxSizeRequest { get => outlineBox.WidthRequest; set => SetBoxSize(value); } /// /// Fontsize of Checkbox text @@ -199,7 +222,11 @@ public Color Color /// public string FontFamily { get => (string)GetValue(FontFamilyProperty); set => SetValue(FontFamilyProperty, value); } - public ImageSource CustomIcon { get => (ImageSource)GetValue(CustomIconProperty); set => SetValue(CustomIconProperty, value); } + [Obsolete("This option is removed. Use CustomIconGeometry")] + public ImageSource CustomIcon { get => default; set { } } + + [TypeConverter(typeof(PathGeometryConverter))] + public Geometry CustomIconGeometry { get => (Geometry)GetValue(CustomIconGeometryProperty); set => SetValue(CustomIconGeometryProperty, value); } public bool IsPressed { get; set; } /// @@ -231,9 +258,9 @@ public LabelPosition LabelPosition public static readonly BindableProperty TextFontSizeProperty = BindableProperty.Create(nameof(TextFontSize), typeof(double), typeof(CheckBox), GlobalSetting.FontSize, propertyChanged: (bo, ov, nv) => (bo as CheckBox).TextFontSize = (double)nv); public static readonly BindableProperty BorderColorProperty = BindableProperty.Create(nameof(BorderColor), typeof(Color), typeof(CheckBox), GlobalSetting.BorderColor, propertyChanged: (bo, ov, nv) => (bo as CheckBox).UpdateBorderColor()); public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(nameof(FontFamily), typeof(string), typeof(CheckBox), Label.FontFamilyProperty.DefaultValue, propertyChanged: (bo, ov, nv) => (bo as CheckBox).UpdateFontFamily(nv?.ToString())); - public static readonly BindableProperty CustomIconProperty = BindableProperty.Create(nameof(CustomIcon), typeof(ImageSource), typeof(CheckBox), default(ImageSource), propertyChanged: (bo, ov, nv) => (bo as CheckBox).UpdateType((bo as CheckBox).Type)); + public static readonly BindableProperty CustomIconGeometryProperty = BindableProperty.Create(nameof(CustomIconGeometry), typeof(Geometry), typeof(CheckBox), defaultValue: GetGeometryFromString(PATH_CHECK), propertyChanged: (bo, ov, nv) => (bo as CheckBox).UpdateType(CheckType.Custom)); public static readonly BindableProperty IsPressedProperty = BindableProperty.Create(nameof(IsPressed), typeof(bool), typeof(CheckBox), propertyChanged: (bo, ov, nv) => (bo as CheckBox).ApplyIsPressedAction(bo as CheckBox, (bool)nv)); - public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(CheckBox), GlobalSetting.CornerRadius, propertyChanged: (bo, ov, nv) => (bo as CheckBox).frmBackground.CornerRadius = (float)nv); + public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(CheckBox), GlobalSetting.CornerRadius, propertyChanged: (bo, ov, nv) => (bo as CheckBox).outlineBox.RadiusX = (float)nv); public static readonly BindableProperty LabelPositionProperty = BindableProperty.Create( propertyName: nameof(LabelPosition), declaringType: typeof(CheckBox), returnType: typeof(LabelPosition), defaultBindingMode: BindingMode.TwoWay, @@ -249,14 +276,14 @@ void ApplyLabelPosition(LabelPosition position) if (position == LabelPosition.After) { lblOption.HorizontalOptions = LayoutOptions.Start; - Children.Add(frmBackground); + Children.Add(IconLayout); Children.Add(lblOption); } else { - lblOption.HorizontalOptions = LayoutOptions.StartAndExpand; + lblOption.HorizontalOptions = LayoutOptions.FillAndExpand; Children.Add(lblOption); - Children.Add(frmBackground); + Children.Add(IconLayout); } } @@ -271,24 +298,24 @@ void UpdateBoxBackground() if (Type == CheckType.Material) return; - frmBackground.BackgroundColor = BoxBackgroundColor; + outlineBox.Fill = BoxBackgroundColor; } void UpdateColors() { - boxSelected.Color = Color; + //selectedIcon.Fill = Color; if (Type == CheckType.Material) { - frmBackground.BorderColor = Color; - frmBackground.BackgroundColor = IsChecked ? Color : Colors.Transparent; - imgSelected.FillColor = Color.ToSurfaceColor(); + outlineBox.Stroke = Color; + outlineBox.Fill = IsChecked ? Color : Colors.Transparent; + selectedIcon.Fill = Color.ToSurfaceColor(); } else { - frmBackground.BorderColor = IsChecked ? Color : BorderColor; - frmBackground.BackgroundColor = BoxBackgroundColor; - imgSelected.FillColor = IconColor == Colors.Transparent ? Color : IconColor; + outlineBox.Stroke = IsChecked ? Color : BorderColor; + outlineBox.Fill = BoxBackgroundColor; + selectedIcon.Fill = IconColor == Colors.Transparent ? Color : IconColor; } } @@ -297,48 +324,39 @@ void UpdateBorderColor() if (Type == CheckType.Material) return; - frmBackground.BorderColor = BorderColor; + outlineBox.Stroke = BorderColor; } - void SetBoxSize(double value) + void SetBoxSize(double size) { - frmBackground.WidthRequest = value; - frmBackground.HeightRequest = value; - boxSelected.WidthRequest = value * .6; //old value 0.72 - boxSelected.HeightRequest = value * 0.6; - //lblSelected.FontSize = value * 0.72; //old value 0.76 //TODO: Do something to resizing - - // TODO: Refactor after MAUI update - (this.Children[0] as View).MinimumWidthRequest = value * 1.4; + outlineBox.HeightRequest = size; + outlineBox.WidthRequest = size; + //selectedIcon.MaximumHeightRequest = size * CHECK_SIZE_RATIO; + //selectedIcon.MaximumWidthRequest = size * CHECK_SIZE_RATIO; } - void UpdateType(CheckType _Type) + void UpdateType(CheckType _Type = CheckType.Custom) { switch (_Type) { case CheckType.Box: - frmBackground.Content = boxSelected; - break; - case CheckType.Check: - imgSelected.Source = ImageSource.FromResource(RESOURCE_CHECK); - frmBackground.Content = imgSelected; + selectedIcon.Data = GetGeometryFromString(PATH_SQUARE); break; - case CheckType.Cross: - imgSelected.Source = ImageSource.FromResource(RESOURCE_CROSS); - frmBackground.Content = imgSelected; + case CheckType.Line: + selectedIcon.Data = GetGeometryFromString(PATH_LINE); break; + + case CheckType.Check: case CheckType.Star: - imgSelected.Source = ImageSource.FromResource(RESOURCE_STAR); - frmBackground.Content = imgSelected; + case CheckType.Cross: + selectedIcon.Data = GetGeometryFromString(PATH_CHECK); break; case CheckType.Material: - imgSelected.Source = ImageSource.FromResource(RESOURCE_CHECK); - frmBackground.CornerRadius = 5; - frmBackground.Content = imgSelected; + outlineBox.RadiusX = 5; + selectedIcon.Data = GetGeometryFromString(PATH_CHECK); break; case CheckType.Custom: - imgSelected.Source = CustomIcon; - frmBackground.Content = imgSelected; + selectedIcon.Data = CustomIconGeometry; break; } @@ -389,16 +407,24 @@ public void DisplayValidation() { } + public static void ApplyIsChecked(CheckBox checkBox, bool isChecked) { - checkBox.frmBackground.Content.IsVisible = isChecked; + checkBox.selectedIcon.ScaleTo(isChecked ? CHECK_SIZE_RATIO : 0, 160); + checkBox.UpdateColors(); } + public static async void ApplyIsPressed(CheckBox checkBox, bool isPressed) { - await checkBox.frmBackground.ScaleTo(isPressed ? .8 : 1, 50, Easing.BounceIn); - var radiusVal = isPressed ? checkBox.frmBackground.CornerRadius * 2f : checkBox.CornerRadius; - checkBox.frmBackground.CornerRadius = radiusVal; + await checkBox.outlineBox.ScaleTo(isPressed ? .8 : 1, 50, Easing.BounceIn); + var radiusVal = isPressed ? checkBox.outlineBox.RadiusX * 2f : checkBox.CornerRadius; + checkBox.outlineBox.RadiusX = radiusVal; + } + + internal static Geometry GetGeometryFromString(string path) + { + return (Geometry)PathGeometryConverter.ConvertFromInvariantString(path); } #endregion @@ -406,9 +432,12 @@ public enum CheckType { Box, Check, + [Obsolete("This option is removed. Use another one.")] Cross, + [Obsolete("This option is removed. Use another one.")] Star, Material, + Line, Custom = 90 } } \ No newline at end of file diff --git a/src/InputKit.Maui/Shared/Resources/check.png b/src/InputKit.Maui/Shared/Resources/check.png deleted file mode 100644 index 5697dba..0000000 Binary files a/src/InputKit.Maui/Shared/Resources/check.png and /dev/null differ diff --git a/src/InputKit.Maui/Shared/Resources/cross.png b/src/InputKit.Maui/Shared/Resources/cross.png deleted file mode 100644 index e2ee25f..0000000 Binary files a/src/InputKit.Maui/Shared/Resources/cross.png and /dev/null differ diff --git a/src/InputKit.Maui/Shared/Resources/star.png b/src/InputKit.Maui/Shared/Resources/star.png deleted file mode 100644 index 918a395..0000000 Binary files a/src/InputKit.Maui/Shared/Resources/star.png and /dev/null differ