Skip to content

Commit

Permalink
Merge pull request #269 from enisn/checkbox-improvements
Browse files Browse the repository at this point in the history
Use drawing for Checkbox
  • Loading branch information
enisn authored Aug 27, 2022
2 parents 430eb93 + 5cdfdbf commit 2edd311
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 69 deletions.
10 changes: 5 additions & 5 deletions sandbox/SandboxMAUI/Pages/CheckBoxPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

<BoxView Color="Black" HeightRequest="1" HorizontalOptions="Fill" Margin="5,10" />

<input:CheckBox Text="Option 0 with Box Type" Type="Box" LabelPosition="After"/>
<input:CheckBox Text="Option 0 with Box Type" Type="Box"/>
<input:CheckBox Text="Option 1 with Check Type" Type="Check" />
<input:CheckBox Text="Option 2 wity Cross Type" Type="Cross" />
<input:CheckBox Text="Option 3 with Custom Type" Type="Custom" CustomIcon="ic_account_balance_black"/>
<input:CheckBox Text="Option 4 with Material Type" Type="Material" />
<input:CheckBox Text="Option 5 with Star Type" Type="Star"/>
<input:CheckBox Text="Option 2 with Line Type" Type="Line" />
<input:CheckBox Text="Option 3 with Material Type" Type="Material" />
<input:CheckBox Text="Option 4 with Custom Type (X)" Type="Custom" CustomIconGeometry="M17.705 7.705l-1.41-1.41L12 10.59 7.705 6.295l-1.41 1.41L10.59 12l-4.295 4.295 1.41 1.41L12 13.41l4.295 4.295 1.41-1.41L13.41 12l4.295-4.295z"/>
<input:CheckBox Text="Option 5 with Material Custom Type (X)" Type="Material" CustomIconGeometry="M17.705 7.705l-1.41-1.41L12 10.59 7.705 6.295l-1.41 1.41L10.59 12l-4.295 4.295 1.41 1.41L12 13.41l4.295 4.295 1.41-1.41L13.41 12l4.295-4.295z"/>
<input:CheckBox Text="Option 6 (Position)" Type="Check" LabelPosition="Before"/>

</StackLayout>
Expand Down
157 changes: 93 additions & 64 deletions src/InputKit.Maui/Shared/Controls/CheckBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand All @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -163,12 +186,12 @@ public Color Color
/// <summary>
/// Size of Checkbox
/// </summary>
public double BoxSize { get => frmBackground.Width; }
public double BoxSize { get => outlineBox.Width; }

/// <summary>
/// SizeRequest of CheckBox
/// </summary>
public double BoxSizeRequest { get => frmBackground.WidthRequest; set => SetBoxSize(value); }
public double BoxSizeRequest { get => outlineBox.WidthRequest; set => SetBoxSize(value); }

/// <summary>
/// Fontsize of Checkbox text
Expand Down Expand Up @@ -199,7 +222,11 @@ public Color Color
/// </summary>
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; }
/// <summary>
Expand Down Expand Up @@ -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,
Expand All @@ -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);
}
}

Expand All @@ -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;
}
}

Expand All @@ -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;
}

Expand Down Expand Up @@ -389,26 +407,37 @@ 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

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
}
}
Binary file removed src/InputKit.Maui/Shared/Resources/check.png
Binary file not shown.
Binary file removed src/InputKit.Maui/Shared/Resources/cross.png
Binary file not shown.
Binary file removed src/InputKit.Maui/Shared/Resources/star.png
Binary file not shown.

0 comments on commit 2edd311

Please sign in to comment.