diff --git a/Handlers/ActivityIndicatorHandler.Linux.cs b/Handlers/ActivityIndicatorHandler.Linux.cs
index 11b9722..3faa3b6 100644
--- a/Handlers/ActivityIndicatorHandler.Linux.cs
+++ b/Handlers/ActivityIndicatorHandler.Linux.cs
@@ -1,63 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for ActivityIndicator control.
///
-public partial class ActivityIndicatorHandler : ViewHandler
+public class ActivityIndicatorHandler : ViewHandler
{
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(IActivityIndicator.IsRunning)] = MapIsRunning,
- [nameof(IActivityIndicator.Color)] = MapColor,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IView.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["IsRunning"] = MapIsRunning,
+ ["Color"] = MapColor,
+ ["Background"] = MapBackground
};
public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
- public ActivityIndicatorHandler() : base(Mapper, CommandMapper) { }
+ public ActivityIndicatorHandler() : base(Mapper, CommandMapper)
+ {
+ }
- protected override SkiaActivityIndicator CreatePlatformView() => new SkiaActivityIndicator();
+ public ActivityIndicatorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
+ : base(mapper ?? Mapper, commandMapper ?? CommandMapper)
+ {
+ }
+
+ protected override SkiaActivityIndicator CreatePlatformView()
+ {
+ return new SkiaActivityIndicator();
+ }
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{
- handler.PlatformView.IsRunning = activityIndicator.IsRunning;
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.IsRunning = activityIndicator.IsRunning;
+ }
}
public static void MapColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{
- if (activityIndicator.Color != null)
+ if (handler.PlatformView != null && activityIndicator.Color != null)
+ {
handler.PlatformView.Color = activityIndicator.Color.ToSKColor();
- handler.PlatformView.Invalidate();
- }
-
- public static void MapIsEnabled(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
- {
- handler.PlatformView.IsEnabled = activityIndicator.IsEnabled;
- handler.PlatformView.Invalidate();
+ }
}
public static void MapBackground(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{
- if (activityIndicator.Background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
- handler.PlatformView.Invalidate();
- }
- }
-
- public static void MapBackgroundColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
- {
- if (activityIndicator is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
- {
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (activityIndicator.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
}
}
diff --git a/Handlers/ButtonHandler.Linux.cs b/Handlers/ButtonHandler.Linux.cs
index 54feb11..3b850d0 100644
--- a/Handlers/ButtonHandler.Linux.cs
+++ b/Handlers/ButtonHandler.Linux.cs
@@ -1,57 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for Button control.
///
-public partial class ButtonHandler : ViewHandler
+public class ButtonHandler : ViewHandler
{
- ///
- /// Maps the property mapper for the handler.
- ///
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(IButton.Text)] = MapText,
- [nameof(IButton.TextColor)] = MapTextColor,
- [nameof(IButton.Background)] = MapBackground,
- [nameof(IButton.Font)] = MapFont,
- [nameof(IButton.Padding)] = MapPadding,
- [nameof(IButton.CornerRadius)] = MapCornerRadius,
- [nameof(IButton.BorderColor)] = MapBorderColor,
- [nameof(IButton.BorderWidth)] = MapBorderWidth,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
+ ["StrokeColor"] = MapStrokeColor,
+ ["StrokeThickness"] = MapStrokeThickness,
+ ["CornerRadius"] = MapCornerRadius,
+ ["Background"] = MapBackground,
+ ["Padding"] = MapPadding,
+ ["IsEnabled"] = MapIsEnabled
};
- ///
- /// Maps the command mapper for the handler.
- ///
- public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper)
- {
- };
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
public ButtonHandler() : base(Mapper, CommandMapper)
{
}
- public ButtonHandler(IPropertyMapper? mapper)
- : base(mapper ?? Mapper, CommandMapper)
- {
- }
-
- public ButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
+ public ButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaButton CreatePlatformView()
{
- var button = new SkiaButton();
- return button;
+ return new SkiaButton();
}
protected override void ConnectHandler(SkiaButton platformView)
@@ -61,18 +46,13 @@ public partial class ButtonHandler : ViewHandler
platformView.Pressed += OnPressed;
platformView.Released += OnReleased;
- // Manually map all properties on connect since MAUI may not trigger updates
- // for properties that were set before handler connection
if (VirtualView != null)
{
- MapText(this, VirtualView);
- MapTextColor(this, VirtualView);
- MapBackground(this, VirtualView);
- MapFont(this, VirtualView);
- MapPadding(this, VirtualView);
+ MapStrokeColor(this, VirtualView);
+ MapStrokeThickness(this, VirtualView);
MapCornerRadius(this, VirtualView);
- MapBorderColor(this, VirtualView);
- MapBorderWidth(this, VirtualView);
+ MapBackground(this, VirtualView);
+ MapPadding(this, VirtualView);
MapIsEnabled(this, VirtualView);
}
}
@@ -100,80 +80,66 @@ public partial class ButtonHandler : ViewHandler
VirtualView?.Released();
}
- public static void MapText(ButtonHandler handler, IButton button)
+ public static void MapStrokeColor(ButtonHandler handler, IButton button)
{
- handler.PlatformView.Text = button.Text ?? "";
- handler.PlatformView.Invalidate();
- }
-
- public static void MapTextColor(ButtonHandler handler, IButton button)
- {
- if (button.TextColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.TextColor = button.TextColor.ToSKColor();
+ var strokeColor = button.StrokeColor;
+ if (strokeColor != null)
+ {
+ handler.PlatformView.BorderColor = strokeColor.ToSKColor();
+ }
}
- handler.PlatformView.Invalidate();
}
- public static void MapBackground(ButtonHandler handler, IButton button)
+ public static void MapStrokeThickness(ButtonHandler handler, IButton button)
{
- var background = button.Background;
- if (background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- // Use ButtonBackgroundColor which is used for rendering, not base BackgroundColor
- handler.PlatformView.ButtonBackgroundColor = solidBrush.Color.ToSKColor();
+ handler.PlatformView.BorderWidth = (float)button.StrokeThickness;
}
- handler.PlatformView.Invalidate();
- }
-
- public static void MapFont(ButtonHandler handler, IButton button)
- {
- var font = button.Font;
- if (font.Family != null)
- {
- handler.PlatformView.FontFamily = font.Family;
- }
- handler.PlatformView.FontSize = (float)font.Size;
- handler.PlatformView.IsBold = font.Weight == FontWeight.Bold;
- handler.PlatformView.Invalidate();
- }
-
- public static void MapPadding(ButtonHandler handler, IButton button)
- {
- var padding = button.Padding;
- handler.PlatformView.Padding = new SKRect(
- (float)padding.Left,
- (float)padding.Top,
- (float)padding.Right,
- (float)padding.Bottom);
- handler.PlatformView.Invalidate();
}
public static void MapCornerRadius(ButtonHandler handler, IButton button)
{
- handler.PlatformView.CornerRadius = button.CornerRadius;
- handler.PlatformView.Invalidate();
- }
-
- public static void MapBorderColor(ButtonHandler handler, IButton button)
- {
- if (button.StrokeColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BorderColor = button.StrokeColor.ToSKColor();
+ handler.PlatformView.CornerRadius = button.CornerRadius;
}
- handler.PlatformView.Invalidate();
}
- public static void MapBorderWidth(ButtonHandler handler, IButton button)
+ public static void MapBackground(ButtonHandler handler, IButton button)
{
- handler.PlatformView.BorderWidth = (float)button.StrokeThickness;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ var background = button.Background;
+ if (background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.ButtonBackgroundColor = solidPaint.Color.ToSKColor();
+ }
+ }
+ }
+
+ public static void MapPadding(ButtonHandler handler, IButton button)
+ {
+ if (handler.PlatformView != null)
+ {
+ var padding = button.Padding;
+ handler.PlatformView.Padding = new SKRect(
+ (float)padding.Left,
+ (float)padding.Top,
+ (float)padding.Right,
+ (float)padding.Bottom);
+ }
}
public static void MapIsEnabled(ButtonHandler handler, IButton button)
{
- Console.WriteLine($"[ButtonHandler] MapIsEnabled called - Text='{handler.PlatformView.Text}', IsEnabled={button.IsEnabled}");
- handler.PlatformView.IsEnabled = button.IsEnabled;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ Console.WriteLine($"[ButtonHandler] MapIsEnabled - Text='{handler.PlatformView.Text}', IsEnabled={button.IsEnabled}");
+ handler.PlatformView.IsEnabled = button.IsEnabled;
+ handler.PlatformView.Invalidate();
+ }
}
}
diff --git a/Handlers/CheckBoxHandler.Linux.cs b/Handlers/CheckBoxHandler.Linux.cs
index d8d81e8..706a38f 100644
--- a/Handlers/CheckBoxHandler.Linux.cs
+++ b/Handlers/CheckBoxHandler.Linux.cs
@@ -1,45 +1,35 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
-using SkiaSharp;
+using Microsoft.Maui.Primitives;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for CheckBox control.
///
-public partial class CheckBoxHandler : ViewHandler
+public class CheckBoxHandler : ViewHandler
{
- ///
- /// Maps the property mapper for the handler.
- ///
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(ICheckBox.IsChecked)] = MapIsChecked,
- [nameof(ICheckBox.Foreground)] = MapForeground,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IView.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["IsChecked"] = MapIsChecked,
+ ["Foreground"] = MapForeground,
+ ["Background"] = MapBackground,
+ ["IsEnabled"] = MapIsEnabled,
+ ["VerticalLayoutAlignment"] = MapVerticalLayoutAlignment,
+ ["HorizontalLayoutAlignment"] = MapHorizontalLayoutAlignment
};
- ///
- /// Maps the command mapper for the handler.
- ///
- public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper)
- {
- };
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
public CheckBoxHandler() : base(Mapper, CommandMapper)
{
}
- public CheckBoxHandler(IPropertyMapper? mapper)
- : base(mapper ?? Mapper, CommandMapper)
- {
- }
-
- public CheckBoxHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
+ public CheckBoxHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
@@ -71,7 +61,7 @@ public partial class CheckBoxHandler : ViewHandler
public static void MapIsChecked(CheckBoxHandler handler, ICheckBox checkBox)
{
- if (handler.PlatformView.IsChecked != checkBox.IsChecked)
+ if (handler.PlatformView != null)
{
handler.PlatformView.IsChecked = checkBox.IsChecked;
}
@@ -79,35 +69,61 @@ public partial class CheckBoxHandler : ViewHandler
public static void MapForeground(CheckBoxHandler handler, ICheckBox checkBox)
{
- var foreground = checkBox.Foreground;
- if (foreground is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BoxColor = solidBrush.Color.ToSKColor();
+ if (checkBox.Foreground is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.CheckColor = solidPaint.Color.ToSKColor();
+ }
}
- handler.PlatformView.Invalidate();
- }
-
- public static void MapIsEnabled(CheckBoxHandler handler, ICheckBox checkBox)
- {
- handler.PlatformView.IsEnabled = checkBox.IsEnabled;
- handler.PlatformView.Invalidate();
}
public static void MapBackground(CheckBoxHandler handler, ICheckBox checkBox)
{
- if (checkBox.Background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (checkBox.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
}
- public static void MapBackgroundColor(CheckBoxHandler handler, ICheckBox checkBox)
+ public static void MapIsEnabled(CheckBoxHandler handler, ICheckBox checkBox)
{
- if (checkBox is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ handler.PlatformView.IsEnabled = checkBox.IsEnabled;
+ }
+ }
+
+ public static void MapVerticalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.VerticalOptions = (int)checkBox.VerticalLayoutAlignment switch
+ {
+ 1 => LayoutOptions.Start,
+ 2 => LayoutOptions.Center,
+ 3 => LayoutOptions.End,
+ 0 => LayoutOptions.Fill,
+ _ => LayoutOptions.Fill
+ };
+ }
+ }
+
+ public static void MapHorizontalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.HorizontalOptions = (int)checkBox.HorizontalLayoutAlignment switch
+ {
+ 1 => LayoutOptions.Start,
+ 2 => LayoutOptions.Center,
+ 3 => LayoutOptions.End,
+ 0 => LayoutOptions.Fill,
+ _ => LayoutOptions.Start
+ };
}
}
}
diff --git a/Handlers/EditorHandler.Linux.cs b/Handlers/EditorHandler.Linux.cs
new file mode 100644
index 0000000..a40c417
--- /dev/null
+++ b/Handlers/EditorHandler.Linux.cs
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Handlers;
+
+namespace Microsoft.Maui.Platform.Linux.Handlers;
+
+///
+/// Linux handler for Editor control.
+///
+public class EditorHandler : ViewHandler
+{
+ public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
+ {
+ ["Text"] = MapText,
+ ["Placeholder"] = MapPlaceholder,
+ ["PlaceholderColor"] = MapPlaceholderColor,
+ ["TextColor"] = MapTextColor,
+ ["CharacterSpacing"] = MapCharacterSpacing,
+ ["IsReadOnly"] = MapIsReadOnly,
+ ["IsTextPredictionEnabled"] = MapIsTextPredictionEnabled,
+ ["MaxLength"] = MapMaxLength,
+ ["CursorPosition"] = MapCursorPosition,
+ ["SelectionLength"] = MapSelectionLength,
+ ["Keyboard"] = MapKeyboard,
+ ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
+ ["VerticalTextAlignment"] = MapVerticalTextAlignment,
+ ["Background"] = MapBackground,
+ ["BackgroundColor"] = MapBackgroundColor
+ };
+
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
+
+ public EditorHandler() : base(Mapper, CommandMapper)
+ {
+ }
+
+ public EditorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
+ : base(mapper ?? Mapper, commandMapper ?? CommandMapper)
+ {
+ }
+
+ protected override SkiaEditor CreatePlatformView()
+ {
+ return new SkiaEditor();
+ }
+
+ protected override void ConnectHandler(SkiaEditor platformView)
+ {
+ base.ConnectHandler(platformView);
+ platformView.TextChanged += OnTextChanged;
+ platformView.Completed += OnCompleted;
+ }
+
+ protected override void DisconnectHandler(SkiaEditor platformView)
+ {
+ platformView.TextChanged -= OnTextChanged;
+ platformView.Completed -= OnCompleted;
+ base.DisconnectHandler(platformView);
+ }
+
+ private void OnTextChanged(object? sender, EventArgs e)
+ {
+ if (VirtualView != null && PlatformView != null)
+ {
+ VirtualView.Text = PlatformView.Text;
+ }
+ }
+
+ private void OnCompleted(object? sender, EventArgs e)
+ {
+ // Editor completed - no specific action needed
+ }
+
+ public static void MapText(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Text = editor.Text ?? "";
+ handler.PlatformView.Invalidate();
+ }
+ }
+
+ public static void MapPlaceholder(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Placeholder = editor.Placeholder ?? "";
+ }
+ }
+
+ public static void MapPlaceholderColor(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null && editor.PlaceholderColor != null)
+ {
+ handler.PlatformView.PlaceholderColor = editor.PlaceholderColor.ToSKColor();
+ }
+ }
+
+ public static void MapTextColor(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null && editor.TextColor != null)
+ {
+ handler.PlatformView.TextColor = editor.TextColor.ToSKColor();
+ }
+ }
+
+ public static void MapCharacterSpacing(EditorHandler handler, IEditor editor)
+ {
+ // Character spacing not implemented for editor
+ }
+
+ public static void MapIsReadOnly(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.IsReadOnly = editor.IsReadOnly;
+ }
+ }
+
+ public static void MapIsTextPredictionEnabled(EditorHandler handler, IEditor editor)
+ {
+ // Text prediction is a mobile feature
+ }
+
+ public static void MapMaxLength(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.MaxLength = editor.MaxLength;
+ }
+ }
+
+ public static void MapCursorPosition(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.CursorPosition = editor.CursorPosition;
+ }
+ }
+
+ public static void MapSelectionLength(EditorHandler handler, IEditor editor)
+ {
+ // Selection length not implemented
+ }
+
+ public static void MapKeyboard(EditorHandler handler, IEditor editor)
+ {
+ // Keyboard type is a mobile feature
+ }
+
+ public static void MapHorizontalTextAlignment(EditorHandler handler, IEditor editor)
+ {
+ // Horizontal text alignment not implemented
+ }
+
+ public static void MapVerticalTextAlignment(EditorHandler handler, IEditor editor)
+ {
+ // Vertical text alignment not implemented
+ }
+
+ public static void MapBackground(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ if (editor.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
+ }
+ }
+
+ public static void MapBackgroundColor(EditorHandler handler, IEditor editor)
+ {
+ if (handler.PlatformView != null)
+ {
+ if (editor is VisualElement ve && ve.BackgroundColor != null)
+ {
+ handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
+ handler.PlatformView.Invalidate();
+ }
+ }
+ }
+}
diff --git a/Handlers/EntryHandler.Linux.cs b/Handlers/EntryHandler.Linux.cs
index 1f2eac6..db07b6c 100644
--- a/Handlers/EntryHandler.Linux.cs
+++ b/Handlers/EntryHandler.Linux.cs
@@ -1,55 +1,47 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for Entry control.
///
-public partial class EntryHandler : ViewHandler
+public class EntryHandler : ViewHandler
{
- ///
- /// Maps the property mapper for the handler.
- ///
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(IEntry.Text)] = MapText,
- [nameof(IEntry.TextColor)] = MapTextColor,
- [nameof(IEntry.Placeholder)] = MapPlaceholder,
- [nameof(IEntry.PlaceholderColor)] = MapPlaceholderColor,
- [nameof(IEntry.Font)] = MapFont,
- [nameof(IEntry.IsPassword)] = MapIsPassword,
- [nameof(IEntry.MaxLength)] = MapMaxLength,
- [nameof(IEntry.IsReadOnly)] = MapIsReadOnly,
- [nameof(IEntry.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
- [nameof(IEntry.CursorPosition)] = MapCursorPosition,
- [nameof(IEntry.SelectionLength)] = MapSelectionLength,
- [nameof(IEntry.ReturnType)] = MapReturnType,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IEntry.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["Text"] = MapText,
+ ["TextColor"] = MapTextColor,
+ ["Font"] = MapFont,
+ ["CharacterSpacing"] = MapCharacterSpacing,
+ ["Placeholder"] = MapPlaceholder,
+ ["PlaceholderColor"] = MapPlaceholderColor,
+ ["IsReadOnly"] = MapIsReadOnly,
+ ["MaxLength"] = MapMaxLength,
+ ["CursorPosition"] = MapCursorPosition,
+ ["SelectionLength"] = MapSelectionLength,
+ ["IsPassword"] = MapIsPassword,
+ ["ReturnType"] = MapReturnType,
+ ["ClearButtonVisibility"] = MapClearButtonVisibility,
+ ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
+ ["VerticalTextAlignment"] = MapVerticalTextAlignment,
+ ["Background"] = MapBackground,
+ ["BackgroundColor"] = MapBackgroundColor
};
- ///
- /// Maps the command mapper for the handler.
- ///
- public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper)
- {
- };
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
public EntryHandler() : base(Mapper, CommandMapper)
{
}
- public EntryHandler(IPropertyMapper? mapper)
- : base(mapper ?? Mapper, CommandMapper)
- {
- }
-
- public EntryHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
+ public EntryHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
@@ -75,9 +67,9 @@ public partial class EntryHandler : ViewHandler
private void OnTextChanged(object? sender, TextChangedEventArgs e)
{
- if (VirtualView != null && VirtualView.Text != e.NewText)
+ if (VirtualView != null && PlatformView != null && VirtualView.Text != e.NewTextValue)
{
- VirtualView.Text = e.NewText;
+ VirtualView.Text = e.NewTextValue ?? string.Empty;
}
}
@@ -88,112 +80,173 @@ public partial class EntryHandler : ViewHandler
public static void MapText(EntryHandler handler, IEntry entry)
{
- if (handler.PlatformView.Text != entry.Text)
+ if (handler.PlatformView != null && handler.PlatformView.Text != entry.Text)
{
- handler.PlatformView.Text = entry.Text ?? "";
+ handler.PlatformView.Text = entry.Text ?? string.Empty;
+ handler.PlatformView.Invalidate();
}
}
public static void MapTextColor(EntryHandler handler, IEntry entry)
{
- if (entry.TextColor != null)
+ if (handler.PlatformView != null && entry.TextColor != null)
{
handler.PlatformView.TextColor = entry.TextColor.ToSKColor();
}
- handler.PlatformView.Invalidate();
- }
-
- public static void MapPlaceholder(EntryHandler handler, IEntry entry)
- {
- handler.PlatformView.Placeholder = entry.Placeholder ?? "";
- handler.PlatformView.Invalidate();
- }
-
- public static void MapPlaceholderColor(EntryHandler handler, IEntry entry)
- {
- if (entry.PlaceholderColor != null)
- {
- handler.PlatformView.PlaceholderColor = entry.PlaceholderColor.ToSKColor();
- }
- handler.PlatformView.Invalidate();
}
public static void MapFont(EntryHandler handler, IEntry entry)
{
- var font = entry.Font;
- if (font.Family != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.FontFamily = font.Family;
+ var font = entry.Font;
+ if (font.Size > 0)
+ {
+ handler.PlatformView.FontSize = (float)font.Size;
+ }
+ if (!string.IsNullOrEmpty(font.Family))
+ {
+ handler.PlatformView.FontFamily = font.Family;
+ }
+ handler.PlatformView.IsBold = (int)font.Weight >= 700;
+ handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic || font.Slant == FontSlant.Oblique;
}
- handler.PlatformView.FontSize = (float)font.Size;
- handler.PlatformView.Invalidate();
}
- public static void MapIsPassword(EntryHandler handler, IEntry entry)
+ public static void MapCharacterSpacing(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.IsPassword = entry.IsPassword;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.CharacterSpacing = (float)entry.CharacterSpacing;
+ }
}
- public static void MapMaxLength(EntryHandler handler, IEntry entry)
+ public static void MapPlaceholder(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.MaxLength = entry.MaxLength;
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Placeholder = entry.Placeholder ?? string.Empty;
+ }
+ }
+
+ public static void MapPlaceholderColor(EntryHandler handler, IEntry entry)
+ {
+ if (handler.PlatformView != null && entry.PlaceholderColor != null)
+ {
+ handler.PlatformView.PlaceholderColor = entry.PlaceholderColor.ToSKColor();
+ }
}
public static void MapIsReadOnly(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.IsReadOnly = entry.IsReadOnly;
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.IsReadOnly = entry.IsReadOnly;
+ }
}
- public static void MapHorizontalTextAlignment(EntryHandler handler, IEntry entry)
+ public static void MapMaxLength(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.HorizontalTextAlignment = entry.HorizontalTextAlignment switch
+ if (handler.PlatformView != null)
{
- Microsoft.Maui.TextAlignment.Start => TextAlignment.Start,
- Microsoft.Maui.TextAlignment.Center => TextAlignment.Center,
- Microsoft.Maui.TextAlignment.End => TextAlignment.End,
- _ => TextAlignment.Start
- };
- handler.PlatformView.Invalidate();
+ handler.PlatformView.MaxLength = entry.MaxLength;
+ }
}
public static void MapCursorPosition(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.CursorPosition = entry.CursorPosition;
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.CursorPosition = entry.CursorPosition;
+ }
}
public static void MapSelectionLength(EntryHandler handler, IEntry entry)
{
- // Selection length is handled internally by SkiaEntry
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.SelectionLength = entry.SelectionLength;
+ }
+ }
+
+ public static void MapIsPassword(EntryHandler handler, IEntry entry)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.IsPassword = entry.IsPassword;
+ }
}
public static void MapReturnType(EntryHandler handler, IEntry entry)
{
- // Return type affects keyboard on mobile; on desktop, Enter always completes
+ // ReturnType affects keyboard on mobile; access PlatformView to ensure it exists
+ _ = handler.PlatformView;
}
- public static void MapIsEnabled(EntryHandler handler, IEntry entry)
+ public static void MapClearButtonVisibility(EntryHandler handler, IEntry entry)
{
- handler.PlatformView.IsEnabled = entry.IsEnabled;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ // ClearButtonVisibility.WhileEditing = 1
+ handler.PlatformView.ShowClearButton = (int)entry.ClearButtonVisibility == 1;
+ }
+ }
+
+ public static void MapHorizontalTextAlignment(EntryHandler handler, IEntry entry)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.HorizontalTextAlignment = (int)entry.HorizontalTextAlignment switch
+ {
+ 0 => TextAlignment.Start,
+ 1 => TextAlignment.Center,
+ 2 => TextAlignment.End,
+ _ => TextAlignment.Start
+ };
+ }
+ }
+
+ public static void MapVerticalTextAlignment(EntryHandler handler, IEntry entry)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.VerticalTextAlignment = (int)entry.VerticalTextAlignment switch
+ {
+ 0 => TextAlignment.Start,
+ 1 => TextAlignment.Center,
+ 2 => TextAlignment.End,
+ _ => TextAlignment.Center
+ };
+ }
}
public static void MapBackground(EntryHandler handler, IEntry entry)
{
- var background = entry.Background;
- if (background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
+ if (entry.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
- handler.PlatformView.Invalidate();
}
public static void MapBackgroundColor(EntryHandler handler, IEntry entry)
{
- if (entry is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
+ if (handler.PlatformView == null)
{
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ return;
+ }
+
+ if (entry is Entry mauiEntry)
+ {
+ Console.WriteLine($"[EntryHandler] MapBackgroundColor: {mauiEntry.BackgroundColor}");
+ if (mauiEntry.BackgroundColor != null)
+ {
+ var color = mauiEntry.BackgroundColor.ToSKColor();
+ Console.WriteLine($"[EntryHandler] Setting EntryBackgroundColor to: {color}");
+ handler.PlatformView.EntryBackgroundColor = color;
+ }
}
}
}
diff --git a/Handlers/LabelHandler.Linux.cs b/Handlers/LabelHandler.Linux.cs
index 3df3fd4..a9562c0 100644
--- a/Handlers/LabelHandler.Linux.cs
+++ b/Handlers/LabelHandler.Linux.cs
@@ -1,52 +1,49 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
+using Microsoft.Maui.Platform.Linux.Window;
+using Microsoft.Maui.Primitives;
using SkiaSharp;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for Label control.
///
-public partial class LabelHandler : ViewHandler
+public class LabelHandler : ViewHandler
{
- ///
- /// Maps the property mapper for the handler.
- ///
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(ILabel.Text)] = MapText,
- [nameof(ILabel.TextColor)] = MapTextColor,
- [nameof(ILabel.Font)] = MapFont,
- [nameof(ILabel.HorizontalTextAlignment)] = MapHorizontalTextAlignment,
- [nameof(ILabel.VerticalTextAlignment)] = MapVerticalTextAlignment,
- [nameof(ILabel.LineBreakMode)] = MapLineBreakMode,
- [nameof(ILabel.MaxLines)] = MapMaxLines,
- [nameof(ILabel.Padding)] = MapPadding,
- [nameof(ILabel.TextDecorations)] = MapTextDecorations,
- [nameof(ILabel.LineHeight)] = MapLineHeight,
- [nameof(ILabel.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["Text"] = MapText,
+ ["TextColor"] = MapTextColor,
+ ["Font"] = MapFont,
+ ["CharacterSpacing"] = MapCharacterSpacing,
+ ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
+ ["VerticalTextAlignment"] = MapVerticalTextAlignment,
+ ["TextDecorations"] = MapTextDecorations,
+ ["LineHeight"] = MapLineHeight,
+ ["LineBreakMode"] = MapLineBreakMode,
+ ["MaxLines"] = MapMaxLines,
+ ["Padding"] = MapPadding,
+ ["Background"] = MapBackground,
+ ["VerticalLayoutAlignment"] = MapVerticalLayoutAlignment,
+ ["HorizontalLayoutAlignment"] = MapHorizontalLayoutAlignment,
+ ["FormattedText"] = MapFormattedText
};
- ///
- /// Maps the command mapper for the handler.
- ///
- public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper)
- {
- };
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
public LabelHandler() : base(Mapper, CommandMapper)
{
}
- public LabelHandler(IPropertyMapper? mapper)
- : base(mapper ?? Mapper, CommandMapper)
- {
- }
-
- public LabelHandler(IPropertyMapper? mapper, CommandMapper? commandMapper)
+ public LabelHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
@@ -56,119 +53,263 @@ public partial class LabelHandler : ViewHandler
return new SkiaLabel();
}
+ protected override void ConnectHandler(SkiaLabel platformView)
+ {
+ base.ConnectHandler(platformView);
+
+ if (VirtualView is View view)
+ {
+ platformView.MauiView = view;
+ if (view.GestureRecognizers.OfType().Any())
+ {
+ platformView.CursorType = CursorType.Hand;
+ }
+ }
+
+ platformView.Tapped += OnPlatformViewTapped;
+ }
+
+ protected override void DisconnectHandler(SkiaLabel platformView)
+ {
+ platformView.Tapped -= OnPlatformViewTapped;
+ platformView.MauiView = null;
+ base.DisconnectHandler(platformView);
+ }
+
+ private void OnPlatformViewTapped(object? sender, EventArgs e)
+ {
+ if (VirtualView is View view)
+ {
+ GestureManager.ProcessTap(view, 0.0, 0.0);
+ }
+ }
+
public static void MapText(LabelHandler handler, ILabel label)
{
- handler.PlatformView.Text = label.Text ?? "";
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Text = label.Text ?? string.Empty;
+ }
}
public static void MapTextColor(LabelHandler handler, ILabel label)
{
- if (label.TextColor != null)
+ if (handler.PlatformView != null && label.TextColor != null)
{
handler.PlatformView.TextColor = label.TextColor.ToSKColor();
}
- handler.PlatformView.Invalidate();
}
public static void MapFont(LabelHandler handler, ILabel label)
{
- var font = label.Font;
- if (font.Family != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.FontFamily = font.Family;
+ var font = label.Font;
+ if (font.Size > 0)
+ {
+ handler.PlatformView.FontSize = (float)font.Size;
+ }
+ if (!string.IsNullOrEmpty(font.Family))
+ {
+ handler.PlatformView.FontFamily = font.Family;
+ }
+ handler.PlatformView.IsBold = (int)font.Weight >= 700;
+ handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic || font.Slant == FontSlant.Oblique;
+ }
+ }
+
+ public static void MapCharacterSpacing(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.CharacterSpacing = (float)label.CharacterSpacing;
}
- handler.PlatformView.FontSize = (float)font.Size;
- handler.PlatformView.IsBold = font.Weight == FontWeight.Bold;
- handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic;
- handler.PlatformView.Invalidate();
}
public static void MapHorizontalTextAlignment(LabelHandler handler, ILabel label)
{
- handler.PlatformView.HorizontalTextAlignment = label.HorizontalTextAlignment switch
+ if (handler.PlatformView != null)
{
- Microsoft.Maui.TextAlignment.Start => TextAlignment.Start,
- Microsoft.Maui.TextAlignment.Center => TextAlignment.Center,
- Microsoft.Maui.TextAlignment.End => TextAlignment.End,
- _ => TextAlignment.Start
- };
- handler.PlatformView.Invalidate();
+ handler.PlatformView.HorizontalTextAlignment = (int)label.HorizontalTextAlignment switch
+ {
+ 0 => TextAlignment.Start,
+ 1 => TextAlignment.Center,
+ 2 => TextAlignment.End,
+ _ => TextAlignment.Start
+ };
+ }
}
public static void MapVerticalTextAlignment(LabelHandler handler, ILabel label)
{
- handler.PlatformView.VerticalTextAlignment = label.VerticalTextAlignment switch
+ if (handler.PlatformView != null)
{
- Microsoft.Maui.TextAlignment.Start => TextAlignment.Start,
- Microsoft.Maui.TextAlignment.Center => TextAlignment.Center,
- Microsoft.Maui.TextAlignment.End => TextAlignment.End,
- _ => TextAlignment.Center
- };
- handler.PlatformView.Invalidate();
- }
-
- public static void MapLineBreakMode(LabelHandler handler, ILabel label)
- {
- handler.PlatformView.LineBreakMode = label.LineBreakMode switch
- {
- Microsoft.Maui.LineBreakMode.NoWrap => LineBreakMode.NoWrap,
- Microsoft.Maui.LineBreakMode.WordWrap => LineBreakMode.WordWrap,
- Microsoft.Maui.LineBreakMode.CharacterWrap => LineBreakMode.CharacterWrap,
- Microsoft.Maui.LineBreakMode.HeadTruncation => LineBreakMode.HeadTruncation,
- Microsoft.Maui.LineBreakMode.TailTruncation => LineBreakMode.TailTruncation,
- Microsoft.Maui.LineBreakMode.MiddleTruncation => LineBreakMode.MiddleTruncation,
- _ => LineBreakMode.TailTruncation
- };
- handler.PlatformView.Invalidate();
- }
-
- public static void MapMaxLines(LabelHandler handler, ILabel label)
- {
- handler.PlatformView.MaxLines = label.MaxLines;
- handler.PlatformView.Invalidate();
- }
-
- public static void MapPadding(LabelHandler handler, ILabel label)
- {
- var padding = label.Padding;
- handler.PlatformView.Padding = new SKRect(
- (float)padding.Left,
- (float)padding.Top,
- (float)padding.Right,
- (float)padding.Bottom);
- handler.PlatformView.Invalidate();
+ handler.PlatformView.VerticalTextAlignment = (int)label.VerticalTextAlignment switch
+ {
+ 0 => TextAlignment.Start,
+ 1 => TextAlignment.Center,
+ 2 => TextAlignment.End,
+ _ => TextAlignment.Center
+ };
+ }
}
public static void MapTextDecorations(LabelHandler handler, ILabel label)
{
- var decorations = label.TextDecorations;
- handler.PlatformView.IsUnderline = decorations.HasFlag(TextDecorations.Underline);
- handler.PlatformView.IsStrikethrough = decorations.HasFlag(TextDecorations.Strikethrough);
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.IsUnderline = (label.TextDecorations & TextDecorations.Underline) != 0;
+ handler.PlatformView.IsStrikethrough = (label.TextDecorations & TextDecorations.Strikethrough) != 0;
+ }
}
public static void MapLineHeight(LabelHandler handler, ILabel label)
{
- handler.PlatformView.LineHeight = (float)label.LineHeight;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.LineHeight = (float)label.LineHeight;
+ }
+ }
+
+ public static void MapLineBreakMode(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView != null)
+ {
+ if (label is Label mauiLabel)
+ {
+ handler.PlatformView.LineBreakMode = (int)mauiLabel.LineBreakMode switch
+ {
+ 0 => LineBreakMode.NoWrap,
+ 1 => LineBreakMode.WordWrap,
+ 2 => LineBreakMode.CharacterWrap,
+ 3 => LineBreakMode.HeadTruncation,
+ 4 => LineBreakMode.TailTruncation,
+ 5 => LineBreakMode.MiddleTruncation,
+ _ => LineBreakMode.TailTruncation
+ };
+ }
+ }
+ }
+
+ public static void MapMaxLines(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView != null)
+ {
+ if (label is Label mauiLabel)
+ {
+ handler.PlatformView.MaxLines = mauiLabel.MaxLines;
+ }
+ }
+ }
+
+ public static void MapPadding(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView != null)
+ {
+ var padding = label.Padding;
+ handler.PlatformView.Padding = new SKRect(
+ (float)padding.Left,
+ (float)padding.Top,
+ (float)padding.Right,
+ (float)padding.Bottom);
+ }
}
public static void MapBackground(LabelHandler handler, ILabel label)
{
- if (label.Background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (label.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
}
- public static void MapBackgroundColor(LabelHandler handler, ILabel label)
+ public static void MapVerticalLayoutAlignment(LabelHandler handler, ILabel label)
{
- if (label is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ handler.PlatformView.VerticalOptions = (int)label.VerticalLayoutAlignment switch
+ {
+ 1 => LayoutOptions.Start,
+ 2 => LayoutOptions.Center,
+ 3 => LayoutOptions.End,
+ 0 => LayoutOptions.Fill,
+ _ => LayoutOptions.Start
+ };
}
}
+
+ public static void MapHorizontalLayoutAlignment(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.HorizontalOptions = (int)label.HorizontalLayoutAlignment switch
+ {
+ 1 => LayoutOptions.Start,
+ 2 => LayoutOptions.Center,
+ 3 => LayoutOptions.End,
+ 0 => LayoutOptions.Fill,
+ _ => LayoutOptions.Start
+ };
+ }
+ }
+
+ public static void MapFormattedText(LabelHandler handler, ILabel label)
+ {
+ if (handler.PlatformView == null)
+ {
+ return;
+ }
+
+ if (label is not Label mauiLabel)
+ {
+ handler.PlatformView.FormattedSpans = null;
+ return;
+ }
+
+ var formattedText = mauiLabel.FormattedText;
+ if (formattedText == null || formattedText.Spans.Count == 0)
+ {
+ handler.PlatformView.FormattedSpans = null;
+ return;
+ }
+
+ var spans = new List();
+ foreach (var span in formattedText.Spans)
+ {
+ var skiaSpan = new SkiaTextSpan
+ {
+ Text = span.Text ?? "",
+ IsBold = span.FontAttributes.HasFlag(FontAttributes.Bold),
+ IsItalic = span.FontAttributes.HasFlag(FontAttributes.Italic),
+ IsUnderline = (span.TextDecorations & TextDecorations.Underline) != 0,
+ IsStrikethrough = (span.TextDecorations & TextDecorations.Strikethrough) != 0,
+ CharacterSpacing = (float)span.CharacterSpacing,
+ LineHeight = (float)span.LineHeight
+ };
+
+ if (span.TextColor != null)
+ {
+ skiaSpan.TextColor = span.TextColor.ToSKColor();
+ }
+ if (span.BackgroundColor != null)
+ {
+ skiaSpan.BackgroundColor = span.BackgroundColor.ToSKColor();
+ }
+ if (!string.IsNullOrEmpty(span.FontFamily))
+ {
+ skiaSpan.FontFamily = span.FontFamily;
+ }
+ if (span.FontSize > 0)
+ {
+ skiaSpan.FontSize = (float)span.FontSize;
+ }
+
+ spans.Add(skiaSpan);
+ }
+
+ handler.PlatformView.FormattedSpans = spans;
+ }
}
diff --git a/Handlers/PickerHandler.Linux.cs b/Handlers/PickerHandler.Linux.cs
new file mode 100644
index 0000000..62a4a52
--- /dev/null
+++ b/Handlers/PickerHandler.Linux.cs
@@ -0,0 +1,178 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Specialized;
+using System.Linq;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
+using Microsoft.Maui.Handlers;
+
+namespace Microsoft.Maui.Platform.Linux.Handlers;
+
+///
+/// Linux handler for Picker control.
+///
+public class PickerHandler : ViewHandler
+{
+ public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
+ {
+ ["Title"] = MapTitle,
+ ["TitleColor"] = MapTitleColor,
+ ["SelectedIndex"] = MapSelectedIndex,
+ ["TextColor"] = MapTextColor,
+ ["Font"] = MapFont,
+ ["CharacterSpacing"] = MapCharacterSpacing,
+ ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
+ ["VerticalTextAlignment"] = MapVerticalTextAlignment,
+ ["Background"] = MapBackground,
+ ["ItemsSource"] = MapItemsSource
+ };
+
+ public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
+
+ private INotifyCollectionChanged? _itemsCollection;
+
+ public PickerHandler() : base(Mapper, CommandMapper)
+ {
+ }
+
+ public PickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
+ : base(mapper ?? Mapper, commandMapper ?? CommandMapper)
+ {
+ }
+
+ protected override SkiaPicker CreatePlatformView()
+ {
+ return new SkiaPicker();
+ }
+
+ protected override void ConnectHandler(SkiaPicker platformView)
+ {
+ base.ConnectHandler(platformView);
+ platformView.SelectedIndexChanged += OnSelectedIndexChanged;
+
+ if (VirtualView is Picker picker && picker.Items is INotifyCollectionChanged itemsCollection)
+ {
+ _itemsCollection = itemsCollection;
+ _itemsCollection.CollectionChanged += OnItemsCollectionChanged;
+ }
+
+ ReloadItems();
+ }
+
+ protected override void DisconnectHandler(SkiaPicker platformView)
+ {
+ platformView.SelectedIndexChanged -= OnSelectedIndexChanged;
+
+ if (_itemsCollection != null)
+ {
+ _itemsCollection.CollectionChanged -= OnItemsCollectionChanged;
+ _itemsCollection = null;
+ }
+
+ base.DisconnectHandler(platformView);
+ }
+
+ private void OnItemsCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
+ {
+ ReloadItems();
+ }
+
+ private void OnSelectedIndexChanged(object? sender, EventArgs e)
+ {
+ if (VirtualView != null && PlatformView != null)
+ {
+ VirtualView.SelectedIndex = PlatformView.SelectedIndex;
+ }
+ }
+
+ private void ReloadItems()
+ {
+ if (PlatformView != null && VirtualView != null)
+ {
+ var items = VirtualView.GetItemsAsArray();
+ PlatformView.SetItems(items.Select(i => i?.ToString() ?? ""));
+ }
+ }
+
+ public static void MapTitle(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Title = picker.Title ?? "";
+ }
+ }
+
+ public static void MapTitleColor(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null && picker.TitleColor != null)
+ {
+ handler.PlatformView.TitleColor = picker.TitleColor.ToSKColor();
+ }
+ }
+
+ public static void MapSelectedIndex(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.SelectedIndex = picker.SelectedIndex;
+ }
+ }
+
+ public static void MapTextColor(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null && picker.TextColor != null)
+ {
+ handler.PlatformView.TextColor = picker.TextColor.ToSKColor();
+ }
+ }
+
+ public static void MapFont(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null)
+ {
+ var font = picker.Font;
+ if (!string.IsNullOrEmpty(font.Family))
+ {
+ handler.PlatformView.FontFamily = font.Family;
+ }
+ if (font.Size > 0)
+ {
+ handler.PlatformView.FontSize = (float)font.Size;
+ }
+ handler.PlatformView.Invalidate();
+ }
+ }
+
+ public static void MapCharacterSpacing(PickerHandler handler, IPicker picker)
+ {
+ // Character spacing not implemented
+ }
+
+ public static void MapHorizontalTextAlignment(PickerHandler handler, IPicker picker)
+ {
+ // Horizontal text alignment not implemented
+ }
+
+ public static void MapVerticalTextAlignment(PickerHandler handler, IPicker picker)
+ {
+ // Vertical text alignment not implemented
+ }
+
+ public static void MapBackground(PickerHandler handler, IPicker picker)
+ {
+ if (handler.PlatformView != null)
+ {
+ if (picker.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
+ }
+ }
+
+ public static void MapItemsSource(PickerHandler handler, IPicker picker)
+ {
+ handler.ReloadItems();
+ }
+}
diff --git a/Handlers/ProgressBarHandler.Linux.cs b/Handlers/ProgressBarHandler.Linux.cs
index e12f5fe..284c45a 100644
--- a/Handlers/ProgressBarHandler.Linux.cs
+++ b/Handlers/ProgressBarHandler.Linux.cs
@@ -1,29 +1,71 @@
// 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 Microsoft.Maui.Controls;
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for ProgressBar control.
///
-public partial class ProgressBarHandler : ViewHandler
+public class ProgressBarHandler : ViewHandler
{
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(IProgress.Progress)] = MapProgress,
- [nameof(IProgress.ProgressColor)] = MapProgressColor,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IView.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["Progress"] = MapProgress,
+ ["ProgressColor"] = MapProgressColor,
+ ["IsEnabled"] = MapIsEnabled,
+ ["Background"] = MapBackground,
+ ["BackgroundColor"] = MapBackgroundColor
};
public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
- public ProgressBarHandler() : base(Mapper, CommandMapper) { }
+ public ProgressBarHandler() : base(Mapper, CommandMapper)
+ {
+ }
- protected override SkiaProgressBar CreatePlatformView() => new SkiaProgressBar();
+ protected override SkiaProgressBar CreatePlatformView()
+ {
+ return new SkiaProgressBar();
+ }
+
+ protected override void ConnectHandler(SkiaProgressBar platformView)
+ {
+ base.ConnectHandler(platformView);
+
+ if (VirtualView is BindableObject bindable)
+ {
+ bindable.PropertyChanged += OnVirtualViewPropertyChanged;
+ }
+
+ if (VirtualView is VisualElement ve)
+ {
+ platformView.IsVisible = ve.IsVisible;
+ }
+ }
+
+ protected override void DisconnectHandler(SkiaProgressBar platformView)
+ {
+ if (VirtualView is BindableObject bindable)
+ {
+ bindable.PropertyChanged -= OnVirtualViewPropertyChanged;
+ }
+
+ base.DisconnectHandler(platformView);
+ }
+
+ private void OnVirtualViewPropertyChanged(object? sender, PropertyChangedEventArgs e)
+ {
+ if (VirtualView is VisualElement ve && e.PropertyName == "IsVisible")
+ {
+ PlatformView.IsVisible = ve.IsVisible;
+ PlatformView.Invalidate();
+ }
+ }
public static void MapProgress(ProgressBarHandler handler, IProgress progress)
{
@@ -33,7 +75,9 @@ public partial class ProgressBarHandler : ViewHandler
/// Linux handler for Slider control.
///
-public partial class SliderHandler : ViewHandler
+public class SliderHandler : ViewHandler
{
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(ISlider.Minimum)] = MapMinimum,
- [nameof(ISlider.Maximum)] = MapMaximum,
- [nameof(ISlider.Value)] = MapValue,
- [nameof(ISlider.MinimumTrackColor)] = MapMinimumTrackColor,
- [nameof(ISlider.MaximumTrackColor)] = MapMaximumTrackColor,
- [nameof(ISlider.ThumbColor)] = MapThumbColor,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IView.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["Minimum"] = MapMinimum,
+ ["Maximum"] = MapMaximum,
+ ["Value"] = MapValue,
+ ["MinimumTrackColor"] = MapMinimumTrackColor,
+ ["MaximumTrackColor"] = MapMaximumTrackColor,
+ ["ThumbColor"] = MapThumbColor,
+ ["Background"] = MapBackground,
+ ["IsEnabled"] = MapIsEnabled
};
public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
- public SliderHandler() : base(Mapper, CommandMapper) { }
+ public SliderHandler() : base(Mapper, CommandMapper)
+ {
+ }
- protected override SkiaSlider CreatePlatformView() => new SkiaSlider();
+ public SliderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
+ : base(mapper ?? Mapper, commandMapper ?? CommandMapper)
+ {
+ }
+
+ protected override SkiaSlider CreatePlatformView()
+ {
+ return new SkiaSlider();
+ }
protected override void ConnectHandler(SkiaSlider platformView)
{
@@ -35,6 +46,14 @@ public partial class SliderHandler : ViewHandler
platformView.ValueChanged += OnValueChanged;
platformView.DragStarted += OnDragStarted;
platformView.DragCompleted += OnDragCompleted;
+
+ if (VirtualView != null)
+ {
+ MapMinimum(this, VirtualView);
+ MapMaximum(this, VirtualView);
+ MapValue(this, VirtualView);
+ MapIsEnabled(this, VirtualView);
+ }
}
protected override void DisconnectHandler(SkiaSlider platformView)
@@ -47,30 +66,41 @@ public partial class SliderHandler : ViewHandler
private void OnValueChanged(object? sender, SliderValueChangedEventArgs e)
{
- if (VirtualView != null && Math.Abs(VirtualView.Value - e.NewValue) > 0.001)
+ if (VirtualView != null && PlatformView != null && Math.Abs(VirtualView.Value - e.NewValue) > 0.0001)
{
VirtualView.Value = e.NewValue;
}
}
- private void OnDragStarted(object? sender, EventArgs e) => VirtualView?.DragStarted();
- private void OnDragCompleted(object? sender, EventArgs e) => VirtualView?.DragCompleted();
+ private void OnDragStarted(object? sender, EventArgs e)
+ {
+ VirtualView?.DragStarted();
+ }
+
+ private void OnDragCompleted(object? sender, EventArgs e)
+ {
+ VirtualView?.DragCompleted();
+ }
public static void MapMinimum(SliderHandler handler, ISlider slider)
{
- handler.PlatformView.Minimum = slider.Minimum;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Minimum = slider.Minimum;
+ }
}
public static void MapMaximum(SliderHandler handler, ISlider slider)
{
- handler.PlatformView.Maximum = slider.Maximum;
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null)
+ {
+ handler.PlatformView.Maximum = slider.Maximum;
+ }
}
public static void MapValue(SliderHandler handler, ISlider slider)
{
- if (Math.Abs(handler.PlatformView.Value - slider.Value) > 0.001)
+ if (handler.PlatformView != null && Math.Abs(handler.PlatformView.Value - slider.Value) > 0.0001)
{
handler.PlatformView.Value = slider.Value;
}
@@ -78,45 +108,44 @@ public partial class SliderHandler : ViewHandler
public static void MapMinimumTrackColor(SliderHandler handler, ISlider slider)
{
- if (slider.MinimumTrackColor != null)
+ if (handler.PlatformView != null && slider.MinimumTrackColor != null)
+ {
handler.PlatformView.ActiveTrackColor = slider.MinimumTrackColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ }
}
public static void MapMaximumTrackColor(SliderHandler handler, ISlider slider)
{
- if (slider.MaximumTrackColor != null)
+ if (handler.PlatformView != null && slider.MaximumTrackColor != null)
+ {
handler.PlatformView.TrackColor = slider.MaximumTrackColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ }
}
public static void MapThumbColor(SliderHandler handler, ISlider slider)
{
- if (slider.ThumbColor != null)
+ if (handler.PlatformView != null && slider.ThumbColor != null)
+ {
handler.PlatformView.ThumbColor = slider.ThumbColor.ToSKColor();
- handler.PlatformView.Invalidate();
- }
-
- public static void MapIsEnabled(SliderHandler handler, ISlider slider)
- {
- handler.PlatformView.IsEnabled = slider.IsEnabled;
- handler.PlatformView.Invalidate();
+ }
}
public static void MapBackground(SliderHandler handler, ISlider slider)
{
- if (slider.Background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (slider.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
}
- public static void MapBackgroundColor(SliderHandler handler, ISlider slider)
+ public static void MapIsEnabled(SliderHandler handler, ISlider slider)
{
- if (slider is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
+ handler.PlatformView.IsEnabled = slider.IsEnabled;
handler.PlatformView.Invalidate();
}
}
diff --git a/Handlers/SwitchHandler.Linux.cs b/Handlers/SwitchHandler.Linux.cs
index fd9873e..79d9ae1 100644
--- a/Handlers/SwitchHandler.Linux.cs
+++ b/Handlers/SwitchHandler.Linux.cs
@@ -1,30 +1,41 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
+using SkiaSharp;
-namespace Microsoft.Maui.Platform;
+namespace Microsoft.Maui.Platform.Linux.Handlers;
///
/// Linux handler for Switch control.
///
-public partial class SwitchHandler : ViewHandler
+public class SwitchHandler : ViewHandler
{
public static IPropertyMapper Mapper = new PropertyMapper(ViewHandler.ViewMapper)
{
- [nameof(ISwitch.IsOn)] = MapIsOn,
- [nameof(ISwitch.TrackColor)] = MapTrackColor,
- [nameof(ISwitch.ThumbColor)] = MapThumbColor,
- [nameof(IView.IsEnabled)] = MapIsEnabled,
- [nameof(IView.Background)] = MapBackground,
- ["BackgroundColor"] = MapBackgroundColor,
+ ["IsOn"] = MapIsOn,
+ ["TrackColor"] = MapTrackColor,
+ ["ThumbColor"] = MapThumbColor,
+ ["Background"] = MapBackground,
+ ["IsEnabled"] = MapIsEnabled
};
public static CommandMapper CommandMapper = new(ViewHandler.ViewCommandMapper);
- public SwitchHandler() : base(Mapper, CommandMapper) { }
+ public SwitchHandler() : base(Mapper, CommandMapper)
+ {
+ }
- protected override SkiaSwitch CreatePlatformView() => new SkiaSwitch();
+ public SwitchHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
+ : base(mapper ?? Mapper, commandMapper ?? CommandMapper)
+ {
+ }
+
+ protected override SkiaSwitch CreatePlatformView()
+ {
+ return new SkiaSwitch();
+ }
protected override void ConnectHandler(SkiaSwitch platformView)
{
@@ -48,7 +59,7 @@ public partial class SwitchHandler : ViewHandler
public static void MapIsOn(SwitchHandler handler, ISwitch @switch)
{
- if (handler.PlatformView.IsOn != @switch.IsOn)
+ if (handler.PlatformView != null)
{
handler.PlatformView.IsOn = @switch.IsOn;
}
@@ -56,39 +67,38 @@ public partial class SwitchHandler : ViewHandler
public static void MapTrackColor(SwitchHandler handler, ISwitch @switch)
{
- if (@switch.TrackColor != null)
- handler.PlatformView.OnTrackColor = @switch.TrackColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (handler.PlatformView != null && @switch.TrackColor != null)
+ {
+ var onTrackColor = @switch.TrackColor.ToSKColor();
+ handler.PlatformView.OnTrackColor = onTrackColor;
+ handler.PlatformView.OffTrackColor = onTrackColor.WithAlpha(128);
+ }
}
public static void MapThumbColor(SwitchHandler handler, ISwitch @switch)
{
- if (@switch.ThumbColor != null)
+ if (handler.PlatformView != null && @switch.ThumbColor != null)
+ {
handler.PlatformView.ThumbColor = @switch.ThumbColor.ToSKColor();
- handler.PlatformView.Invalidate();
- }
-
- public static void MapIsEnabled(SwitchHandler handler, ISwitch @switch)
- {
- handler.PlatformView.IsEnabled = @switch.IsEnabled;
- handler.PlatformView.Invalidate();
+ }
}
public static void MapBackground(SwitchHandler handler, ISwitch @switch)
{
- if (@switch.Background is SolidColorBrush solidBrush && solidBrush.Color != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor();
- handler.PlatformView.Invalidate();
+ if (@switch.Background is SolidPaint solidPaint && solidPaint.Color != null)
+ {
+ handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor();
+ }
}
}
- public static void MapBackgroundColor(SwitchHandler handler, ISwitch @switch)
+ public static void MapIsEnabled(SwitchHandler handler, ISwitch @switch)
{
- if (@switch is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
+ if (handler.PlatformView != null)
{
- handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
- handler.PlatformView.Invalidate();
+ handler.PlatformView.IsEnabled = @switch.IsEnabled;
}
}
}
diff --git a/MERGE_TRACKING.md b/MERGE_TRACKING.md
index fac3a69..ac642d7 100644
--- a/MERGE_TRACKING.md
+++ b/MERGE_TRACKING.md
@@ -8,47 +8,49 @@
## HANDLERS
+**CRITICAL**: All handlers must use namespace `Microsoft.Maui.Platform.Linux.Handlers` and follow decompiled EXACTLY.
+
| File | Status | Notes |
|------|--------|-------|
-| ActivityIndicatorHandler.cs | [x] | Verified - matches decompiled |
-| ApplicationHandler.cs | [x] | Verified - matches decompiled |
+| ActivityIndicatorHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Removed IsEnabled/BackgroundColor (not in production), fixed namespace |
+| ApplicationHandler.cs | [ ] | NEEDS VERIFICATION |
| BorderHandler.cs | [ ] | BLOCKED - needs SkiaBorder.MauiView and Tapped |
-| BoxViewHandler.cs | [x] | Verified |
-| ButtonHandler.cs | [x] | Contains TextButtonHandler - Verified |
-| CheckBoxHandler.cs | [x] | Verified |
-| CollectionViewHandler.cs | [x] | FIXED - Added OnItemTapped gesture handling, MauiView assignment |
-| DatePickerHandler.cs | [x] | Verified |
-| EditorHandler.cs | [x] | Verified |
-| EntryHandler.cs | [x] | Verified |
-| FlexLayoutHandler.cs | [x] | Verified - matches decompiled |
-| FlyoutPageHandler.cs | [x] | Verified - matches decompiled |
+| BoxViewHandler.cs | [ ] | NEEDS VERIFICATION |
+| ButtonHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Removed MapText/TextColor/Font (not in production), fixed namespace, added null checks |
+| CheckBoxHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Added VerticalLayoutAlignment/HorizontalLayoutAlignment, fixed namespace |
+| CollectionViewHandler.cs | [ ] | NEEDS VERIFICATION |
+| DatePickerHandler.cs | [ ] | NEEDS VERIFICATION |
+| EditorHandler.Linux.cs | [x] | **CREATED 2026-01-01** - Was missing, created from decompiled |
+| EntryHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Added CharacterSpacing/ClearButtonVisibility/VerticalTextAlignment, fixed namespace, null checks |
+| FlexLayoutHandler.cs | [ ] | NEEDS VERIFICATION |
+| FlyoutPageHandler.cs | [ ] | NEEDS VERIFICATION |
| FrameHandler.cs | [ ] | BLOCKED - needs SkiaFrame.MauiView and Tapped event |
-| GestureManager.cs | [x] | FIXED - Added third fallback (TappedEvent fields), type info dump, swipe Right handling |
-| GraphicsViewHandler.cs | [x] | Verified - matches decompiled |
+| GestureManager.cs | [ ] | NEEDS VERIFICATION |
+| GraphicsViewHandler.cs | [ ] | NEEDS VERIFICATION |
| GtkWebViewHandler.cs | [x] | Added new file from decompiled |
-| GtkWebViewManager.cs | [ ] | |
-| GtkWebViewPlatformView.cs | [ ] | |
+| GtkWebViewManager.cs | [ ] | NEEDS VERIFICATION |
+| GtkWebViewPlatformView.cs | [ ] | NEEDS VERIFICATION |
| GtkWebViewProxy.cs | [x] | Added new file from decompiled |
-| ImageButtonHandler.cs | [x] | FIXED - added MapBackgroundColor |
-| ImageHandler.cs | [x] | Verified |
-| ItemsViewHandler.cs | [x] | Verified - matches decompiled |
-| LabelHandler.cs | [x] | Verified |
-| LayoutHandler.cs | [x] | Contains GridHandler, StackLayoutHandler, LayoutHandlerUpdate - Verified |
-| NavigationPageHandler.cs | [x] | FIXED - Added LoadToolbarIcon, Icon loading, content handling, animated params |
-| PageHandler.cs | [x] | Added MapBackgroundColor |
-| PickerHandler.cs | [x] | Verified |
-| ProgressBarHandler.cs | [x] | Verified |
-| RadioButtonHandler.cs | [x] | Verified - matches decompiled |
-| ScrollViewHandler.cs | [x] | Verified |
-| SearchBarHandler.cs | [x] | Verified - matches decompiled |
-| ShellHandler.cs | [x] | Verified - matches decompiled |
-| SliderHandler.cs | [x] | Verified |
-| StepperHandler.cs | [x] | FIXED - Added MapIncrement, MapIsEnabled, dark theme colors |
-| SwitchHandler.cs | [x] | Verified |
-| TabbedPageHandler.cs | [x] | Verified - matches decompiled |
-| TimePickerHandler.cs | [x] | FIXED - Added dark theme colors |
-| WebViewHandler.cs | [x] | Fixed namespace-qualified event args |
-| WindowHandler.cs | [x] | Verified - Contains SkiaWindow, SizeChangedEventArgs |
+| ImageButtonHandler.cs | [ ] | NEEDS VERIFICATION |
+| ImageHandler.cs | [ ] | NEEDS VERIFICATION |
+| ItemsViewHandler.cs | [ ] | NEEDS VERIFICATION |
+| LabelHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Added CharacterSpacing/LayoutAlignment/FormattedText, ConnectHandler gesture logic, fixed namespace |
+| LayoutHandler.cs | [ ] | NEEDS VERIFICATION |
+| NavigationPageHandler.cs | [ ] | NEEDS VERIFICATION |
+| PageHandler.cs | [ ] | NEEDS VERIFICATION |
+| PickerHandler.Linux.cs | [x] | **CREATED 2026-01-01** - Was missing, created from decompiled with collection changed tracking |
+| ProgressBarHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Added ConnectHandler/DisconnectHandler IsVisible tracking, fixed namespace |
+| RadioButtonHandler.cs | [ ] | NEEDS VERIFICATION |
+| ScrollViewHandler.cs | [ ] | NEEDS VERIFICATION |
+| SearchBarHandler.cs | [ ] | NEEDS VERIFICATION |
+| ShellHandler.cs | [ ] | NEEDS VERIFICATION |
+| SliderHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Removed BackgroundColor (use base), fixed namespace, added ConnectHandler init calls |
+| StepperHandler.cs | [ ] | NEEDS VERIFICATION |
+| SwitchHandler.Linux.cs | [x] | **FIXED 2026-01-01** - Added OffTrackColor logic, fixed namespace, removed extra BackgroundColor |
+| TabbedPageHandler.cs | [ ] | NEEDS VERIFICATION |
+| TimePickerHandler.cs | [ ] | NEEDS VERIFICATION |
+| WebViewHandler.cs | [ ] | NEEDS VERIFICATION |
+| WindowHandler.cs | [ ] | NEEDS VERIFICATION |
---
@@ -56,13 +58,13 @@
| File | Status | Notes |
|------|--------|-------|
-| SkiaActivityIndicator.cs | [ ] | |
+| SkiaActivityIndicator.cs | [x] | Verified - all TwoWay, logic matches |
| SkiaAlertDialog.cs | [ ] | |
| SkiaBorder.cs | [ ] | Contains SkiaFrame |
-| SkiaBoxView.cs | [ ] | |
-| SkiaButton.cs | [ ] | |
+| SkiaBoxView.cs | [x] | Verified - all TwoWay, logic matches |
+| SkiaButton.cs | [x] | Verified - all TwoWay, logic matches |
| SkiaCarouselView.cs | [ ] | |
-| SkiaCheckBox.cs | [ ] | |
+| SkiaCheckBox.cs | [x] | Verified - IsChecked=OneWay, rest TwoWay, logic matches |
| SkiaCollectionView.cs | [ ] | |
| SkiaContentPresenter.cs | [ ] | |
| SkiaContextMenu.cs | [ ] | |
@@ -81,17 +83,17 @@
| SkiaMenuBar.cs | [ ] | Contains MenuItem, MenuBarItem |
| SkiaNavigationPage.cs | [ ] | |
| SkiaPage.cs | [x] | Added SkiaToolbarItem.Icon property |
-| SkiaPicker.cs | [ ] | |
-| SkiaProgressBar.cs | [ ] | |
+| SkiaPicker.cs | [x] | FIXED - SelectedIndex=OneWay, all others=TwoWay (was missing) |
+| SkiaProgressBar.cs | [x] | Verified - Progress=OneWay, rest TwoWay, logic matches |
| SkiaRadioButton.cs | [ ] | |
| SkiaRefreshView.cs | [ ] | |
| SkiaScrollView.cs | [ ] | |
| SkiaSearchBar.cs | [ ] | |
| SkiaShell.cs | [ ] | Contains ShellSection, ShellContent |
-| SkiaSlider.cs | [ ] | |
+| SkiaSlider.cs | [x] | FIXED - Value=OneWay, rest TwoWay (agent had inverted all) |
| SkiaStepper.cs | [ ] | |
| SkiaSwipeView.cs | [ ] | |
-| SkiaSwitch.cs | [ ] | |
+| SkiaSwitch.cs | [x] | FIXED - IsOn=OneWay (agent had TwoWay) |
| SkiaTabbedPage.cs | [ ] | |
| SkiaTemplatedView.cs | [ ] | |
| SkiaTimePicker.cs | [ ] | |
@@ -204,3 +206,11 @@
|------|--------|-------|
| LinuxApplication.cs | [ ] | |
| LinuxApplicationOptions.cs | [ ] | |
+
+---
+
+## TYPES
+
+| File | Status | Notes |
+|------|--------|-------|
+| ToggledEventArgs.cs | [x] | ADDED - was missing, required by SkiaSwitch |
diff --git a/Types/ToggledEventArgs.cs b/Types/ToggledEventArgs.cs
new file mode 100644
index 0000000..54ac8b1
--- /dev/null
+++ b/Types/ToggledEventArgs.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Maui.Platform;
+
+public class ToggledEventArgs : EventArgs
+{
+ public bool Value { get; }
+
+ public ToggledEventArgs(bool value)
+ {
+ Value = value;
+ }
+}
diff --git a/Views/CheckedChangedEventArgs.cs b/Views/CheckedChangedEventArgs.cs
new file mode 100644
index 0000000..001dce4
--- /dev/null
+++ b/Views/CheckedChangedEventArgs.cs
@@ -0,0 +1,16 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+
+namespace Microsoft.Maui.Platform;
+
+public class CheckedChangedEventArgs : EventArgs
+{
+ public bool IsChecked { get; }
+
+ public CheckedChangedEventArgs(bool isChecked)
+ {
+ IsChecked = isChecked;
+ }
+}
diff --git a/Views/SkiaActivityIndicator.cs b/Views/SkiaActivityIndicator.cs
index bdf374c..cbe554d 100644
--- a/Views/SkiaActivityIndicator.cs
+++ b/Views/SkiaActivityIndicator.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Maui.Controls;
using SkiaSharp;
namespace Microsoft.Maui.Platform;
@@ -21,6 +22,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(bool),
typeof(SkiaActivityIndicator),
false,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).OnIsRunningChanged());
///
@@ -32,6 +34,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(SKColor),
typeof(SkiaActivityIndicator),
new SKColor(0x21, 0x96, 0xF3),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
///
@@ -43,6 +46,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(SKColor),
typeof(SkiaActivityIndicator),
new SKColor(0xBD, 0xBD, 0xBD),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
///
@@ -54,6 +58,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(float),
typeof(SkiaActivityIndicator),
32f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
///
@@ -65,6 +70,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(float),
typeof(SkiaActivityIndicator),
3f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).InvalidateMeasure());
///
@@ -75,7 +81,8 @@ public class SkiaActivityIndicator : SkiaView
nameof(RotationSpeed),
typeof(float),
typeof(SkiaActivityIndicator),
- 360f);
+ 360f,
+ BindingMode.TwoWay);
///
/// Bindable property for ArcCount.
@@ -86,6 +93,7 @@ public class SkiaActivityIndicator : SkiaView
typeof(int),
typeof(SkiaActivityIndicator),
12,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaActivityIndicator)b).Invalidate());
#endregion
diff --git a/Views/SkiaBorder.cs b/Views/SkiaBorder.cs
index 79b5268..11a157f 100644
--- a/Views/SkiaBorder.cs
+++ b/Views/SkiaBorder.cs
@@ -1,6 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using Microsoft.Maui.Controls;
+using Microsoft.Maui.Platform.Linux.Handlers;
using SkiaSharp;
namespace Microsoft.Maui.Platform;
@@ -14,54 +17,56 @@ public class SkiaBorder : SkiaLayoutView
public static readonly BindableProperty StrokeThicknessProperty =
BindableProperty.Create(nameof(StrokeThickness), typeof(float), typeof(SkiaBorder), 1f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(SkiaBorder), 0f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty StrokeProperty =
BindableProperty.Create(nameof(Stroke), typeof(SKColor), typeof(SkiaBorder), SKColors.Black,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty PaddingLeftProperty =
BindableProperty.Create(nameof(PaddingLeft), typeof(float), typeof(SkiaBorder), 0f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
public static readonly BindableProperty PaddingTopProperty =
BindableProperty.Create(nameof(PaddingTop), typeof(float), typeof(SkiaBorder), 0f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
public static readonly BindableProperty PaddingRightProperty =
BindableProperty.Create(nameof(PaddingRight), typeof(float), typeof(SkiaBorder), 0f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
public static readonly BindableProperty PaddingBottomProperty =
BindableProperty.Create(nameof(PaddingBottom), typeof(float), typeof(SkiaBorder), 0f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).InvalidateMeasure());
public static readonly BindableProperty HasShadowProperty =
BindableProperty.Create(nameof(HasShadow), typeof(bool), typeof(SkiaBorder), false,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty ShadowColorProperty =
BindableProperty.Create(nameof(ShadowColor), typeof(SKColor), typeof(SkiaBorder), new SKColor(0, 0, 0, 40),
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty ShadowBlurRadiusProperty =
BindableProperty.Create(nameof(ShadowBlurRadius), typeof(float), typeof(SkiaBorder), 4f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty ShadowOffsetXProperty =
BindableProperty.Create(nameof(ShadowOffsetX), typeof(float), typeof(SkiaBorder), 2f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
public static readonly BindableProperty ShadowOffsetYProperty =
BindableProperty.Create(nameof(ShadowOffsetY), typeof(float), typeof(SkiaBorder), 2f,
- propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBorder)b).Invalidate());
#endregion
+ private bool _isPressed;
+
#region Properties
public float StrokeThickness
@@ -138,6 +143,14 @@ public class SkiaBorder : SkiaLayoutView
#endregion
+ #region Events
+
+ public event EventHandler? Tapped;
+
+ #endregion
+
+ #region SetPadding Methods
+
///
/// Sets uniform padding on all sides.
///
@@ -166,16 +179,18 @@ public class SkiaBorder : SkiaLayoutView
PaddingBottom = bottom;
}
+ #endregion
+
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
var strokeThickness = StrokeThickness;
var cornerRadius = CornerRadius;
var borderRect = new SKRect(
- bounds.Left + strokeThickness / 2,
- bounds.Top + strokeThickness / 2,
- bounds.Right - strokeThickness / 2,
- bounds.Bottom - strokeThickness / 2);
+ bounds.Left + strokeThickness / 2f,
+ bounds.Top + strokeThickness / 2f,
+ bounds.Right - strokeThickness / 2f,
+ bounds.Bottom - strokeThickness / 2f);
// Draw shadow if enabled
if (HasShadow)
@@ -204,7 +219,7 @@ public class SkiaBorder : SkiaLayoutView
canvas.DrawRoundRect(new SKRoundRect(borderRect, cornerRadius), bgPaint);
// Draw border
- if (strokeThickness > 0)
+ if (strokeThickness > 0f)
{
using var borderPaint = new SKPaint
{
@@ -244,16 +259,16 @@ public class SkiaBorder : SkiaLayoutView
protected override SKSize MeasureOverride(SKSize availableSize)
{
var strokeThickness = StrokeThickness;
- var paddingWidth = PaddingLeft + PaddingRight + strokeThickness * 2;
- var paddingHeight = PaddingTop + PaddingBottom + strokeThickness * 2;
+ var paddingWidth = PaddingLeft + PaddingRight + strokeThickness * 2f;
+ var paddingHeight = PaddingTop + PaddingBottom + strokeThickness * 2f;
// Respect explicit size requests
- var requestedWidth = WidthRequest >= 0 ? (float)WidthRequest : availableSize.Width;
- var requestedHeight = HeightRequest >= 0 ? (float)HeightRequest : availableSize.Height;
+ var requestedWidth = WidthRequest >= 0.0 ? (float)WidthRequest : availableSize.Width;
+ var requestedHeight = HeightRequest >= 0.0 ? (float)HeightRequest : availableSize.Height;
var childAvailable = new SKSize(
- Math.Max(0, requestedWidth - paddingWidth),
- Math.Max(0, requestedHeight - paddingHeight));
+ Math.Max(0f, requestedWidth - paddingWidth),
+ Math.Max(0f, requestedHeight - paddingHeight));
var maxChildSize = SKSize.Empty;
@@ -266,8 +281,8 @@ public class SkiaBorder : SkiaLayoutView
}
// Use requested size if set, otherwise use child size + padding
- var width = WidthRequest >= 0 ? (float)WidthRequest : maxChildSize.Width + paddingWidth;
- var height = HeightRequest >= 0 ? (float)HeightRequest : maxChildSize.Height + paddingHeight;
+ var width = WidthRequest >= 0.0 ? (float)WidthRequest : maxChildSize.Width + paddingWidth;
+ var height = HeightRequest >= 0.0 ? (float)HeightRequest : maxChildSize.Height + paddingHeight;
return new SKSize(width, height);
}
@@ -290,6 +305,85 @@ public class SkiaBorder : SkiaLayoutView
return bounds;
}
+
+ private bool HasTapGestureRecognizers()
+ {
+ if (MauiView?.GestureRecognizers == null)
+ {
+ return false;
+ }
+
+ foreach (var gestureRecognizer in MauiView.GestureRecognizers)
+ {
+ if (gestureRecognizer is TapGestureRecognizer)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public override SkiaView? HitTest(float x, float y)
+ {
+ if (IsVisible && IsEnabled)
+ {
+ var bounds = Bounds;
+ if (bounds.Contains(new SKPoint(x, y)))
+ {
+ if (HasTapGestureRecognizers())
+ {
+ Console.WriteLine("[SkiaBorder.HitTest] Intercepting for gesture - returning self");
+ return this;
+ }
+ return base.HitTest(x, y);
+ }
+ }
+ return null;
+ }
+
+ public override void OnPointerPressed(PointerEventArgs e)
+ {
+ if (HasTapGestureRecognizers())
+ {
+ _isPressed = true;
+ e.Handled = true;
+ Console.WriteLine("[SkiaBorder] OnPointerPressed INTERCEPTED for gesture, MauiView=" + MauiView?.GetType().Name);
+ if (MauiView != null)
+ {
+ GestureManager.ProcessPointerDown(MauiView, e.X, e.Y);
+ }
+ }
+ else
+ {
+ base.OnPointerPressed(e);
+ }
+ }
+
+ public override void OnPointerReleased(PointerEventArgs e)
+ {
+ if (_isPressed)
+ {
+ _isPressed = false;
+ e.Handled = true;
+ Console.WriteLine("[SkiaBorder] OnPointerReleased - processing gesture recognizers, MauiView=" + MauiView?.GetType().Name);
+ if (MauiView != null)
+ {
+ GestureManager.ProcessPointerUp(MauiView, e.X, e.Y);
+ }
+ Tapped?.Invoke(this, EventArgs.Empty);
+ }
+ else
+ {
+ base.OnPointerReleased(e);
+ }
+ }
+
+ public override void OnPointerExited(PointerEventArgs e)
+ {
+ base.OnPointerExited(e);
+ _isPressed = false;
+ }
}
///
diff --git a/Views/SkiaBoxView.cs b/Views/SkiaBoxView.cs
index 3a8f100..e33fb0f 100644
--- a/Views/SkiaBoxView.cs
+++ b/Views/SkiaBoxView.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using Microsoft.Maui.Controls;
using SkiaSharp;
namespace Microsoft.Maui.Platform;
@@ -12,11 +13,11 @@ public class SkiaBoxView : SkiaView
{
public static readonly BindableProperty ColorProperty =
BindableProperty.Create(nameof(Color), typeof(SKColor), typeof(SkiaBoxView), SKColors.Transparent,
- propertyChanged: (b, o, n) => ((SkiaBoxView)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBoxView)b).Invalidate());
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(nameof(CornerRadius), typeof(float), typeof(SkiaBoxView), 0f,
- propertyChanged: (b, o, n) => ((SkiaBoxView)b).Invalidate());
+ BindingMode.TwoWay, propertyChanged: (b, o, n) => ((SkiaBoxView)b).Invalidate());
public SKColor Color
{
diff --git a/Views/SkiaButton.cs b/Views/SkiaButton.cs
index 45cb8f9..8ee6148 100644
--- a/Views/SkiaButton.cs
+++ b/Views/SkiaButton.cs
@@ -1,8 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using SkiaSharp;
+using System;
+using System.Windows.Input;
+using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform.Linux.Rendering;
+using SkiaSharp;
namespace Microsoft.Maui.Platform;
@@ -13,443 +16,322 @@ public class SkiaButton : SkiaView
{
#region BindableProperties
- ///
- /// Bindable property for Text.
- ///
- public static readonly BindableProperty TextProperty =
- BindableProperty.Create(
- nameof(Text),
- typeof(string),
- typeof(SkiaButton),
- "",
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnTextChanged());
+ public static readonly BindableProperty TextProperty = BindableProperty.Create(
+ nameof(Text),
+ typeof(string),
+ typeof(SkiaButton),
+ "",
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnTextChanged());
- ///
- /// Bindable property for TextColor.
- ///
- public static readonly BindableProperty TextColorProperty =
- BindableProperty.Create(
- nameof(TextColor),
- typeof(SKColor),
- typeof(SkiaButton),
- SKColors.White,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
+ nameof(TextColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ SKColors.White,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for ButtonBackgroundColor (distinct from base BackgroundColor).
- ///
- public static readonly BindableProperty ButtonBackgroundColorProperty =
- BindableProperty.Create(
- nameof(ButtonBackgroundColor),
- typeof(SKColor),
- typeof(SkiaButton),
- new SKColor(0x21, 0x96, 0xF3), // Material Blue
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty ButtonBackgroundColorProperty = BindableProperty.Create(
+ nameof(ButtonBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ new SKColor(33, 150, 243), // Material Blue
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for PressedBackgroundColor.
- ///
- public static readonly BindableProperty PressedBackgroundColorProperty =
- BindableProperty.Create(
- nameof(PressedBackgroundColor),
- typeof(SKColor),
- typeof(SkiaButton),
- new SKColor(0x19, 0x76, 0xD2),
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty PressedBackgroundColorProperty = BindableProperty.Create(
+ nameof(PressedBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ new SKColor(25, 118, 210),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for DisabledBackgroundColor.
- ///
- public static readonly BindableProperty DisabledBackgroundColorProperty =
- BindableProperty.Create(
- nameof(DisabledBackgroundColor),
- typeof(SKColor),
- typeof(SkiaButton),
- new SKColor(0xBD, 0xBD, 0xBD),
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty DisabledBackgroundColorProperty = BindableProperty.Create(
+ nameof(DisabledBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ new SKColor(189, 189, 189),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for HoveredBackgroundColor.
- ///
- public static readonly BindableProperty HoveredBackgroundColorProperty =
- BindableProperty.Create(
- nameof(HoveredBackgroundColor),
- typeof(SKColor),
- typeof(SkiaButton),
- new SKColor(0x42, 0xA5, 0xF5),
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty HoveredBackgroundColorProperty = BindableProperty.Create(
+ nameof(HoveredBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ new SKColor(66, 165, 245),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for BorderColor.
- ///
- public static readonly BindableProperty BorderColorProperty =
- BindableProperty.Create(
- nameof(BorderColor),
- typeof(SKColor),
- typeof(SkiaButton),
- SKColors.Transparent,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty BorderColorProperty = BindableProperty.Create(
+ nameof(BorderColor),
+ typeof(SKColor),
+ typeof(SkiaButton),
+ SKColors.Transparent,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for FontFamily.
- ///
- public static readonly BindableProperty FontFamilyProperty =
- BindableProperty.Create(
- nameof(FontFamily),
- typeof(string),
- typeof(SkiaButton),
- "Sans",
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
+ public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(
+ nameof(FontFamily),
+ typeof(string),
+ typeof(SkiaButton),
+ "Sans",
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
- ///
- /// Bindable property for FontSize.
- ///
- public static readonly BindableProperty FontSizeProperty =
- BindableProperty.Create(
- nameof(FontSize),
- typeof(float),
- typeof(SkiaButton),
- 14f,
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
+ public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(
+ nameof(FontSize),
+ typeof(float),
+ typeof(SkiaButton),
+ 14f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
- ///
- /// Bindable property for IsBold.
- ///
- public static readonly BindableProperty IsBoldProperty =
- BindableProperty.Create(
- nameof(IsBold),
- typeof(bool),
- typeof(SkiaButton),
- false,
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
+ public static readonly BindableProperty IsBoldProperty = BindableProperty.Create(
+ nameof(IsBold),
+ typeof(bool),
+ typeof(SkiaButton),
+ false,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
- ///
- /// Bindable property for IsItalic.
- ///
- public static readonly BindableProperty IsItalicProperty =
- BindableProperty.Create(
- nameof(IsItalic),
- typeof(bool),
- typeof(SkiaButton),
- false,
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
+ public static readonly BindableProperty IsItalicProperty = BindableProperty.Create(
+ nameof(IsItalic),
+ typeof(bool),
+ typeof(SkiaButton),
+ false,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnFontChanged());
- ///
- /// Bindable property for CharacterSpacing.
- ///
- public static readonly BindableProperty CharacterSpacingProperty =
- BindableProperty.Create(
- nameof(CharacterSpacing),
- typeof(float),
- typeof(SkiaButton),
- 0f,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty CharacterSpacingProperty = BindableProperty.Create(
+ nameof(CharacterSpacing),
+ typeof(float),
+ typeof(SkiaButton),
+ 0f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for CornerRadius.
- ///
- public static readonly BindableProperty CornerRadiusProperty =
- BindableProperty.Create(
- nameof(CornerRadius),
- typeof(float),
- typeof(SkiaButton),
- 4f,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty CornerRadiusProperty = BindableProperty.Create(
+ nameof(CornerRadius),
+ typeof(float),
+ typeof(SkiaButton),
+ 4f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for BorderWidth.
- ///
- public static readonly BindableProperty BorderWidthProperty =
- BindableProperty.Create(
- nameof(BorderWidth),
- typeof(float),
- typeof(SkiaButton),
- 0f,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty BorderWidthProperty = BindableProperty.Create(
+ nameof(BorderWidth),
+ typeof(float),
+ typeof(SkiaButton),
+ 0f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for Padding.
- ///
- public static readonly BindableProperty PaddingProperty =
- BindableProperty.Create(
- nameof(Padding),
- typeof(SKRect),
- typeof(SkiaButton),
- new SKRect(16, 8, 16, 8),
- propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
+ public static readonly BindableProperty PaddingProperty = BindableProperty.Create(
+ nameof(Padding),
+ typeof(SKRect),
+ typeof(SkiaButton),
+ new SKRect(16f, 8f, 16f, 8f),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
- ///
- /// Bindable property for Command.
- ///
- public static readonly BindableProperty CommandProperty =
- BindableProperty.Create(
- nameof(Command),
- typeof(System.Windows.Input.ICommand),
- typeof(SkiaButton),
- null,
- propertyChanged: (b, o, n) => ((SkiaButton)b).OnCommandChanged((System.Windows.Input.ICommand?)o, (System.Windows.Input.ICommand?)n));
+ public static readonly BindableProperty CommandProperty = BindableProperty.Create(
+ nameof(Command),
+ typeof(ICommand),
+ typeof(SkiaButton),
+ null,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).OnCommandChanged((ICommand?)o, (ICommand?)n));
- ///
- /// Bindable property for CommandParameter.
- ///
- public static readonly BindableProperty CommandParameterProperty =
- BindableProperty.Create(
- nameof(CommandParameter),
- typeof(object),
- typeof(SkiaButton),
- null);
+ public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
+ nameof(CommandParameter),
+ typeof(object),
+ typeof(SkiaButton),
+ null,
+ BindingMode.TwoWay);
- ///
- /// Bindable property for ImageSource.
- ///
- public static readonly BindableProperty ImageSourceProperty =
- BindableProperty.Create(
- nameof(ImageSource),
- typeof(SKBitmap),
- typeof(SkiaButton),
- null,
- propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
+ public static readonly BindableProperty ImageSourceProperty = BindableProperty.Create(
+ nameof(ImageSource),
+ typeof(SKBitmap),
+ typeof(SkiaButton),
+ null,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).Invalidate());
- ///
- /// Bindable property for ImageSpacing.
- ///
- public static readonly BindableProperty ImageSpacingProperty =
- BindableProperty.Create(
- nameof(ImageSpacing),
- typeof(float),
- typeof(SkiaButton),
- 8f,
- propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
+ public static readonly BindableProperty ImageSpacingProperty = BindableProperty.Create(
+ nameof(ImageSpacing),
+ typeof(float),
+ typeof(SkiaButton),
+ 8f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
- ///
- /// Bindable property for ContentLayoutPosition (0=Left, 1=Top, 2=Right, 3=Bottom).
- ///
- public static readonly BindableProperty ContentLayoutPositionProperty =
- BindableProperty.Create(
- nameof(ContentLayoutPosition),
- typeof(int),
- typeof(SkiaButton),
- 0,
- propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
+ public static readonly BindableProperty ContentLayoutPositionProperty = BindableProperty.Create(
+ nameof(ContentLayoutPosition),
+ typeof(int),
+ typeof(SkiaButton),
+ 0,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaButton)b).InvalidateMeasure());
+
+ #endregion
+
+ #region Fields
+
+ private bool _focusFromKeyboard;
#endregion
#region Properties
- ///
- /// Gets or sets the button text.
- ///
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
- ///
- /// Gets or sets the text color.
- ///
public SKColor TextColor
{
get => (SKColor)GetValue(TextColorProperty);
set => SetValue(TextColorProperty, value);
}
- ///
- /// Gets or sets the button background color.
- ///
public SKColor ButtonBackgroundColor
{
get => (SKColor)GetValue(ButtonBackgroundColorProperty);
set => SetValue(ButtonBackgroundColorProperty, value);
}
- ///
- /// Gets or sets the pressed background color.
- ///
public SKColor PressedBackgroundColor
{
get => (SKColor)GetValue(PressedBackgroundColorProperty);
set => SetValue(PressedBackgroundColorProperty, value);
}
- ///
- /// Gets or sets the disabled background color.
- ///
public SKColor DisabledBackgroundColor
{
get => (SKColor)GetValue(DisabledBackgroundColorProperty);
set => SetValue(DisabledBackgroundColorProperty, value);
}
- ///
- /// Gets or sets the hovered background color.
- ///
public SKColor HoveredBackgroundColor
{
get => (SKColor)GetValue(HoveredBackgroundColorProperty);
set => SetValue(HoveredBackgroundColorProperty, value);
}
- ///
- /// Gets or sets the border color.
- ///
public SKColor BorderColor
{
get => (SKColor)GetValue(BorderColorProperty);
set => SetValue(BorderColorProperty, value);
}
- ///
- /// Gets or sets the font family.
- ///
public string FontFamily
{
get => (string)GetValue(FontFamilyProperty);
set => SetValue(FontFamilyProperty, value);
}
- ///
- /// Gets or sets the font size.
- ///
public float FontSize
{
get => (float)GetValue(FontSizeProperty);
set => SetValue(FontSizeProperty, value);
}
- ///
- /// Gets or sets whether the text is bold.
- ///
public bool IsBold
{
get => (bool)GetValue(IsBoldProperty);
set => SetValue(IsBoldProperty, value);
}
- ///
- /// Gets or sets whether the text is italic.
- ///
public bool IsItalic
{
get => (bool)GetValue(IsItalicProperty);
set => SetValue(IsItalicProperty, value);
}
- ///
- /// Gets or sets the character spacing.
- ///
public float CharacterSpacing
{
get => (float)GetValue(CharacterSpacingProperty);
set => SetValue(CharacterSpacingProperty, value);
}
- ///
- /// Gets or sets the corner radius.
- ///
public float CornerRadius
{
get => (float)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
- ///
- /// Gets or sets the border width.
- ///
public float BorderWidth
{
get => (float)GetValue(BorderWidthProperty);
set => SetValue(BorderWidthProperty, value);
}
- ///
- /// Gets or sets the padding.
- ///
public SKRect Padding
{
get => (SKRect)GetValue(PaddingProperty);
set => SetValue(PaddingProperty, value);
}
- ///
- /// Gets or sets the command to execute when clicked.
- ///
- public System.Windows.Input.ICommand? Command
+ public ICommand? Command
{
- get => (System.Windows.Input.ICommand?)GetValue(CommandProperty);
+ get => (ICommand?)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
- ///
- /// Gets or sets the command parameter.
- ///
public object? CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
- ///
- /// Gets or sets the image source for the button.
- ///
public SKBitmap? ImageSource
{
get => (SKBitmap?)GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
- ///
- /// Gets or sets the spacing between the image and text.
- ///
public float ImageSpacing
{
get => (float)GetValue(ImageSpacingProperty);
set => SetValue(ImageSpacingProperty, value);
}
- ///
- /// Gets or sets the content layout position (0=Left, 1=Top, 2=Right, 3=Bottom).
- ///
public int ContentLayoutPosition
{
get => (int)GetValue(ContentLayoutPositionProperty);
set => SetValue(ContentLayoutPositionProperty, value);
}
- ///
- /// Gets whether the button is currently pressed.
- ///
public bool IsPressed { get; private set; }
- ///
- /// Gets whether the pointer is currently over the button.
- ///
public bool IsHovered { get; private set; }
#endregion
- private bool _focusFromKeyboard;
+ #region Events
- ///
- /// Event raised when the button is clicked.
- ///
public event EventHandler? Clicked;
- ///
- /// Event raised when the button is pressed.
- ///
public event EventHandler? Pressed;
- ///
- /// Event raised when the button is released.
- ///
public event EventHandler? Released;
+ #endregion
+
+ #region Constructor
+
public SkiaButton()
{
IsFocusable = true;
}
+ #endregion
+
+ #region Private Methods
+
private void OnTextChanged()
{
InvalidateMeasure();
@@ -462,13 +344,12 @@ public class SkiaButton : SkiaView
Invalidate();
}
- private void OnCommandChanged(System.Windows.Input.ICommand? oldCommand, System.Windows.Input.ICommand? newCommand)
+ private void OnCommandChanged(ICommand? oldCommand, ICommand? newCommand)
{
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= OnCanExecuteChanged;
}
-
if (newCommand != null)
{
newCommand.CanExecuteChanged += OnCanExecuteChanged;
@@ -489,55 +370,52 @@ public class SkiaButton : SkiaView
}
}
+ #endregion
+
+ #region Drawing
+
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
{
- // Check if this is a "text only" button (transparent background)
- var isTextOnly = ButtonBackgroundColor.Alpha == 0;
+ SKColor buttonBackgroundColor = ButtonBackgroundColor;
+ bool isTextOnly = buttonBackgroundColor.Alpha == 0;
- // Determine background color based on state
- SKColor bgColor;
+ SKColor color;
if (!IsEnabled)
{
- bgColor = isTextOnly ? SKColors.Transparent : DisabledBackgroundColor;
+ color = isTextOnly ? SKColors.Transparent : DisabledBackgroundColor;
}
else if (IsPressed)
{
- // For text-only buttons, use a subtle press effect
- bgColor = isTextOnly ? new SKColor(0, 0, 0, 20) : PressedBackgroundColor;
+ color = isTextOnly ? new SKColor(0, 0, 0, 20) : PressedBackgroundColor;
}
else if (IsHovered)
{
- // For text-only buttons, use a subtle hover effect instead of full background
- bgColor = isTextOnly ? new SKColor(0, 0, 0, 10) : HoveredBackgroundColor;
+ color = isTextOnly ? new SKColor(0, 0, 0, 10) : HoveredBackgroundColor;
}
else
{
- bgColor = ButtonBackgroundColor;
+ color = ButtonBackgroundColor;
}
- // Draw shadow (for elevation effect) - skip for text-only buttons
if (IsEnabled && !IsPressed && !isTextOnly)
{
DrawShadow(canvas, bounds);
}
- // Create rounded rect for background and border
- var rect = new SKRoundRect(bounds, CornerRadius);
+ var roundRect = new SKRoundRect(bounds, CornerRadius);
- // Draw background with rounded corners (skip if fully transparent)
- if (bgColor.Alpha > 0)
+ if (color.Alpha > 0)
{
using var bgPaint = new SKPaint
{
- Color = bgColor,
+ Color = color,
IsAntialias = true,
Style = SKPaintStyle.Fill
};
- canvas.DrawRoundRect(rect, bgPaint);
+ canvas.DrawRoundRect(roundRect, bgPaint);
}
- // Draw border
- if (BorderWidth > 0 && BorderColor != SKColors.Transparent)
+ if (BorderWidth > 0f && BorderColor != SKColors.Transparent)
{
using var borderPaint = new SKPaint
{
@@ -546,40 +424,37 @@ public class SkiaButton : SkiaView
Style = SKPaintStyle.Stroke,
StrokeWidth = BorderWidth
};
- canvas.DrawRoundRect(rect, borderPaint);
+ canvas.DrawRoundRect(roundRect, borderPaint);
}
- // Draw focus ring only for keyboard focus
if (IsFocused && _focusFromKeyboard)
{
using var focusPaint = new SKPaint
{
- Color = new SKColor(0x21, 0x96, 0xF3, 0x80),
+ Color = new SKColor(33, 150, 243, 128),
IsAntialias = true,
Style = SKPaintStyle.Stroke,
- StrokeWidth = 2
+ StrokeWidth = 2f
};
- var focusRect = new SKRoundRect(bounds, CornerRadius + 2);
- focusRect.Inflate(2, 2);
+ var focusRect = new SKRoundRect(bounds, CornerRadius + 2f);
+ focusRect.Inflate(2f, 2f);
canvas.DrawRoundRect(focusRect, focusPaint);
}
- // Draw content (text and/or image)
DrawContent(canvas, bounds, isTextOnly);
}
private void DrawContent(SKCanvas canvas, SKRect bounds, bool isTextOnly)
{
- var fontStyle = new SKFontStyle(
+ var style = new SKFontStyle(
IsBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal,
SKFontStyleWidth.Normal,
IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
- var typeface = SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(FontFamily, fontStyle)
- ?? SKTypeface.Default;
- using var font = new SKFont(typeface, FontSize);
+ using var font = new SKFont(
+ SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(FontFamily, style) ?? SKTypeface.Default,
+ FontSize, 1f, 0f);
- // Determine text color
SKColor textColorToUse;
if (!IsEnabled)
{
@@ -604,7 +479,6 @@ public class SkiaButton : SkiaView
IsAntialias = true
};
- // Measure text
var textBounds = new SKRect();
bool hasText = !string.IsNullOrEmpty(Text);
if (hasText)
@@ -612,21 +486,21 @@ public class SkiaButton : SkiaView
textPaint.MeasureText(Text, ref textBounds);
}
- // Calculate image size
bool hasImage = ImageSource != null;
- float imageWidth = 0;
- float imageHeight = 0;
+ float imageWidth = 0f;
+ float imageHeight = 0f;
if (hasImage)
{
- float maxImageSize = Math.Min(bounds.Height - 8, 24f);
- float scale = Math.Min(maxImageSize / ImageSource!.Width, maxImageSize / ImageSource.Height);
+ float maxSize = Math.Min(bounds.Height - 8f, 24f);
+ float scale = Math.Min(maxSize / ImageSource!.Width, maxSize / ImageSource.Height);
imageWidth = ImageSource.Width * scale;
imageHeight = ImageSource.Height * scale;
}
- // Calculate total content size and position
bool isHorizontal = ContentLayoutPosition == 0 || ContentLayoutPosition == 2;
- float totalWidth, totalHeight;
+ float totalWidth;
+ float totalHeight;
+
if (hasImage && hasText)
{
if (isHorizontal)
@@ -651,63 +525,66 @@ public class SkiaButton : SkiaView
totalHeight = textBounds.Height;
}
- float startX = bounds.MidX - totalWidth / 2;
- float startY = bounds.MidY - totalHeight / 2;
+ float startX = bounds.MidX - totalWidth / 2f;
+ float startY = bounds.MidY - totalHeight / 2f;
- // Draw image and text based on layout position
if (hasImage)
{
- float imageX, imageY;
- float textX = 0, textY = 0;
+ float imageX;
+ float imageY;
+ float textX = 0f;
+ float textY = 0f;
switch (ContentLayoutPosition)
{
- case 1: // Top - image above text
- imageX = bounds.MidX - imageWidth / 2;
+ case 1: // Top
+ imageX = bounds.MidX - imageWidth / 2f;
imageY = startY;
- textX = bounds.MidX - textBounds.Width / 2;
+ textX = bounds.MidX - textBounds.Width / 2f;
textY = startY + imageHeight + ImageSpacing - textBounds.Top;
break;
- case 2: // Right - image to right of text
+ case 2: // Right
textX = startX;
textY = bounds.MidY - textBounds.MidY;
imageX = startX + textBounds.Width + ImageSpacing;
- imageY = bounds.MidY - imageHeight / 2;
+ imageY = bounds.MidY - imageHeight / 2f;
break;
- case 3: // Bottom - image below text
- textX = bounds.MidX - textBounds.Width / 2;
+ case 3: // Bottom
+ textX = bounds.MidX - textBounds.Width / 2f;
textY = startY - textBounds.Top;
- imageX = bounds.MidX - imageWidth / 2;
+ imageX = bounds.MidX - imageWidth / 2f;
imageY = startY + textBounds.Height + ImageSpacing;
break;
- default: // 0 = Left - image to left of text
+ default: // 0 = Left
imageX = startX;
- imageY = bounds.MidY - imageHeight / 2;
+ imageY = bounds.MidY - imageHeight / 2f;
textX = startX + imageWidth + ImageSpacing;
textY = bounds.MidY - textBounds.MidY;
break;
}
- // Draw image
var imageRect = new SKRect(imageX, imageY, imageX + imageWidth, imageY + imageHeight);
using var imagePaint = new SKPaint { IsAntialias = true };
+
if (!IsEnabled)
{
- imagePaint.ColorFilter = SKColorFilter.CreateBlendMode(new SKColor(128, 128, 128, 128), SKBlendMode.Modulate);
+ imagePaint.ColorFilter = SKColorFilter.CreateBlendMode(
+ new SKColor(128, 128, 128, 128), SKBlendMode.Modulate);
}
+
canvas.DrawBitmap(ImageSource!, imageRect, imagePaint);
- // Draw text
if (hasText)
{
canvas.DrawText(Text!, textX, textY, textPaint);
}
+ return;
}
- else if (hasText)
+
+ if (hasText)
{
- // Just text, centered
- var x = bounds.MidX - textBounds.MidX;
- var y = bounds.MidY - textBounds.MidY;
+ float x = bounds.MidX - textBounds.MidX;
+ float y = bounds.MidY - textBounds.MidY;
canvas.DrawText(Text!, x, y, textPaint);
}
}
@@ -718,25 +595,27 @@ public class SkiaButton : SkiaView
{
Color = new SKColor(0, 0, 0, 50),
IsAntialias = true,
- MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 4)
+ MaskFilter = SKMaskFilter.CreateBlur(SKBlurStyle.Normal, 4f)
};
- var shadowRect = new SKRect(
- bounds.Left + 2,
- bounds.Top + 4,
- bounds.Right + 2,
- bounds.Bottom + 4);
-
- var roundRect = new SKRoundRect(shadowRect, CornerRadius);
- canvas.DrawRoundRect(roundRect, shadowPaint);
+ var shadowRect = new SKRoundRect(
+ new SKRect(bounds.Left + 2f, bounds.Top + 4f, bounds.Right + 2f, bounds.Bottom + 4f),
+ CornerRadius);
+ canvas.DrawRoundRect(shadowRect, shadowPaint);
}
+ #endregion
+
+ #region Pointer Events
+
public override void OnPointerEntered(PointerEventArgs e)
{
- if (!IsEnabled) return;
- IsHovered = true;
- SkiaVisualStateManager.GoToState(this, SkiaVisualStateManager.CommonStates.PointerOver);
- Invalidate();
+ if (IsEnabled)
+ {
+ IsHovered = true;
+ SkiaVisualStateManager.GoToState(this, "PointerOver");
+ Invalidate();
+ }
}
public override void OnPointerExited(PointerEventArgs e)
@@ -746,53 +625,51 @@ public class SkiaButton : SkiaView
{
IsPressed = false;
}
- SkiaVisualStateManager.GoToState(this, IsEnabled ? SkiaVisualStateManager.CommonStates.Normal : SkiaVisualStateManager.CommonStates.Disabled);
+ SkiaVisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled");
Invalidate();
}
public override void OnPointerPressed(PointerEventArgs e)
{
Console.WriteLine($"[SkiaButton] OnPointerPressed - Text='{Text}', IsEnabled={IsEnabled}");
- if (!IsEnabled) return;
-
- IsPressed = true;
- _focusFromKeyboard = false;
- SkiaVisualStateManager.GoToState(this, SkiaVisualStateManager.CommonStates.Pressed);
- Invalidate();
- Pressed?.Invoke(this, EventArgs.Empty);
+ if (IsEnabled)
+ {
+ IsPressed = true;
+ _focusFromKeyboard = false;
+ SkiaVisualStateManager.GoToState(this, "Pressed");
+ Invalidate();
+ Pressed?.Invoke(this, EventArgs.Empty);
+ }
}
public override void OnPointerReleased(PointerEventArgs e)
{
- if (!IsEnabled) return;
-
- var wasPressed = IsPressed;
- IsPressed = false;
- SkiaVisualStateManager.GoToState(this, IsHovered ? SkiaVisualStateManager.CommonStates.PointerOver : SkiaVisualStateManager.CommonStates.Normal);
- Invalidate();
-
- Released?.Invoke(this, EventArgs.Empty);
-
- // Fire click if button was pressed
- // Note: Hit testing already verified the pointer is over this button,
- // so we don't need to re-check bounds (which would fail due to coordinate system differences)
- if (wasPressed)
+ if (IsEnabled)
{
- Clicked?.Invoke(this, EventArgs.Empty);
- Command?.Execute(CommandParameter);
+ bool wasPressed = IsPressed;
+ IsPressed = false;
+ SkiaVisualStateManager.GoToState(this, IsHovered ? "PointerOver" : "Normal");
+ Invalidate();
+ Released?.Invoke(this, EventArgs.Empty);
+ if (wasPressed)
+ {
+ Clicked?.Invoke(this, EventArgs.Empty);
+ Command?.Execute(CommandParameter);
+ }
}
}
+ #endregion
+
+ #region Keyboard Events
+
public override void OnKeyDown(KeyEventArgs e)
{
- if (!IsEnabled) return;
-
- // Activate on Enter or Space
- if (e.Key == Key.Enter || e.Key == Key.Space)
+ if (IsEnabled && (e.Key == Key.Enter || e.Key == Key.Space))
{
IsPressed = true;
_focusFromKeyboard = true;
- SkiaVisualStateManager.GoToState(this, SkiaVisualStateManager.CommonStates.Pressed);
+ SkiaVisualStateManager.GoToState(this, "Pressed");
Invalidate();
Pressed?.Invoke(this, EventArgs.Empty);
e.Handled = true;
@@ -801,14 +678,12 @@ public class SkiaButton : SkiaView
public override void OnKeyUp(KeyEventArgs e)
{
- if (!IsEnabled) return;
-
- if (e.Key == Key.Enter || e.Key == Key.Space)
+ if (IsEnabled && (e.Key == Key.Enter || e.Key == Key.Space))
{
if (IsPressed)
{
IsPressed = false;
- SkiaVisualStateManager.GoToState(this, SkiaVisualStateManager.CommonStates.Normal);
+ SkiaVisualStateManager.GoToState(this, "Normal");
Invalidate();
Released?.Invoke(this, EventArgs.Empty);
Clicked?.Invoke(this, EventArgs.Empty);
@@ -818,54 +693,70 @@ public class SkiaButton : SkiaView
}
}
+ #endregion
+
+ #region State Changes
+
protected override void OnEnabledChanged()
{
base.OnEnabledChanged();
- SkiaVisualStateManager.GoToState(this, IsEnabled ? SkiaVisualStateManager.CommonStates.Normal : SkiaVisualStateManager.CommonStates.Disabled);
+ SkiaVisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled");
}
+ #endregion
+
+ #region Measurement
+
protected override SKSize MeasureOverride(SKSize availableSize)
{
- // Ensure we never return NaN - use safe defaults
- var paddingLeft = float.IsNaN(Padding.Left) ? 16f : Padding.Left;
- var paddingRight = float.IsNaN(Padding.Right) ? 16f : Padding.Right;
- var paddingTop = float.IsNaN(Padding.Top) ? 8f : Padding.Top;
- var paddingBottom = float.IsNaN(Padding.Bottom) ? 8f : Padding.Bottom;
- var fontSize = float.IsNaN(FontSize) || FontSize <= 0 ? 14f : FontSize;
+ SKRect padding = Padding;
+ float paddingLeft = float.IsNaN(padding.Left) ? 16f : padding.Left;
+ float paddingRight = float.IsNaN(padding.Right) ? 16f : padding.Right;
+ float paddingTop = float.IsNaN(padding.Top) ? 8f : padding.Top;
+ float paddingBottom = float.IsNaN(padding.Bottom) ? 8f : padding.Bottom;
+ float fontSize = (float.IsNaN(FontSize) || FontSize <= 0f) ? 14f : FontSize;
if (string.IsNullOrEmpty(Text))
{
- return new SKSize(
- paddingLeft + paddingRight + 40, // Minimum width
- paddingTop + paddingBottom + fontSize);
+ return new SKSize(paddingLeft + paddingRight + 40f, paddingTop + paddingBottom + fontSize);
}
- var fontStyle = new SKFontStyle(
- IsBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal,
- SKFontStyleWidth.Normal,
- IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
- var typeface = SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(FontFamily, fontStyle)
- ?? SKTypeface.Default;
+ var style = new SKFontStyle(
+ IsBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal,
+ SKFontStyleWidth.Normal,
+ IsItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
+
+ using var font = new SKFont(
+ SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(FontFamily, style) ?? SKTypeface.Default,
+ fontSize, 1f, 0f);
- using var font = new SKFont(typeface, fontSize);
using var paint = new SKPaint(font);
-
var textBounds = new SKRect();
paint.MeasureText(Text, ref textBounds);
- var width = textBounds.Width + paddingLeft + paddingRight;
- var height = textBounds.Height + paddingTop + paddingBottom;
+ float width = textBounds.Width + paddingLeft + paddingRight;
+ float height = textBounds.Height + paddingTop + paddingBottom;
- // Ensure valid, non-NaN return values
- if (float.IsNaN(width) || width < 0) width = 72f;
- if (float.IsNaN(height) || height < 0) height = 30f;
+ if (float.IsNaN(width) || width < 0f)
+ {
+ width = 72f;
+ }
+ if (float.IsNaN(height) || height < 0f)
+ {
+ height = 30f;
+ }
- // Respect WidthRequest and HeightRequest when set
- if (WidthRequest >= 0)
+ if (WidthRequest >= 0.0)
+ {
width = (float)WidthRequest;
- if (HeightRequest >= 0)
+ }
+ if (HeightRequest >= 0.0)
+ {
height = (float)HeightRequest;
+ }
return new SKSize(width, height);
}
+
+ #endregion
}
diff --git a/Views/SkiaCheckBox.cs b/Views/SkiaCheckBox.cs
index 7fddd92..222ae11 100644
--- a/Views/SkiaCheckBox.cs
+++ b/Views/SkiaCheckBox.cs
@@ -1,8 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using Microsoft.Maui.Controls;
using SkiaSharp;
-using Microsoft.Maui.Platform.Linux.Rendering;
namespace Microsoft.Maui.Platform;
@@ -13,241 +14,179 @@ public class SkiaCheckBox : SkiaView
{
#region BindableProperties
- ///
- /// Bindable property for IsChecked.
- ///
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create(
nameof(IsChecked),
typeof(bool),
typeof(SkiaCheckBox),
false,
- BindingMode.TwoWay,
+ BindingMode.OneWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).OnIsCheckedChanged());
- ///
- /// Bindable property for CheckColor.
- ///
public static readonly BindableProperty CheckColorProperty =
BindableProperty.Create(
nameof(CheckColor),
typeof(SKColor),
typeof(SkiaCheckBox),
SKColors.White,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for BoxColor.
- ///
public static readonly BindableProperty BoxColorProperty =
BindableProperty.Create(
nameof(BoxColor),
typeof(SKColor),
typeof(SkiaCheckBox),
- new SKColor(0x21, 0x96, 0xF3),
+ new SKColor(33, 150, 243),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for UncheckedBoxColor.
- ///
public static readonly BindableProperty UncheckedBoxColorProperty =
BindableProperty.Create(
nameof(UncheckedBoxColor),
typeof(SKColor),
typeof(SkiaCheckBox),
SKColors.White,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for BorderColor.
- ///
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create(
nameof(BorderColor),
typeof(SKColor),
typeof(SkiaCheckBox),
- new SKColor(0x75, 0x75, 0x75),
+ new SKColor(117, 117, 117),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for DisabledColor.
- ///
public static readonly BindableProperty DisabledColorProperty =
BindableProperty.Create(
nameof(DisabledColor),
typeof(SKColor),
typeof(SkiaCheckBox),
- new SKColor(0xBD, 0xBD, 0xBD),
+ new SKColor(189, 189, 189),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for HoveredBorderColor.
- ///
public static readonly BindableProperty HoveredBorderColorProperty =
BindableProperty.Create(
nameof(HoveredBorderColor),
typeof(SKColor),
typeof(SkiaCheckBox),
- new SKColor(0x21, 0x96, 0xF3),
+ new SKColor(33, 150, 243),
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for BoxSize.
- ///
public static readonly BindableProperty BoxSizeProperty =
BindableProperty.Create(
nameof(BoxSize),
typeof(float),
typeof(SkiaCheckBox),
20f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).InvalidateMeasure());
- ///
- /// Bindable property for CornerRadius.
- ///
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create(
nameof(CornerRadius),
typeof(float),
typeof(SkiaCheckBox),
3f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for BorderWidth.
- ///
public static readonly BindableProperty BorderWidthProperty =
BindableProperty.Create(
nameof(BorderWidth),
typeof(float),
typeof(SkiaCheckBox),
2f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
- ///
- /// Bindable property for CheckStrokeWidth.
- ///
public static readonly BindableProperty CheckStrokeWidthProperty =
BindableProperty.Create(
nameof(CheckStrokeWidth),
typeof(float),
typeof(SkiaCheckBox),
2.5f,
+ BindingMode.TwoWay,
propertyChanged: (b, o, n) => ((SkiaCheckBox)b).Invalidate());
#endregion
#region Properties
- ///
- /// Gets or sets whether the checkbox is checked.
- ///
public bool IsChecked
{
get => (bool)GetValue(IsCheckedProperty);
set => SetValue(IsCheckedProperty, value);
}
- ///
- /// Gets or sets the check color.
- ///
public SKColor CheckColor
{
get => (SKColor)GetValue(CheckColorProperty);
set => SetValue(CheckColorProperty, value);
}
- ///
- /// Gets or sets the box color when checked.
- ///
public SKColor BoxColor
{
get => (SKColor)GetValue(BoxColorProperty);
set => SetValue(BoxColorProperty, value);
}
- ///
- /// Gets or sets the box color when unchecked.
- ///
public SKColor UncheckedBoxColor
{
get => (SKColor)GetValue(UncheckedBoxColorProperty);
set => SetValue(UncheckedBoxColorProperty, value);
}
- ///
- /// Gets or sets the border color.
- ///
public SKColor BorderColor
{
get => (SKColor)GetValue(BorderColorProperty);
set => SetValue(BorderColorProperty, value);
}
- ///
- /// Gets or sets the disabled color.
- ///
public SKColor DisabledColor
{
get => (SKColor)GetValue(DisabledColorProperty);
set => SetValue(DisabledColorProperty, value);
}
- ///
- /// Gets or sets the hovered border color.
- ///
public SKColor HoveredBorderColor
{
get => (SKColor)GetValue(HoveredBorderColorProperty);
set => SetValue(HoveredBorderColorProperty, value);
}
- ///
- /// Gets or sets the box size.
- ///
public float BoxSize
{
get => (float)GetValue(BoxSizeProperty);
set => SetValue(BoxSizeProperty, value);
}
- ///
- /// Gets or sets the corner radius.
- ///
public float CornerRadius
{
get => (float)GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
- ///
- /// Gets or sets the border width.
- ///
public float BorderWidth
{
get => (float)GetValue(BorderWidthProperty);
set => SetValue(BorderWidthProperty, value);
}
- ///
- /// Gets or sets the check stroke width.
- ///
public float CheckStrokeWidth
{
get => (float)GetValue(CheckStrokeWidthProperty);
set => SetValue(CheckStrokeWidthProperty, value);
}
- ///
- /// Gets whether the pointer is over the checkbox.
- ///
public bool IsHovered { get; private set; }
#endregion
- ///
- /// Event raised when checked state changes.
- ///
public event EventHandler? CheckedChanged;
public SkiaCheckBox()
@@ -258,7 +197,7 @@ public class SkiaCheckBox : SkiaView
private void OnIsCheckedChanged()
{
CheckedChanged?.Invoke(this, new CheckedChangedEventArgs(IsChecked));
- SkiaVisualStateManager.GoToState(this, IsChecked ? SkiaVisualStateManager.CommonStates.Checked : SkiaVisualStateManager.CommonStates.Unchecked);
+ SkiaVisualStateManager.GoToState(this, IsChecked ? "Checked" : "Unchecked");
Invalidate();
}
@@ -266,13 +205,19 @@ public class SkiaCheckBox : SkiaView
{
// Center the checkbox box in bounds
var boxRect = new SKRect(
- bounds.Left + (bounds.Width - BoxSize) / 2,
- bounds.Top + (bounds.Height - BoxSize) / 2,
- bounds.Left + (bounds.Width - BoxSize) / 2 + BoxSize,
- bounds.Top + (bounds.Height - BoxSize) / 2 + BoxSize);
+ bounds.Left + (bounds.Width - BoxSize) / 2f,
+ bounds.Top + (bounds.Height - BoxSize) / 2f,
+ bounds.Left + (bounds.Width - BoxSize) / 2f + BoxSize,
+ bounds.Top + (bounds.Height - BoxSize) / 2f + BoxSize);
var roundRect = new SKRoundRect(boxRect, CornerRadius);
+ // Debug logging when checked
+ if (IsChecked)
+ {
+ Console.WriteLine($"[SkiaCheckBox] OnDraw CHECKED - BoxColor=({BoxColor.Red},{BoxColor.Green},{BoxColor.Blue}), UncheckedBoxColor=({UncheckedBoxColor.Red},{UncheckedBoxColor.Green},{UncheckedBoxColor.Blue})");
+ }
+
// Draw background
using var bgPaint = new SKPaint
{
@@ -305,10 +250,10 @@ public class SkiaCheckBox : SkiaView
Color = BoxColor.WithAlpha(80),
IsAntialias = true,
Style = SKPaintStyle.Stroke,
- StrokeWidth = 3
+ StrokeWidth = 3f
};
var focusRect = new SKRoundRect(boxRect, CornerRadius);
- focusRect.Inflate(4, 4);
+ focusRect.Inflate(4f, 4f);
canvas.DrawRoundRect(focusRect, focusPaint);
}
@@ -323,7 +268,7 @@ public class SkiaCheckBox : SkiaView
{
using var paint = new SKPaint
{
- Color = CheckColor,
+ Color = SKColors.White,
IsAntialias = true,
Style = SKPaintStyle.Stroke,
StrokeWidth = CheckStrokeWidth,
@@ -331,14 +276,12 @@ public class SkiaCheckBox : SkiaView
StrokeJoin = SKStrokeJoin.Round
};
- // Checkmark path - a simple check
- var padding = BoxSize * 0.2f;
- var left = boxRect.Left + padding;
- var right = boxRect.Right - padding;
- var top = boxRect.Top + padding;
- var bottom = boxRect.Bottom - padding;
+ float padding = BoxSize * 0.2f;
+ float left = boxRect.Left + padding;
+ float right = boxRect.Right - padding;
+ float top = boxRect.Top + padding;
+ float bottom = boxRect.Bottom - padding;
- // Check starts from bottom-left, goes to middle-bottom, then to top-right
using var path = new SKPath();
path.MoveTo(left, boxRect.MidY);
path.LineTo(boxRect.MidX - padding * 0.3f, bottom - padding * 0.5f);
@@ -349,37 +292,37 @@ public class SkiaCheckBox : SkiaView
public override void OnPointerEntered(PointerEventArgs e)
{
- if (!IsEnabled) return;
- IsHovered = true;
- SkiaVisualStateManager.GoToState(this, SkiaVisualStateManager.CommonStates.PointerOver);
- Invalidate();
+ if (IsEnabled)
+ {
+ IsHovered = true;
+ SkiaVisualStateManager.GoToState(this, "PointerOver");
+ Invalidate();
+ }
}
public override void OnPointerExited(PointerEventArgs e)
{
IsHovered = false;
- SkiaVisualStateManager.GoToState(this, IsEnabled ? SkiaVisualStateManager.CommonStates.Normal : SkiaVisualStateManager.CommonStates.Disabled);
+ SkiaVisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled");
Invalidate();
}
public override void OnPointerPressed(PointerEventArgs e)
{
- if (!IsEnabled) return;
- IsChecked = !IsChecked;
- e.Handled = true;
+ if (IsEnabled)
+ {
+ IsChecked = !IsChecked;
+ e.Handled = true;
+ }
}
public override void OnPointerReleased(PointerEventArgs e)
{
- // Toggle handled in OnPointerPressed
}
public override void OnKeyDown(KeyEventArgs e)
{
- if (!IsEnabled) return;
-
- // Toggle on Space
- if (e.Key == Key.Space)
+ if (IsEnabled && e.Key == Key.Space)
{
IsChecked = !IsChecked;
e.Handled = true;
@@ -389,25 +332,11 @@ public class SkiaCheckBox : SkiaView
protected override void OnEnabledChanged()
{
base.OnEnabledChanged();
- SkiaVisualStateManager.GoToState(this, IsEnabled ? SkiaVisualStateManager.CommonStates.Normal : SkiaVisualStateManager.CommonStates.Disabled);
+ SkiaVisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled");
}
protected override SKSize MeasureOverride(SKSize availableSize)
{
- // Add some padding around the box for touch targets
- return new SKSize(BoxSize + 8, BoxSize + 8);
- }
-}
-
-///
-/// Event args for checked changed events.
-///
-public class CheckedChangedEventArgs : EventArgs
-{
- public bool IsChecked { get; }
-
- public CheckedChangedEventArgs(bool isChecked)
- {
- IsChecked = isChecked;
+ return new SKSize(BoxSize + 8f, BoxSize + 8f);
}
}
diff --git a/Views/SkiaCollectionView.cs b/Views/SkiaCollectionView.cs
index 9860d79..74ab1eb 100644
--- a/Views/SkiaCollectionView.cs
+++ b/Views/SkiaCollectionView.cs
@@ -1,9 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Maui.Controls;
using SkiaSharp;
-using System.Collections;
-using Microsoft.Maui.Graphics;
namespace Microsoft.Maui.Platform;
@@ -33,203 +35,110 @@ public class SkiaCollectionView : SkiaItemsView
{
#region BindableProperties
- ///
- /// Bindable property for SelectionMode.
- ///
- public static readonly BindableProperty SelectionModeProperty =
- BindableProperty.Create(
- nameof(SelectionMode),
- typeof(SkiaSelectionMode),
- typeof(SkiaCollectionView),
- SkiaSelectionMode.Single,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectionModeChanged());
+ public static readonly BindableProperty SelectionModeProperty = BindableProperty.Create(
+ nameof(SelectionMode),
+ typeof(SkiaSelectionMode),
+ typeof(SkiaCollectionView),
+ SkiaSelectionMode.Single,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectionModeChanged());
- ///
- /// Bindable property for SelectedItem.
- ///
- public static readonly BindableProperty SelectedItemProperty =
- BindableProperty.Create(
- nameof(SelectedItem),
- typeof(object),
- typeof(SkiaCollectionView),
- null,
- BindingMode.TwoWay,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectedItemChanged(n));
+ public static readonly BindableProperty SelectedItemProperty = BindableProperty.Create(
+ nameof(SelectedItem),
+ typeof(object),
+ typeof(SkiaCollectionView),
+ null,
+ BindingMode.OneWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnSelectedItemChanged(n));
- ///
- /// Bindable property for Orientation.
- ///
- public static readonly BindableProperty OrientationProperty =
- BindableProperty.Create(
- nameof(Orientation),
- typeof(ItemsLayoutOrientation),
- typeof(SkiaCollectionView),
- ItemsLayoutOrientation.Vertical,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty OrientationProperty = BindableProperty.Create(
+ nameof(Orientation),
+ typeof(ItemsLayoutOrientation),
+ typeof(SkiaCollectionView),
+ ItemsLayoutOrientation.Vertical,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for SpanCount.
- ///
- public static readonly BindableProperty SpanCountProperty =
- BindableProperty.Create(
- nameof(SpanCount),
- typeof(int),
- typeof(SkiaCollectionView),
- 1,
- coerceValue: (b, v) => Math.Max(1, (int)v),
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty SpanCountProperty = BindableProperty.Create(
+ nameof(SpanCount),
+ typeof(int),
+ typeof(SkiaCollectionView),
+ 1,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate(),
+ coerceValue: (b, v) => Math.Max(1, (int)v));
- ///
- /// Bindable property for GridItemWidth.
- ///
- public static readonly BindableProperty GridItemWidthProperty =
- BindableProperty.Create(
- nameof(GridItemWidth),
- typeof(float),
- typeof(SkiaCollectionView),
- 100f,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty GridItemWidthProperty = BindableProperty.Create(
+ nameof(GridItemWidth),
+ typeof(float),
+ typeof(SkiaCollectionView),
+ 100f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for Header.
- ///
- public static readonly BindableProperty HeaderProperty =
- BindableProperty.Create(
- nameof(Header),
- typeof(object),
- typeof(SkiaCollectionView),
- null,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnHeaderChanged(n));
+ public static readonly BindableProperty HeaderProperty = BindableProperty.Create(
+ nameof(Header),
+ typeof(object),
+ typeof(SkiaCollectionView),
+ null,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnHeaderChanged(n));
- ///
- /// Bindable property for Footer.
- ///
- public static readonly BindableProperty FooterProperty =
- BindableProperty.Create(
- nameof(Footer),
- typeof(object),
- typeof(SkiaCollectionView),
- null,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnFooterChanged(n));
+ public static readonly BindableProperty FooterProperty = BindableProperty.Create(
+ nameof(Footer),
+ typeof(object),
+ typeof(SkiaCollectionView),
+ null,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).OnFooterChanged(n));
- ///
- /// Bindable property for HeaderHeight.
- ///
- public static readonly BindableProperty HeaderHeightProperty =
- BindableProperty.Create(
- nameof(HeaderHeight),
- typeof(float),
- typeof(SkiaCollectionView),
- 0f,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty HeaderHeightProperty = BindableProperty.Create(
+ nameof(HeaderHeight),
+ typeof(float),
+ typeof(SkiaCollectionView),
+ 0f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for FooterHeight.
- ///
- public static readonly BindableProperty FooterHeightProperty =
- BindableProperty.Create(
- nameof(FooterHeight),
- typeof(float),
- typeof(SkiaCollectionView),
- 0f,
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty FooterHeightProperty = BindableProperty.Create(
+ nameof(FooterHeight),
+ typeof(float),
+ typeof(SkiaCollectionView),
+ 0f,
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for SelectionColor.
- ///
- public static readonly BindableProperty SelectionColorProperty =
- BindableProperty.Create(
- nameof(SelectionColor),
- typeof(SKColor),
- typeof(SkiaCollectionView),
- new SKColor(0x21, 0x96, 0xF3, 0x59),
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty SelectionColorProperty = BindableProperty.Create(
+ nameof(SelectionColor),
+ typeof(SKColor),
+ typeof(SkiaCollectionView),
+ new SKColor(33, 150, 243, 89),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for HeaderBackgroundColor.
- ///
- public static readonly BindableProperty HeaderBackgroundColorProperty =
- BindableProperty.Create(
- nameof(HeaderBackgroundColor),
- typeof(SKColor),
- typeof(SkiaCollectionView),
- new SKColor(0xF5, 0xF5, 0xF5),
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty HeaderBackgroundColorProperty = BindableProperty.Create(
+ nameof(HeaderBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaCollectionView),
+ new SKColor(245, 245, 245),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
- ///
- /// Bindable property for FooterBackgroundColor.
- ///
- public static readonly BindableProperty FooterBackgroundColorProperty =
- BindableProperty.Create(
- nameof(FooterBackgroundColor),
- typeof(SKColor),
- typeof(SkiaCollectionView),
- new SKColor(0xF5, 0xF5, 0xF5),
- propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
+ public static readonly BindableProperty FooterBackgroundColorProperty = BindableProperty.Create(
+ nameof(FooterBackgroundColor),
+ typeof(SKColor),
+ typeof(SkiaCollectionView),
+ new SKColor(245, 245, 245),
+ BindingMode.TwoWay,
+ propertyChanged: (b, o, n) => ((SkiaCollectionView)b).Invalidate());
#endregion
- private List