Merge branch 'main' into refactoring-fixes

This commit is contained in:
Javier Suárez 2023-04-08 11:56:49 +02:00
Родитель 31edabd74c bd81ba916d
Коммит ece9078109
8 изменённых файлов: 328 добавлений и 0 удалений

Двоичные данные
images/alohakit-captcha.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 40 KiB

Просмотреть файл

@ -52,6 +52,7 @@
<ItemGroup Condition="$(TargetFramework.Contains('-windows'))"> <ItemGroup Condition="$(TargetFramework.Contains('-windows'))">
<!-- Required - WinUI does not yet have buildTransitive for everything --> <!-- Required - WinUI does not yet have buildTransitive for everything -->
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.5" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.1.5" />
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.0.3.1" />
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.0.4" /> <PackageReference Include="Microsoft.Graphics.Win2D" Version="1.0.4" />
</ItemGroup> </ItemGroup>

Просмотреть файл

@ -17,6 +17,9 @@ namespace AlohaKit.Gallery.ViewModels
new SectionModel(typeof(ButtonView), "Button", new SectionModel(typeof(ButtonView), "Button",
"The Button responds to a tap or click."), "The Button responds to a tap or click."),
new SectionModel(typeof(CaptchaView), "Captcha",
"Displays a distorted word."),
new SectionModel(typeof(CheckBoxView), "CheckBox", new SectionModel(typeof(CheckBoxView), "CheckBox",
"CheckBox is a type of button that can either be checked or empty."), "CheckBox is a type of button that can either be checked or empty."),

Просмотреть файл

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="AlohaKit.Gallery.Views.CaptchaView"
xmlns:controls="clr-namespace:AlohaKit.Controls;assembly=AlohaKit"
Title="Captcha">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- DESCRIPTION -->
<StackLayout
Style="{StaticResource SectionContainerStyle}">
<Label
Text="Displays a distorted word."/>
</StackLayout>
<!-- FEATURES -->
<StackLayout
Grid.Row="1"
BackgroundColor="{AppThemeBinding Light={StaticResource LightBackgroundSecondaryColor}, Dark={StaticResource DarkBackgroundSecondaryColor}}"
Style="{StaticResource SectionContainerStyle}">
<Label
Text="Features"
Style="{StaticResource SectionTitleStyle}"/>
<Label
Text="- Can manage the word complexity."/>
<Label
Text="- All the colors can be customized."/>
</StackLayout>
<!-- SETTINGS -->
<StackLayout
Grid.Row="2"
Style="{StaticResource SectionContainerStyle}">
<Label
Text="Settings"
Style="{StaticResource SectionTitleStyle}"/>
<StackLayout
Orientation="Horizontal"
Margin="0, 6">
<Label
Text="TextColor"
VerticalOptions="Center"
Style="{StaticResource SettingsTextStyle}"/>
<Entry
x:Name="TextColorEntry"
Placeholder="TextColor"
Text="#000000"
TextColor="White"
TextChanged="OnTextColorEntryTextChanged"
Style="{StaticResource SettingsEntryStyle}"/>
</StackLayout>
</StackLayout>
<controls:Captcha
Grid.Row="3"
x:Name="Captcha"/>
</Grid>
</ContentPage>

Просмотреть файл

@ -0,0 +1,42 @@
namespace AlohaKit.Gallery.Views;
public partial class CaptchaView : ContentPage
{
public CaptchaView()
{
InitializeComponent();
UpdateColors();
}
void OnTextColorEntryTextChanged(object sender, TextChangedEventArgs e)
{
UpdateColors();
}
void UpdateColors()
{
var textColor = GetColorFromString(TextColorEntry.Text);
if (textColor != null)
{
TextColorEntry.BackgroundColor = textColor;
Captcha.TextColor = textColor;
}
}
Color GetColorFromString(string value)
{
if (string.IsNullOrEmpty(value))
return null;
try
{
return Color.FromArgb(value[0].Equals('#') ? value : $"#{value}");
}
catch (Exception)
{
return null;
}
}
}

Просмотреть файл

