Preview 3: Complete control implementation with XAML data binding
Major milestone adding full control functionality: Controls Enhanced: - Entry/Editor: Full keyboard input, cursor navigation, selection, clipboard - CollectionView: Data binding, selection highlighting, scrolling - CheckBox/Switch/Slider: Interactive state management - Picker/DatePicker/TimePicker: Dropdown selection with popup overlays - ProgressBar/ActivityIndicator: Animated progress display - Button: Press/release visual states - Border/Frame: Rounded corners, stroke styling - Label: Text wrapping, alignment, decorations - Grid/StackLayout: Margin and padding support Features Added: - DisplayAlert dialogs with button actions - NavigationPage with toolbar and back navigation - Shell with flyout menu navigation - XAML value converters for data binding - Margin support in all layout containers - Popup overlay system for pickers New Samples: - TodoApp: Full CRUD task manager with NavigationPage - ShellDemo: Comprehensive control showcase Removed: - ControlGallery (replaced by ShellDemo) - LinuxDemo (replaced by TodoApp) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
259
Converters/SKColorTypeConverter.cs
Normal file
259
Converters/SKColorTypeConverter.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using SkiaSharp;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Type converter for converting between MAUI Color and SKColor.
|
||||
/// Enables XAML styling with Color values that get applied to Skia controls.
|
||||
/// </summary>
|
||||
public class SKColorTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) ||
|
||||
sourceType == typeof(Color) ||
|
||||
base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) ||
|
||||
destinationType == typeof(Color) ||
|
||||
base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is Color mauiColor)
|
||||
{
|
||||
return ToSKColor(mauiColor);
|
||||
}
|
||||
|
||||
if (value is string str)
|
||||
{
|
||||
return ParseColor(str);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
if (value is SKColor skColor)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return $"#{skColor.Alpha:X2}{skColor.Red:X2}{skColor.Green:X2}{skColor.Blue:X2}";
|
||||
}
|
||||
|
||||
if (destinationType == typeof(Color))
|
||||
{
|
||||
return ToMauiColor(skColor);
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a MAUI Color to an SKColor.
|
||||
/// </summary>
|
||||
public static SKColor ToSKColor(Color mauiColor)
|
||||
{
|
||||
return new SKColor(
|
||||
(byte)(mauiColor.Red * 255),
|
||||
(byte)(mauiColor.Green * 255),
|
||||
(byte)(mauiColor.Blue * 255),
|
||||
(byte)(mauiColor.Alpha * 255));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an SKColor to a MAUI Color.
|
||||
/// </summary>
|
||||
public static Color ToMauiColor(SKColor skColor)
|
||||
{
|
||||
return new Color(
|
||||
skColor.Red / 255f,
|
||||
skColor.Green / 255f,
|
||||
skColor.Blue / 255f,
|
||||
skColor.Alpha / 255f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a color string (hex, named, or rgb format).
|
||||
/// </summary>
|
||||
private static SKColor ParseColor(string colorString)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(colorString))
|
||||
return SKColors.Black;
|
||||
|
||||
colorString = colorString.Trim();
|
||||
|
||||
// Try hex format
|
||||
if (colorString.StartsWith("#"))
|
||||
{
|
||||
return SKColor.Parse(colorString);
|
||||
}
|
||||
|
||||
// Try named colors
|
||||
var namedColor = GetNamedColor(colorString.ToLowerInvariant());
|
||||
if (namedColor.HasValue)
|
||||
return namedColor.Value;
|
||||
|
||||
// Try rgb/rgba format
|
||||
if (colorString.StartsWith("rgb", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return ParseRgbColor(colorString);
|
||||
}
|
||||
|
||||
// Fallback to SKColor.Parse
|
||||
if (SKColor.TryParse(colorString, out var parsed))
|
||||
return parsed;
|
||||
|
||||
return SKColors.Black;
|
||||
}
|
||||
|
||||
private static SKColor? GetNamedColor(string name) => name switch
|
||||
{
|
||||
"transparent" => SKColors.Transparent,
|
||||
"black" => SKColors.Black,
|
||||
"white" => SKColors.White,
|
||||
"red" => SKColors.Red,
|
||||
"green" => SKColors.Green,
|
||||
"blue" => SKColors.Blue,
|
||||
"yellow" => SKColors.Yellow,
|
||||
"cyan" => SKColors.Cyan,
|
||||
"magenta" => SKColors.Magenta,
|
||||
"gray" or "grey" => SKColors.Gray,
|
||||
"darkgray" or "darkgrey" => SKColors.DarkGray,
|
||||
"lightgray" or "lightgrey" => SKColors.LightGray,
|
||||
"orange" => new SKColor(0xFF, 0xA5, 0x00),
|
||||
"pink" => new SKColor(0xFF, 0xC0, 0xCB),
|
||||
"purple" => new SKColor(0x80, 0x00, 0x80),
|
||||
"brown" => new SKColor(0xA5, 0x2A, 0x2A),
|
||||
"navy" => new SKColor(0x00, 0x00, 0x80),
|
||||
"teal" => new SKColor(0x00, 0x80, 0x80),
|
||||
"olive" => new SKColor(0x80, 0x80, 0x00),
|
||||
"silver" => new SKColor(0xC0, 0xC0, 0xC0),
|
||||
"maroon" => new SKColor(0x80, 0x00, 0x00),
|
||||
"lime" => new SKColor(0x00, 0xFF, 0x00),
|
||||
"aqua" => new SKColor(0x00, 0xFF, 0xFF),
|
||||
"fuchsia" => new SKColor(0xFF, 0x00, 0xFF),
|
||||
"gold" => new SKColor(0xFF, 0xD7, 0x00),
|
||||
"coral" => new SKColor(0xFF, 0x7F, 0x50),
|
||||
"salmon" => new SKColor(0xFA, 0x80, 0x72),
|
||||
"crimson" => new SKColor(0xDC, 0x14, 0x3C),
|
||||
"indigo" => new SKColor(0x4B, 0x00, 0x82),
|
||||
"violet" => new SKColor(0xEE, 0x82, 0xEE),
|
||||
"turquoise" => new SKColor(0x40, 0xE0, 0xD0),
|
||||
"tan" => new SKColor(0xD2, 0xB4, 0x8C),
|
||||
"chocolate" => new SKColor(0xD2, 0x69, 0x1E),
|
||||
"tomato" => new SKColor(0xFF, 0x63, 0x47),
|
||||
"steelblue" => new SKColor(0x46, 0x82, 0xB4),
|
||||
"skyblue" => new SKColor(0x87, 0xCE, 0xEB),
|
||||
"slategray" or "slategrey" => new SKColor(0x70, 0x80, 0x90),
|
||||
"seagreen" => new SKColor(0x2E, 0x8B, 0x57),
|
||||
"royalblue" => new SKColor(0x41, 0x69, 0xE1),
|
||||
"plum" => new SKColor(0xDD, 0xA0, 0xDD),
|
||||
"peru" => new SKColor(0xCD, 0x85, 0x3F),
|
||||
"orchid" => new SKColor(0xDA, 0x70, 0xD6),
|
||||
"orangered" => new SKColor(0xFF, 0x45, 0x00),
|
||||
"olivedrab" => new SKColor(0x6B, 0x8E, 0x23),
|
||||
"midnightblue" => new SKColor(0x19, 0x19, 0x70),
|
||||
"mediumblue" => new SKColor(0x00, 0x00, 0xCD),
|
||||
"limegreen" => new SKColor(0x32, 0xCD, 0x32),
|
||||
"hotpink" => new SKColor(0xFF, 0x69, 0xB4),
|
||||
"honeydew" => new SKColor(0xF0, 0xFF, 0xF0),
|
||||
"greenyellow" => new SKColor(0xAD, 0xFF, 0x2F),
|
||||
"forestgreen" => new SKColor(0x22, 0x8B, 0x22),
|
||||
"firebrick" => new SKColor(0xB2, 0x22, 0x22),
|
||||
"dodgerblue" => new SKColor(0x1E, 0x90, 0xFF),
|
||||
"deeppink" => new SKColor(0xFF, 0x14, 0x93),
|
||||
"deepskyblue" => new SKColor(0x00, 0xBF, 0xFF),
|
||||
"darkviolet" => new SKColor(0x94, 0x00, 0xD3),
|
||||
"darkturquoise" => new SKColor(0x00, 0xCE, 0xD1),
|
||||
"darkslategray" or "darkslategrey" => new SKColor(0x2F, 0x4F, 0x4F),
|
||||
"darkred" => new SKColor(0x8B, 0x00, 0x00),
|
||||
"darkorange" => new SKColor(0xFF, 0x8C, 0x00),
|
||||
"darkolivegreen" => new SKColor(0x55, 0x6B, 0x2F),
|
||||
"darkmagenta" => new SKColor(0x8B, 0x00, 0x8B),
|
||||
"darkkhaki" => new SKColor(0xBD, 0xB7, 0x6B),
|
||||
"darkgreen" => new SKColor(0x00, 0x64, 0x00),
|
||||
"darkgoldenrod" => new SKColor(0xB8, 0x86, 0x0B),
|
||||
"darkcyan" => new SKColor(0x00, 0x8B, 0x8B),
|
||||
"darkblue" => new SKColor(0x00, 0x00, 0x8B),
|
||||
"cornflowerblue" => new SKColor(0x64, 0x95, 0xED),
|
||||
"cadetblue" => new SKColor(0x5F, 0x9E, 0xA0),
|
||||
"blueviolet" => new SKColor(0x8A, 0x2B, 0xE2),
|
||||
"azure" => new SKColor(0xF0, 0xFF, 0xFF),
|
||||
"aquamarine" => new SKColor(0x7F, 0xFF, 0xD4),
|
||||
"aliceblue" => new SKColor(0xF0, 0xF8, 0xFF),
|
||||
_ => null
|
||||
};
|
||||
|
||||
private static SKColor ParseRgbColor(string colorString)
|
||||
{
|
||||
try
|
||||
{
|
||||
var isRgba = colorString.StartsWith("rgba", StringComparison.OrdinalIgnoreCase);
|
||||
var startIndex = colorString.IndexOf('(');
|
||||
var endIndex = colorString.IndexOf(')');
|
||||
|
||||
if (startIndex == -1 || endIndex == -1)
|
||||
return SKColors.Black;
|
||||
|
||||
var values = colorString.Substring(startIndex + 1, endIndex - startIndex - 1)
|
||||
.Split(',')
|
||||
.Select(v => v.Trim())
|
||||
.ToArray();
|
||||
|
||||
if (values.Length < 3)
|
||||
return SKColors.Black;
|
||||
|
||||
var r = byte.Parse(values[0]);
|
||||
var g = byte.Parse(values[1]);
|
||||
var b = byte.Parse(values[2]);
|
||||
byte a = 255;
|
||||
|
||||
if (isRgba && values.Length >= 4)
|
||||
{
|
||||
var alphaValue = float.Parse(values[3], CultureInfo.InvariantCulture);
|
||||
a = (byte)(alphaValue <= 1 ? alphaValue * 255 : alphaValue);
|
||||
}
|
||||
|
||||
return new SKColor(r, g, b, a);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return SKColors.Black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for color conversion.
|
||||
/// </summary>
|
||||
public static class ColorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a MAUI Color to an SKColor.
|
||||
/// </summary>
|
||||
public static SKColor ToSKColor(this Color color)
|
||||
{
|
||||
return SKColorTypeConverter.ToSKColor(color);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an SKColor to a MAUI Color.
|
||||
/// </summary>
|
||||
public static Color ToMauiColor(this SKColor color)
|
||||
{
|
||||
return SKColorTypeConverter.ToMauiColor(color);
|
||||
}
|
||||
}
|
||||
328
Converters/SKRectTypeConverter.cs
Normal file
328
Converters/SKRectTypeConverter.cs
Normal file
@@ -0,0 +1,328 @@
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Globalization;
|
||||
using SkiaSharp;
|
||||
using Microsoft.Maui;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Type converter for converting between MAUI Thickness and SKRect (for padding/margin).
|
||||
/// Enables XAML styling with Thickness values that get applied to Skia controls.
|
||||
/// </summary>
|
||||
public class SKRectTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) ||
|
||||
sourceType == typeof(Thickness) ||
|
||||
sourceType == typeof(double) ||
|
||||
sourceType == typeof(float) ||
|
||||
base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) ||
|
||||
destinationType == typeof(Thickness) ||
|
||||
base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is Thickness thickness)
|
||||
{
|
||||
return ThicknessToSKRect(thickness);
|
||||
}
|
||||
|
||||
if (value is double d)
|
||||
{
|
||||
return new SKRect((float)d, (float)d, (float)d, (float)d);
|
||||
}
|
||||
|
||||
if (value is float f)
|
||||
{
|
||||
return new SKRect(f, f, f, f);
|
||||
}
|
||||
|
||||
if (value is string str)
|
||||
{
|
||||
return ParseRect(str);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
if (value is SKRect rect)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return $"{rect.Left},{rect.Top},{rect.Right},{rect.Bottom}";
|
||||
}
|
||||
|
||||
if (destinationType == typeof(Thickness))
|
||||
{
|
||||
return SKRectToThickness(rect);
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a MAUI Thickness to an SKRect (used as padding storage).
|
||||
/// </summary>
|
||||
public static SKRect ThicknessToSKRect(Thickness thickness)
|
||||
{
|
||||
return new SKRect(
|
||||
(float)thickness.Left,
|
||||
(float)thickness.Top,
|
||||
(float)thickness.Right,
|
||||
(float)thickness.Bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts an SKRect (used as padding storage) to a MAUI Thickness.
|
||||
/// </summary>
|
||||
public static Thickness SKRectToThickness(SKRect rect)
|
||||
{
|
||||
return new Thickness(rect.Left, rect.Top, rect.Right, rect.Bottom);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a string into an SKRect for padding/margin.
|
||||
/// Supports formats: "uniform", "horizontal,vertical", "left,top,right,bottom"
|
||||
/// </summary>
|
||||
private static SKRect ParseRect(string str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
return SKRect.Empty;
|
||||
|
||||
str = str.Trim();
|
||||
var parts = str.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
// Uniform padding
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var uniform))
|
||||
{
|
||||
return new SKRect(uniform, uniform, uniform, uniform);
|
||||
}
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
// Horizontal, Vertical
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var horizontal) &&
|
||||
float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var vertical))
|
||||
{
|
||||
return new SKRect(horizontal, vertical, horizontal, vertical);
|
||||
}
|
||||
}
|
||||
else if (parts.Length == 4)
|
||||
{
|
||||
// Left, Top, Right, Bottom
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var left) &&
|
||||
float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var top) &&
|
||||
float.TryParse(parts[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var right) &&
|
||||
float.TryParse(parts[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var bottom))
|
||||
{
|
||||
return new SKRect(left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
return SKRect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type converter for SKSize.
|
||||
/// </summary>
|
||||
public class SKSizeTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) ||
|
||||
sourceType == typeof(Size) ||
|
||||
base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) ||
|
||||
destinationType == typeof(Size) ||
|
||||
base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is Size size)
|
||||
{
|
||||
return new SKSize((float)size.Width, (float)size.Height);
|
||||
}
|
||||
|
||||
if (value is string str)
|
||||
{
|
||||
return ParseSize(str);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
if (value is SKSize size)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return $"{size.Width},{size.Height}";
|
||||
}
|
||||
|
||||
if (destinationType == typeof(Size))
|
||||
{
|
||||
return new Size(size.Width, size.Height);
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
private static SKSize ParseSize(string str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
return SKSize.Empty;
|
||||
|
||||
str = str.Trim();
|
||||
var parts = str.Split(new[] { ',', ' ', 'x', 'X' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var uniform))
|
||||
{
|
||||
return new SKSize(uniform, uniform);
|
||||
}
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var width) &&
|
||||
float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var height))
|
||||
{
|
||||
return new SKSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
return SKSize.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type converter for SKPoint.
|
||||
/// </summary>
|
||||
public class SKPointTypeConverter : TypeConverter
|
||||
{
|
||||
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
|
||||
{
|
||||
return sourceType == typeof(string) ||
|
||||
sourceType == typeof(Point) ||
|
||||
base.CanConvertFrom(context, sourceType);
|
||||
}
|
||||
|
||||
public override bool CanConvertTo(ITypeDescriptorContext? context, Type? destinationType)
|
||||
{
|
||||
return destinationType == typeof(string) ||
|
||||
destinationType == typeof(Point) ||
|
||||
base.CanConvertTo(context, destinationType);
|
||||
}
|
||||
|
||||
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
|
||||
{
|
||||
if (value is Point point)
|
||||
{
|
||||
return new SKPoint((float)point.X, (float)point.Y);
|
||||
}
|
||||
|
||||
if (value is string str)
|
||||
{
|
||||
return ParsePoint(str);
|
||||
}
|
||||
|
||||
return base.ConvertFrom(context, culture, value);
|
||||
}
|
||||
|
||||
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
|
||||
{
|
||||
if (value is SKPoint point)
|
||||
{
|
||||
if (destinationType == typeof(string))
|
||||
{
|
||||
return $"{point.X},{point.Y}";
|
||||
}
|
||||
|
||||
if (destinationType == typeof(Point))
|
||||
{
|
||||
return new Point(point.X, point.Y);
|
||||
}
|
||||
}
|
||||
|
||||
return base.ConvertTo(context, culture, value, destinationType);
|
||||
}
|
||||
|
||||
private static SKPoint ParsePoint(string str)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
return SKPoint.Empty;
|
||||
|
||||
str = str.Trim();
|
||||
var parts = str.Split(new[] { ',', ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
if (float.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var x) &&
|
||||
float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var y))
|
||||
{
|
||||
return new SKPoint(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
return SKPoint.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods for SkiaSharp type conversions.
|
||||
/// </summary>
|
||||
public static class SKTypeExtensions
|
||||
{
|
||||
public static SKRect ToSKRect(this Thickness thickness)
|
||||
{
|
||||
return SKRectTypeConverter.ThicknessToSKRect(thickness);
|
||||
}
|
||||
|
||||
public static Thickness ToThickness(this SKRect rect)
|
||||
{
|
||||
return SKRectTypeConverter.SKRectToThickness(rect);
|
||||
}
|
||||
|
||||
public static SKSize ToSKSize(this Size size)
|
||||
{
|
||||
return new SKSize((float)size.Width, (float)size.Height);
|
||||
}
|
||||
|
||||
public static Size ToSize(this SKSize size)
|
||||
{
|
||||
return new Size(size.Width, size.Height);
|
||||
}
|
||||
|
||||
public static SKPoint ToSKPoint(this Point point)
|
||||
{
|
||||
return new SKPoint((float)point.X, (float)point.Y);
|
||||
}
|
||||
|
||||
public static Point ToPoint(this SKPoint point)
|
||||
{
|
||||
return new Point(point.X, point.Y);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user