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.
|
|
|
|
|
|
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;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
2026-01-16 04:23:47 +00:00
|
|
|
using Microsoft.Maui.Controls;
|
|
|
|
|
using Microsoft.Maui.Graphics;
|
2025-12-19 09:30:16 +00:00
|
|
|
using Microsoft.Maui.Platform.Linux.Rendering;
|
2026-01-17 02:23:05 +00:00
|
|
|
using Microsoft.Maui.Platform.Linux.Services;
|
2026-01-16 04:23:47 +00:00
|
|
|
using SkiaSharp;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
namespace Microsoft.Maui.Platform;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Skia-rendered label control matching the .NET MAUI Label API.
|
2025-12-19 09:30:16 +00:00
|
|
|
/// </summary>
|
|
|
|
|
public class SkiaLabel : SkiaView
|
|
|
|
|
{
|
2025-12-21 13:26:56 -05:00
|
|
|
#region BindableProperties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Text.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty TextProperty = BindableProperty.Create(
|
|
|
|
|
nameof(Text),
|
|
|
|
|
typeof(string),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
string.Empty,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnTextChanged());
|
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
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for TextColor.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is null to match MAUI Label.TextColor (falls back to platform default).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty TextColorProperty = BindableProperty.Create(
|
|
|
|
|
nameof(TextColor),
|
|
|
|
|
typeof(Color),
|
|
|
|
|
typeof(SkiaLabel),
|
2026-01-17 01:43:42 +00:00
|
|
|
null,
|
2026-01-16 04:23:47 +00:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FontFamily.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty FontFamilyProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FontFamily),
|
|
|
|
|
typeof(string),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
string.Empty,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnFontChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FontSize.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty FontSizeProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FontSize),
|
|
|
|
|
typeof(double),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
14.0,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnFontChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Bindable property for FontAttributes.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty FontAttributesProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FontAttributes),
|
|
|
|
|
typeof(FontAttributes),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
FontAttributes.None,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnFontChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Bindable property for FontAutoScalingEnabled.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty FontAutoScalingEnabledProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FontAutoScalingEnabled),
|
|
|
|
|
typeof(bool),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
true,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnFontChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Bindable property for CharacterSpacing.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty CharacterSpacingProperty = BindableProperty.Create(
|
|
|
|
|
nameof(CharacterSpacing),
|
|
|
|
|
typeof(double),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
0.0,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).InvalidateMeasure());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Bindable property for TextDecorations.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty TextDecorationsProperty = BindableProperty.Create(
|
|
|
|
|
nameof(TextDecorations),
|
|
|
|
|
typeof(TextDecorations),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
TextDecorations.None,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for HorizontalTextAlignment.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty HorizontalTextAlignmentProperty = BindableProperty.Create(
|
|
|
|
|
nameof(HorizontalTextAlignment),
|
|
|
|
|
typeof(TextAlignment),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
TextAlignment.Start,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for VerticalTextAlignment.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is Start to match MAUI Label.VerticalTextAlignment.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty VerticalTextAlignmentProperty = BindableProperty.Create(
|
|
|
|
|
nameof(VerticalTextAlignment),
|
|
|
|
|
typeof(TextAlignment),
|
|
|
|
|
typeof(SkiaLabel),
|
2026-01-17 01:43:42 +00:00
|
|
|
TextAlignment.Start,
|
2026-01-16 04:23:47 +00:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for LineBreakMode.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty LineBreakModeProperty = BindableProperty.Create(
|
|
|
|
|
nameof(LineBreakMode),
|
|
|
|
|
typeof(LineBreakMode),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
LineBreakMode.TailTruncation,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for MaxLines.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty MaxLinesProperty = BindableProperty.Create(
|
|
|
|
|
nameof(MaxLines),
|
|
|
|
|
typeof(int),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
0,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnTextChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for LineHeight.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Default is -1 to match MAUI Label.LineHeight (platform default).
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty LineHeightProperty = BindableProperty.Create(
|
|
|
|
|
nameof(LineHeight),
|
|
|
|
|
typeof(double),
|
|
|
|
|
typeof(SkiaLabel),
|
2026-01-17 01:43:42 +00:00
|
|
|
-1.0,
|
2026-01-16 04:23:47 +00:00
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnTextChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Bindable property for TextTransform.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty TextTransformProperty = BindableProperty.Create(
|
|
|
|
|
nameof(TextTransform),
|
|
|
|
|
typeof(TextTransform),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
TextTransform.Default,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).Invalidate());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for TextType.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static readonly BindableProperty TextTypeProperty = BindableProperty.Create(
|
|
|
|
|
nameof(TextType),
|
|
|
|
|
typeof(TextType),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
TextType.Text,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnTextChanged());
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for Padding.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public static new readonly BindableProperty PaddingProperty = BindableProperty.Create(
|
|
|
|
|
nameof(Padding),
|
|
|
|
|
typeof(Thickness),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
new Thickness(0),
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).InvalidateMeasure());
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Bindable property for FormattedText.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static readonly BindableProperty FormattedTextProperty = BindableProperty.Create(
|
|
|
|
|
nameof(FormattedText),
|
|
|
|
|
typeof(FormattedString),
|
|
|
|
|
typeof(SkiaLabel),
|
|
|
|
|
null,
|
|
|
|
|
propertyChanged: (b, o, n) => ((SkiaLabel)b).OnFormattedTextChanged((FormattedString?)o, (FormattedString?)n));
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the text content.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Text
|
|
|
|
|
{
|
|
|
|
|
get => (string)GetValue(TextProperty);
|
|
|
|
|
set => SetValue(TextProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the text color.
|
2026-01-17 01:43:42 +00:00
|
|
|
/// Null means use platform default (black on Linux).
|
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 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:23:47 +00:00
|
|
|
public double FontSize
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (double)GetValue(FontSizeProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(FontSizeProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the font attributes.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public FontAttributes FontAttributes
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (FontAttributes)GetValue(FontAttributesProperty);
|
|
|
|
|
set => SetValue(FontAttributesProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets whether font auto-scaling is enabled.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public bool FontAutoScalingEnabled
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (bool)GetValue(FontAutoScalingEnabledProperty);
|
|
|
|
|
set => SetValue(FontAutoScalingEnabledProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the character spacing.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public double CharacterSpacing
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (double)GetValue(CharacterSpacingProperty);
|
|
|
|
|
set => SetValue(CharacterSpacingProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the text decorations.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public TextDecorations TextDecorations
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (TextDecorations)GetValue(TextDecorationsProperty);
|
|
|
|
|
set => SetValue(TextDecorationsProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <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 the line break mode.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public LineBreakMode LineBreakMode
|
|
|
|
|
{
|
|
|
|
|
get => (LineBreakMode)GetValue(LineBreakModeProperty);
|
|
|
|
|
set => SetValue(LineBreakModeProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the maximum number of lines.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
|
|
|
|
public int MaxLines
|
|
|
|
|
{
|
|
|
|
|
get => (int)GetValue(MaxLinesProperty);
|
|
|
|
|
set => SetValue(MaxLinesProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets the line height multiplier.
|
|
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public double LineHeight
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (double)GetValue(LineHeightProperty);
|
2025-12-21 13:26:56 -05:00
|
|
|
set => SetValue(LineHeightProperty, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the text transform.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public TextTransform TextTransform
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (TextTransform)GetValue(TextTransformProperty);
|
|
|
|
|
set => SetValue(TextTransformProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the text type.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public TextType TextType
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (TextType)GetValue(TextTypeProperty);
|
|
|
|
|
set => SetValue(TextTypeProperty, value);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the padding.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public new Thickness Padding
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (Thickness)GetValue(PaddingProperty);
|
|
|
|
|
set => SetValue(PaddingProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Gets or sets the formatted text.
|
2025-12-21 13:26:56 -05:00
|
|
|
/// </summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
public FormattedString? FormattedText
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
get => (FormattedString?)GetValue(FormattedTextProperty);
|
|
|
|
|
set => SetValue(FormattedTextProperty, value);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2026-01-16 04:39:50 +00:00
|
|
|
#region Selection State
|
|
|
|
|
|
|
|
|
|
private int _selectionStart = -1;
|
|
|
|
|
private int _selectionLength = 0;
|
|
|
|
|
private bool _isSelecting = false;
|
|
|
|
|
private DateTime _lastClickTime = DateTime.MinValue;
|
|
|
|
|
private float _lastClickX;
|
|
|
|
|
private const double DoubleClickThresholdMs = 400;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets or sets whether text selection is enabled.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public bool IsTextSelectionEnabled { get; set; } = true;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the currently selected text.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string SelectedText
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (_selectionStart < 0 || _selectionLength == 0) return string.Empty;
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
var start = Math.Min(_selectionStart, _selectionStart + _selectionLength);
|
|
|
|
|
var length = Math.Abs(_selectionLength);
|
|
|
|
|
if (start < 0 || start >= text.Length) return string.Empty;
|
|
|
|
|
return text.Substring(start, Math.Min(length, text.Length - start));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
#region Events
|
2025-12-21 13:26:56 -05:00
|
|
|
|
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
|
|
|
/// <summary>
|
2026-01-16 04:23:47 +00:00
|
|
|
/// Occurs when the label is tapped.
|
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
|
|
|
/// </summary>
|
|
|
|
|
public event EventHandler? Tapped;
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
/// <summary>
|
|
|
|
|
/// Raises the Tapped event.
|
|
|
|
|
/// </summary>
|
|
|
|
|
protected virtual void OnTapped()
|
|
|
|
|
{
|
|
|
|
|
Tapped?.Invoke(this, EventArgs.Empty);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:39:50 +00:00
|
|
|
public override void OnPointerPressed(PointerEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnPointerPressed(e);
|
|
|
|
|
|
|
|
|
|
if (!IsTextSelectionEnabled || string.IsNullOrEmpty(Text)) return;
|
|
|
|
|
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
|
|
|
|
|
|
// Calculate character position from click
|
|
|
|
|
var screenBounds = ScreenBounds;
|
2026-01-17 05:22:37 +00:00
|
|
|
var clickX = e.X - (float)screenBounds.Left - (float)Padding.Left;
|
2026-01-16 04:39:50 +00:00
|
|
|
var charIndex = GetCharacterIndexAtX(clickX);
|
|
|
|
|
|
|
|
|
|
// Check for double-click (select word)
|
|
|
|
|
var now = DateTime.UtcNow;
|
|
|
|
|
var timeSinceLastClick = (now - _lastClickTime).TotalMilliseconds;
|
|
|
|
|
var distanceFromLastClick = Math.Abs(e.X - _lastClickX);
|
|
|
|
|
|
|
|
|
|
if (timeSinceLastClick < DoubleClickThresholdMs && distanceFromLastClick < 10)
|
|
|
|
|
{
|
|
|
|
|
// Double-click: select word
|
|
|
|
|
SelectWordAt(charIndex);
|
|
|
|
|
_lastClickTime = DateTime.MinValue;
|
|
|
|
|
_isSelecting = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Single click: start selection
|
|
|
|
|
_selectionStart = charIndex;
|
|
|
|
|
_selectionLength = 0;
|
|
|
|
|
_isSelecting = true;
|
|
|
|
|
_lastClickTime = now;
|
|
|
|
|
_lastClickX = e.X;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnPointerMoved(PointerEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnPointerMoved(e);
|
|
|
|
|
|
|
|
|
|
if (!IsTextSelectionEnabled || !_isSelecting) return;
|
|
|
|
|
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
|
|
|
|
|
|
var screenBounds = ScreenBounds;
|
2026-01-17 05:22:37 +00:00
|
|
|
var clickX = e.X - (float)screenBounds.Left - (float)Padding.Left;
|
2026-01-16 04:39:50 +00:00
|
|
|
var charIndex = GetCharacterIndexAtX(clickX);
|
|
|
|
|
|
|
|
|
|
_selectionLength = charIndex - _selectionStart;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
public override void OnPointerReleased(PointerEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnPointerReleased(e);
|
2026-01-16 04:39:50 +00:00
|
|
|
|
|
|
|
|
if (_isSelecting && _selectionLength == 0)
|
|
|
|
|
{
|
|
|
|
|
// No drag happened, it's a tap
|
|
|
|
|
OnTapped();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_isSelecting = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override void OnKeyDown(KeyEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
base.OnKeyDown(e);
|
|
|
|
|
|
|
|
|
|
if (!IsTextSelectionEnabled) return;
|
|
|
|
|
|
|
|
|
|
// Ctrl+A: Select All
|
|
|
|
|
if (e.Key == Key.A && e.Modifiers.HasFlag(KeyModifiers.Control))
|
|
|
|
|
{
|
|
|
|
|
SelectAll();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
// Ctrl+C: Copy
|
|
|
|
|
else if (e.Key == Key.C && e.Modifiers.HasFlag(KeyModifiers.Control))
|
|
|
|
|
{
|
|
|
|
|
CopyToClipboard();
|
|
|
|
|
e.Handled = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Selects all text in the label.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void SelectAll()
|
|
|
|
|
{
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
_selectionStart = 0;
|
|
|
|
|
_selectionLength = text.Length;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Clears the current selection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void ClearSelection()
|
|
|
|
|
{
|
|
|
|
|
_selectionStart = -1;
|
|
|
|
|
_selectionLength = 0;
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SelectWordAt(int charIndex)
|
|
|
|
|
{
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(text) || charIndex < 0 || charIndex >= text.Length) return;
|
|
|
|
|
|
|
|
|
|
int start = charIndex;
|
|
|
|
|
int end = charIndex;
|
|
|
|
|
|
|
|
|
|
// Move start backwards to beginning of word
|
|
|
|
|
while (start > 0 && IsWordChar(text[start - 1]))
|
|
|
|
|
start--;
|
|
|
|
|
|
|
|
|
|
// Move end forwards to end of word
|
|
|
|
|
while (end < text.Length && IsWordChar(text[end]))
|
|
|
|
|
end++;
|
|
|
|
|
|
|
|
|
|
_selectionStart = start;
|
|
|
|
|
_selectionLength = end - start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool IsWordChar(char c)
|
|
|
|
|
{
|
|
|
|
|
return char.IsLetterOrDigit(c) || c == '_';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int GetCharacterIndexAtX(float x)
|
|
|
|
|
{
|
|
|
|
|
var text = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(text)) return 0;
|
|
|
|
|
|
|
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
|
|
|
|
var fontFamily = string.IsNullOrEmpty(FontFamily) ? "Sans" : FontFamily;
|
|
|
|
|
|
|
|
|
|
using var font = new SKFont(
|
|
|
|
|
SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(fontFamily, GetFontStyle()) ?? SKTypeface.Default,
|
|
|
|
|
fontSize);
|
|
|
|
|
using var paint = new SKPaint(font);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i <= text.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
var substring = text.Substring(0, i);
|
|
|
|
|
var width = paint.MeasureText(substring);
|
|
|
|
|
if (CharacterSpacing != 0 && i > 0)
|
|
|
|
|
{
|
|
|
|
|
width += (float)(CharacterSpacing * i);
|
|
|
|
|
}
|
|
|
|
|
if (width > x)
|
|
|
|
|
{
|
|
|
|
|
return i > 0 ? i - 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return text.Length;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CopyToClipboard()
|
|
|
|
|
{
|
|
|
|
|
var selectedText = SelectedText;
|
|
|
|
|
if (!string.IsNullOrEmpty(selectedText))
|
|
|
|
|
{
|
|
|
|
|
SystemClipboard.SetText(selectedText);
|
|
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Private Methods
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
private void OnTextChanged()
|
|
|
|
|
{
|
|
|
|
|
InvalidateMeasure();
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnFontChanged()
|
|
|
|
|
{
|
|
|
|
|
InvalidateMeasure();
|
|
|
|
|
Invalidate();
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void OnFormattedTextChanged(FormattedString? oldValue, FormattedString? newValue)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
if (oldValue != null)
|
|
|
|
|
{
|
|
|
|
|
oldValue.PropertyChanged -= OnFormattedTextPropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
if (newValue != null)
|
|
|
|
|
{
|
|
|
|
|
newValue.PropertyChanged += OnFormattedTextPropertyChanged;
|
|
|
|
|
}
|
|
|
|
|
OnTextChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnFormattedTextPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
OnTextChanged();
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private SKColor ToSKColor(Color? color)
|
|
|
|
|
{
|
2026-01-17 03:36:37 +00:00
|
|
|
if (color == null) return SkiaTheme.TextPrimarySK;
|
|
|
|
|
return color.ToSKColor();
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private string GetDisplayText()
|
|
|
|
|
{
|
|
|
|
|
var text = Text ?? string.Empty;
|
|
|
|
|
|
|
|
|
|
// Handle TextType.Html by stripping tags (basic implementation)
|
|
|
|
|
if (TextType == TextType.Html)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
text = System.Text.RegularExpressions.Regex.Replace(text, "<[^>]*>", "");
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
|
|
|
|
|
// Apply text transform
|
|
|
|
|
return TextTransform switch
|
|
|
|
|
{
|
|
|
|
|
TextTransform.Uppercase => text.ToUpperInvariant(),
|
|
|
|
|
TextTransform.Lowercase => text.ToLowerInvariant(),
|
|
|
|
|
_ => text
|
|
|
|
|
};
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private SKFontStyle GetFontStyle()
|
|
|
|
|
{
|
|
|
|
|
bool isBold = FontAttributes.HasFlag(FontAttributes.Bold);
|
|
|
|
|
bool isItalic = FontAttributes.HasFlag(FontAttributes.Italic);
|
|
|
|
|
|
|
|
|
|
return new SKFontStyle(
|
|
|
|
|
isBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal,
|
|
|
|
|
SKFontStyleWidth.Normal,
|
|
|
|
|
isItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
|
|
|
|
|
}
|
|
|
|
|
|
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 effective horizontal alignment for the given alignment,
|
|
|
|
|
/// accounting for FlowDirection (RTL flips Start/End).
|
|
|
|
|
/// </summary>
|
|
|
|
|
private float GetHorizontalPosition(TextAlignment alignment, float boundsLeft, float boundsRight, float textWidth)
|
|
|
|
|
{
|
|
|
|
|
bool isRtl = IsRightToLeft();
|
|
|
|
|
|
|
|
|
|
return alignment switch
|
|
|
|
|
{
|
|
|
|
|
TextAlignment.Start => isRtl ? boundsRight - textWidth : boundsLeft,
|
|
|
|
|
TextAlignment.Center => (boundsLeft + boundsRight) / 2 - textWidth / 2,
|
|
|
|
|
TextAlignment.End => isRtl ? boundsLeft : boundsRight - textWidth,
|
|
|
|
|
_ => isRtl ? boundsRight - textWidth : boundsLeft
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Drawing
|
|
|
|
|
|
2025-12-19 09:30:16 +00:00
|
|
|
protected override void OnDraw(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
var padding = Padding;
|
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
|
|
|
var contentBounds = new SKRect(
|
2026-01-16 04:23:47 +00:00
|
|
|
bounds.Left + (float)padding.Left,
|
|
|
|
|
bounds.Top + (float)padding.Top,
|
|
|
|
|
bounds.Right - (float)padding.Right,
|
|
|
|
|
bounds.Bottom - (float)padding.Bottom);
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// If we have FormattedText, draw that instead
|
|
|
|
|
if (FormattedText != null && FormattedText.Spans.Count > 0)
|
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
|
|
|
{
|
|
|
|
|
DrawFormattedText(canvas, contentBounds);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
string displayText = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(displayText)) return;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
|
|
|
|
var fontFamily = string.IsNullOrEmpty(FontFamily) ? "Sans" : FontFamily;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
using var font = new SKFont(
|
|
|
|
|
SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(fontFamily, GetFontStyle()) ?? SKTypeface.Default,
|
|
|
|
|
fontSize);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
using var paint = new SKPaint(font)
|
|
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
Color = ToSKColor(TextColor),
|
2025-12-19 09:30:16 +00:00
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Check if we need multi-line rendering
|
|
|
|
|
bool needsMultiLine = LineBreakMode == LineBreakMode.WordWrap ||
|
|
|
|
|
LineBreakMode == LineBreakMode.CharacterWrap ||
|
|
|
|
|
MaxLines > 1 ||
|
|
|
|
|
displayText.Contains('\n');
|
|
|
|
|
|
2025-12-21 13:26:56 -05:00
|
|
|
if (needsMultiLine)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-24 01:59:53 +00:00
|
|
|
var textBoundsDbg = new SKRect();
|
|
|
|
|
paint.MeasureText(displayText, ref textBoundsDbg);
|
|
|
|
|
if (displayText.StartsWith("Full XAML") || displayText.StartsWith("Shell nav"))
|
|
|
|
|
Console.WriteLine($"[Label OnDraw] '{displayText.Substring(0, Math.Min(15, displayText.Length))}' textW={textBoundsDbg.Width:F0} boundsW={contentBounds.Width:F0} LineBreakMode={LineBreakMode}");
|
2026-01-16 04:23:47 +00:00
|
|
|
DrawMultiLineText(canvas, paint, font, contentBounds, displayText);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
DrawSingleLineText(canvas, paint, contentBounds, displayText);
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void DrawSingleLineText(SKCanvas canvas, SKPaint paint, SKRect bounds, string text)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
|
|
|
|
var textBounds = new SKRect();
|
2026-01-16 04:23:47 +00:00
|
|
|
paint.MeasureText(text, ref textBounds);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
// Apply truncation if needed
|
2026-01-16 04:23:47 +00:00
|
|
|
string displayText = text;
|
|
|
|
|
float availableWidth = bounds.Width;
|
|
|
|
|
|
|
|
|
|
if (textBounds.Width > availableWidth && LineBreakMode != LineBreakMode.NoWrap)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
displayText = TruncateText(text, paint, availableWidth, LineBreakMode);
|
2025-12-19 09:30:16 +00:00
|
|
|
paint.MeasureText(displayText, ref textBounds);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Account for character spacing in measurement
|
|
|
|
|
float textWidth = textBounds.Width;
|
|
|
|
|
if (CharacterSpacing != 0 && displayText.Length > 1)
|
|
|
|
|
{
|
|
|
|
|
textWidth += (float)(CharacterSpacing * (displayText.Length - 1));
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
// Calculate position based on alignment and FlowDirection
|
|
|
|
|
float x = GetHorizontalPosition(HorizontalTextAlignment, bounds.Left, bounds.Right, textWidth);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
|
|
|
|
float y = VerticalTextAlignment switch
|
|
|
|
|
{
|
|
|
|
|
TextAlignment.Start => bounds.Top - textBounds.Top,
|
|
|
|
|
TextAlignment.Center => bounds.MidY - textBounds.MidY,
|
|
|
|
|
TextAlignment.End => bounds.Bottom - textBounds.Bottom,
|
|
|
|
|
_ => bounds.MidY - textBounds.MidY
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-16 04:39:50 +00:00
|
|
|
// Draw selection highlight if applicable
|
|
|
|
|
if (_selectionStart >= 0 && _selectionLength != 0)
|
|
|
|
|
{
|
|
|
|
|
DrawSelectionHighlight(canvas, paint, x, y, displayText, textBounds);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
DrawTextWithSpacing(canvas, displayText, x, y, paint);
|
|
|
|
|
DrawTextDecorations(canvas, paint, x, y, textBounds);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:39:50 +00:00
|
|
|
private void DrawSelectionHighlight(SKCanvas canvas, SKPaint paint, float x, float y, string text, SKRect textBounds)
|
|
|
|
|
{
|
|
|
|
|
var selStart = Math.Min(_selectionStart, _selectionStart + _selectionLength);
|
|
|
|
|
var selEnd = Math.Max(_selectionStart, _selectionStart + _selectionLength);
|
|
|
|
|
|
|
|
|
|
// Clamp to text length
|
|
|
|
|
selStart = Math.Max(0, Math.Min(selStart, text.Length));
|
|
|
|
|
selEnd = Math.Max(0, Math.Min(selEnd, text.Length));
|
|
|
|
|
|
|
|
|
|
if (selStart >= selEnd) return;
|
|
|
|
|
|
|
|
|
|
var textToStart = text.Substring(0, selStart);
|
|
|
|
|
var textToEnd = text.Substring(0, selEnd);
|
|
|
|
|
|
|
|
|
|
float startX = x + paint.MeasureText(textToStart);
|
|
|
|
|
float endX = x + paint.MeasureText(textToEnd);
|
|
|
|
|
|
|
|
|
|
if (CharacterSpacing != 0)
|
|
|
|
|
{
|
|
|
|
|
startX += (float)(CharacterSpacing * selStart);
|
|
|
|
|
endX += (float)(CharacterSpacing * selEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using var selectionPaint = new SKPaint
|
|
|
|
|
{
|
2026-01-17 03:36:37 +00:00
|
|
|
Color = SkiaTheme.PrimaryLightSK,
|
2026-01-16 04:39:50 +00:00
|
|
|
Style = SKPaintStyle.Fill
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
float selectionTop = y + textBounds.Top;
|
|
|
|
|
float selectionBottom = y + textBounds.Bottom;
|
|
|
|
|
canvas.DrawRect(new SKRect(startX, selectionTop, endX, selectionBottom), selectionPaint);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void DrawMultiLineText(SKCanvas canvas, SKPaint paint, SKFont font, SKRect bounds, string text)
|
|
|
|
|
{
|
2026-01-17 01:43:42 +00:00
|
|
|
// LineHeight -1 means platform default (use 1.0 multiplier)
|
|
|
|
|
double effectiveLineHeight = LineHeight < 0 ? 1.0 : LineHeight;
|
|
|
|
|
float lineHeight = (float)(FontSize * effectiveLineHeight);
|
2026-01-16 04:23:47 +00:00
|
|
|
float y = bounds.Top;
|
|
|
|
|
int lineCount = 0;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
var lines = WrapText(text, paint, bounds.Width);
|
|
|
|
|
|
|
|
|
|
foreach (var line in lines)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
if (MaxLines > 0 && lineCount >= MaxLines) break;
|
|
|
|
|
if (y + lineHeight > bounds.Bottom && MaxLines == 0) break;
|
|
|
|
|
|
|
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
paint.MeasureText(line, ref textBounds);
|
|
|
|
|
|
|
|
|
|
float textWidth = textBounds.Width;
|
|
|
|
|
if (CharacterSpacing != 0 && line.Length > 1)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
textWidth += (float)(CharacterSpacing * (line.Length - 1));
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
// Use FlowDirection-aware positioning
|
|
|
|
|
float x = GetHorizontalPosition(HorizontalTextAlignment, bounds.Left, bounds.Right, textWidth);
|
2026-01-16 04:23:47 +00:00
|
|
|
|
|
|
|
|
float textY = y - textBounds.Top;
|
|
|
|
|
DrawTextWithSpacing(canvas, line, x, textY, paint);
|
|
|
|
|
DrawTextDecorations(canvas, paint, x, textY, textBounds);
|
|
|
|
|
|
|
|
|
|
y += lineHeight;
|
|
|
|
|
lineCount++;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void DrawTextWithSpacing(SKCanvas canvas, string text, float x, float y, SKPaint paint)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-17 02:23:05 +00:00
|
|
|
if (string.IsNullOrEmpty(text)) return;
|
|
|
|
|
|
|
|
|
|
// Get the preferred typeface from the current paint
|
|
|
|
|
var fontFamily = string.IsNullOrEmpty(FontFamily) ? "Sans" : FontFamily;
|
|
|
|
|
var preferredTypeface = SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(fontFamily, GetFontStyle())
|
|
|
|
|
?? SKTypeface.Default;
|
|
|
|
|
|
|
|
|
|
if (CharacterSpacing == 0 || text.Length <= 1)
|
|
|
|
|
{
|
|
|
|
|
// No character spacing - use font fallback for the whole string
|
|
|
|
|
DrawTextWithFallback(canvas, text, x, y, paint, preferredTypeface);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// With character spacing, we need to draw character by character with fallback
|
|
|
|
|
float currentX = x;
|
|
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
|
|
|
|
|
|
|
|
|
// Use font fallback to get runs for proper glyph coverage
|
|
|
|
|
var runs = FontFallbackManager.Instance.ShapeTextWithFallback(text, preferredTypeface);
|
|
|
|
|
|
|
|
|
|
foreach (var run in runs)
|
|
|
|
|
{
|
|
|
|
|
// Draw each character in the run with spacing
|
|
|
|
|
foreach (char c in run.Text)
|
|
|
|
|
{
|
|
|
|
|
string charStr = c.ToString();
|
|
|
|
|
using var charFont = new SKFont(run.Typeface, fontSize);
|
|
|
|
|
using var charPaint = new SKPaint(charFont)
|
|
|
|
|
{
|
|
|
|
|
Color = paint.Color,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
canvas.DrawText(charStr, currentX, y, charPaint);
|
|
|
|
|
currentX += charPaint.MeasureText(charStr) + (float)CharacterSpacing;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Draws text with font fallback for emoji, CJK, and other scripts.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void DrawTextWithFallback(SKCanvas canvas, string text, float x, float y, SKPaint paint, SKTypeface preferredTypeface)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use FontFallbackManager for mixed-script text
|
|
|
|
|
var runs = FontFallbackManager.Instance.ShapeTextWithFallback(text, preferredTypeface);
|
|
|
|
|
|
|
|
|
|
if (runs.Count <= 1)
|
|
|
|
|
{
|
|
|
|
|
// Single run or no fallback needed - draw directly
|
|
|
|
|
canvas.DrawText(text, x, y, paint);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Multiple runs with different fonts
|
|
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
|
|
|
|
float currentX = x;
|
|
|
|
|
|
|
|
|
|
foreach (var run in runs)
|
|
|
|
|
{
|
|
|
|
|
using var runFont = new SKFont(run.Typeface, fontSize);
|
|
|
|
|
using var runPaint = new SKPaint(runFont)
|
|
|
|
|
{
|
|
|
|
|
Color = paint.Color,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
canvas.DrawText(run.Text, currentX, y, runPaint);
|
|
|
|
|
currentX += runPaint.MeasureText(run.Text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Draws formatted span text with font fallback for emoji, CJK, and other scripts.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void DrawFormattedSpanWithFallback(SKCanvas canvas, string text, float x, float y, SKPaint paint, SKTypeface preferredTypeface, float fontSize)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(text))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use FontFallbackManager for mixed-script text
|
|
|
|
|
var runs = FontFallbackManager.Instance.ShapeTextWithFallback(text, preferredTypeface);
|
|
|
|
|
|
|
|
|
|
if (runs.Count <= 1)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-17 02:23:05 +00:00
|
|
|
// Single run or no fallback needed - draw directly
|
2026-01-16 04:23:47 +00:00
|
|
|
canvas.DrawText(text, x, y, paint);
|
|
|
|
|
return;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
// Multiple runs with different fonts
|
2026-01-16 04:23:47 +00:00
|
|
|
float currentX = x;
|
2026-01-17 02:23:05 +00:00
|
|
|
|
|
|
|
|
foreach (var run in runs)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-17 02:23:05 +00:00
|
|
|
using var runFont = new SKFont(run.Typeface, fontSize);
|
|
|
|
|
using var runPaint = new SKPaint(runFont)
|
|
|
|
|
{
|
|
|
|
|
Color = paint.Color,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
canvas.DrawText(run.Text, currentX, y, runPaint);
|
|
|
|
|
currentX += runPaint.MeasureText(run.Text);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void DrawTextDecorations(SKCanvas canvas, SKPaint paint, float x, float y, SKRect textBounds)
|
|
|
|
|
{
|
|
|
|
|
if (TextDecorations == TextDecorations.None) return;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
using var linePaint = new SKPaint
|
|
|
|
|
{
|
|
|
|
|
Color = paint.Color,
|
|
|
|
|
StrokeWidth = 1,
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
float textWidth = textBounds.Width;
|
|
|
|
|
if (CharacterSpacing != 0)
|
|
|
|
|
{
|
|
|
|
|
// Approximate width adjustment for decorations
|
|
|
|
|
textWidth += (float)(CharacterSpacing * Math.Max(0, Text?.Length - 1 ?? 0));
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (TextDecorations.HasFlag(TextDecorations.Underline))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
float underlineY = y + 2;
|
|
|
|
|
canvas.DrawLine(x, underlineY, x + textWidth, underlineY, linePaint);
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (TextDecorations.HasFlag(TextDecorations.Strikethrough))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
float strikeY = y - textBounds.Height / 3;
|
|
|
|
|
canvas.DrawLine(x, strikeY, x + textWidth, strikeY, linePaint);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void DrawFormattedText(SKCanvas canvas, SKRect bounds)
|
|
|
|
|
{
|
|
|
|
|
if (FormattedText == null) return;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
float x = bounds.Left;
|
|
|
|
|
float y = bounds.Top;
|
2026-01-17 01:43:42 +00:00
|
|
|
// LineHeight -1 means platform default (use 1.0 multiplier)
|
|
|
|
|
double effectiveLineHeight = LineHeight < 0 ? 1.0 : LineHeight;
|
|
|
|
|
float lineHeight = (float)(FontSize * effectiveLineHeight);
|
2026-01-16 04:23:47 +00:00
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Calculate baseline for first line
|
|
|
|
|
using var measureFont = new SKFont(SKTypeface.Default, fontSize);
|
|
|
|
|
using var measurePaint = new SKPaint(measureFont);
|
|
|
|
|
var metrics = measurePaint.FontMetrics;
|
|
|
|
|
y -= metrics.Ascent;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
foreach (var span in FormattedText.Spans)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(span.Text)) continue;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Get span-specific styling
|
|
|
|
|
var spanFontSize = span.FontSize > 0 ? (float)span.FontSize : fontSize;
|
|
|
|
|
var spanFontFamily = !string.IsNullOrEmpty(span.FontFamily) ? span.FontFamily :
|
|
|
|
|
(!string.IsNullOrEmpty(FontFamily) ? FontFamily : "Sans");
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
bool isBold = span.FontAttributes.HasFlag(FontAttributes.Bold) ||
|
|
|
|
|
FontAttributes.HasFlag(FontAttributes.Bold);
|
|
|
|
|
bool isItalic = span.FontAttributes.HasFlag(FontAttributes.Italic) ||
|
|
|
|
|
FontAttributes.HasFlag(FontAttributes.Italic);
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
var fontStyle = new SKFontStyle(
|
|
|
|
|
isBold ? SKFontStyleWeight.Bold : SKFontStyleWeight.Normal,
|
|
|
|
|
SKFontStyleWidth.Normal,
|
|
|
|
|
isItalic ? SKFontStyleSlant.Italic : SKFontStyleSlant.Upright);
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
using var font = new SKFont(
|
|
|
|
|
SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(spanFontFamily, fontStyle) ?? SKTypeface.Default,
|
|
|
|
|
spanFontSize);
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
var spanColor = span.TextColor ?? TextColor;
|
|
|
|
|
using var paint = new SKPaint(font)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
Color = ToSKColor(spanColor),
|
|
|
|
|
IsAntialias = true
|
|
|
|
|
};
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
paint.MeasureText(span.Text, ref textBounds);
|
|
|
|
|
|
|
|
|
|
// Check if we need to wrap to next line
|
|
|
|
|
if (x + textBounds.Width > bounds.Right && x > bounds.Left)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
x = bounds.Left;
|
|
|
|
|
y += lineHeight;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-17 02:23:05 +00:00
|
|
|
// Use font fallback for this span
|
|
|
|
|
var preferredTypeface = SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(spanFontFamily, fontStyle)
|
|
|
|
|
?? SKTypeface.Default;
|
|
|
|
|
DrawFormattedSpanWithFallback(canvas, span.Text, x, y, paint, preferredTypeface, spanFontSize);
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Draw span decorations
|
|
|
|
|
if (span.TextDecorations != TextDecorations.None)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
using var linePaint = new SKPaint { Color = paint.Color, StrokeWidth = 1, IsAntialias = true };
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (span.TextDecorations.HasFlag(TextDecorations.Underline))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
canvas.DrawLine(x, y + 2, x + textBounds.Width, y + 2, linePaint);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
if (span.TextDecorations.HasFlag(TextDecorations.Strikethrough))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
float strikeY = y - textBounds.Height / 3;
|
|
|
|
|
canvas.DrawLine(x, strikeY, x + textBounds.Width, strikeY, linePaint);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
x += textBounds.Width;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private string TruncateText(string text, SKPaint paint, float maxWidth, LineBreakMode mode)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
if (string.IsNullOrEmpty(text)) return text;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
var bounds = new SKRect();
|
|
|
|
|
paint.MeasureText(text, ref bounds);
|
|
|
|
|
if (bounds.Width <= maxWidth) return text;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
string ellipsis = "...";
|
|
|
|
|
float ellipsisWidth = paint.MeasureText(ellipsis);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
switch (mode)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
case LineBreakMode.HeadTruncation:
|
|
|
|
|
for (int i = 1; i < text.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
string truncated = ellipsis + text.Substring(i);
|
|
|
|
|
if (paint.MeasureText(truncated) <= maxWidth)
|
|
|
|
|
return truncated;
|
|
|
|
|
}
|
|
|
|
|
return ellipsis;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
case LineBreakMode.MiddleTruncation:
|
|
|
|
|
int half = text.Length / 2;
|
|
|
|
|
for (int i = 0; i < half; i++)
|
|
|
|
|
{
|
|
|
|
|
string truncated = text.Substring(0, half - i) + ellipsis + text.Substring(half + i);
|
|
|
|
|
if (paint.MeasureText(truncated) <= maxWidth)
|
|
|
|
|
return truncated;
|
|
|
|
|
}
|
|
|
|
|
return ellipsis;
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
case LineBreakMode.TailTruncation:
|
|
|
|
|
default:
|
|
|
|
|
for (int i = text.Length - 1; i > 0; i--)
|
|
|
|
|
{
|
|
|
|
|
string truncated = text.Substring(0, i) + ellipsis;
|
|
|
|
|
if (paint.MeasureText(truncated) <= maxWidth)
|
|
|
|
|
return truncated;
|
|
|
|
|
}
|
|
|
|
|
return ellipsis;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private List<string> WrapText(string text, SKPaint paint, float maxWidth)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
var lines = new List<string>();
|
|
|
|
|
if (string.IsNullOrEmpty(text)) return lines;
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Split by existing newlines first
|
|
|
|
|
var paragraphs = text.Split('\n');
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
foreach (var paragraph in paragraphs)
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
if (string.IsNullOrEmpty(paragraph))
|
|
|
|
|
{
|
|
|
|
|
lines.Add(string.Empty);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (LineBreakMode == LineBreakMode.CharacterWrap)
|
|
|
|
|
{
|
|
|
|
|
WrapByCharacter(paragraph, paint, maxWidth, lines);
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
else
|
2026-01-16 04:23:47 +00:00
|
|
|
{
|
|
|
|
|
WrapByWord(paragraph, paint, maxWidth, lines);
|
|
|
|
|
}
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
return lines;
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void WrapByWord(string text, SKPaint paint, float maxWidth, List<string> lines)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
var words = text.Split(' ');
|
|
|
|
|
string currentLine = "";
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
foreach (var word in words)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
string testLine = string.IsNullOrEmpty(currentLine) ? word : currentLine + " " + word;
|
|
|
|
|
float width = paint.MeasureText(testLine);
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (width > maxWidth && !string.IsNullOrEmpty(currentLine))
|
|
|
|
|
{
|
|
|
|
|
lines.Add(currentLine);
|
|
|
|
|
currentLine = word;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
else
|
2026-01-16 04:23:47 +00:00
|
|
|
{
|
|
|
|
|
currentLine = testLine;
|
|
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (!string.IsNullOrEmpty(currentLine))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
lines.Add(currentLine);
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
private void WrapByCharacter(string text, SKPaint paint, float maxWidth, List<string> lines)
|
|
|
|
|
{
|
|
|
|
|
string currentLine = "";
|
2025-12-19 09:30:16 +00:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
foreach (char c in text)
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
string testLine = currentLine + c;
|
|
|
|
|
float width = paint.MeasureText(testLine);
|
2025-12-21 13:26:56 -05:00
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (width > maxWidth && !string.IsNullOrEmpty(currentLine))
|
2025-12-21 13:26:56 -05:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
lines.Add(currentLine);
|
|
|
|
|
currentLine = c.ToString();
|
2025-12-21 13:26:56 -05:00
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
else
|
2025-12-19 09:30:16 +00:00
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
currentLine = testLine;
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (!string.IsNullOrEmpty(currentLine))
|
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
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
lines.Add(currentLine);
|
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
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
#endregion
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
#region Measurement
|
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
|
|
|
|
2026-01-17 05:22:37 +00:00
|
|
|
protected override Size MeasureOverride(Size availableSize)
|
2026-01-16 04:23:47 +00:00
|
|
|
{
|
|
|
|
|
var padding = Padding;
|
2026-01-17 05:22:37 +00:00
|
|
|
double paddingH = padding.Left + padding.Right;
|
|
|
|
|
double paddingV = padding.Top + padding.Bottom;
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
string displayText = GetDisplayText();
|
|
|
|
|
if (string.IsNullOrEmpty(displayText) && (FormattedText == null || FormattedText.Spans.Count == 0))
|
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
|
|
|
{
|
2026-01-17 05:22:37 +00:00
|
|
|
return new Size(paddingH, paddingV + FontSize);
|
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
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
float fontSize = FontSize > 0 ? (float)FontSize : 14f;
|
|
|
|
|
var fontFamily = string.IsNullOrEmpty(FontFamily) ? "Sans" : FontFamily;
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
using var font = new SKFont(
|
|
|
|
|
SkiaRenderingEngine.Current?.ResourceCache.GetTypeface(fontFamily, GetFontStyle()) ?? SKTypeface.Default,
|
|
|
|
|
fontSize);
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
using var paint = new SKPaint(font);
|
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
|
|
|
|
2026-01-17 05:22:37 +00:00
|
|
|
double width, height;
|
2026-01-17 01:43:42 +00:00
|
|
|
// LineHeight -1 means platform default (use 1.0 multiplier)
|
|
|
|
|
double effectiveLineHeight = LineHeight < 0 ? 1.0 : LineHeight;
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
if (FormattedText != null && FormattedText.Spans.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
// Measure formatted text
|
|
|
|
|
width = 0;
|
2026-01-17 05:22:37 +00:00
|
|
|
height = fontSize * effectiveLineHeight;
|
2026-01-16 04:23:47 +00:00
|
|
|
foreach (var span in FormattedText.Spans)
|
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
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
if (!string.IsNullOrEmpty(span.Text))
|
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
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
width += paint.MeasureText(span.Text);
|
|
|
|
|
}
|
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
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
var textBounds = new SKRect();
|
|
|
|
|
paint.MeasureText(displayText, ref textBounds);
|
|
|
|
|
width = textBounds.Width;
|
|
|
|
|
height = textBounds.Height;
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Account for character spacing
|
|
|
|
|
if (CharacterSpacing != 0 && displayText.Length > 1)
|
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
|
|
|
{
|
2026-01-17 05:22:37 +00:00
|
|
|
width += CharacterSpacing * (displayText.Length - 1);
|
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
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Account for multi-line
|
|
|
|
|
if (displayText.Contains('\n') || MaxLines > 1)
|
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
|
|
|
{
|
2026-01-16 04:23:47 +00:00
|
|
|
var lines = displayText.Split('\n');
|
|
|
|
|
int lineCount = MaxLines > 0 ? Math.Min(lines.Length, MaxLines) : lines.Length;
|
2026-01-17 05:22:37 +00:00
|
|
|
height = lineCount * fontSize * effectiveLineHeight;
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
width += paddingH;
|
|
|
|
|
height += paddingV;
|
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
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
// Respect explicit size requests
|
|
|
|
|
if (WidthRequest >= 0)
|
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
|
|
|
{
|
2026-01-17 05:22:37 +00:00
|
|
|
width = WidthRequest;
|
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
|
|
|
}
|
2026-01-16 04:23:47 +00:00
|
|
|
if (HeightRequest >= 0)
|
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
|
|
|
{
|
2026-01-17 05:22:37 +00:00
|
|
|
height = HeightRequest;
|
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
|
|
|
}
|
|
|
|
|
|
2026-01-17 05:22:37 +00:00
|
|
|
return new Size(Math.Max(width, 1.0), Math.Max(height, 1.0));
|
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
|
|
|
}
|
|
|
|
|
|
2026-01-16 04:23:47 +00:00
|
|
|
#endregion
|
2025-12-19 09:30:16 +00:00
|
|
|
}
|
|
|
|
|
|