@ -0,0 +1,110 @@
namespace AlohaKit.Controls
{
public class Captcha : GraphicsView
{
public Captcha()
{
HeightRequest = 50;
WidthRequest = 150;
Drawable = CaptchaDrawable = new CaptchaDrawable();
}
public CaptchaDrawable CaptchaDrawable { get; set; }
public static readonly BindableProperty LevelProperty =
BindableProperty.Create(nameof(Level), typeof(CaptchaLevel), typeof(Captcha), CaptchaLevel.Normal,
propertyChanged: (bindableObject, oldValue, newValue) =>
{
if (newValue != null && bindableObject is Captcha captcha)
{
captcha.UpdateLevel();
}
});
public CaptchaLevel Level
{
get => (CaptchaLevel)GetValue(LevelProperty);
set => SetValue(LevelProperty, value);
}
public static readonly BindableProperty TextColorProperty =
BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(Captcha), Colors.Black,
propertyChanged: (bindableObject, oldValue, newValue) =>
{
if (newValue != null && bindableObject is Captcha captcha)
{
captcha.UpdateTextColor();
}
});
public Color TextColor
{
get => (Color)GetValue(TextColorProperty);
set => SetValue(TextColorProperty, value);
}
protected override void OnParentChanged()
{
base.OnParentChanged();
if (Parent != null)
{
UpdateLevel();
UpdateTextColor();
}
}
void UpdateLevel()
{
if (CaptchaDrawable == null)
return;
if (CaptchaDrawable.Level != Level)
{
CaptchaDrawable.Level = Level;
var word = GenerateRandomWord(GetWordLength(Level));
CaptchaDrawable.Word = word;
Invalidate();
}
}
void UpdateTextColor()
{
if (CaptchaDrawable == null)
return;
CaptchaDrawable.TextColor = TextColor;
Invalidate();
}
string GenerateRandomWord(int length)
{
var random = new Random();
const string chars = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
int GetWordLength(CaptchaLevel level)
{
switch (level)
{
case CaptchaLevel.Weak:
return 4;
default:
case CaptchaLevel.Normal:
return 6;
case CaptchaLevel.Strong:
return 8;
}
}
}
}

Просмотреть файл

@ -0,0 +1,102 @@
namespace AlohaKit.Controls
{
public class CaptchaDrawable : IDrawable
{
public string Word { get; set; }
public CaptchaLevel Level { get; set; }
public Color TextColor { get; set; }
public void Draw(ICanvas canvas, RectF dirtyRect)
{
DrawText(canvas, dirtyRect);
DrawArtifacts(canvas, dirtyRect);
}
void DrawText(ICanvas canvas, RectF dirtyRect)
{
canvas.SaveState();
var height = dirtyRect.Height;
var width = dirtyRect.Width;
int minLetterDistanceY = 0;
int maxLetterDistanceY = (int)height / Word.Length;
int minLetterDistanceX = 6;
int maxLetterDistanceX = (int)width / Word.Length;
var coordRandom = new Random();
var letterPoint = new PointF(coordRandom.Next(minLetterDistanceY, maxLetterDistanceX), height / 2);
foreach (var l in Word)
{
letterPoint.Y += coordRandom.Next(0, maxLetterDistanceY);
canvas.FontSize = 24;
canvas.FontColor = TextColor;
canvas.DrawString(l.ToString(), letterPoint.X, letterPoint.Y,HorizontalAlignment.Center);
letterPoint.X += coordRandom.Next(minLetterDistanceX, maxLetterDistanceX);
}
canvas.RestoreState();
}
void DrawArtifacts(ICanvas canvas, RectF dirtyRect)
{
canvas.SaveState();
var randomLines = new Random();
for (var i = 0; i < GetArtifactLength(Level); i++)
{
var x1 = randomLines.Next(0, (int)dirtyRect.Width);
var y1 = randomLines.Next(0, (int)dirtyRect.Height);
var x2 = randomLines.Next(0, (int)dirtyRect.Width);
var y2 = randomLines.Next(0, (int)dirtyRect.Height);
canvas.StrokeColor = TextColor.WithAlpha(0.8f);
var randomStrokeSize = new Random();
canvas.StrokeSize = randomStrokeSize.Next(1, GetArtifactWidth(Level));
var p1 = new PointF(x1, y1);
var p2 = new PointF(x2, y2);
canvas.DrawLine(p1, p2);
}
canvas.RestoreState();
}
int GetArtifactLength(CaptchaLevel level)
{
switch (level)
{
case CaptchaLevel.Weak:
return 4;
default:
case CaptchaLevel.Normal:
return 6;
case CaptchaLevel.Strong:
return 8;
}
}
int GetArtifactWidth(CaptchaLevel level)
{
switch (level)
{
case CaptchaLevel.Weak:
return 2;
default:
case CaptchaLevel.Normal:
return 3;
case CaptchaLevel.Strong:
return 4;
}
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
namespace AlohaKit.Controls
{
public enum CaptchaLevel
{
Weak,
Normal,
Strong
}
}