2025-12-19 09:30:16 +00:00
|
|
|
// Licensed to the .NET Foundation under one or more agreements.
|
|
|
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
|
|
|
|
2026-01-16 04:40:02 +00:00
|
|
|
using System;
|
Major production merge: GTK support, context menus, and dispatcher fixes
Core Infrastructure:
- Add Dispatching folder with LinuxDispatcher, LinuxDispatcherProvider, LinuxDispatcherTimer
- Add Native folder with P/Invoke wrappers (GTK, GLib, GDK, Cairo, WebKit)
- Add GTK host window system with GtkHostWindow and GtkSkiaSurfaceWidget
- Update LinuxApplication with GTK mode, theme handling, and icon support
- Fix duplicate LinuxDispatcher in LinuxMauiContext
Handlers:
- Add GtkWebViewManager and GtkWebViewPlatformView for GTK WebView
- Add FlexLayoutHandler and GestureManager
- Update multiple handlers with ToViewHandler fix and missing mappers
- Add MauiHandlerExtensions with ToViewHandler extension method
Views:
- Add SkiaContextMenu with hover, keyboard, and dark theme support
- Add LinuxDialogService with context menu management
- Add SkiaFlexLayout for flex container support
- Update SkiaShell with RefreshTheme, MauiShell, ContentRenderer
- Update SkiaWebView with SetMainWindow, ProcessGtkEvents
- Update SkiaImage with LoadFromBitmap method
Services:
- Add AppInfoService, ConnectivityService, DeviceDisplayService, DeviceInfoService
- Add GtkHostService, GtkContextMenuService, MauiIconGenerator
Window:
- Add CursorType enum and GtkHostWindow
- Update X11Window with SetIcon, SetCursor methods
Build: SUCCESS (0 errors)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:19:58 -05:00
|
|
|
using System.Collections.Generic;
|
2026-01-16 04:40:02 +00:00
|
|
|
using Microsoft.Maui.Controls;
|
|
|
|
|
using Microsoft.Maui.Graphics;
|
2026-01-17 15:19:03 +00:00
|
|
|
using Microsoft.Maui.Platform.Linux;
|
2025-12-19 09:30:16 +00:00
|
|
|
using Microsoft.Maui.Platform.Linux.Rendering;
|
Major production merge: GTK support, context menus, and dispatcher fixes
Core Infrastructure:
- Add Dispatching folder with LinuxDispatcher, LinuxDispatcherProvider, LinuxDispatcherTimer
- Add Native folder with P/Invoke wrappers (GTK, GLib, GDK, Cairo, WebKit)
- Add GTK host window system with GtkHostWindow and GtkSkiaSurfaceWidget
- Update LinuxApplication with GTK mode, theme handling, and icon support
- Fix duplicate LinuxDispatcher in LinuxMauiContext
Handlers:
- Add GtkWebViewManager and GtkWebViewPlatformView for GTK WebView
- Add FlexLayoutHandler and GestureManager
- Update multiple handlers with ToViewHandler fix and missing mappers
- Add MauiHandlerExtensions with ToViewHandler extension method
Views:
- Add SkiaContextMenu with hover, keyboard, and dark theme support
- Add LinuxDialogService with context menu management
- Add SkiaFlexLayout for flex container support
- Update SkiaShell with RefreshTheme, MauiShell, ContentRenderer
- Update SkiaWebView with SetMainWindow, ProcessGtkEvents
- Update SkiaImage with LoadFromBitmap method
Services:
- Add AppInfoService, ConnectivityService, DeviceDisplayService, DeviceInfoService
- Add GtkHostService, GtkContextMenuService, MauiIconGenerator
Window:
- Add CursorType enum and GtkHostWindow
- Update X11Window with SetIcon, SetCursor methods
Build: SUCCESS (0 errors)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 11:19:58 -05:00
|
|
|
using Microsoft.Maui.Platform.Linux.Services;
|
2026-01-16 04:40:02 +00:00
|
|
|
using SkiaSharp;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
namespace Microsoft.Maui.Platform;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-12-21 13:26:56 -05:00
|
|
|
/// Skia-rendered text entry control with full XAML styling and data binding support.
|
2026-01-17 02:23:05 +00:00
|
|
|
/// Implements IInputContext for IME (Input Method Editor) support.
|
2025-12-19 09:30:16 +00:00
|
|
|
/// </summary>
|
2026-03-06 22:36:23 -05:00
|
|
|
public partial class SkiaEntry : SkiaView, IInputContext
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
#region BindableProperties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Text.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty TextProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(Text),
|
|
|
|
|
typeof(string),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
"",
|
Fix Views: SkiaEntry, SkiaEditor, SkiaShell, SkiaWebView
- SkiaEntry.cs: TextProperty BindingMode.OneWay (was TwoWay)
- SkiaEditor.cs: All BindingModes corrected (Text=OneWay, others=TwoWay)
- SkiaShell.cs: Added FlyoutTextColor, ContentBackgroundColor properties,
route registration system, query parameter support, OnScroll handler
- SkiaWebView.cs: Full rewrite with X11 embedding, GTK window positioning,
hardware acceleration settings, load-changed callbacks, position tracking
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-01 14:27:33 -05:00
|
|
|
BindingMode.OneWay,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).OnTextPropertyChanged((string)o, (string)n));
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Placeholder.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty PlaceholderProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(Placeholder),
|
|
|
|
|
typeof(string),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
"",
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for PlaceholderColor.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is null to match MAUI Entry.PlaceholderColor (falls back to platform default).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty PlaceholderColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(PlaceholderColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-17 01:43:42 +00:00
|
|
|
null,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for TextColor.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is null to match MAUI Entry.TextColor (falls back to platform default).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty TextColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(TextColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-17 01:43:42 +00:00
|
|
|
null,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
/// Bindable property for EntryBackgroundColor (specific to entry, separate from base BackgroundColor).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty EntryBackgroundColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(EntryBackgroundColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-17 08:06:22 +00:00
|
|
|
Colors.Transparent,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for BorderColor.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty BorderColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(BorderColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
Color.FromRgb(0xBD, 0xBD, 0xBD),
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FocusedBorderColor.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty FocusedBorderColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(FocusedBorderColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
Color.FromRgb(0x21, 0x96, 0xF3),
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for SelectionColor.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty SelectionColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(SelectionColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
Color.FromRgba(0x21, 0x96, 0xF3, 0x80),
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for CursorColor.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty CursorColorProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(CursorColor),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Color),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
Color.FromRgb(0x21, 0x96, 0xF3),
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FontFamily.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is empty string to match MAUI Entry.FontFamily (falls back to platform default).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty FontFamilyProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(FontFamily),
|
|
|
|
|
typeof(string),
|
|
|
|
|
typeof(SkiaEntry),
|
2026-01-17 01:43:42 +00:00
|
|
|
string.Empty,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).InvalidateMeasure());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FontSize.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty FontSizeProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(FontSize),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(double),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
14.0,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).InvalidateMeasure());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for CornerRadius.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty CornerRadiusProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(CornerRadius),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(double),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
4.0,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for BorderWidth.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty BorderWidthProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(BorderWidth),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(double),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
1.0,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Padding.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty PaddingProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(Padding),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(Thickness),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
new Thickness(12, 8),
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).InvalidateMeasure());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for IsPassword.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty IsPasswordProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(IsPassword),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
false,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for PasswordChar.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty PasswordCharProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(PasswordChar),
|
|
|
|
|
typeof(char),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
'*', // Use asterisk for universal font compatibility
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for MaxLength.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty MaxLengthProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(MaxLength),
|
|
|
|
|
typeof(int),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
0);
|
|
|
|
|
|
2026-01-17 08:51:13 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for SelectAllOnDoubleClick.
|
|
|
|
|
/// When true, double-clicking selects all text instead of just the word.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty SelectAllOnDoubleClickProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(SelectAllOnDoubleClick),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
false);
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for IsReadOnly.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty IsReadOnlyProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(IsReadOnly),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
false,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for HorizontalTextAlignment.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty HorizontalTextAlignmentProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(HorizontalTextAlignment),
|
|
|
|
|
typeof(TextAlignment),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
TextAlignment.Start,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for VerticalTextAlignment.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is Start to match MAUI Entry.VerticalTextAlignment.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty VerticalTextAlignmentProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(VerticalTextAlignment),
|
|
|
|
|
typeof(TextAlignment),
|
|
|
|
|
typeof(SkiaEntry),
|
2026-01-17 01:43:42 +00:00
|
|
|
TextAlignment.Start,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for ShowClearButton.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty ShowClearButtonProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(ShowClearButton),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
false,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for CharacterSpacing.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty CharacterSpacingProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(CharacterSpacing),
|
2026-01-16 04:40:02 +00:00
|
|
|
typeof(double),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
0.0,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FontAttributes.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty FontAttributesProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(FontAttributes),
|
|
|
|
|
typeof(FontAttributes),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
FontAttributes.None,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).InvalidateMeasure());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for ReturnType.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty ReturnTypeProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(ReturnType),
|
|
|
|
|
typeof(ReturnType),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
ReturnType.Default);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for ReturnCommand.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty ReturnCommandProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(ReturnCommand),
|
|
|
|
|
typeof(System.Windows.Input.ICommand),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
null);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for ReturnCommandParameter.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty ReturnCommandParameterProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(ReturnCommandParameter),
|
|
|
|
|
typeof(object),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
null);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Keyboard.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty KeyboardProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(Keyboard),
|
|
|
|
|
typeof(Keyboard),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
Keyboard.Default);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for ClearButtonVisibility.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty ClearButtonVisibilityProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(ClearButtonVisibility),
|
|
|
|
|
typeof(ClearButtonVisibility),
|
2025-12-21 13:26:56 -05:00
|
|
|
typeof(SkiaEntry),
|
2026-01-16 04:40:02 +00:00
|
|
|
ClearButtonVisibility.Never,
|
2025-12-21 13:26:56 -05:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaEntry)b).Invalidate());
|
|
|
|
|
|
2026-01-16 04:40:02 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for IsTextPredictionEnabled.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty IsTextPredictionEnabledProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(IsTextPredictionEnabled),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
true);
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for IsSpellCheckEnabled.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty IsSpellCheckEnabledProperty =
|
|
|
|
|
BindableProperty.Create(
|
|
|
|
|
nameof(IsSpellCheckEnabled),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaEntry),
|
|
|
|
|
true);
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the text content.
|
|
|
|
|
/// </summary>
|
2025-12-19 09:30:16 +00:00
|
|
|
public string Text
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
get => (string)GetValue(TextProperty);
|
|
|
|
|
set => SetValue(TextProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the placeholder text.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Placeholder
|
|
|
|
|
{
|
|
|
|
|
get => (string)GetValue(PlaceholderProperty);
|
|
|
|
|
set => SetValue(PlaceholderProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Gets or sets the placeholder color. Null means platform default (gray).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-17 01:43:42 +00:00
|
|
|
public Color? PlaceholderColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-17 01:43:42 +00:00
|
|
|
get => (Color?)GetValue(PlaceholderColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(PlaceholderColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Gets or sets the text color. Null means platform default (black).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-17 01:43:42 +00:00
|
|
|
public Color? TextColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-17 01:43:42 +00:00
|
|
|
get => (Color?)GetValue(TextColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(TextColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the entry background color.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Color EntryBackgroundColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Color)GetValue(EntryBackgroundColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(EntryBackgroundColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the border color.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Color BorderColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Color)GetValue(BorderColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(BorderColorProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the focused border color.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Color FocusedBorderColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Color)GetValue(FocusedBorderColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(FocusedBorderColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the selection color.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Color SelectionColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Color)GetValue(SelectionColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(SelectionColorProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the cursor color.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Color CursorColor
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Color)GetValue(CursorColorProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(CursorColorProperty, value);
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the font family.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string FontFamily
|
|
|
|
|
{
|
|
|
|
|
get => (string)GetValue(FontFamilyProperty);
|
|
|
|
|
set => SetValue(FontFamilyProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the font size.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public double FontSize
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (double)GetValue(FontSizeProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(FontSizeProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the corner radius.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public double CornerRadius
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (double)GetValue(CornerRadiusProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(CornerRadiusProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the border width.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public double BorderWidth
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (double)GetValue(BorderWidthProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(BorderWidthProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the padding.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public Thickness Padding
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (Thickness)GetValue(PaddingProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(PaddingProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether this is a password field.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsPassword
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(IsPasswordProperty);
|
|
|
|
|
set => SetValue(IsPasswordProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the password masking character.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public char PasswordChar
|
|
|
|
|
{
|
|
|
|
|
get => (char)GetValue(PasswordCharProperty);
|
|
|
|
|
set => SetValue(PasswordCharProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the maximum text length. 0 = unlimited.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public int MaxLength
|
|
|
|
|
{
|
|
|
|
|
get => (int)GetValue(MaxLengthProperty);
|
|
|
|
|
set => SetValue(MaxLengthProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 08:51:13 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether double-clicking selects all text instead of just the word.
|
|
|
|
|
/// Useful for URL bars and similar inputs.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool SelectAllOnDoubleClick
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(SelectAllOnDoubleClickProperty);
|
|
|
|
|
set => SetValue(SelectAllOnDoubleClickProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether the entry is read-only.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsReadOnly
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(IsReadOnlyProperty);
|
|
|
|
|
set => SetValue(IsReadOnlyProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the horizontal text alignment.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TextAlignment HorizontalTextAlignment
|
|
|
|
|
{
|
|
|
|
|
get => (TextAlignment)GetValue(HorizontalTextAlignmentProperty);
|
|
|
|
|
set => SetValue(HorizontalTextAlignmentProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the vertical text alignment.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public TextAlignment VerticalTextAlignment
|
|
|
|
|
{
|
|
|
|
|
get => (TextAlignment)GetValue(VerticalTextAlignmentProperty);
|
|
|
|
|
set => SetValue(VerticalTextAlignmentProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether to show the clear button.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool ShowClearButton
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(ShowClearButtonProperty);
|
|
|
|
|
set => SetValue(ShowClearButtonProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the character spacing.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:40:02 +00:00
|
|
|
public double CharacterSpacing
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:40:02 +00:00
|
|
|
get => (double)GetValue(CharacterSpacingProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(CharacterSpacingProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:40:02 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the font attributes (bold, italic).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public FontAttributes FontAttributes
|
|
|
|
|
{
|
|
|
|
|
get => (FontAttributes)GetValue(FontAttributesProperty);
|
|
|
|
|
set => SetValue(FontAttributesProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the return key type for the soft keyboard.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ReturnType ReturnType
|
|
|
|
|
{
|
|
|
|
|
get => (ReturnType)GetValue(ReturnTypeProperty);
|
|
|
|
|
set => SetValue(ReturnTypeProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the command to execute when the return key is pressed.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public System.Windows.Input.ICommand? ReturnCommand
|
|
|
|
|
{
|
|
|
|
|
get => (System.Windows.Input.ICommand?)GetValue(ReturnCommandProperty);
|
|
|
|
|
set => SetValue(ReturnCommandProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the parameter for the return command.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public object? ReturnCommandParameter
|
|
|
|
|
{
|
|
|
|
|
get => GetValue(ReturnCommandParameterProperty);
|
|
|
|
|
set => SetValue(ReturnCommandParameterProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the keyboard type for this entry.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Keyboard Keyboard
|
|
|
|
|
{
|
|
|
|
|
get => (Keyboard)GetValue(KeyboardProperty);
|
|
|
|
|
set => SetValue(KeyboardProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets when the clear button is visible.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public ClearButtonVisibility ClearButtonVisibility
|
|
|
|
|
{
|
|
|
|
|
get => (ClearButtonVisibility)GetValue(ClearButtonVisibilityProperty);
|
|
|
|
|
set => SetValue(ClearButtonVisibilityProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the cursor position.
|
|
|
|
|
/// </summary>
|
2025-12-19 09:30:16 +00:00
|
|
|
public int CursorPosition
|
|
|
|
|
{
|
|
|
|
|
get => _cursorPosition;
|
|
|
|
|
set
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
_cursorPosition = Math.Clamp(value, 0, Text.Length);
|
2025-12-19 09:30:16 +00:00
|
|
|
ResetCursorBlink();
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the selection length.
|
|
|
|
|
/// </summary>
|
2025-12-19 09:30:16 +00:00
|
|
|
public int SelectionLength
|
|
|
|
|
{
|
|
|
|
|
get => _selectionLength;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
_selectionLength = value;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:40:02 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether text prediction is enabled.
|
|
|
|
|
/// Note: This is a hint to the input system; actual behavior depends on platform support.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsTextPredictionEnabled
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(IsTextPredictionEnabledProperty);
|
|
|
|
|
set => SetValue(IsTextPredictionEnabledProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether spell checking is enabled.
|
|
|
|
|
/// Note: This is a hint to the input system; actual behavior depends on platform support.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsSpellCheckEnabled
|
|
|
|
|
{
|
|
|
|
|
get => (bool)GetValue(IsSpellCheckEnabledProperty);
|
|
|
|
|
set => SetValue(IsSpellCheckEnabledProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
private int _cursorPosition;
|
|
|
|
|
private int _selectionStart;
|
|
|
|
|
private int _selectionLength;
|
|
|
|
|
private float _scrollOffset;
|
|
|
|
|
private DateTime _cursorBlinkTime = DateTime.UtcNow;
|
|
|
|
|
private bool _cursorVisible = true;
|
|
|
|
|
private bool _isSelecting; // For mouse-based text selection
|
|
|
|
|
private DateTime _lastClickTime = DateTime.MinValue;
|
|
|
|
|
private float _lastClickX;
|
|
|
|
|
private const double DoubleClickThresholdMs = 400;
|
|
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
// IME (Input Method Editor) support
|
|
|
|
|
private string _preEditText = string.Empty;
|
|
|
|
|
private int _preEditCursorPosition;
|
|
|
|
|
private IInputMethodService? _inputMethodService;
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Event raised when text changes.
|
|
|
|
|
/// </summary>
|
2025-12-19 09:30:16 +00:00
|
|
|
public event EventHandler<TextChangedEventArgs>? TextChanged;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Event raised when Enter is pressed.
|
|
|
|
|
/// </summary>
|
2025-12-19 09:30:16 +00:00
|
|
|
public event EventHandler? Completed;
|
|
|
|
|
|
|
|
|
|
public SkiaEntry()
|
|
|
|
|
{
|
|
|
|
|
IsFocusable = true;
|
2026-01-17 02:23:05 +00:00
|
|
|
// Get IME service from factory
|
|
|
|
|
_inputMethodService = InputMethodServiceFactory.Instance;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:40:02 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a MAUI Color to SkiaSharp SKColor for rendering.
|
|
|
|
|
/// </summary>
|
2026-01-17 01:43:42 +00:00
|
|
|
private static SKColor ToSKColor(Color? color)
|
2026-01-16 04:40:02 +00:00
|
|
|
{
|
|
|
|
|
if (color == null) return SKColors.Transparent;
|
2026-01-17 03:36:37 +00:00
|
|
|
return color.ToSKColor();
|
2026-01-16 04:40:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-17 01:43:42 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the effective text color (platform default black if null).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private SKColor GetEffectiveTextColor()
|
|
|
|
|
{
|
2026-01-17 03:36:37 +00:00
|
|
|
return TextColor != null ? ToSKColor(TextColor) : SkiaTheme.TextPrimarySK;
|
2026-01-17 01:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the effective placeholder color (platform default gray if null).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private SKColor GetEffectivePlaceholderColor()
|
|
|
|
|
{
|
2026-01-17 03:36:37 +00:00
|
|
|
return PlaceholderColor != null ? ToSKColor(PlaceholderColor) : SkiaTheme.TextDisabledSK;
|
2026-01-17 01:43:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the effective font family (platform default "Sans" if empty).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string GetEffectiveFontFamily()
|
|
|
|
|
{
|
|
|
|
|
return string.IsNullOrEmpty(FontFamily) ? "Sans" : FontFamily;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Determines if text should be rendered right-to-left based on FlowDirection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private bool IsRightToLeft()
|
|
|
|
|
{
|
|
|
|
|
return FlowDirection == FlowDirection.RightToLeft;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the horizontal alignment accounting for FlowDirection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private float GetEffectiveTextX(SKRect contentBounds, float textWidth)
|
|
|
|
|
{
|
|
|
|
|
bool isRtl = IsRightToLeft();
|
|
|
|
|
|
|
|
|
|
return HorizontalTextAlignment switch
|
|
|
|
|
{
|
|
|
|
|
TextAlignment.Start => isRtl ? contentBounds.Right - textWidth - _scrollOffset : contentBounds.Left - _scrollOffset,
|
|
|
|
|
TextAlignment.Center => contentBounds.MidX - textWidth / 2,
|
|
|
|
|
TextAlignment.End => isRtl ? contentBounds.Left - _scrollOffset : contentBounds.Right - textWidth - _scrollOffset,
|
|
|
|
|
_ => isRtl ? contentBounds.Right - textWidth - _scrollOffset : contentBounds.Left - _scrollOffset
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
private void OnTextPropertyChanged(string oldText, string newText)
|
|
|
|
|
{
|
|
|
|
|
_cursorPosition = Math.Min(_cursorPosition, (newText ?? "").Length);
|
|
|
|
|
_scrollOffset = 0; // Reset scroll when text changes externally
|
|
|
|
|
_selectionLength = 0;
|
|
|
|
|
TextChanged?.Invoke(this, new TextChangedEventArgs(oldText, newText ?? ""));
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
private string GetDisplayText()
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
if (IsPassword && !string.IsNullOrEmpty(Text))
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
return new string(PasswordChar, Text.Length);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
return Text;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-06 22:36:23 -05:00
|
|
|
private SKFontStyle GetFontStyle() => TextRenderingHelper.GetFontStyle(FontAttributes);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Event args for text changed events.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class TextChangedEventArgs : EventArgs
|
|
|
|
|
{
|
|
|
|
|
public string OldTextValue { get; }
|
|
|
|
|
public string NewTextValue { get; }
|
|
|
|
|
|
|
|
|
|
public TextChangedEventArgs(string oldText, string newText)
|
|
|
|
|
{
|
|
|
|
|
OldTextValue = oldText;
|
|
|
|
|
NewTextValue = newText;
|
|
|
|
|
}
|
|
|
|
|
}
|