Update with recovered code from VM binaries (Jan 1)

Recovered from decompiled OpenMaui.Controls.Linux.dll:
- SkiaShell.cs: FlyoutHeader, FlyoutFooter, scroll support (918 -> 1325 lines)
- X11Window.cs: Cursor support (XCreateFontCursor, XDefineCursor)
- All handlers with dark mode support
- All services with latest implementations
- LinuxApplication with theme change handling
This commit is contained in:
2026-01-01 06:22:48 -05:00
parent 1e84c6168a
commit 1f096c38dc
254 changed files with 49359 additions and 38457 deletions

View File

@@ -1,35 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ActivityIndicatorHandler : ViewHandler<IActivityIndicator, SkiaActivityIndicator>
/// Handler for ActivityIndicator on Linux using Skia rendering.
/// Maps IActivityIndicator interface to SkiaActivityIndicator platform view.
/// </summary>
public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator, SkiaActivityIndicator>
{ {
public static IPropertyMapper<IActivityIndicator, ActivityIndicatorHandler> Mapper = new PropertyMapper<IActivityIndicator, ActivityIndicatorHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IActivityIndicator, ActivityIndicatorHandler> Mapper = (IPropertyMapper<IActivityIndicator, ActivityIndicatorHandler>)(object)new PropertyMapper<IActivityIndicator, ActivityIndicatorHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IActivityIndicator.IsRunning)] = MapIsRunning, ["IsRunning"] = MapIsRunning,
[nameof(IActivityIndicator.Color)] = MapColor, ["Color"] = MapColor,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<IActivityIndicator, ActivityIndicatorHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IActivityIndicator, ActivityIndicatorHandler> CommandMapper = new CommandMapper<IActivityIndicator, ActivityIndicatorHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ActivityIndicatorHandler() : base(Mapper, CommandMapper) public ActivityIndicatorHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ActivityIndicatorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ActivityIndicatorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -40,25 +31,32 @@ public partial class ActivityIndicatorHandler : ViewHandler<IActivityIndicator,
public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator) public static void MapIsRunning(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView != null)
handler.PlatformView.IsRunning = activityIndicator.IsRunning; {
((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView.IsRunning = activityIndicator.IsRunning;
}
} }
public static void MapColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator) public static void MapColor(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView != null && activityIndicator.Color != null)
if (activityIndicator.Color is not null) {
handler.PlatformView.Color = activityIndicator.Color.ToSKColor(); ((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView.Color = activityIndicator.Color.ToSKColor();
}
} }
public static void MapBackground(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator) public static void MapBackground(ActivityIndicatorHandler handler, IActivityIndicator activityIndicator)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView != null)
if (activityIndicator.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)activityIndicator).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IActivityIndicator, SkiaActivityIndicator>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
} }

View File

@@ -1,36 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform.Linux.Hosting;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ApplicationHandler : ElementHandler<IApplication, LinuxApplicationContext>
/// Handler for MAUI Application on Linux.
/// Bridges the MAUI Application lifecycle with LinuxApplication.
/// </summary>
public partial class ApplicationHandler : ElementHandler<IApplication, LinuxApplicationContext>
{ {
public static IPropertyMapper<IApplication, ApplicationHandler> Mapper = public static IPropertyMapper<IApplication, ApplicationHandler> Mapper = (IPropertyMapper<IApplication, ApplicationHandler>)(object)new PropertyMapper<IApplication, ApplicationHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ElementHandler.ElementMapper });
new PropertyMapper<IApplication, ApplicationHandler>(ElementHandler.ElementMapper)
public static CommandMapper<IApplication, ApplicationHandler> CommandMapper = new CommandMapper<IApplication, ApplicationHandler>((CommandMapper)(object)ElementHandler.ElementCommandMapper)
{ {
["OpenWindow"] = MapOpenWindow,
["CloseWindow"] = MapCloseWindow
}; };
public static CommandMapper<IApplication, ApplicationHandler> CommandMapper = public ApplicationHandler()
new(ElementHandler.ElementCommandMapper) : base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{
[nameof(IApplication.OpenWindow)] = MapOpenWindow,
[nameof(IApplication.CloseWindow)] = MapCloseWindow,
};
public ApplicationHandler() : base(Mapper, CommandMapper)
{ {
} }
public ApplicationHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ApplicationHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -42,7 +30,7 @@ public partial class ApplicationHandler : ElementHandler<IApplication, LinuxAppl
protected override void ConnectHandler(LinuxApplicationContext platformView) protected override void ConnectHandler(LinuxApplicationContext platformView)
{ {
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.Application = VirtualView; platformView.Application = base.VirtualView;
} }
protected override void DisconnectHandler(LinuxApplicationContext platformView) protected override void DisconnectHandler(LinuxApplicationContext platformView)
@@ -53,85 +41,19 @@ public partial class ApplicationHandler : ElementHandler<IApplication, LinuxAppl
public static void MapOpenWindow(ApplicationHandler handler, IApplication application, object? args) public static void MapOpenWindow(ApplicationHandler handler, IApplication application, object? args)
{ {
if (args is IWindow window) IWindow val = (IWindow)((args is IWindow) ? args : null);
if (val != null)
{ {
handler.PlatformView?.OpenWindow(window); ((ElementHandler<IApplication, LinuxApplicationContext>)(object)handler).PlatformView?.OpenWindow(val);
} }
} }
public static void MapCloseWindow(ApplicationHandler handler, IApplication application, object? args) public static void MapCloseWindow(ApplicationHandler handler, IApplication application, object? args)
{ {
if (args is IWindow window) IWindow val = (IWindow)((args is IWindow) ? args : null);
if (val != null)
{ {
handler.PlatformView?.CloseWindow(window); ((ElementHandler<IApplication, LinuxApplicationContext>)(object)handler).PlatformView?.CloseWindow(val);
} }
} }
} }
/// <summary>
/// Platform context for the MAUI Application on Linux.
/// Manages windows and the application lifecycle.
/// </summary>
public class LinuxApplicationContext
{
private readonly List<IWindow> _windows = new();
private IApplication? _application;
/// <summary>
/// Gets or sets the MAUI Application.
/// </summary>
public IApplication? Application
{
get => _application;
set
{
_application = value;
if (_application != null)
{
// Initialize windows from the application
foreach (var window in _application.Windows)
{
if (!_windows.Contains(window))
{
_windows.Add(window);
}
}
}
}
}
/// <summary>
/// Gets the list of open windows.
/// </summary>
public IReadOnlyList<IWindow> Windows => _windows;
/// <summary>
/// Opens a window and creates its handler.
/// </summary>
public void OpenWindow(IWindow window)
{
if (!_windows.Contains(window))
{
_windows.Add(window);
}
}
/// <summary>
/// Closes a window and cleans up its handler.
/// </summary>
public void CloseWindow(IWindow window)
{
_windows.Remove(window);
if (_windows.Count == 0)
{
// Last window closed, stop the application
LinuxApplication.Current?.MainWindow?.Stop();
}
}
/// <summary>
/// Gets the main window of the application.
/// </summary>
public IWindow? MainWindow => _windows.Count > 0 ? _windows[0] : null;
}

View File

@@ -1,42 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Controls.Shapes;
using SkiaSharp; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class BorderHandler : ViewHandler<IBorderView, SkiaBorder>
/// Handler for Border on Linux using Skia rendering.
/// </summary>
public partial class BorderHandler : ViewHandler<IBorderView, SkiaBorder>
{ {
public static IPropertyMapper<IBorderView, BorderHandler> Mapper = public static IPropertyMapper<IBorderView, BorderHandler> Mapper = (IPropertyMapper<IBorderView, BorderHandler>)(object)new PropertyMapper<IBorderView, BorderHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IBorderView, BorderHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IBorderView.Content)] = MapContent, ["Content"] = MapContent,
[nameof(IBorderStroke.Stroke)] = MapStroke, ["Stroke"] = MapStroke,
[nameof(IBorderStroke.StrokeThickness)] = MapStrokeThickness, ["StrokeThickness"] = MapStrokeThickness,
["StrokeShape"] = MapStrokeShape, // StrokeShape is on Border, not IBorderStroke ["StrokeShape"] = MapStrokeShape,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor, ["BackgroundColor"] = MapBackgroundColor,
[nameof(IPadding.Padding)] = MapPadding, ["Padding"] = MapPadding
}; };
public static CommandMapper<IBorderView, BorderHandler> CommandMapper = public static CommandMapper<IBorderView, BorderHandler> CommandMapper = new CommandMapper<IBorderView, BorderHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public BorderHandler() : base(Mapper, CommandMapper) public BorderHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public BorderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public BorderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -48,111 +40,147 @@ public partial class BorderHandler : ViewHandler<IBorderView, SkiaBorder>
protected override void ConnectHandler(SkiaBorder platformView) protected override void ConnectHandler(SkiaBorder platformView)
{ {
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
IBorderView virtualView = base.VirtualView;
View val = (View)(object)((virtualView is View) ? virtualView : null);
if (val != null)
{
platformView.MauiView = val;
}
platformView.Tapped += OnPlatformViewTapped;
} }
protected override void DisconnectHandler(SkiaBorder platformView) protected override void DisconnectHandler(SkiaBorder platformView)
{ {
platformView.Tapped -= OnPlatformViewTapped;
platformView.MauiView = null;
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnPlatformViewTapped(object? sender, EventArgs e)
{
IBorderView virtualView = base.VirtualView;
View val = (View)(object)((virtualView is View) ? virtualView : null);
if (val != null)
{
GestureManager.ProcessTap(val, 0.0, 0.0);
}
}
public static void MapContent(BorderHandler handler, IBorderView border) public static void MapContent(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null || handler.MauiContext is null) return; if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
handler.PlatformView.ClearChildren();
var content = border.PresentedContent;
if (content != null)
{ {
// Create handler for content if it doesn't exist return;
if (content.Handler == null)
{
Console.WriteLine($"[BorderHandler] Creating handler for content: {content.GetType().Name}");
content.Handler = content.ToHandler(handler.MauiContext);
} }
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.ClearChildren();
if (content.Handler?.PlatformView is SkiaView skiaContent) IView presentedContent = ((IContentView)border).PresentedContent;
if (presentedContent != null)
{ {
Console.WriteLine($"[BorderHandler] Adding content: {skiaContent.GetType().Name}"); if (presentedContent.Handler == null)
handler.PlatformView.AddChild(skiaContent); {
Console.WriteLine("[BorderHandler] Creating handler for content: " + ((object)presentedContent).GetType().Name);
presentedContent.Handler = presentedContent.ToViewHandler(((ElementHandler)handler).MauiContext);
}
IViewHandler handler2 = presentedContent.Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView skiaView)
{
Console.WriteLine("[BorderHandler] Adding content: " + ((object)skiaView).GetType().Name);
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.AddChild(skiaView);
} }
} }
} }
public static void MapStroke(BorderHandler handler, IBorderView border) public static void MapStroke(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView != null)
if (border.Stroke is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.Stroke = solidPaint.Color.ToSKColor(); Paint stroke = ((IStroke)border).Stroke;
SolidPaint val = (SolidPaint)(object)((stroke is SolidPaint) ? stroke : null);
if (val != null && val.Color != null)
{
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.Stroke = val.Color.ToSKColor();
}
} }
} }
public static void MapStrokeThickness(BorderHandler handler, IBorderView border) public static void MapStrokeThickness(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView != null)
handler.PlatformView.StrokeThickness = (float)border.StrokeThickness; {
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.StrokeThickness = (float)((IStroke)border).StrokeThickness;
}
} }
public static void MapBackground(BorderHandler handler, IBorderView border) public static void MapBackground(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView != null)
if (border.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)border).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapBackgroundColor(BorderHandler handler, IBorderView border) public static void MapBackgroundColor(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; //IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView != null)
if (border is VisualElement ve && ve.BackgroundColor != null)
{ {
handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor(); VisualElement val = (VisualElement)(object)((border is VisualElement) ? border : null);
handler.PlatformView.Invalidate(); if (val != null && val.BackgroundColor != null)
{
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.BackgroundColor = val.BackgroundColor.ToSKColor();
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.Invalidate();
}
} }
} }
public static void MapPadding(BorderHandler handler, IBorderView border) public static void MapPadding(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var padding = border.Padding; if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView != null)
handler.PlatformView.PaddingLeft = (float)padding.Left; {
handler.PlatformView.PaddingTop = (float)padding.Top; Thickness padding = ((IPadding)border).Padding;
handler.PlatformView.PaddingRight = (float)padding.Right; ((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.PaddingLeft = (float)((Thickness)(ref padding)).Left;
handler.PlatformView.PaddingBottom = (float)padding.Bottom; ((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.PaddingTop = (float)((Thickness)(ref padding)).Top;
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.PaddingRight = (float)((Thickness)(ref padding)).Right;
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.PaddingBottom = (float)((Thickness)(ref padding)).Bottom;
}
} }
public static void MapStrokeShape(BorderHandler handler, IBorderView border) public static void MapStrokeShape(BorderHandler handler, IBorderView border)
{ {
if (handler.PlatformView is null) return; //IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
// StrokeShape is on the Border control class, not IBorderView interface if (((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView == null)
if (border is not Border borderControl) return;
var shape = borderControl.StrokeShape;
if (shape is Microsoft.Maui.Controls.Shapes.RoundRectangle roundRect)
{ {
// RoundRectangle can have different corner radii, but we use a uniform one return;
// Take the top-left corner as the uniform radius
var cornerRadius = roundRect.CornerRadius;
handler.PlatformView.CornerRadius = (float)cornerRadius.TopLeft;
} }
else if (shape is Microsoft.Maui.Controls.Shapes.Rectangle) Border val = (Border)(object)((border is Border) ? border : null);
if (val != null)
{ {
handler.PlatformView.CornerRadius = 0; IShape strokeShape = val.StrokeShape;
} RoundRectangle val2 = (RoundRectangle)(object)((strokeShape is RoundRectangle) ? strokeShape : null);
else if (shape is Microsoft.Maui.Controls.Shapes.Ellipse) if (val2 != null)
{ {
// For ellipse, use half the min dimension as corner radius CornerRadius cornerRadius = val2.CornerRadius;
// This will be applied during rendering when bounds are known ((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.CornerRadius = (float)((CornerRadius)(ref cornerRadius)).TopLeft;
handler.PlatformView.CornerRadius = float.MaxValue; // Marker for "fully rounded" }
else if (strokeShape is Rectangle)
{
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.CornerRadius = 0f;
}
else if (strokeShape is Ellipse)
{
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.CornerRadius = float.MaxValue;
}
((ViewHandler<IBorderView, SkiaBorder>)(object)handler).PlatformView.Invalidate();
} }
handler.PlatformView.Invalidate();
} }
} }

View File

@@ -1,27 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class BoxViewHandler : ViewHandler<BoxView, SkiaBoxView>
/// Handler for BoxView on Linux.
/// </summary>
public partial class BoxViewHandler : ViewHandler<BoxView, SkiaBoxView>
{ {
public static IPropertyMapper<BoxView, BoxViewHandler> Mapper = public static IPropertyMapper<BoxView, BoxViewHandler> Mapper = (IPropertyMapper<BoxView, BoxViewHandler>)(object)new PropertyMapper<BoxView, BoxViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<BoxView, BoxViewHandler>(ViewMapper)
{ {
[nameof(BoxView.Color)] = MapColor, ["Color"] = MapColor,
[nameof(BoxView.CornerRadius)] = MapCornerRadius, ["CornerRadius"] = MapCornerRadius,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor, ["BackgroundColor"] = MapBackgroundColor
}; };
public BoxViewHandler() : base(Mapper) public BoxViewHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)null)
{ {
} }
@@ -32,36 +26,41 @@ public partial class BoxViewHandler : ViewHandler<BoxView, SkiaBoxView>
public static void MapColor(BoxViewHandler handler, BoxView boxView) public static void MapColor(BoxViewHandler handler, BoxView boxView)
{ {
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
if (boxView.Color != null) if (boxView.Color != null)
{ {
handler.PlatformView.Color = new SKColor( ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView.Color = new SKColor((byte)(boxView.Color.Red * 255f), (byte)(boxView.Color.Green * 255f), (byte)(boxView.Color.Blue * 255f), (byte)(boxView.Color.Alpha * 255f));
(byte)(boxView.Color.Red * 255),
(byte)(boxView.Color.Green * 255),
(byte)(boxView.Color.Blue * 255),
(byte)(boxView.Color.Alpha * 255));
} }
} }
public static void MapCornerRadius(BoxViewHandler handler, BoxView boxView) public static void MapCornerRadius(BoxViewHandler handler, BoxView boxView)
{ {
handler.PlatformView.CornerRadius = (float)boxView.CornerRadius.TopLeft; //IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
SkiaBoxView platformView = ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView;
CornerRadius cornerRadius = boxView.CornerRadius;
platformView.CornerRadius = (float)((CornerRadius)(ref cornerRadius)).TopLeft;
} }
public static void MapBackground(BoxViewHandler handler, BoxView boxView) public static void MapBackground(BoxViewHandler handler, BoxView boxView)
{ {
if (boxView.Background is SolidColorBrush solidBrush && solidBrush.Color != null) //IL_0023: Unknown result type (might be due to invalid IL or missing references)
Brush background = ((VisualElement)boxView).Background;
SolidColorBrush val = (SolidColorBrush)(object)((background is SolidColorBrush) ? background : null);
if (val != null && val.Color != null)
{ {
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor(); ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
handler.PlatformView.Invalidate(); ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView.Invalidate();
} }
} }
public static void MapBackgroundColor(BoxViewHandler handler, BoxView boxView) public static void MapBackgroundColor(BoxViewHandler handler, BoxView boxView)
{ {
if (boxView.BackgroundColor != null) //IL_0014: Unknown result type (might be due to invalid IL or missing references)
if (((VisualElement)boxView).BackgroundColor != null)
{ {
handler.PlatformView.BackgroundColor = boxView.BackgroundColor.ToSKColor(); ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView.BackgroundColor = ((VisualElement)boxView).BackgroundColor.ToSKColor();
handler.PlatformView.Invalidate(); ((ViewHandler<BoxView, SkiaBoxView>)(object)handler).PlatformView.Invalidate();
} }
} }
} }

View File

@@ -1,45 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ButtonHandler : ViewHandler<IButton, SkiaButton>
/// Handler for Button on Linux using Skia rendering.
/// Maps IButton interface to SkiaButton platform view.
/// </summary>
public partial class ButtonHandler : ViewHandler<IButton, SkiaButton>
{ {
public static IPropertyMapper<IButton, ButtonHandler> Mapper = new PropertyMapper<IButton, ButtonHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IButton, ButtonHandler> Mapper = (IPropertyMapper<IButton, ButtonHandler>)(object)new PropertyMapper<IButton, ButtonHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IButtonStroke.StrokeColor)] = MapStrokeColor, ["StrokeColor"] = MapStrokeColor,
[nameof(IButtonStroke.StrokeThickness)] = MapStrokeThickness, ["StrokeThickness"] = MapStrokeThickness,
[nameof(IButtonStroke.CornerRadius)] = MapCornerRadius, ["CornerRadius"] = MapCornerRadius,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
[nameof(IPadding.Padding)] = MapPadding, ["Padding"] = MapPadding,
[nameof(IView.IsEnabled)] = MapIsEnabled, ["IsEnabled"] = MapIsEnabled
}; };
public static CommandMapper<IButton, ButtonHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IButton, ButtonHandler> CommandMapper = new CommandMapper<IButton, ButtonHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ButtonHandler() : base(Mapper, CommandMapper) public ButtonHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
protected override SkiaButton CreatePlatformView() protected override SkiaButton CreatePlatformView()
{ {
var button = new SkiaButton(); return new SkiaButton();
return button;
} }
protected override void ConnectHandler(SkiaButton platformView) protected override void ConnectHandler(SkiaButton platformView)
@@ -48,17 +40,14 @@ public partial class ButtonHandler : ViewHandler<IButton, SkiaButton>
platformView.Clicked += OnClicked; platformView.Clicked += OnClicked;
platformView.Pressed += OnPressed; platformView.Pressed += OnPressed;
platformView.Released += OnReleased; platformView.Released += OnReleased;
if (base.VirtualView != null)
// Manually map all properties on connect since MAUI may not trigger updates
// for properties that were set before handler connection
if (VirtualView != null)
{ {
MapStrokeColor(this, VirtualView); MapStrokeColor(this, base.VirtualView);
MapStrokeThickness(this, VirtualView); MapStrokeThickness(this, base.VirtualView);
MapCornerRadius(this, VirtualView); MapCornerRadius(this, base.VirtualView);
MapBackground(this, VirtualView); MapBackground(this, base.VirtualView);
MapPadding(this, VirtualView); MapPadding(this, base.VirtualView);
MapIsEnabled(this, VirtualView); MapIsEnabled(this, base.VirtualView);
} }
} }
@@ -70,129 +59,95 @@ public partial class ButtonHandler : ViewHandler<IButton, SkiaButton>
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnClicked(object? sender, EventArgs e) => VirtualView?.Clicked(); private void OnClicked(object? sender, EventArgs e)
private void OnPressed(object? sender, EventArgs e) => VirtualView?.Pressed(); {
private void OnReleased(object? sender, EventArgs e) => VirtualView?.Released(); IButton virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.Clicked();
}
}
private void OnPressed(object? sender, EventArgs e)
{
IButton virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.Pressed();
}
}
private void OnReleased(object? sender, EventArgs e)
{
IButton virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.Released();
}
}
public static void MapStrokeColor(ButtonHandler handler, IButton button) public static void MapStrokeColor(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; //IL_001a: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
var strokeColor = button.StrokeColor; {
if (strokeColor is not null) Color strokeColor = ((IButtonStroke)button).StrokeColor;
handler.PlatformView.BorderColor = strokeColor.ToSKColor(); if (strokeColor != null)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.BorderColor = strokeColor.ToSKColor();
}
}
} }
public static void MapStrokeThickness(ButtonHandler handler, IButton button) public static void MapStrokeThickness(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
handler.PlatformView.BorderWidth = (float)button.StrokeThickness; {
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.BorderWidth = (float)((IButtonStroke)button).StrokeThickness;
}
} }
public static void MapCornerRadius(ButtonHandler handler, IButton button) public static void MapCornerRadius(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
handler.PlatformView.CornerRadius = button.CornerRadius; {
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.CornerRadius = ((IButtonStroke)button).CornerRadius;
}
} }
public static void MapBackground(ButtonHandler handler, IButton button) public static void MapBackground(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
if (button.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
// Set ButtonBackgroundColor (used for rendering) not base BackgroundColor Paint background = ((IView)button).Background;
handler.PlatformView.ButtonBackgroundColor = solidPaint.Color.ToSKColor(); SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.ButtonBackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapPadding(ButtonHandler handler, IButton button) public static void MapPadding(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var padding = button.Padding; //IL_0036: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.Padding = new SKRect( if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
(float)padding.Left, {
(float)padding.Top, Thickness padding = ((IPadding)button).Padding;
(float)padding.Right, ((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.Padding = new SKRect((float)((Thickness)(ref padding)).Left, (float)((Thickness)(ref padding)).Top, (float)((Thickness)(ref padding)).Right, (float)((Thickness)(ref padding)).Bottom);
(float)padding.Bottom); }
} }
public static void MapIsEnabled(ButtonHandler handler, IButton button) public static void MapIsEnabled(ButtonHandler handler, IButton button)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
Console.WriteLine($"[ButtonHandler] MapIsEnabled - Text='{handler.PlatformView.Text}', IsEnabled={button.IsEnabled}"); {
handler.PlatformView.IsEnabled = button.IsEnabled; Console.WriteLine($"[ButtonHandler] MapIsEnabled - Text='{((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.Text}', IsEnabled={((IView)button).IsEnabled}");
handler.PlatformView.Invalidate(); ((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.IsEnabled = ((IView)button).IsEnabled;
} ((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.Invalidate();
} }
/// <summary>
/// Handler for TextButton on Linux - extends ButtonHandler with text support.
/// Maps ITextButton interface (which includes IText properties).
/// </summary>
public partial class TextButtonHandler : ButtonHandler
{
public static new IPropertyMapper<ITextButton, TextButtonHandler> Mapper =
new PropertyMapper<ITextButton, TextButtonHandler>(ButtonHandler.Mapper)
{
[nameof(IText.Text)] = MapText,
[nameof(ITextStyle.TextColor)] = MapTextColor,
[nameof(ITextStyle.Font)] = MapFont,
[nameof(ITextStyle.CharacterSpacing)] = MapCharacterSpacing,
};
public TextButtonHandler() : base(Mapper)
{
}
protected override void ConnectHandler(SkiaButton platformView)
{
base.ConnectHandler(platformView);
// Manually map text properties on connect since MAUI may not trigger updates
// for properties that were set before handler connection
if (VirtualView is ITextButton textButton)
{
MapText(this, textButton);
MapTextColor(this, textButton);
MapFont(this, textButton);
MapCharacterSpacing(this, textButton);
}
}
public static void MapText(TextButtonHandler handler, ITextButton button)
{
if (handler.PlatformView is null) return;
handler.PlatformView.Text = button.Text ?? string.Empty;
}
public static void MapTextColor(TextButtonHandler handler, ITextButton button)
{
if (handler.PlatformView is null) return;
if (button.TextColor is not null)
handler.PlatformView.TextColor = button.TextColor.ToSKColor();
}
public static void MapFont(TextButtonHandler handler, ITextButton button)
{
if (handler.PlatformView is null) return;
var font = button.Font;
if (font.Size > 0)
handler.PlatformView.FontSize = (float)font.Size;
if (!string.IsNullOrEmpty(font.Family))
handler.PlatformView.FontFamily = font.Family;
handler.PlatformView.IsBold = font.Weight >= FontWeight.Bold;
handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic || font.Slant == FontSlant.Oblique;
}
public static void MapCharacterSpacing(TextButtonHandler handler, ITextButton button)
{
if (handler.PlatformView is null) return;
handler.PlatformView.CharacterSpacing = (float)button.CharacterSpacing;
} }
} }

View File

@@ -1,37 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements. using Microsoft.Maui.Controls;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Primitives;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class CheckBoxHandler : ViewHandler<ICheckBox, SkiaCheckBox>
/// Handler for CheckBox on Linux using Skia rendering.
/// Maps ICheckBox interface to SkiaCheckBox platform view.
/// </summary>
public partial class CheckBoxHandler : ViewHandler<ICheckBox, SkiaCheckBox>
{ {
public static IPropertyMapper<ICheckBox, CheckBoxHandler> Mapper = new PropertyMapper<ICheckBox, CheckBoxHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ICheckBox, CheckBoxHandler> Mapper = (IPropertyMapper<ICheckBox, CheckBoxHandler>)(object)new PropertyMapper<ICheckBox, CheckBoxHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(ICheckBox.IsChecked)] = MapIsChecked, ["IsChecked"] = MapIsChecked,
[nameof(ICheckBox.Foreground)] = MapForeground, ["Foreground"] = MapForeground,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
[nameof(IView.VerticalLayoutAlignment)] = MapVerticalLayoutAlignment, ["IsEnabled"] = MapIsEnabled,
[nameof(IView.HorizontalLayoutAlignment)] = MapHorizontalLayoutAlignment, ["VerticalLayoutAlignment"] = MapVerticalLayoutAlignment,
["HorizontalLayoutAlignment"] = MapHorizontalLayoutAlignment
}; };
public static CommandMapper<ICheckBox, CheckBoxHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ICheckBox, CheckBoxHandler> CommandMapper = new CommandMapper<ICheckBox, CheckBoxHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public CheckBoxHandler() : base(Mapper, CommandMapper) public CheckBoxHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public CheckBoxHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public CheckBoxHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -52,65 +46,119 @@ public partial class CheckBoxHandler : ViewHandler<ICheckBox, SkiaCheckBox>
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnCheckedChanged(object? sender, Platform.CheckedChangedEventArgs e) private void OnCheckedChanged(object? sender, CheckedChangedEventArgs e)
{ {
if (VirtualView is not null && VirtualView.IsChecked != e.IsChecked) if (base.VirtualView != null && base.VirtualView.IsChecked != e.IsChecked)
{ {
VirtualView.IsChecked = e.IsChecked; base.VirtualView.IsChecked = e.IsChecked;
} }
} }
public static void MapIsChecked(CheckBoxHandler handler, ICheckBox checkBox) public static void MapIsChecked(CheckBoxHandler handler, ICheckBox checkBox)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
handler.PlatformView.IsChecked = checkBox.IsChecked; {
((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView.IsChecked = checkBox.IsChecked;
}
} }
public static void MapForeground(CheckBoxHandler handler, ICheckBox checkBox) public static void MapForeground(CheckBoxHandler handler, ICheckBox checkBox)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
if (checkBox.Foreground is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.CheckColor = solidPaint.Color.ToSKColor(); Paint foreground = checkBox.Foreground;
SolidPaint val = (SolidPaint)(object)((foreground is SolidPaint) ? foreground : null);
if (val != null && val.Color != null)
{
((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView.CheckColor = val.Color.ToSKColor();
}
} }
} }
public static void MapBackground(CheckBoxHandler handler, ICheckBox checkBox) public static void MapBackground(CheckBoxHandler handler, ICheckBox checkBox)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
if (checkBox.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)checkBox).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
}
}
public static void MapIsEnabled(CheckBoxHandler handler, ICheckBox checkBox)
{
if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
{
((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView.IsEnabled = ((IView)checkBox).IsEnabled;
} }
} }
public static void MapVerticalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox) public static void MapVerticalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalOptions = checkBox.VerticalLayoutAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected I4, but got Unknown
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
{ {
Primitives.LayoutAlignment.Start => LayoutOptions.Start, SkiaCheckBox platformView = ((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView;
Primitives.LayoutAlignment.Center => LayoutOptions.Center, LayoutAlignment verticalLayoutAlignment = ((IView)checkBox).VerticalLayoutAlignment;
Primitives.LayoutAlignment.End => LayoutOptions.End, platformView.VerticalOptions = (LayoutOptions)((int)verticalLayoutAlignment switch
Primitives.LayoutAlignment.Fill => LayoutOptions.Fill, {
_ => LayoutOptions.Fill 1 => LayoutOptions.Start,
}; 2 => LayoutOptions.Center,
3 => LayoutOptions.End,
0 => LayoutOptions.Fill,
_ => LayoutOptions.Fill,
});
}
} }
public static void MapHorizontalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox) public static void MapHorizontalLayoutAlignment(CheckBoxHandler handler, ICheckBox checkBox)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalOptions = checkBox.HorizontalLayoutAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected I4, but got Unknown
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView != null)
{ {
Primitives.LayoutAlignment.Start => LayoutOptions.Start, SkiaCheckBox platformView = ((ViewHandler<ICheckBox, SkiaCheckBox>)(object)handler).PlatformView;
Primitives.LayoutAlignment.Center => LayoutOptions.Center, LayoutAlignment horizontalLayoutAlignment = ((IView)checkBox).HorizontalLayoutAlignment;
Primitives.LayoutAlignment.End => LayoutOptions.End, platformView.HorizontalOptions = (LayoutOptions)((int)horizontalLayoutAlignment switch
Primitives.LayoutAlignment.Fill => LayoutOptions.Fill, {
_ => LayoutOptions.Start 1 => LayoutOptions.Start,
}; 2 => LayoutOptions.Center,
3 => LayoutOptions.End,
0 => LayoutOptions.Fill,
_ => LayoutOptions.Start,
});
}
} }
} }

View File

@@ -1,58 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.Linq;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Handlers;
using SkiaSharp; using Microsoft.Maui.Platform.Linux.Hosting;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class CollectionViewHandler : ViewHandler<CollectionView, SkiaCollectionView>
/// Handler for CollectionView on Linux using Skia rendering.
/// Maps CollectionView to SkiaCollectionView platform view.
/// </summary>
public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCollectionView>
{ {
private bool _isUpdatingSelection; private bool _isUpdatingSelection;
public static IPropertyMapper<CollectionView, CollectionViewHandler> Mapper = public static IPropertyMapper<CollectionView, CollectionViewHandler> Mapper = (IPropertyMapper<CollectionView, CollectionViewHandler>)(object)new PropertyMapper<CollectionView, CollectionViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<CollectionView, CollectionViewHandler>(ViewHandler.ViewMapper)
{ {
// ItemsView properties ["ItemsSource"] = MapItemsSource,
[nameof(ItemsView.ItemsSource)] = MapItemsSource, ["ItemTemplate"] = MapItemTemplate,
[nameof(ItemsView.ItemTemplate)] = MapItemTemplate, ["EmptyView"] = MapEmptyView,
[nameof(ItemsView.EmptyView)] = MapEmptyView, ["HorizontalScrollBarVisibility"] = MapHorizontalScrollBarVisibility,
[nameof(ItemsView.HorizontalScrollBarVisibility)] = MapHorizontalScrollBarVisibility, ["VerticalScrollBarVisibility"] = MapVerticalScrollBarVisibility,
[nameof(ItemsView.VerticalScrollBarVisibility)] = MapVerticalScrollBarVisibility, ["SelectedItem"] = MapSelectedItem,
["SelectedItems"] = MapSelectedItems,
// SelectableItemsView properties ["SelectionMode"] = MapSelectionMode,
[nameof(SelectableItemsView.SelectedItem)] = MapSelectedItem, ["Header"] = MapHeader,
[nameof(SelectableItemsView.SelectedItems)] = MapSelectedItems, ["Footer"] = MapFooter,
[nameof(SelectableItemsView.SelectionMode)] = MapSelectionMode, ["ItemsLayout"] = MapItemsLayout,
["Background"] = MapBackground,
// StructuredItemsView properties ["BackgroundColor"] = MapBackgroundColor
[nameof(StructuredItemsView.Header)] = MapHeader,
[nameof(StructuredItemsView.Footer)] = MapFooter,
[nameof(StructuredItemsView.ItemsLayout)] = MapItemsLayout,
[nameof(IView.Background)] = MapBackground,
[nameof(CollectionView.BackgroundColor)] = MapBackgroundColor,
}; };
public static CommandMapper<CollectionView, CollectionViewHandler> CommandMapper = public static CommandMapper<CollectionView, CollectionViewHandler> CommandMapper = new CommandMapper<CollectionView, CollectionViewHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper) { ["ScrollTo"] = MapScrollTo };
new(ViewHandler.ViewCommandMapper)
{
["ScrollTo"] = MapScrollTo,
};
public CollectionViewHandler() : base(Mapper, CommandMapper) public CollectionViewHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public CollectionViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public CollectionViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -79,28 +63,38 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
private void OnSelectionChanged(object? sender, CollectionSelectionChangedEventArgs e) private void OnSelectionChanged(object? sender, CollectionSelectionChangedEventArgs e)
{ {
if (VirtualView is null || _isUpdatingSelection) return; //IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Invalid comparison between Unknown and I4
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Invalid comparison between Unknown and I4
if (base.VirtualView == null || _isUpdatingSelection)
{
return;
}
try try
{ {
_isUpdatingSelection = true; _isUpdatingSelection = true;
if ((int)((SelectableItemsView)base.VirtualView).SelectionMode == 1)
// Update virtual view selection
if (VirtualView.SelectionMode == SelectionMode.Single)
{ {
var newItem = e.CurrentSelection.FirstOrDefault(); object obj = e.CurrentSelection.FirstOrDefault();
if (!Equals(VirtualView.SelectedItem, newItem)) if (!object.Equals(((SelectableItemsView)base.VirtualView).SelectedItem, obj))
{ {
VirtualView.SelectedItem = newItem; ((SelectableItemsView)base.VirtualView).SelectedItem = obj;
} }
} }
else if (VirtualView.SelectionMode == SelectionMode.Multiple) else
{ {
// Clear and update selected items if ((int)((SelectableItemsView)base.VirtualView).SelectionMode != 2)
VirtualView.SelectedItems.Clear();
foreach (var item in e.CurrentSelection)
{ {
VirtualView.SelectedItems.Add(item); return;
}
((SelectableItemsView)base.VirtualView).SelectedItems.Clear();
{
foreach (object item in e.CurrentSelection)
{
((SelectableItemsView)base.VirtualView).SelectedItems.Add(item);
}
return;
} }
} }
} }
@@ -112,122 +106,187 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
private void OnScrolled(object? sender, ItemsScrolledEventArgs e) private void OnScrolled(object? sender, ItemsScrolledEventArgs e)
{ {
VirtualView?.SendScrolled(new ItemsViewScrolledEventArgs //IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Expected O, but got Unknown
CollectionView virtualView = base.VirtualView;
if (virtualView != null)
{
((ItemsView)virtualView).SendScrolled(new ItemsViewScrolledEventArgs
{ {
VerticalOffset = e.ScrollOffset, VerticalOffset = e.ScrollOffset,
VerticalDelta = 0, VerticalDelta = 0.0,
HorizontalOffset = 0, HorizontalOffset = 0.0,
HorizontalDelta = 0 HorizontalDelta = 0.0
}); });
} }
}
private void OnItemTapped(object? sender, ItemsViewItemTappedEventArgs e) private void OnItemTapped(object? sender, ItemsViewItemTappedEventArgs e)
{ {
// Item tap is handled through selection //IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
//IL_01d8: Invalid comparison between Unknown and I4
//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Invalid comparison between Unknown and I4
if (base.VirtualView == null || _isUpdatingSelection)
{
return;
}
try
{
_isUpdatingSelection = true;
Console.WriteLine($"[CollectionViewHandler] OnItemTapped index={e.Index}, item={e.Item}, SelectionMode={((SelectableItemsView)base.VirtualView).SelectionMode}");
SkiaView skiaView = base.PlatformView?.GetItemView(e.Index);
Console.WriteLine($"[CollectionViewHandler] GetItemView({e.Index}) returned: {((object)skiaView)?.GetType().Name ?? "null"}, MauiView={((object)skiaView?.MauiView)?.GetType().Name ?? "null"}");
if (skiaView?.MauiView != null)
{
Console.WriteLine($"[CollectionViewHandler] Found MauiView: {((object)skiaView.MauiView).GetType().Name}, GestureRecognizers={skiaView.MauiView.GestureRecognizers?.Count ?? 0}");
if (GestureManager.ProcessTap(skiaView.MauiView, 0.0, 0.0))
{
Console.WriteLine("[CollectionViewHandler] Gesture processed successfully");
return;
}
}
if ((int)((SelectableItemsView)base.VirtualView).SelectionMode == 1)
{
((SelectableItemsView)base.VirtualView).SelectedItem = e.Item;
}
else if ((int)((SelectableItemsView)base.VirtualView).SelectionMode == 2)
{
if (((SelectableItemsView)base.VirtualView).SelectedItems.Contains(e.Item))
{
((SelectableItemsView)base.VirtualView).SelectedItems.Remove(e.Item);
}
else
{
((SelectableItemsView)base.VirtualView).SelectedItems.Add(e.Item);
}
}
}
finally
{
_isUpdatingSelection = false;
}
} }
public static void MapItemsSource(CollectionViewHandler handler, CollectionView collectionView) public static void MapItemsSource(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
handler.PlatformView.ItemsSource = collectionView.ItemsSource; {
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ItemsSource = ((ItemsView)collectionView).ItemsSource;
}
} }
public static void MapItemTemplate(CollectionViewHandler handler, CollectionView collectionView) public static void MapItemTemplate(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null || handler.MauiContext is null) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
{
var template = collectionView.ItemTemplate; return;
}
DataTemplate template = ((ItemsView)collectionView).ItemTemplate;
if (template != null) if (template != null)
{ {
// Set up a renderer that creates views from the DataTemplate ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ItemViewCreator = delegate(object item)
handler.PlatformView.ItemViewCreator = (item) =>
{ {
try try
{ {
// Create view from template object obj = ((ElementTemplate)template).CreateContent();
var content = template.CreateContent(); View val = (View)((obj is View) ? obj : null);
if (content is View view) if (val != null)
{ {
// Set binding context FIRST so bindings evaluate ((BindableObject)val).BindingContext = item;
view.BindingContext = item; PropagateBindingContext(val, item);
if (((VisualElement)val).Handler == null && ((ElementHandler)handler).MauiContext != null)
// Force binding evaluation by accessing the visual tree
// This ensures child bindings are evaluated before handler creation
PropagateBindingContext(view, item);
// Create handler for the view
if (view.Handler == null && handler.MauiContext != null)
{ {
view.Handler = view.ToHandler(handler.MauiContext); ((VisualElement)val).Handler = ((IView)(object)val).ToViewHandler(((ElementHandler)handler).MauiContext);
} }
IViewHandler handler2 = ((VisualElement)val).Handler;
if (view.Handler?.PlatformView is SkiaView skiaView) if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView skiaView)
{ {
skiaView.MauiView = val;
Console.WriteLine($"[CollectionViewHandler.ItemViewCreator] Set MauiView={((object)val).GetType().Name} on {((object)skiaView).GetType().Name}, GestureRecognizers={val.GestureRecognizers?.Count ?? 0}");
return skiaView; return skiaView;
} }
} }
else if (content is ViewCell cell) else
{ {
cell.BindingContext = item; ViewCell val2 = (ViewCell)((obj is ViewCell) ? obj : null);
var cellView = cell.View; if (val2 != null)
if (cellView != null)
{ {
if (cellView.Handler == null && handler.MauiContext != null) ((BindableObject)val2).BindingContext = item;
View view = val2.View;
if (view != null)
{ {
cellView.Handler = cellView.ToHandler(handler.MauiContext); if (((VisualElement)view).Handler == null && ((ElementHandler)handler).MauiContext != null)
{
((VisualElement)view).Handler = ((IView)(object)view).ToViewHandler(((ElementHandler)handler).MauiContext);
} }
IViewHandler handler3 = ((VisualElement)view).Handler;
if (cellView.Handler?.PlatformView is SkiaView skiaView) if (((handler3 != null) ? ((IElementHandler)handler3).PlatformView : null) is SkiaView result)
{ {
return skiaView; return result;
}
} }
} }
} }
} }
catch catch
{ {
// Ignore template creation errors
} }
return null; return (SkiaView?)null;
}; };
} }
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.Invalidate();
handler.PlatformView.Invalidate();
} }
public static void MapEmptyView(CollectionViewHandler handler, CollectionView collectionView) public static void MapEmptyView(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
handler.PlatformView.EmptyView = collectionView.EmptyView;
if (collectionView.EmptyView is string text)
{ {
handler.PlatformView.EmptyViewText = text; ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.EmptyView = ((ItemsView)collectionView).EmptyView;
if (((ItemsView)collectionView).EmptyView is string emptyViewText)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.EmptyViewText = emptyViewText;
}
} }
} }
public static void MapHorizontalScrollBarVisibility(CollectionViewHandler handler, CollectionView collectionView) public static void MapHorizontalScrollBarVisibility(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalScrollBarVisibility = (ScrollBarVisibility)collectionView.HorizontalScrollBarVisibility; //IL_001a: Expected I4, but got Unknown
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.HorizontalScrollBarVisibility = (ScrollBarVisibility)((ItemsView)collectionView).HorizontalScrollBarVisibility;
}
} }
public static void MapVerticalScrollBarVisibility(CollectionViewHandler handler, CollectionView collectionView) public static void MapVerticalScrollBarVisibility(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalScrollBarVisibility = (ScrollBarVisibility)collectionView.VerticalScrollBarVisibility; //IL_001a: Expected I4, but got Unknown
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.VerticalScrollBarVisibility = (ScrollBarVisibility)((ItemsView)collectionView).VerticalScrollBarVisibility;
}
} }
public static void MapSelectedItem(CollectionViewHandler handler, CollectionView collectionView) public static void MapSelectedItem(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null || handler._isUpdatingSelection) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView == null || handler._isUpdatingSelection)
{
return;
}
try try
{ {
handler._isUpdatingSelection = true; handler._isUpdatingSelection = true;
if (!Equals(handler.PlatformView.SelectedItem, collectionView.SelectedItem)) if (!object.Equals(((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.SelectedItem, ((SelectableItemsView)collectionView).SelectedItem))
{ {
handler.PlatformView.SelectedItem = collectionView.SelectedItem; ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.SelectedItem = ((SelectableItemsView)collectionView).SelectedItem;
} }
} }
finally finally
@@ -238,17 +297,17 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
public static void MapSelectedItems(CollectionViewHandler handler, CollectionView collectionView) public static void MapSelectedItems(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null || handler._isUpdatingSelection) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView == null || handler._isUpdatingSelection)
{
return;
}
try try
{ {
handler._isUpdatingSelection = true; handler._isUpdatingSelection = true;
IList<object> selectedItems = ((SelectableItemsView)collectionView).SelectedItems;
// Sync selected items
var selectedItems = collectionView.SelectedItems;
if (selectedItems != null && selectedItems.Count > 0) if (selectedItems != null && selectedItems.Count > 0)
{ {
handler.PlatformView.SelectedItem = selectedItems.First(); ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.SelectedItem = selectedItems.First();
} }
} }
finally finally
@@ -259,116 +318,141 @@ public partial class CollectionViewHandler : ViewHandler<CollectionView, SkiaCol
public static void MapSelectionMode(CollectionViewHandler handler, CollectionView collectionView) public static void MapSelectionMode(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.SelectionMode = collectionView.SelectionMode switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected I4, but got Unknown
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
{ {
SelectionMode.None => SkiaSelectionMode.None, SkiaCollectionView platformView = ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView;
SelectionMode.Single => SkiaSelectionMode.Single, SelectionMode selectionMode = ((SelectableItemsView)collectionView).SelectionMode;
SelectionMode.Multiple => SkiaSelectionMode.Multiple, platformView.SelectionMode = (int)selectionMode switch
_ => SkiaSelectionMode.None {
0 => SkiaSelectionMode.None,
1 => SkiaSelectionMode.Single,
2 => SkiaSelectionMode.Multiple,
_ => SkiaSelectionMode.None,
}; };
} }
}
public static void MapHeader(CollectionViewHandler handler, CollectionView collectionView) public static void MapHeader(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
handler.PlatformView.Header = collectionView.Header; {
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.Header = ((StructuredItemsView)collectionView).Header;
}
} }
public static void MapFooter(CollectionViewHandler handler, CollectionView collectionView) public static void MapFooter(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null)
handler.PlatformView.Footer = collectionView.Footer; {
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.Footer = ((StructuredItemsView)collectionView).Footer;
}
} }
public static void MapItemsLayout(CollectionViewHandler handler, CollectionView collectionView) public static void MapItemsLayout(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
var layout = collectionView.ItemsLayout; if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView == null)
if (layout is LinearItemsLayout linearLayout)
{ {
handler.PlatformView.Orientation = linearLayout.Orientation == Controls.ItemsLayoutOrientation.Vertical return;
? Platform.ItemsLayoutOrientation.Vertical
: Platform.ItemsLayoutOrientation.Horizontal;
handler.PlatformView.SpanCount = 1;
handler.PlatformView.ItemSpacing = (float)linearLayout.ItemSpacing;
} }
else if (layout is GridItemsLayout gridLayout) IItemsLayout itemsLayout = ((StructuredItemsView)collectionView).ItemsLayout;
LinearItemsLayout val = (LinearItemsLayout)(object)((itemsLayout is LinearItemsLayout) ? itemsLayout : null);
if (val != null)
{ {
handler.PlatformView.Orientation = gridLayout.Orientation == Controls.ItemsLayoutOrientation.Vertical ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.Orientation = (((int)((ItemsLayout)val).Orientation != 0) ? ItemsLayoutOrientation.Horizontal : ItemsLayoutOrientation.Vertical);
? Platform.ItemsLayoutOrientation.Vertical ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.SpanCount = 1;
: Platform.ItemsLayoutOrientation.Horizontal; ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ItemSpacing = (float)val.ItemSpacing;
handler.PlatformView.SpanCount = gridLayout.Span; return;
handler.PlatformView.ItemSpacing = (float)gridLayout.VerticalItemSpacing; }
GridItemsLayout val2 = (GridItemsLayout)(object)((itemsLayout is GridItemsLayout) ? itemsLayout : null);
if (val2 != null)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.Orientation = (((int)((ItemsLayout)val2).Orientation != 0) ? ItemsLayoutOrientation.Horizontal : ItemsLayoutOrientation.Vertical);
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.SpanCount = val2.Span;
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ItemSpacing = (float)val2.VerticalItemSpacing;
} }
} }
public static void MapBackground(CollectionViewHandler handler, CollectionView collectionView) public static void MapBackground(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_002d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null && ((VisualElement)collectionView).BackgroundColor == null)
// Don't override if BackgroundColor is explicitly set
if (collectionView.BackgroundColor is not null)
return;
if (collectionView.Background is SolidColorBrush solidBrush)
{ {
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor(); Brush background = ((VisualElement)collectionView).Background;
SolidColorBrush val = (SolidColorBrush)(object)((background is SolidColorBrush) ? background : null);
if (val != null)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapBackgroundColor(CollectionViewHandler handler, CollectionView collectionView) public static void MapBackgroundColor(CollectionViewHandler handler, CollectionView collectionView)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView != null && ((VisualElement)collectionView).BackgroundColor != null)
if (collectionView.BackgroundColor is not null)
{ {
handler.PlatformView.BackgroundColor = collectionView.BackgroundColor.ToSKColor(); ((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.BackgroundColor = ((VisualElement)collectionView).BackgroundColor.ToSKColor();
} }
} }
public static void MapScrollTo(CollectionViewHandler handler, CollectionView collectionView, object? args) public static void MapScrollTo(CollectionViewHandler handler, CollectionView collectionView, object? args)
{ {
if (handler.PlatformView is null || args is not ScrollToRequestEventArgs scrollArgs) //IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Invalid comparison between Unknown and I4
if (((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView == null)
{
return; return;
if (scrollArgs.Mode == ScrollToMode.Position)
{
handler.PlatformView.ScrollToIndex(scrollArgs.Index, scrollArgs.IsAnimated);
} }
else if (scrollArgs.Item != null) ScrollToRequestEventArgs e = (ScrollToRequestEventArgs)((args is ScrollToRequestEventArgs) ? args : null);
if (e != null)
{ {
handler.PlatformView.ScrollToItem(scrollArgs.Item, scrollArgs.IsAnimated); if ((int)e.Mode == 1)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ScrollToIndex(e.Index, e.IsAnimated);
}
else if (e.Item != null)
{
((ViewHandler<CollectionView, SkiaCollectionView>)(object)handler).PlatformView.ScrollToItem(e.Item, e.IsAnimated);
}
} }
} }
/// <summary>
/// Recursively propagates binding context to all child views to force binding evaluation.
/// </summary>
private static void PropagateBindingContext(View view, object? bindingContext) private static void PropagateBindingContext(View view, object? bindingContext)
{ {
view.BindingContext = bindingContext; ((BindableObject)view).BindingContext = bindingContext;
Layout val = (Layout)(object)((view is Layout) ? view : null);
// Propagate to children if (val != null)
if (view is Layout layout)
{ {
foreach (var child in layout.Children) foreach (IView child in val.Children)
{ {
if (child is View childView) View val2 = (View)(object)((child is View) ? child : null);
if (val2 != null)
{ {
PropagateBindingContext(childView, bindingContext); PropagateBindingContext(val2, bindingContext);
} }
} }
return;
} }
else if (view is ContentView contentView && contentView.Content != null) ContentView val3 = (ContentView)(object)((view is ContentView) ? view : null);
if (val3 != null && val3.Content != null)
{ {
PropagateBindingContext(contentView.Content, bindingContext); PropagateBindingContext(val3.Content, bindingContext);
return;
} }
else if (view is Border border && border.Content is View borderContent) Border val4 = (Border)(object)((view is Border) ? view : null);
if (val4 != null)
{ {
PropagateBindingContext(borderContent, bindingContext); View content = val4.Content;
if (content != null)
{
PropagateBindingContext(content, bindingContext);
}
} }
} }
} }

View File

@@ -0,0 +1,60 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class ContentPageHandler : PageHandler
{
public new static IPropertyMapper<ContentPage, ContentPageHandler> Mapper = (IPropertyMapper<ContentPage, ContentPageHandler>)(object)new PropertyMapper<ContentPage, ContentPageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)PageHandler.Mapper }) { ["Content"] = MapContent };
public new static CommandMapper<ContentPage, ContentPageHandler> CommandMapper = new CommandMapper<ContentPage, ContentPageHandler>((CommandMapper)(object)PageHandler.CommandMapper);
public ContentPageHandler()
: base((IPropertyMapper?)(object)Mapper, (CommandMapper?)(object)CommandMapper)
{
}
public ContentPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base((IPropertyMapper?)(((object)mapper) ?? ((object)Mapper)), (CommandMapper?)(((object)commandMapper) ?? ((object)CommandMapper)))
{
}
protected override SkiaPage CreatePlatformView()
{
return new SkiaContentPage();
}
public static void MapContent(ContentPageHandler handler, ContentPage page)
{
if (((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
{
return;
}
View content = page.Content;
if (content != null)
{
if (((VisualElement)content).Handler == null)
{
Console.WriteLine("[ContentPageHandler] Creating handler for content: " + ((object)content).GetType().Name);
((VisualElement)content).Handler = ((IView)(object)content).ToViewHandler(((ElementHandler)handler).MauiContext);
}
IViewHandler handler2 = ((VisualElement)content).Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView skiaView)
{
Console.WriteLine("[ContentPageHandler] Setting content: " + ((object)skiaView).GetType().Name);
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.Content = skiaView;
}
else
{
IViewHandler handler3 = ((VisualElement)content).Handler;
Console.WriteLine("[ContentPageHandler] Content handler PlatformView is not SkiaView: " + (((handler3 == null) ? null : ((IElementHandler)handler3).PlatformView?.GetType().Name) ?? "null"));
}
}
else
{
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.Content = null;
}
}
}

View File

@@ -1,42 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class DatePickerHandler : ViewHandler<IDatePicker, SkiaDatePicker>
/// Handler for DatePicker on Linux using Skia rendering.
/// </summary>
public partial class DatePickerHandler : ViewHandler<IDatePicker, SkiaDatePicker>
{ {
public static IPropertyMapper<IDatePicker, DatePickerHandler> Mapper = public static IPropertyMapper<IDatePicker, DatePickerHandler> Mapper = (IPropertyMapper<IDatePicker, DatePickerHandler>)(object)new PropertyMapper<IDatePicker, DatePickerHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IDatePicker, DatePickerHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IDatePicker.Date)] = MapDate, ["Date"] = MapDate,
[nameof(IDatePicker.MinimumDate)] = MapMinimumDate, ["MinimumDate"] = MapMinimumDate,
[nameof(IDatePicker.MaximumDate)] = MapMaximumDate, ["MaximumDate"] = MapMaximumDate,
[nameof(IDatePicker.Format)] = MapFormat, ["Format"] = MapFormat,
[nameof(IDatePicker.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(IDatePicker.CharacterSpacing)] = MapCharacterSpacing, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<IDatePicker, DatePickerHandler> CommandMapper = public static CommandMapper<IDatePicker, DatePickerHandler> CommandMapper = new CommandMapper<IDatePicker, DatePickerHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public DatePickerHandler() : base(Mapper, CommandMapper) public DatePickerHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public DatePickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public DatePickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -47,8 +38,24 @@ public partial class DatePickerHandler : ViewHandler<IDatePicker, SkiaDatePicker
protected override void ConnectHandler(SkiaDatePicker platformView) protected override void ConnectHandler(SkiaDatePicker platformView)
{ {
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Invalid comparison between Unknown and I4
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.DateSelected += OnDateSelected; platformView.DateSelected += OnDateSelected;
Application current = Application.Current;
if (current != null && (int)current.UserAppTheme == 2)
{
platformView.CalendarBackgroundColor = new SKColor((byte)30, (byte)30, (byte)30);
platformView.TextColor = new SKColor((byte)224, (byte)224, (byte)224);
platformView.BorderColor = new SKColor((byte)97, (byte)97, (byte)97);
platformView.DisabledDayColor = new SKColor((byte)97, (byte)97, (byte)97);
platformView.BackgroundColor = new SKColor((byte)45, (byte)45, (byte)45);
}
} }
protected override void DisconnectHandler(SkiaDatePicker platformView) protected override void DisconnectHandler(SkiaDatePicker platformView)
@@ -59,56 +66,68 @@ public partial class DatePickerHandler : ViewHandler<IDatePicker, SkiaDatePicker
private void OnDateSelected(object? sender, EventArgs e) private void OnDateSelected(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
{
VirtualView.Date = PlatformView.Date; base.VirtualView.Date = base.PlatformView.Date;
}
} }
public static void MapDate(DatePickerHandler handler, IDatePicker datePicker) public static void MapDate(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null)
handler.PlatformView.Date = datePicker.Date; {
((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.Date = datePicker.Date;
}
} }
public static void MapMinimumDate(DatePickerHandler handler, IDatePicker datePicker) public static void MapMinimumDate(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null)
handler.PlatformView.MinimumDate = datePicker.MinimumDate; {
((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.MinimumDate = datePicker.MinimumDate;
}
} }
public static void MapMaximumDate(DatePickerHandler handler, IDatePicker datePicker) public static void MapMaximumDate(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null)
handler.PlatformView.MaximumDate = datePicker.MaximumDate; {
((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.MaximumDate = datePicker.MaximumDate;
}
} }
public static void MapFormat(DatePickerHandler handler, IDatePicker datePicker) public static void MapFormat(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null)
handler.PlatformView.Format = datePicker.Format ?? "d"; {
((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.Format = datePicker.Format ?? "d";
}
} }
public static void MapTextColor(DatePickerHandler handler, IDatePicker datePicker) public static void MapTextColor(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (datePicker.TextColor is not null) if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null && ((ITextStyle)datePicker).TextColor != null)
{ {
handler.PlatformView.TextColor = datePicker.TextColor.ToSKColor(); ((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.TextColor = ((ITextStyle)datePicker).TextColor.ToSKColor();
} }
} }
public static void MapCharacterSpacing(DatePickerHandler handler, IDatePicker datePicker) public static void MapCharacterSpacing(DatePickerHandler handler, IDatePicker datePicker)
{ {
// Character spacing would require custom text rendering
} }
public static void MapBackground(DatePickerHandler handler, IDatePicker datePicker) public static void MapBackground(DatePickerHandler handler, IDatePicker datePicker)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView != null)
if (datePicker.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)datePicker).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IDatePicker, SkiaDatePicker>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
} }

View File

@@ -1,50 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class EditorHandler : ViewHandler<IEditor, SkiaEditor>
/// Handler for Editor (multiline text) on Linux using Skia rendering.
/// </summary>
public partial class EditorHandler : ViewHandler<IEditor, SkiaEditor>
{ {
public static IPropertyMapper<IEditor, EditorHandler> Mapper = public static IPropertyMapper<IEditor, EditorHandler> Mapper = (IPropertyMapper<IEditor, EditorHandler>)(object)new PropertyMapper<IEditor, EditorHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IEditor, EditorHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IEditor.Text)] = MapText, ["Text"] = MapText,
[nameof(IEditor.Placeholder)] = MapPlaceholder, ["Placeholder"] = MapPlaceholder,
[nameof(IEditor.PlaceholderColor)] = MapPlaceholderColor, ["PlaceholderColor"] = MapPlaceholderColor,
[nameof(IEditor.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(IEditor.CharacterSpacing)] = MapCharacterSpacing, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(IEditor.IsReadOnly)] = MapIsReadOnly, ["IsReadOnly"] = MapIsReadOnly,
[nameof(IEditor.IsTextPredictionEnabled)] = MapIsTextPredictionEnabled, ["IsTextPredictionEnabled"] = MapIsTextPredictionEnabled,
[nameof(IEditor.MaxLength)] = MapMaxLength, ["MaxLength"] = MapMaxLength,
[nameof(IEditor.CursorPosition)] = MapCursorPosition, ["CursorPosition"] = MapCursorPosition,
[nameof(IEditor.SelectionLength)] = MapSelectionLength, ["SelectionLength"] = MapSelectionLength,
[nameof(IEditor.Keyboard)] = MapKeyboard, ["Keyboard"] = MapKeyboard,
[nameof(IEditor.HorizontalTextAlignment)] = MapHorizontalTextAlignment, ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
[nameof(IEditor.VerticalTextAlignment)] = MapVerticalTextAlignment, ["VerticalTextAlignment"] = MapVerticalTextAlignment,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor, ["BackgroundColor"] = MapBackgroundColor
}; };
public static CommandMapper<IEditor, EditorHandler> CommandMapper = public static CommandMapper<IEditor, EditorHandler> CommandMapper = new CommandMapper<IEditor, EditorHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public EditorHandler() : base(Mapper, CommandMapper) public EditorHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public EditorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public EditorHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -69,113 +59,124 @@ public partial class EditorHandler : ViewHandler<IEditor, SkiaEditor>
private void OnTextChanged(object? sender, EventArgs e) private void OnTextChanged(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
{
VirtualView.Text = PlatformView.Text; ((ITextInput)base.VirtualView).Text = base.PlatformView.Text;
}
} }
private void OnCompleted(object? sender, EventArgs e) private void OnCompleted(object? sender, EventArgs e)
{ {
// Editor doesn't typically have a completed event, but we could trigger it
} }
public static void MapText(EditorHandler handler, IEditor editor) public static void MapText(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
handler.PlatformView.Text = editor.Text ?? ""; {
handler.PlatformView.Invalidate(); ((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.Text = ((ITextInput)editor).Text ?? "";
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.Invalidate();
}
} }
public static void MapPlaceholder(EditorHandler handler, IEditor editor) public static void MapPlaceholder(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
handler.PlatformView.Placeholder = editor.Placeholder ?? ""; {
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.Placeholder = ((IPlaceholder)editor).Placeholder ?? "";
}
} }
public static void MapPlaceholderColor(EditorHandler handler, IEditor editor) public static void MapPlaceholderColor(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (editor.PlaceholderColor is not null) if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null && ((IPlaceholder)editor).PlaceholderColor != null)
{ {
handler.PlatformView.PlaceholderColor = editor.PlaceholderColor.ToSKColor(); ((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.PlaceholderColor = ((IPlaceholder)editor).PlaceholderColor.ToSKColor();
} }
} }
public static void MapTextColor(EditorHandler handler, IEditor editor) public static void MapTextColor(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (editor.TextColor is not null) if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null && ((ITextStyle)editor).TextColor != null)
{ {
handler.PlatformView.TextColor = editor.TextColor.ToSKColor(); ((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.TextColor = ((ITextStyle)editor).TextColor.ToSKColor();
} }
} }
public static void MapCharacterSpacing(EditorHandler handler, IEditor editor) public static void MapCharacterSpacing(EditorHandler handler, IEditor editor)
{ {
// Character spacing would require custom text rendering
} }
public static void MapIsReadOnly(EditorHandler handler, IEditor editor) public static void MapIsReadOnly(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
handler.PlatformView.IsReadOnly = editor.IsReadOnly; {
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.IsReadOnly = ((ITextInput)editor).IsReadOnly;
}
} }
public static void MapIsTextPredictionEnabled(EditorHandler handler, IEditor editor) public static void MapIsTextPredictionEnabled(EditorHandler handler, IEditor editor)
{ {
// Text prediction not applicable to desktop
} }
public static void MapMaxLength(EditorHandler handler, IEditor editor) public static void MapMaxLength(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
handler.PlatformView.MaxLength = editor.MaxLength; {
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.MaxLength = ((ITextInput)editor).MaxLength;
}
} }
public static void MapCursorPosition(EditorHandler handler, IEditor editor) public static void MapCursorPosition(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
handler.PlatformView.CursorPosition = editor.CursorPosition; {
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.CursorPosition = ((ITextInput)editor).CursorPosition;
}
} }
public static void MapSelectionLength(EditorHandler handler, IEditor editor) public static void MapSelectionLength(EditorHandler handler, IEditor editor)
{ {
// Selection would need to be added to SkiaEditor
} }
public static void MapKeyboard(EditorHandler handler, IEditor editor) public static void MapKeyboard(EditorHandler handler, IEditor editor)
{ {
// Virtual keyboard type not applicable to desktop
} }
public static void MapHorizontalTextAlignment(EditorHandler handler, IEditor editor) public static void MapHorizontalTextAlignment(EditorHandler handler, IEditor editor)
{ {
// Text alignment would require changes to SkiaEditor drawing
} }
public static void MapVerticalTextAlignment(EditorHandler handler, IEditor editor) public static void MapVerticalTextAlignment(EditorHandler handler, IEditor editor)
{ {
// Text alignment would require changes to SkiaEditor drawing
} }
public static void MapBackground(EditorHandler handler, IEditor editor) public static void MapBackground(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
if (editor.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)editor).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapBackgroundColor(EditorHandler handler, IEditor editor) public static void MapBackgroundColor(EditorHandler handler, IEditor editor)
{ {
if (handler.PlatformView is null) return; //IL_0027: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView != null)
if (editor is VisualElement ve && ve.BackgroundColor != null)
{ {
handler.PlatformView.BackgroundColor = ve.BackgroundColor.ToSKColor(); VisualElement val = (VisualElement)(object)((editor is VisualElement) ? editor : null);
handler.PlatformView.Invalidate(); if (val != null && val.BackgroundColor != null)
{
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.BackgroundColor = val.BackgroundColor.ToSKColor();
((ViewHandler<IEditor, SkiaEditor>)(object)handler).PlatformView.Invalidate();
}
} }
} }
} }

View File

@@ -1,48 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class EntryHandler : ViewHandler<IEntry, SkiaEntry>
/// Handler for Entry on Linux using Skia rendering.
/// Maps IEntry interface to SkiaEntry platform view.
/// </summary>
public partial class EntryHandler : ViewHandler<IEntry, SkiaEntry>
{ {
public static IPropertyMapper<IEntry, EntryHandler> Mapper = new PropertyMapper<IEntry, EntryHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IEntry, EntryHandler> Mapper = (IPropertyMapper<IEntry, EntryHandler>)(object)new PropertyMapper<IEntry, EntryHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(ITextInput.Text)] = MapText, ["Text"] = MapText,
[nameof(ITextStyle.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(ITextStyle.Font)] = MapFont, ["Font"] = MapFont,
[nameof(ITextStyle.CharacterSpacing)] = MapCharacterSpacing, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(IPlaceholder.Placeholder)] = MapPlaceholder, ["Placeholder"] = MapPlaceholder,
[nameof(IPlaceholder.PlaceholderColor)] = MapPlaceholderColor, ["PlaceholderColor"] = MapPlaceholderColor,
[nameof(ITextInput.IsReadOnly)] = MapIsReadOnly, ["IsReadOnly"] = MapIsReadOnly,
[nameof(ITextInput.MaxLength)] = MapMaxLength, ["MaxLength"] = MapMaxLength,
[nameof(ITextInput.CursorPosition)] = MapCursorPosition, ["CursorPosition"] = MapCursorPosition,
[nameof(ITextInput.SelectionLength)] = MapSelectionLength, ["SelectionLength"] = MapSelectionLength,
[nameof(IEntry.IsPassword)] = MapIsPassword, ["IsPassword"] = MapIsPassword,
[nameof(IEntry.ReturnType)] = MapReturnType, ["ReturnType"] = MapReturnType,
[nameof(IEntry.ClearButtonVisibility)] = MapClearButtonVisibility, ["ClearButtonVisibility"] = MapClearButtonVisibility,
[nameof(ITextAlignment.HorizontalTextAlignment)] = MapHorizontalTextAlignment, ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
[nameof(ITextAlignment.VerticalTextAlignment)] = MapVerticalTextAlignment, ["VerticalTextAlignment"] = MapVerticalTextAlignment,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor
}; };
public static CommandMapper<IEntry, EntryHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IEntry, EntryHandler> CommandMapper = new CommandMapper<IEntry, EntryHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public EntryHandler() : base(Mapper, CommandMapper) public EntryHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public EntryHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public EntryHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -65,151 +60,221 @@ public partial class EntryHandler : ViewHandler<IEntry, SkiaEntry>
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnTextChanged(object? sender, Platform.TextChangedEventArgs e) private void OnTextChanged(object? sender, TextChangedEventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null && ((ITextInput)base.VirtualView).Text != e.NewTextValue)
if (VirtualView.Text != e.NewTextValue)
{ {
VirtualView.Text = e.NewTextValue ?? string.Empty; ((ITextInput)base.VirtualView).Text = e.NewTextValue ?? string.Empty;
} }
} }
private void OnCompleted(object? sender, EventArgs e) private void OnCompleted(object? sender, EventArgs e)
{ {
VirtualView?.Completed(); IEntry virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.Completed();
}
} }
public static void MapText(EntryHandler handler, IEntry entry) public static void MapText(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null && ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.Text != ((ITextInput)entry).Text)
if (handler.PlatformView.Text != entry.Text)
{ {
handler.PlatformView.Text = entry.Text ?? string.Empty; ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.Text = ((ITextInput)entry).Text ?? string.Empty;
handler.PlatformView.Invalidate(); ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.Invalidate();
} }
} }
public static void MapTextColor(EntryHandler handler, IEntry entry) public static void MapTextColor(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null && ((ITextStyle)entry).TextColor != null)
if (entry.TextColor is not null) {
handler.PlatformView.TextColor = entry.TextColor.ToSKColor(); ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.TextColor = ((ITextStyle)entry).TextColor.ToSKColor();
}
} }
public static void MapFont(EntryHandler handler, IEntry entry) public static void MapFont(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var font = entry.Font; //IL_005d: Unknown result type (might be due to invalid IL or missing references)
if (font.Size > 0) //IL_0067: Invalid comparison between Unknown and I4
handler.PlatformView.FontSize = (float)font.Size; //IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Invalid comparison between Unknown and I4
if (!string.IsNullOrEmpty(font.Family)) //IL_0083: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.FontFamily = font.Family; //IL_0089: Invalid comparison between Unknown and I4
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.IsBold = font.Weight >= FontWeight.Bold; {
handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic || font.Slant == FontSlant.Oblique; Font font = ((ITextStyle)entry).Font;
if (((Font)(ref font)).Size > 0.0)
{
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.FontSize = (float)((Font)(ref font)).Size;
}
if (!string.IsNullOrEmpty(((Font)(ref font)).Family))
{
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.FontFamily = ((Font)(ref font)).Family;
}
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.IsBold = (int)((Font)(ref font)).Weight >= 700;
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.IsItalic = (int)((Font)(ref font)).Slant == 1 || (int)((Font)(ref font)).Slant == 2;
}
} }
public static void MapCharacterSpacing(EntryHandler handler, IEntry entry) public static void MapCharacterSpacing(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.CharacterSpacing = (float)entry.CharacterSpacing; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.CharacterSpacing = (float)((ITextStyle)entry).CharacterSpacing;
}
} }
public static void MapPlaceholder(EntryHandler handler, IEntry entry) public static void MapPlaceholder(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.Placeholder = entry.Placeholder ?? string.Empty; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.Placeholder = ((IPlaceholder)entry).Placeholder ?? string.Empty;
}
} }
public static void MapPlaceholderColor(EntryHandler handler, IEntry entry) public static void MapPlaceholderColor(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null && ((IPlaceholder)entry).PlaceholderColor != null)
if (entry.PlaceholderColor is not null) {
handler.PlatformView.PlaceholderColor = entry.PlaceholderColor.ToSKColor(); ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.PlaceholderColor = ((IPlaceholder)entry).PlaceholderColor.ToSKColor();
}
} }
public static void MapIsReadOnly(EntryHandler handler, IEntry entry) public static void MapIsReadOnly(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.IsReadOnly = entry.IsReadOnly; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.IsReadOnly = ((ITextInput)entry).IsReadOnly;
}
} }
public static void MapMaxLength(EntryHandler handler, IEntry entry) public static void MapMaxLength(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.MaxLength = entry.MaxLength; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.MaxLength = ((ITextInput)entry).MaxLength;
}
} }
public static void MapCursorPosition(EntryHandler handler, IEntry entry) public static void MapCursorPosition(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.CursorPosition = entry.CursorPosition; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.CursorPosition = ((ITextInput)entry).CursorPosition;
}
} }
public static void MapSelectionLength(EntryHandler handler, IEntry entry) public static void MapSelectionLength(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.SelectionLength = entry.SelectionLength; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.SelectionLength = ((ITextInput)entry).SelectionLength;
}
} }
public static void MapIsPassword(EntryHandler handler, IEntry entry) public static void MapIsPassword(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
handler.PlatformView.IsPassword = entry.IsPassword; {
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.IsPassword = entry.IsPassword;
}
} }
public static void MapReturnType(EntryHandler handler, IEntry entry) public static void MapReturnType(EntryHandler handler, IEntry entry)
{ {
// ReturnType affects keyboard behavior - stored for virtual keyboard integration _ = ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView;
if (handler.PlatformView is null) return;
// handler.PlatformView.ReturnType = entry.ReturnType; // Would need property on SkiaEntry
} }
public static void MapClearButtonVisibility(EntryHandler handler, IEntry entry) public static void MapClearButtonVisibility(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.ShowClearButton = entry.ClearButtonVisibility == ClearButtonVisibility.WhileEditing; //IL_0016: Invalid comparison between Unknown and I4
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
{
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.ShowClearButton = (int)entry.ClearButtonVisibility == 1;
}
} }
public static void MapHorizontalTextAlignment(EntryHandler handler, IEntry entry) public static void MapHorizontalTextAlignment(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalTextAlignment = entry.HorizontalTextAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected I4, but got Unknown
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
{ {
Microsoft.Maui.TextAlignment.Start => Platform.TextAlignment.Start, SkiaEntry platformView = ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView;
Microsoft.Maui.TextAlignment.Center => Platform.TextAlignment.Center, TextAlignment horizontalTextAlignment = ((ITextAlignment)entry).HorizontalTextAlignment;
Microsoft.Maui.TextAlignment.End => Platform.TextAlignment.End, platformView.HorizontalTextAlignment = (int)horizontalTextAlignment switch
_ => Platform.TextAlignment.Start {
0 => TextAlignment.Start,
1 => TextAlignment.Center,
2 => TextAlignment.End,
_ => TextAlignment.Start,
}; };
} }
}
public static void MapVerticalTextAlignment(EntryHandler handler, IEntry entry) public static void MapVerticalTextAlignment(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalTextAlignment = entry.VerticalTextAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected I4, but got Unknown
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
{ {
Microsoft.Maui.TextAlignment.Start => Platform.TextAlignment.Start, SkiaEntry platformView = ((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView;
Microsoft.Maui.TextAlignment.Center => Platform.TextAlignment.Center, TextAlignment verticalTextAlignment = ((ITextAlignment)entry).VerticalTextAlignment;
Microsoft.Maui.TextAlignment.End => Platform.TextAlignment.End, platformView.VerticalTextAlignment = (int)verticalTextAlignment switch
_ => Platform.TextAlignment.Center {
0 => TextAlignment.Start,
1 => TextAlignment.Center,
2 => TextAlignment.End,
_ => TextAlignment.Center,
}; };
} }
}
public static void MapBackground(EntryHandler handler, IEntry entry) public static void MapBackground(EntryHandler handler, IEntry entry)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView != null)
if (entry.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)entry).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
}
}
public static void MapBackgroundColor(EntryHandler handler, IEntry entry)
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView == null)
{
return;
}
Entry val = (Entry)(object)((entry is Entry) ? entry : null);
if (val != null)
{
Console.WriteLine($"[EntryHandler] MapBackgroundColor: {((VisualElement)val).BackgroundColor}");
if (((VisualElement)val).BackgroundColor != null)
{
SKColor val2 = ((VisualElement)val).BackgroundColor.ToSKColor();
Console.WriteLine($"[EntryHandler] Setting EntryBackgroundColor to: {val2}");
((ViewHandler<IEntry, SkiaEntry>)(object)handler).PlatformView.EntryBackgroundColor = val2;
}
} }
} }
} }

View File

@@ -0,0 +1,139 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Layouts;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class FlexLayoutHandler : LayoutHandler
{
public new static IPropertyMapper<FlexLayout, FlexLayoutHandler> Mapper = (IPropertyMapper<FlexLayout, FlexLayoutHandler>)(object)new PropertyMapper<FlexLayout, FlexLayoutHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)LayoutHandler.Mapper })
{
["Direction"] = MapDirection,
["Wrap"] = MapWrap,
["JustifyContent"] = MapJustifyContent,
["AlignItems"] = MapAlignItems,
["AlignContent"] = MapAlignContent
};
public FlexLayoutHandler()
: base((IPropertyMapper?)(object)Mapper)
{
}
protected override SkiaLayoutView CreatePlatformView()
{
return new SkiaFlexLayout();
}
public static void MapDirection(FlexLayoutHandler handler, FlexLayout layout)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Expected I4, but got Unknown
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaFlexLayout skiaFlexLayout)
{
SkiaFlexLayout skiaFlexLayout2 = skiaFlexLayout;
FlexDirection direction = layout.Direction;
skiaFlexLayout2.Direction = (int)direction switch
{
0 => FlexDirection.Row,
1 => FlexDirection.RowReverse,
2 => FlexDirection.Column,
3 => FlexDirection.ColumnReverse,
_ => FlexDirection.Row,
};
}
}
public static void MapWrap(FlexLayoutHandler handler, FlexLayout layout)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected I4, but got Unknown
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaFlexLayout skiaFlexLayout)
{
SkiaFlexLayout skiaFlexLayout2 = skiaFlexLayout;
FlexWrap wrap = layout.Wrap;
skiaFlexLayout2.Wrap = (int)wrap switch
{
0 => FlexWrap.NoWrap,
1 => FlexWrap.Wrap,
2 => FlexWrap.WrapReverse,
_ => FlexWrap.NoWrap,
};
}
}
public static void MapJustifyContent(FlexLayoutHandler handler, FlexLayout layout)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected I4, but got Unknown
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaFlexLayout skiaFlexLayout)
{
SkiaFlexLayout skiaFlexLayout2 = skiaFlexLayout;
FlexJustify justifyContent = layout.JustifyContent;
skiaFlexLayout2.JustifyContent = (justifyContent - 2) switch
{
1 => FlexJustify.Start,
0 => FlexJustify.Center,
2 => FlexJustify.End,
3 => FlexJustify.SpaceBetween,
4 => FlexJustify.SpaceAround,
5 => FlexJustify.SpaceEvenly,
_ => FlexJustify.Start,
};
}
}
public static void MapAlignItems(FlexLayoutHandler handler, FlexLayout layout)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Expected I4, but got Unknown
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaFlexLayout skiaFlexLayout)
{
SkiaFlexLayout skiaFlexLayout2 = skiaFlexLayout;
FlexAlignItems alignItems = layout.AlignItems;
skiaFlexLayout2.AlignItems = (alignItems - 1) switch
{
2 => FlexAlignItems.Start,
1 => FlexAlignItems.Center,
3 => FlexAlignItems.End,
0 => FlexAlignItems.Stretch,
_ => FlexAlignItems.Stretch,
};
}
}
public static void MapAlignContent(FlexLayoutHandler handler, FlexLayout layout)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Expected I4, but got Unknown
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaFlexLayout skiaFlexLayout)
{
SkiaFlexLayout skiaFlexLayout2 = skiaFlexLayout;
FlexAlignContent alignContent = layout.AlignContent;
skiaFlexLayout2.AlignContent = (alignContent - 1) switch
{
2 => FlexAlignContent.Start,
1 => FlexAlignContent.Center,
3 => FlexAlignContent.End,
0 => FlexAlignContent.Stretch,
4 => FlexAlignContent.SpaceBetween,
5 => FlexAlignContent.SpaceAround,
6 => FlexAlignContent.SpaceAround,
_ => FlexAlignContent.Stretch,
};
}
}
}

View File

@@ -1,36 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class FlyoutPageHandler : ViewHandler<IFlyoutView, SkiaFlyoutPage>
/// Handler for FlyoutPage on Linux using Skia rendering.
/// Maps IFlyoutView interface to SkiaFlyoutPage platform view.
/// </summary>
public partial class FlyoutPageHandler : ViewHandler<IFlyoutView, SkiaFlyoutPage>
{ {
public static IPropertyMapper<IFlyoutView, FlyoutPageHandler> Mapper = new PropertyMapper<IFlyoutView, FlyoutPageHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IFlyoutView, FlyoutPageHandler> Mapper = (IPropertyMapper<IFlyoutView, FlyoutPageHandler>)(object)new PropertyMapper<IFlyoutView, FlyoutPageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IFlyoutView.IsPresented)] = MapIsPresented, ["IsPresented"] = MapIsPresented,
[nameof(IFlyoutView.FlyoutWidth)] = MapFlyoutWidth, ["FlyoutWidth"] = MapFlyoutWidth,
[nameof(IFlyoutView.IsGestureEnabled)] = MapIsGestureEnabled, ["IsGestureEnabled"] = MapIsGestureEnabled,
[nameof(IFlyoutView.FlyoutBehavior)] = MapFlyoutBehavior, ["FlyoutBehavior"] = MapFlyoutBehavior
}; };
public static CommandMapper<IFlyoutView, FlyoutPageHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IFlyoutView, FlyoutPageHandler> CommandMapper = new CommandMapper<IFlyoutView, FlyoutPageHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public FlyoutPageHandler() : base(Mapper, CommandMapper) public FlyoutPageHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public FlyoutPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public FlyoutPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -55,37 +46,49 @@ public partial class FlyoutPageHandler : ViewHandler<IFlyoutView, SkiaFlyoutPage
private void OnIsPresentedChanged(object? sender, EventArgs e) private void OnIsPresentedChanged(object? sender, EventArgs e)
{ {
// Sync back to the virtual view
} }
public static void MapIsPresented(FlyoutPageHandler handler, IFlyoutView flyoutView) public static void MapIsPresented(FlyoutPageHandler handler, IFlyoutView flyoutView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView != null)
handler.PlatformView.IsPresented = flyoutView.IsPresented; {
((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView.IsPresented = flyoutView.IsPresented;
}
} }
public static void MapFlyoutWidth(FlyoutPageHandler handler, IFlyoutView flyoutView) public static void MapFlyoutWidth(FlyoutPageHandler handler, IFlyoutView flyoutView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView != null)
handler.PlatformView.FlyoutWidth = (float)flyoutView.FlyoutWidth; {
((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView.FlyoutWidth = (float)flyoutView.FlyoutWidth;
}
} }
public static void MapIsGestureEnabled(FlyoutPageHandler handler, IFlyoutView flyoutView) public static void MapIsGestureEnabled(FlyoutPageHandler handler, IFlyoutView flyoutView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView != null)
handler.PlatformView.GestureEnabled = flyoutView.IsGestureEnabled; {
((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView.GestureEnabled = flyoutView.IsGestureEnabled;
}
} }
public static void MapFlyoutBehavior(FlyoutPageHandler handler, IFlyoutView flyoutView) public static void MapFlyoutBehavior(FlyoutPageHandler handler, IFlyoutView flyoutView)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.FlyoutLayoutBehavior = flyoutView.FlyoutBehavior switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected I4, but got Unknown
if (((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView != null)
{ {
Microsoft.Maui.FlyoutBehavior.Disabled => FlyoutLayoutBehavior.Default, SkiaFlyoutPage platformView = ((ViewHandler<IFlyoutView, SkiaFlyoutPage>)(object)handler).PlatformView;
Microsoft.Maui.FlyoutBehavior.Flyout => FlyoutLayoutBehavior.Popover, FlyoutBehavior flyoutBehavior = flyoutView.FlyoutBehavior;
Microsoft.Maui.FlyoutBehavior.Locked => FlyoutLayoutBehavior.Split, platformView.FlyoutLayoutBehavior = (int)flyoutBehavior switch
_ => FlyoutLayoutBehavior.Default {
0 => FlyoutLayoutBehavior.Default,
1 => FlyoutLayoutBehavior.Popover,
2 => FlyoutLayoutBehavior.Split,
_ => FlyoutLayoutBehavior.Default,
}; };
} }
}
} }

View File

@@ -1,34 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class FrameHandler : ViewHandler<Frame, SkiaFrame>
/// Handler for Frame on Linux using SkiaFrame.
/// </summary>
public partial class FrameHandler : ViewHandler<Frame, SkiaFrame>
{ {
public static IPropertyMapper<Frame, FrameHandler> Mapper = public static IPropertyMapper<Frame, FrameHandler> Mapper = (IPropertyMapper<Frame, FrameHandler>)(object)new PropertyMapper<Frame, FrameHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<Frame, FrameHandler>(ViewMapper)
{ {
[nameof(Frame.BorderColor)] = MapBorderColor, ["BorderColor"] = MapBorderColor,
[nameof(Frame.CornerRadius)] = MapCornerRadius, ["CornerRadius"] = MapCornerRadius,
[nameof(Frame.HasShadow)] = MapHasShadow, ["HasShadow"] = MapHasShadow,
[nameof(Frame.BackgroundColor)] = MapBackgroundColor, ["BackgroundColor"] = MapBackgroundColor,
[nameof(Frame.Padding)] = MapPadding, ["Padding"] = MapPadding,
[nameof(Frame.Content)] = MapContent, ["Content"] = MapContent
}; };
public FrameHandler() : base(Mapper) public FrameHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)null)
{ {
} }
public FrameHandler(IPropertyMapper? mapper) public FrameHandler(IPropertyMapper? mapper)
: base(mapper ?? Mapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)null)
{ {
} }
@@ -37,67 +34,100 @@ public partial class FrameHandler : ViewHandler<Frame, SkiaFrame>
return new SkiaFrame(); return new SkiaFrame();
} }
protected override void ConnectHandler(SkiaFrame platformView)
{
base.ConnectHandler(platformView);
View virtualView = (View)(object)base.VirtualView;
if (virtualView != null)
{
platformView.MauiView = virtualView;
}
platformView.Tapped += OnPlatformViewTapped;
}
protected override void DisconnectHandler(SkiaFrame platformView)
{
platformView.Tapped -= OnPlatformViewTapped;
platformView.MauiView = null;
base.DisconnectHandler(platformView);
}
private void OnPlatformViewTapped(object? sender, EventArgs e)
{
View virtualView = (View)(object)base.VirtualView;
if (virtualView != null)
{
GestureManager.ProcessTap(virtualView, 0.0, 0.0);
}
}
public static void MapBorderColor(FrameHandler handler, Frame frame) public static void MapBorderColor(FrameHandler handler, Frame frame)
{ {
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
if (frame.BorderColor != null) if (frame.BorderColor != null)
{ {
handler.PlatformView.Stroke = new SKColor( ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.Stroke = new SKColor((byte)(frame.BorderColor.Red * 255f), (byte)(frame.BorderColor.Green * 255f), (byte)(frame.BorderColor.Blue * 255f), (byte)(frame.BorderColor.Alpha * 255f));
(byte)(frame.BorderColor.Red * 255),
(byte)(frame.BorderColor.Green * 255),
(byte)(frame.BorderColor.Blue * 255),
(byte)(frame.BorderColor.Alpha * 255));
} }
} }
public static void MapCornerRadius(FrameHandler handler, Frame frame) public static void MapCornerRadius(FrameHandler handler, Frame frame)
{ {
handler.PlatformView.CornerRadius = frame.CornerRadius; ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.CornerRadius = frame.CornerRadius;
} }
public static void MapHasShadow(FrameHandler handler, Frame frame) public static void MapHasShadow(FrameHandler handler, Frame frame)
{ {
handler.PlatformView.HasShadow = frame.HasShadow; ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.HasShadow = frame.HasShadow;
} }
public static void MapBackgroundColor(FrameHandler handler, Frame frame) public static void MapBackgroundColor(FrameHandler handler, Frame frame)
{ {
if (frame.BackgroundColor != null) //IL_0056: Unknown result type (might be due to invalid IL or missing references)
if (((VisualElement)frame).BackgroundColor != null)
{ {
handler.PlatformView.BackgroundColor = new SKColor( ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.BackgroundColor = new SKColor((byte)(((VisualElement)frame).BackgroundColor.Red * 255f), (byte)(((VisualElement)frame).BackgroundColor.Green * 255f), (byte)(((VisualElement)frame).BackgroundColor.Blue * 255f), (byte)(((VisualElement)frame).BackgroundColor.Alpha * 255f));
(byte)(frame.BackgroundColor.Red * 255),
(byte)(frame.BackgroundColor.Green * 255),
(byte)(frame.BackgroundColor.Blue * 255),
(byte)(frame.BackgroundColor.Alpha * 255));
} }
} }
public static void MapPadding(FrameHandler handler, Frame frame) public static void MapPadding(FrameHandler handler, Frame frame)
{ {
handler.PlatformView.SetPadding( //IL_0007: Unknown result type (might be due to invalid IL or missing references)
(float)frame.Padding.Left, //IL_000c: Unknown result type (might be due to invalid IL or missing references)
(float)frame.Padding.Top, //IL_0016: Unknown result type (might be due to invalid IL or missing references)
(float)frame.Padding.Right, //IL_001b: Unknown result type (might be due to invalid IL or missing references)
(float)frame.Padding.Bottom); //IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
SkiaFrame platformView = ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView;
Thickness padding = ((Layout)frame).Padding;
float left = (float)((Thickness)(ref padding)).Left;
padding = ((Layout)frame).Padding;
float top = (float)((Thickness)(ref padding)).Top;
padding = ((Layout)frame).Padding;
float right = (float)((Thickness)(ref padding)).Right;
padding = ((Layout)frame).Padding;
platformView.SetPadding(left, top, right, (float)((Thickness)(ref padding)).Bottom);
} }
public static void MapContent(FrameHandler handler, Frame frame) public static void MapContent(FrameHandler handler, Frame frame)
{ {
if (handler.PlatformView is null || handler.MauiContext is null) return; if (((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
{
handler.PlatformView.ClearChildren(); return;
}
var content = frame.Content; ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.ClearChildren();
View content = ((ContentView)frame).Content;
if (content != null) if (content != null)
{ {
// Create handler for content if it doesn't exist if (((VisualElement)content).Handler == null)
if (content.Handler == null)
{ {
content.Handler = content.ToHandler(handler.MauiContext); ((VisualElement)content).Handler = ((IView)(object)content).ToViewHandler(((ElementHandler)handler).MauiContext);
} }
IViewHandler handler2 = ((VisualElement)content).Handler;
if (content.Handler?.PlatformView is SkiaView skiaContent) if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView child)
{ {
handler.PlatformView.AddChild(skiaContent); ((ViewHandler<Frame, SkiaFrame>)(object)handler).PlatformView.AddChild(child);
} }
} }
} }

547
Handlers/GestureManager.cs Normal file
View File

@@ -0,0 +1,547 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Input;
using Microsoft.Maui.Controls;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public static class GestureManager
{
private class GestureTrackingState
{
public double StartX { get; set; }
public double StartY { get; set; }
public double CurrentX { get; set; }
public double CurrentY { get; set; }
public DateTime StartTime { get; set; }
public bool IsPanning { get; set; }
public bool IsPressed { get; set; }
}
private enum PointerEventType
{
Entered,
Exited,
Pressed,
Moved,
Released
}
private static MethodInfo? _sendTappedMethod;
private static readonly Dictionary<View, (DateTime lastTap, int tapCount)> _tapTracking = new Dictionary<View, (DateTime, int)>();
private static readonly Dictionary<View, GestureTrackingState> _gestureState = new Dictionary<View, GestureTrackingState>();
private const double SwipeMinDistance = 50.0;
private const double SwipeMaxTime = 500.0;
private const double SwipeDirectionThreshold = 0.5;
private const double PanMinDistance = 10.0;
public static bool ProcessTap(View? view, double x, double y)
{
if (view == null)
{
return false;
}
View val = view;
while (val != null)
{
IList<IGestureRecognizer> gestureRecognizers = val.GestureRecognizers;
if (gestureRecognizers != null && gestureRecognizers.Count > 0 && ProcessTapOnView(val, x, y))
{
return true;
}
Element parent = ((Element)val).Parent;
val = (View)(object)((parent is View) ? parent : null);
}
return false;
}
private static bool ProcessTapOnView(View view, double x, double y)
{
//IL_031f: Unknown result type (might be due to invalid IL or missing references)
//IL_0326: Expected O, but got Unknown
//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
//IL_03d2: Expected O, but got Unknown
//IL_026e: Unknown result type (might be due to invalid IL or missing references)
//IL_0275: Expected O, but got Unknown
IList<IGestureRecognizer> gestureRecognizers = view.GestureRecognizers;
if (gestureRecognizers == null || gestureRecognizers.Count == 0)
{
return false;
}
bool result = false;
foreach (IGestureRecognizer item in gestureRecognizers)
{
TapGestureRecognizer val = (TapGestureRecognizer)(object)((item is TapGestureRecognizer) ? item : null);
if (val == null)
{
continue;
}
Console.WriteLine($"[GestureManager] Processing TapGestureRecognizer on {((object)view).GetType().Name}, CommandParameter={val.CommandParameter}, NumberOfTapsRequired={val.NumberOfTapsRequired}");
int numberOfTapsRequired = val.NumberOfTapsRequired;
if (numberOfTapsRequired > 1)
{
DateTime utcNow = DateTime.UtcNow;
if (!_tapTracking.TryGetValue(view, out (DateTime, int) value))
{
_tapTracking[view] = (utcNow, 1);
Console.WriteLine($"[GestureManager] First tap 1/{numberOfTapsRequired}");
continue;
}
if (!((utcNow - value.Item1).TotalMilliseconds < 300.0))
{
_tapTracking[view] = (utcNow, 1);
Console.WriteLine($"[GestureManager] Tap timeout, reset to 1/{numberOfTapsRequired}");
continue;
}
int num = value.Item2 + 1;
if (num < numberOfTapsRequired)
{
_tapTracking[view] = (utcNow, num);
Console.WriteLine($"[GestureManager] Tap {num}/{numberOfTapsRequired}, waiting for more taps");
continue;
}
_tapTracking.Remove(view);
}
bool flag = false;
try
{
if ((object)_sendTappedMethod == null)
{
_sendTappedMethod = typeof(TapGestureRecognizer).GetMethod("SendTapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
}
if (_sendTappedMethod != null)
{
Console.WriteLine($"[GestureManager] Found SendTapped method with {_sendTappedMethod.GetParameters().Length} params");
TappedEventArgs e = new TappedEventArgs(val.CommandParameter);
_sendTappedMethod.Invoke(val, new object[2] { view, e });
Console.WriteLine("[GestureManager] SendTapped invoked successfully");
flag = true;
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] SendTapped failed: " + ex.Message);
}
if (!flag)
{
try
{
FieldInfo fieldInfo = typeof(TapGestureRecognizer).GetField("Tapped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(TapGestureRecognizer).GetField("_tapped", BindingFlags.Instance | BindingFlags.NonPublic);
if (fieldInfo != null && fieldInfo.GetValue(val) is EventHandler<TappedEventArgs> eventHandler)
{
Console.WriteLine("[GestureManager] Invoking Tapped event directly");
TappedEventArgs e2 = new TappedEventArgs(val.CommandParameter);
eventHandler(val, e2);
flag = true;
}
}
catch (Exception ex2)
{
Console.WriteLine("[GestureManager] Direct event invoke failed: " + ex2.Message);
}
}
if (!flag)
{
try
{
string[] array = new string[3] { "TappedEvent", "_TappedHandler", "<Tapped>k__BackingField" };
foreach (string text in array)
{
FieldInfo field = typeof(TapGestureRecognizer).GetField(text, BindingFlags.Instance | BindingFlags.NonPublic);
if (field != null)
{
Console.WriteLine("[GestureManager] Found field: " + text);
if (field.GetValue(val) is EventHandler<TappedEventArgs> eventHandler2)
{
TappedEventArgs e3 = new TappedEventArgs(val.CommandParameter);
eventHandler2(val, e3);
Console.WriteLine("[GestureManager] Event fired via " + text);
flag = true;
break;
}
}
}
}
catch (Exception ex3)
{
Console.WriteLine("[GestureManager] Backing field approach failed: " + ex3.Message);
}
}
if (!flag)
{
Console.WriteLine("[GestureManager] Could not fire event, dumping type info...");
MethodInfo[] methods = typeof(TapGestureRecognizer).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (MethodInfo methodInfo in methods)
{
if (methodInfo.Name.Contains("Tap", StringComparison.OrdinalIgnoreCase) || methodInfo.Name.Contains("Send", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine($"[GestureManager] Method: {methodInfo.Name}({string.Join(", ", from p in methodInfo.GetParameters()
select p.ParameterType.Name)})");
}
}
}
ICommand command = val.Command;
if (command != null && command.CanExecute(val.CommandParameter))
{
Console.WriteLine("[GestureManager] Executing Command");
val.Command.Execute(val.CommandParameter);
}
result = true;
}
return result;
}
public static bool HasGestureRecognizers(View? view)
{
if (view == null)
{
return false;
}
return view.GestureRecognizers?.Count > 0;
}
public static bool HasTapGestureRecognizer(View? view)
{
if (((view != null) ? view.GestureRecognizers : null) == null)
{
return false;
}
foreach (IGestureRecognizer gestureRecognizer in view.GestureRecognizers)
{
if (gestureRecognizer is TapGestureRecognizer)
{
return true;
}
}
return false;
}
public static void ProcessPointerDown(View? view, double x, double y)
{
if (view != null)
{
_gestureState[view] = new GestureTrackingState
{
StartX = x,
StartY = y,
CurrentX = x,
CurrentY = y,
StartTime = DateTime.UtcNow,
IsPanning = false,
IsPressed = true
};
ProcessPointerEvent(view, x, y, PointerEventType.Pressed);
}
}
public static void ProcessPointerMove(View? view, double x, double y)
{
if (view == null)
{
return;
}
if (!_gestureState.TryGetValue(view, out GestureTrackingState value))
{
ProcessPointerEvent(view, x, y, PointerEventType.Moved);
return;
}
value.CurrentX = x;
value.CurrentY = y;
if (!value.IsPressed)
{
ProcessPointerEvent(view, x, y, PointerEventType.Moved);
return;
}
double num = x - value.StartX;
double num2 = y - value.StartY;
if (Math.Sqrt(num * num + num2 * num2) >= 10.0)
{
ProcessPanGesture(view, num, num2, (GestureStatus)(value.IsPanning ? 1 : 0));
value.IsPanning = true;
}
ProcessPointerEvent(view, x, y, PointerEventType.Moved);
}
public static void ProcessPointerUp(View? view, double x, double y)
{
if (view == null)
{
return;
}
if (_gestureState.TryGetValue(view, out GestureTrackingState value))
{
value.CurrentX = x;
value.CurrentY = y;
double num = x - value.StartX;
double num2 = y - value.StartY;
double num3 = Math.Sqrt(num * num + num2 * num2);
double totalMilliseconds = (DateTime.UtcNow - value.StartTime).TotalMilliseconds;
if (num3 >= 50.0 && totalMilliseconds <= 500.0)
{
SwipeDirection swipeDirection = DetermineSwipeDirection(num, num2);
if (swipeDirection != SwipeDirection.Right)
{
ProcessSwipeGesture(view, swipeDirection);
}
else if (Math.Abs(num) > Math.Abs(num2) * 0.5)
{
ProcessSwipeGesture(view, (!(num > 0.0)) ? SwipeDirection.Left : SwipeDirection.Right);
}
}
if (value.IsPanning)
{
ProcessPanGesture(view, num, num2, (GestureStatus)2);
}
else if (num3 < 15.0 && totalMilliseconds < 500.0)
{
Console.WriteLine($"[GestureManager] Detected tap on {((object)view).GetType().Name} (distance={num3:F1}, elapsed={totalMilliseconds:F0}ms)");
ProcessTap(view, x, y);
}
_gestureState.Remove(view);
}
ProcessPointerEvent(view, x, y, PointerEventType.Released);
}
public static void ProcessPointerEntered(View? view, double x, double y)
{
if (view != null)
{
ProcessPointerEvent(view, x, y, PointerEventType.Entered);
}
}
public static void ProcessPointerExited(View? view, double x, double y)
{
if (view != null)
{
ProcessPointerEvent(view, x, y, PointerEventType.Exited);
}
}
private static SwipeDirection DetermineSwipeDirection(double deltaX, double deltaY)
{
double num = Math.Abs(deltaX);
double num2 = Math.Abs(deltaY);
if (num > num2 * 0.5)
{
if (!(deltaX > 0.0))
{
return SwipeDirection.Left;
}
return SwipeDirection.Right;
}
if (num2 > num * 0.5)
{
if (!(deltaY > 0.0))
{
return SwipeDirection.Up;
}
return SwipeDirection.Down;
}
if (!(deltaX > 0.0))
{
return SwipeDirection.Left;
}
return SwipeDirection.Right;
}
private static void ProcessSwipeGesture(View view, SwipeDirection direction)
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
IList<IGestureRecognizer> gestureRecognizers = view.GestureRecognizers;
if (gestureRecognizers == null)
{
return;
}
foreach (IGestureRecognizer item in gestureRecognizers)
{
SwipeGestureRecognizer val = (SwipeGestureRecognizer)(object)((item is SwipeGestureRecognizer) ? item : null);
if (val == null || !((Enum)val.Direction).HasFlag((Enum)direction))
{
continue;
}
Console.WriteLine($"[GestureManager] Swipe detected: {direction}");
try
{
MethodInfo method = typeof(SwipeGestureRecognizer).GetMethod("SendSwiped", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{
method.Invoke(val, new object[2] { view, direction });
Console.WriteLine("[GestureManager] SendSwiped invoked successfully");
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] SendSwiped failed: " + ex.Message);
}
ICommand command = val.Command;
if (command != null && command.CanExecute(val.CommandParameter))
{
val.Command.Execute(val.CommandParameter);
}
}
}
private static void ProcessPanGesture(View view, double totalX, double totalY, GestureStatus status)
{
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Expected I4, but got Unknown
IList<IGestureRecognizer> gestureRecognizers = view.GestureRecognizers;
if (gestureRecognizers == null)
{
return;
}
foreach (IGestureRecognizer item in gestureRecognizers)
{
PanGestureRecognizer val = (PanGestureRecognizer)(object)((item is PanGestureRecognizer) ? item : null);
if (val == null)
{
continue;
}
Console.WriteLine($"[GestureManager] Pan gesture: status={status}, totalX={totalX:F1}, totalY={totalY:F1}");
try
{
MethodInfo method = typeof(PanGestureRecognizer).GetMethod("SendPan", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{
method.Invoke(val, new object[4]
{
view,
totalX,
totalY,
(int)status
});
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] SendPan failed: " + ex.Message);
}
}
}
private static void ProcessPointerEvent(View view, double x, double y, PointerEventType eventType)
{
IList<IGestureRecognizer> gestureRecognizers = view.GestureRecognizers;
if (gestureRecognizers == null)
{
return;
}
foreach (IGestureRecognizer item in gestureRecognizers)
{
PointerGestureRecognizer val = (PointerGestureRecognizer)(object)((item is PointerGestureRecognizer) ? item : null);
if (val == null)
{
continue;
}
try
{
string text = eventType switch
{
PointerEventType.Entered => "SendPointerEntered",
PointerEventType.Exited => "SendPointerExited",
PointerEventType.Pressed => "SendPointerPressed",
PointerEventType.Moved => "SendPointerMoved",
PointerEventType.Released => "SendPointerReleased",
_ => null,
};
if (text != null)
{
MethodInfo method = typeof(PointerGestureRecognizer).GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (method != null)
{
object obj = CreatePointerEventArgs(view, x, y);
method.Invoke(val, new object[2] { view, obj });
}
}
}
catch (Exception ex)
{
Console.WriteLine("[GestureManager] Pointer event failed: " + ex.Message);
}
}
}
private static object CreatePointerEventArgs(View view, double x, double y)
{
try
{
Type type = typeof(PointerGestureRecognizer).Assembly.GetType("Microsoft.Maui.Controls.PointerEventArgs");
if (type != null)
{
ConstructorInfo constructorInfo = type.GetConstructors().FirstOrDefault();
if (constructorInfo != null)
{
return constructorInfo.Invoke(new object[0]);
}
}
}
catch
{
}
return null;
}
public static bool HasSwipeGestureRecognizer(View? view)
{
if (((view != null) ? view.GestureRecognizers : null) == null)
{
return false;
}
foreach (IGestureRecognizer gestureRecognizer in view.GestureRecognizers)
{
if (gestureRecognizer is SwipeGestureRecognizer)
{
return true;
}
}
return false;
}
public static bool HasPanGestureRecognizer(View? view)
{
if (((view != null) ? view.GestureRecognizers : null) == null)
{
return false;
}
foreach (IGestureRecognizer gestureRecognizer in view.GestureRecognizers)
{
if (gestureRecognizer is PanGestureRecognizer)
{
return true;
}
}
return false;
}
public static bool HasPointerGestureRecognizer(View? view)
{
if (((view != null) ? view.GestureRecognizers : null) == null)
{
return false;
}
foreach (IGestureRecognizer gestureRecognizer in view.GestureRecognizers)
{
if (gestureRecognizer is PointerGestureRecognizer)
{
return true;
}
}
return false;
}
}

View File

@@ -1,36 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class GraphicsViewHandler : ViewHandler<IGraphicsView, SkiaGraphicsView>
/// Handler for GraphicsView on Linux using Skia rendering.
/// Maps IGraphicsView interface to SkiaGraphicsView platform view.
/// IGraphicsView has: Drawable, Invalidate()
/// </summary>
public partial class GraphicsViewHandler : ViewHandler<IGraphicsView, SkiaGraphicsView>
{ {
public static IPropertyMapper<IGraphicsView, GraphicsViewHandler> Mapper = new PropertyMapper<IGraphicsView, GraphicsViewHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IGraphicsView, GraphicsViewHandler> Mapper = (IPropertyMapper<IGraphicsView, GraphicsViewHandler>)(object)new PropertyMapper<IGraphicsView, GraphicsViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IGraphicsView.Drawable)] = MapDrawable, ["Drawable"] = MapDrawable,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<IGraphicsView, GraphicsViewHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IGraphicsView, GraphicsViewHandler> CommandMapper = new CommandMapper<IGraphicsView, GraphicsViewHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper) { ["Invalidate"] = MapInvalidate };
{
[nameof(IGraphicsView.Invalidate)] = MapInvalidate,
};
public GraphicsViewHandler() : base(Mapper, CommandMapper) public GraphicsViewHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public GraphicsViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public GraphicsViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -41,22 +30,28 @@ public partial class GraphicsViewHandler : ViewHandler<IGraphicsView, SkiaGraphi
public static void MapDrawable(GraphicsViewHandler handler, IGraphicsView graphicsView) public static void MapDrawable(GraphicsViewHandler handler, IGraphicsView graphicsView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IGraphicsView, SkiaGraphicsView>)(object)handler).PlatformView != null)
handler.PlatformView.Drawable = graphicsView.Drawable; {
((ViewHandler<IGraphicsView, SkiaGraphicsView>)(object)handler).PlatformView.Drawable = graphicsView.Drawable;
}
} }
public static void MapBackground(GraphicsViewHandler handler, IGraphicsView graphicsView) public static void MapBackground(GraphicsViewHandler handler, IGraphicsView graphicsView)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IGraphicsView, SkiaGraphicsView>)(object)handler).PlatformView != null)
if (graphicsView.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)graphicsView).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IGraphicsView, SkiaGraphicsView>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapInvalidate(GraphicsViewHandler handler, IGraphicsView graphicsView, object? args) public static void MapInvalidate(GraphicsViewHandler handler, IGraphicsView graphicsView, object? args)
{ {
handler.PlatformView?.Invalidate(); ((ViewHandler<IGraphicsView, SkiaGraphicsView>)(object)handler).PlatformView?.Invalidate();
} }
} }

170
Handlers/GridHandler.cs Normal file
View File

@@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class GridHandler : LayoutHandler
{
public new static IPropertyMapper<IGridLayout, GridHandler> Mapper = (IPropertyMapper<IGridLayout, GridHandler>)(object)new PropertyMapper<IGridLayout, GridHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)LayoutHandler.Mapper })
{
["RowSpacing"] = MapRowSpacing,
["ColumnSpacing"] = MapColumnSpacing,
["RowDefinitions"] = MapRowDefinitions,
["ColumnDefinitions"] = MapColumnDefinitions
};
public GridHandler()
: base((IPropertyMapper?)(object)Mapper)
{
}
protected override SkiaLayoutView CreatePlatformView()
{
return new SkiaGrid();
}
protected override void ConnectHandler(SkiaLayoutView platformView)
{
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
try
{
ILayout virtualView = ((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView;
IGridLayout val = (IGridLayout)(object)((virtualView is IGridLayout) ? virtualView : null);
if (val == null || ((ElementHandler)this).MauiContext == null || !(platformView is SkiaGrid skiaGrid))
{
return;
}
Console.WriteLine($"[GridHandler] ConnectHandler: {((ICollection<IView>)val).Count} children, {val.RowDefinitions.Count} rows, {val.ColumnDefinitions.Count} cols");
ILayout virtualView2 = ((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView;
VisualElement val2 = (VisualElement)(object)((virtualView2 is VisualElement) ? virtualView2 : null);
if (val2 != null && val2.BackgroundColor != null)
{
platformView.BackgroundColor = val2.BackgroundColor.ToSKColor();
}
IPadding virtualView3 = (IPadding)(object)((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView;
if (virtualView3 != null)
{
Thickness padding = virtualView3.Padding;
platformView.Padding = new SKRect((float)((Thickness)(ref padding)).Left, (float)((Thickness)(ref padding)).Top, (float)((Thickness)(ref padding)).Right, (float)((Thickness)(ref padding)).Bottom);
Console.WriteLine($"[GridHandler] Applied Padding: L={((Thickness)(ref padding)).Left}, T={((Thickness)(ref padding)).Top}, R={((Thickness)(ref padding)).Right}, B={((Thickness)(ref padding)).Bottom}");
}
MapRowDefinitions(this, val);
MapColumnDefinitions(this, val);
for (int i = 0; i < ((ICollection<IView>)val).Count; i++)
{
IView val3 = ((IList<IView>)val)[i];
if (val3 != null)
{
Console.WriteLine($"[GridHandler] Processing child {i}: {((object)val3).GetType().Name}");
if (val3.Handler == null)
{
val3.Handler = val3.ToViewHandler(((ElementHandler)this).MauiContext);
}
int num = 0;
int num2 = 0;
int rowSpan = 1;
int columnSpan = 1;
View val4 = (View)(object)((val3 is View) ? val3 : null);
if (val4 != null)
{
num = Grid.GetRow((BindableObject)(object)val4);
num2 = Grid.GetColumn((BindableObject)(object)val4);
rowSpan = Grid.GetRowSpan((BindableObject)(object)val4);
columnSpan = Grid.GetColumnSpan((BindableObject)(object)val4);
}
Console.WriteLine($"[GridHandler] Child {i} at row={num}, col={num2}, handler={((object)val3.Handler)?.GetType().Name}");
IViewHandler handler = val3.Handler;
if (((handler != null) ? ((IElementHandler)handler).PlatformView : null) is SkiaView child)
{
skiaGrid.AddChild(child, num, num2, rowSpan, columnSpan);
Console.WriteLine($"[GridHandler] Added child {i} to grid");
}
}
}
Console.WriteLine("[GridHandler] ConnectHandler complete");
}
catch (Exception ex)
{
Console.WriteLine("[GridHandler] EXCEPTION in ConnectHandler: " + ex.GetType().Name + ": " + ex.Message);
Console.WriteLine("[GridHandler] Stack trace: " + ex.StackTrace);
throw;
}
}
public static void MapRowSpacing(GridHandler handler, IGridLayout layout)
{
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaGrid skiaGrid)
{
skiaGrid.RowSpacing = (float)layout.RowSpacing;
}
}
public static void MapColumnSpacing(GridHandler handler, IGridLayout layout)
{
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaGrid skiaGrid)
{
skiaGrid.ColumnSpacing = (float)layout.ColumnSpacing;
}
}
public static void MapRowDefinitions(GridHandler handler, IGridLayout layout)
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (!(((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaGrid skiaGrid))
{
return;
}
skiaGrid.RowDefinitions.Clear();
foreach (IGridRowDefinition rowDefinition in layout.RowDefinitions)
{
GridLength height = rowDefinition.Height;
if (((GridLength)(ref height)).IsAbsolute)
{
skiaGrid.RowDefinitions.Add(new GridLength((float)((GridLength)(ref height)).Value));
}
else if (((GridLength)(ref height)).IsAuto)
{
skiaGrid.RowDefinitions.Add(GridLength.Auto);
}
else
{
skiaGrid.RowDefinitions.Add(new GridLength((float)((GridLength)(ref height)).Value, GridUnitType.Star));
}
}
}
public static void MapColumnDefinitions(GridHandler handler, IGridLayout layout)
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (!(((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaGrid skiaGrid))
{
return;
}
skiaGrid.ColumnDefinitions.Clear();
foreach (IGridColumnDefinition columnDefinition in layout.ColumnDefinitions)
{
GridLength width = columnDefinition.Width;
if (((GridLength)(ref width)).IsAbsolute)
{
skiaGrid.ColumnDefinitions.Add(new GridLength((float)((GridLength)(ref width)).Value));
}
else if (((GridLength)(ref width)).IsAuto)
{
skiaGrid.ColumnDefinitions.Add(GridLength.Auto);
}
else
{
skiaGrid.ColumnDefinitions.Add(new GridLength((float)((GridLength)(ref width)).Value, GridUnitType.Star));
}
}
}
}

View File

@@ -0,0 +1,240 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Native;
using Microsoft.Maui.Platform.Linux.Services;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class GtkWebViewHandler : ViewHandler<IWebView, GtkWebViewProxy>
{
private GtkWebViewPlatformView? _platformWebView;
private bool _isRegisteredWithHost;
private SKRect _lastBounds;
public static IPropertyMapper<IWebView, GtkWebViewHandler> Mapper = (IPropertyMapper<IWebView, GtkWebViewHandler>)(object)new PropertyMapper<IWebView, GtkWebViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper }) { ["Source"] = MapSource };
public static CommandMapper<IWebView, GtkWebViewHandler> CommandMapper = new CommandMapper<IWebView, GtkWebViewHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper)
{
["GoBack"] = MapGoBack,
["GoForward"] = MapGoForward,
["Reload"] = MapReload
};
public GtkWebViewHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{
}
public GtkWebViewHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null)
: base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{
}
protected override GtkWebViewProxy CreatePlatformView()
{
_platformWebView = new GtkWebViewPlatformView();
return new GtkWebViewProxy(this, _platformWebView);
}
protected override void ConnectHandler(GtkWebViewProxy platformView)
{
base.ConnectHandler(platformView);
if (_platformWebView != null)
{
_platformWebView.NavigationStarted += OnNavigationStarted;
_platformWebView.NavigationCompleted += OnNavigationCompleted;
}
Console.WriteLine("[GtkWebViewHandler] ConnectHandler - WebView ready");
}
protected override void DisconnectHandler(GtkWebViewProxy platformView)
{
if (_platformWebView != null)
{
_platformWebView.NavigationStarted -= OnNavigationStarted;
_platformWebView.NavigationCompleted -= OnNavigationCompleted;
UnregisterFromHost();
_platformWebView.Dispose();
_platformWebView = null;
}
base.DisconnectHandler(platformView);
}
private void OnNavigationStarted(object? sender, string uri)
{
Console.WriteLine("[GtkWebViewHandler] Navigation started: " + uri);
try
{
GLibNative.IdleAdd(delegate
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
try
{
IWebView virtualView = base.VirtualView;
IWebViewController val = (IWebViewController)(object)((virtualView is IWebViewController) ? virtualView : null);
if (val != null)
{
WebNavigatingEventArgs e = new WebNavigatingEventArgs((WebNavigationEvent)3, (WebViewSource)null, uri);
val.SendNavigating(e);
Console.WriteLine("[GtkWebViewHandler] Sent Navigating event to VirtualView");
}
}
catch (Exception ex2)
{
Console.WriteLine("[GtkWebViewHandler] Error in SendNavigating: " + ex2.Message);
}
return false;
});
}
catch (Exception ex)
{
Console.WriteLine("[GtkWebViewHandler] Error dispatching navigation started: " + ex.Message);
}
}
private void OnNavigationCompleted(object? sender, (string Url, bool Success) e)
{
Console.WriteLine($"[GtkWebViewHandler] Navigation completed: {e.Url} (Success: {e.Success})");
try
{
GLibNative.IdleAdd(delegate
{
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
try
{
IWebView virtualView = base.VirtualView;
IWebViewController val = (IWebViewController)(object)((virtualView is IWebViewController) ? virtualView : null);
if (val != null)
{
WebNavigationResult val2 = (WebNavigationResult)(e.Success ? 1 : 4);
WebNavigatedEventArgs e2 = new WebNavigatedEventArgs((WebNavigationEvent)3, (WebViewSource)null, e.Url, val2);
val.SendNavigated(e2);
bool flag = _platformWebView?.CanGoBack() ?? false;
bool flag2 = _platformWebView?.CanGoForward() ?? false;
val.CanGoBack = flag;
val.CanGoForward = flag2;
Console.WriteLine($"[GtkWebViewHandler] Sent Navigated, CanGoBack={flag}, CanGoForward={flag2}");
}
}
catch (Exception ex2)
{
Console.WriteLine("[GtkWebViewHandler] Error in SendNavigated: " + ex2.Message);
}
return false;
});
}
catch (Exception ex)
{
Console.WriteLine("[GtkWebViewHandler] Error dispatching navigation completed: " + ex.Message);
}
}
internal void RegisterWithHost(SKRect bounds)
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_011e: Unknown result type (might be due to invalid IL or missing references)
//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
if (_platformWebView == null)
{
return;
}
GtkHostService instance = GtkHostService.Instance;
if (instance.HostWindow == null || instance.WebViewManager == null)
{
Console.WriteLine("[GtkWebViewHandler] Warning: GTK host not initialized, cannot register WebView");
return;
}
int num = (int)((SKRect)(ref bounds)).Left;
int num2 = (int)((SKRect)(ref bounds)).Top;
int num3 = (int)((SKRect)(ref bounds)).Width;
int num4 = (int)((SKRect)(ref bounds)).Height;
if (num3 <= 0 || num4 <= 0)
{
Console.WriteLine($"[GtkWebViewHandler] Skipping invalid bounds: {bounds}");
return;
}
if (!_isRegisteredWithHost)
{
instance.HostWindow.AddWebView(_platformWebView.Widget, num, num2, num3, num4);
_isRegisteredWithHost = true;
Console.WriteLine($"[GtkWebViewHandler] Registered WebView at ({num}, {num2}) size {num3}x{num4}");
}
else if (bounds != _lastBounds)
{
instance.HostWindow.MoveResizeWebView(_platformWebView.Widget, num, num2, num3, num4);
Console.WriteLine($"[GtkWebViewHandler] Updated WebView to ({num}, {num2}) size {num3}x{num4}");
}
_lastBounds = bounds;
}
private void UnregisterFromHost()
{
if (_isRegisteredWithHost && _platformWebView != null)
{
GtkHostService instance = GtkHostService.Instance;
if (instance.HostWindow != null)
{
instance.HostWindow.RemoveWebView(_platformWebView.Widget);
Console.WriteLine("[GtkWebViewHandler] Unregistered WebView from host");
}
_isRegisteredWithHost = false;
}
}
public static void MapSource(GtkWebViewHandler handler, IWebView webView)
{
if (handler._platformWebView == null)
{
return;
}
IWebViewSource source = webView.Source;
Console.WriteLine("[GtkWebViewHandler] MapSource: " + (((object)source)?.GetType().Name ?? "null"));
UrlWebViewSource val = (UrlWebViewSource)(object)((source is UrlWebViewSource) ? source : null);
if (val != null)
{
string url = val.Url;
if (!string.IsNullOrEmpty(url))
{
handler._platformWebView.Navigate(url);
}
return;
}
HtmlWebViewSource val2 = (HtmlWebViewSource)(object)((source is HtmlWebViewSource) ? source : null);
if (val2 != null)
{
string html = val2.Html;
if (!string.IsNullOrEmpty(html))
{
handler._platformWebView.LoadHtml(html, val2.BaseUrl);
}
}
}
public static void MapGoBack(GtkWebViewHandler handler, IWebView webView, object? args)
{
Console.WriteLine($"[GtkWebViewHandler] MapGoBack called, CanGoBack={handler._platformWebView?.CanGoBack()}");
handler._platformWebView?.GoBack();
}
public static void MapGoForward(GtkWebViewHandler handler, IWebView webView, object? args)
{
Console.WriteLine($"[GtkWebViewHandler] MapGoForward called, CanGoForward={handler._platformWebView?.CanGoForward()}");
handler._platformWebView?.GoForward();
}
public static void MapReload(GtkWebViewHandler handler, IWebView webView, object? args)
{
Console.WriteLine("[GtkWebViewHandler] MapReload called");
handler._platformWebView?.Reload();
}
}

View File

@@ -0,0 +1,61 @@
using System.Collections.Generic;
using Microsoft.Maui.Platform.Linux.Window;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public sealed class GtkWebViewManager
{
private readonly GtkHostWindow _host;
private readonly Dictionary<object, GtkWebViewPlatformView> _webViews = new Dictionary<object, GtkWebViewPlatformView>();
public GtkWebViewManager(GtkHostWindow host)
{
_host = host;
}
public GtkWebViewPlatformView CreateWebView(object key, int x, int y, int width, int height)
{
GtkWebViewPlatformView gtkWebViewPlatformView = new GtkWebViewPlatformView();
_webViews[key] = gtkWebViewPlatformView;
_host.AddWebView(gtkWebViewPlatformView.Widget, x, y, width, height);
return gtkWebViewPlatformView;
}
public void UpdateLayout(object key, int x, int y, int width, int height)
{
if (_webViews.TryGetValue(key, out GtkWebViewPlatformView value))
{
_host.MoveResizeWebView(value.Widget, x, y, width, height);
}
}
public GtkWebViewPlatformView? GetWebView(object key)
{
if (!_webViews.TryGetValue(key, out GtkWebViewPlatformView value))
{
return null;
}
return value;
}
public void RemoveWebView(object key)
{
if (_webViews.TryGetValue(key, out GtkWebViewPlatformView value))
{
_host.RemoveWebView(value.Widget);
value.Dispose();
_webViews.Remove(key);
}
}
public void Clear()
{
foreach (KeyValuePair<object, GtkWebViewPlatformView> webView in _webViews)
{
_host.RemoveWebView(webView.Value.Widget);
webView.Value.Dispose();
}
_webViews.Clear();
}
}

View File

@@ -0,0 +1,183 @@
using System;
using Microsoft.Maui.Platform.Linux.Native;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public sealed class GtkWebViewPlatformView : IDisposable
{
private IntPtr _widget;
private bool _disposed;
private string? _currentUri;
private ulong _loadChangedSignalId;
private WebKitNative.LoadChangedCallback? _loadChangedCallback;
public IntPtr Widget => _widget;
public string? CurrentUri => _currentUri;
public event EventHandler<string>? NavigationStarted;
public event EventHandler<(string Url, bool Success)>? NavigationCompleted;
public event EventHandler<string>? TitleChanged;
public GtkWebViewPlatformView()
{
if (!WebKitNative.Initialize())
{
throw new InvalidOperationException("Failed to initialize WebKitGTK. Is libwebkit2gtk-4.x installed?");
}
_widget = WebKitNative.WebViewNew();
if (_widget == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to create WebKitWebView widget");
}
WebKitNative.ConfigureSettings(_widget);
_loadChangedCallback = OnLoadChanged;
_loadChangedSignalId = WebKitNative.ConnectLoadChanged(_widget, _loadChangedCallback);
Console.WriteLine("[GtkWebViewPlatformView] Created WebKitWebView widget");
}
private void OnLoadChanged(IntPtr webView, int loadEvent, IntPtr userData)
{
try
{
string text = WebKitNative.GetUri(webView) ?? _currentUri ?? "";
switch ((WebKitNative.WebKitLoadEvent)loadEvent)
{
case WebKitNative.WebKitLoadEvent.Started:
Console.WriteLine("[GtkWebViewPlatformView] Load started: " + text);
this.NavigationStarted?.Invoke(this, text);
break;
case WebKitNative.WebKitLoadEvent.Finished:
_currentUri = text;
Console.WriteLine("[GtkWebViewPlatformView] Load finished: " + text);
this.NavigationCompleted?.Invoke(this, (text, true));
break;
case WebKitNative.WebKitLoadEvent.Committed:
_currentUri = text;
Console.WriteLine("[GtkWebViewPlatformView] Load committed: " + text);
break;
case WebKitNative.WebKitLoadEvent.Redirected:
break;
}
}
catch (Exception ex)
{
Console.WriteLine("[GtkWebViewPlatformView] Error in OnLoadChanged: " + ex.Message);
Console.WriteLine("[GtkWebViewPlatformView] Stack trace: " + ex.StackTrace);
}
}
public void Navigate(string uri)
{
if (_widget != IntPtr.Zero)
{
WebKitNative.LoadUri(_widget, uri);
Console.WriteLine("[GtkWebViewPlatformView] Navigate to: " + uri);
}
}
public void LoadHtml(string html, string? baseUri = null)
{
if (_widget != IntPtr.Zero)
{
WebKitNative.LoadHtml(_widget, html, baseUri);
Console.WriteLine("[GtkWebViewPlatformView] Load HTML content");
}
}
public void GoBack()
{
if (_widget != IntPtr.Zero)
{
WebKitNative.GoBack(_widget);
}
}
public void GoForward()
{
if (_widget != IntPtr.Zero)
{
WebKitNative.GoForward(_widget);
}
}
public bool CanGoBack()
{
if (_widget == IntPtr.Zero)
{
return false;
}
return WebKitNative.CanGoBack(_widget);
}
public bool CanGoForward()
{
if (_widget == IntPtr.Zero)
{
return false;
}
return WebKitNative.CanGoForward(_widget);
}
public void Reload()
{
if (_widget != IntPtr.Zero)
{
WebKitNative.Reload(_widget);
}
}
public void Stop()
{
if (_widget != IntPtr.Zero)
{
WebKitNative.StopLoading(_widget);
}
}
public string? GetTitle()
{
if (_widget == IntPtr.Zero)
{
return null;
}
return WebKitNative.GetTitle(_widget);
}
public string? GetUri()
{
if (_widget == IntPtr.Zero)
{
return null;
}
return WebKitNative.GetUri(_widget);
}
public void SetJavascriptEnabled(bool enabled)
{
if (_widget != IntPtr.Zero)
{
WebKitNative.SetJavascriptEnabled(_widget, enabled);
}
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
if (_widget != IntPtr.Zero)
{
WebKitNative.DisconnectLoadChanged(_widget);
}
_widget = IntPtr.Zero;
_loadChangedCallback = null;
}
}
}

104
Handlers/GtkWebViewProxy.cs Normal file
View File

@@ -0,0 +1,104 @@
using System;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class GtkWebViewProxy : SkiaView
{
private readonly GtkWebViewHandler _handler;
private readonly GtkWebViewPlatformView _platformView;
public GtkWebViewPlatformView PlatformView => _platformView;
public bool CanGoBack => _platformView.CanGoBack();
public bool CanGoForward => _platformView.CanGoForward();
public GtkWebViewProxy(GtkWebViewHandler handler, GtkWebViewPlatformView platformView)
{
_handler = handler;
_platformView = platformView;
}
public override void Arrange(SKRect bounds)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
base.Arrange(bounds);
SKRect bounds2 = TransformToWindow(bounds);
_handler.RegisterWithHost(bounds2);
}
private SKRect TransformToWindow(SKRect localBounds)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
float num = ((SKRect)(ref localBounds)).Left;
float num2 = ((SKRect)(ref localBounds)).Top;
for (SkiaView parent = base.Parent; parent != null; parent = parent.Parent)
{
float num3 = num;
SKRect bounds = parent.Bounds;
num = num3 + ((SKRect)(ref bounds)).Left;
float num4 = num2;
bounds = parent.Bounds;
num2 = num4 + ((SKRect)(ref bounds)).Top;
}
return new SKRect(num, num2, num + ((SKRect)(ref localBounds)).Width, num2 + ((SKRect)(ref localBounds)).Height);
}
public override void Draw(SKCanvas canvas)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
SKPaint val = new SKPaint
{
Color = new SKColor((byte)0, (byte)0, (byte)0, (byte)0),
Style = (SKPaintStyle)0
};
try
{
canvas.DrawRect(base.Bounds, val);
}
finally
{
((IDisposable)val)?.Dispose();
}
}
public void Navigate(string url)
{
_platformView.Navigate(url);
}
public void LoadHtml(string html, string? baseUrl = null)
{
_platformView.LoadHtml(html, baseUrl);
}
public void GoBack()
{
_platformView.GoBack();
}
public void GoForward()
{
_platformView.GoForward();
}
public void Reload()
{
_platformView.Reload();
}
}

View File

@@ -1,41 +1,114 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.IO;
using System.Threading;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ImageButtonHandler : ViewHandler<IImageButton, SkiaImageButton>
/// Handler for ImageButton on Linux using Skia rendering.
/// Maps IImageButton interface to SkiaImageButton platform view.
/// IImageButton extends: IImage, IView, IButtonStroke, IPadding
/// </summary>
public partial class ImageButtonHandler : ViewHandler<IImageButton, SkiaImageButton>
{ {
public static IPropertyMapper<IImageButton, ImageButtonHandler> Mapper = new PropertyMapper<IImageButton, ImageButtonHandler>(ViewHandler.ViewMapper) internal class ImageSourceServiceResultManager
{ {
[nameof(IImage.Aspect)] = MapAspect, private readonly ImageButtonHandler _handler;
[nameof(IImage.IsOpaque)] = MapIsOpaque,
[nameof(IImageSourcePart.Source)] = MapSource, private CancellationTokenSource? _cts;
[nameof(IButtonStroke.StrokeColor)] = MapStrokeColor,
[nameof(IButtonStroke.StrokeThickness)] = MapStrokeThickness, public ImageSourceServiceResultManager(ImageButtonHandler handler)
[nameof(IButtonStroke.CornerRadius)] = MapCornerRadius, {
[nameof(IPadding.Padding)] = MapPadding, _handler = handler;
[nameof(IView.Background)] = MapBackground, }
public async void UpdateImageSourceAsync()
{
_cts?.Cancel();
_cts = new CancellationTokenSource();
CancellationToken token = _cts.Token;
try
{
IImageButton virtualView = ((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).VirtualView;
IImageSource val = ((virtualView != null) ? ((IImageSourcePart)virtualView).Source : null);
if (val == null)
{
((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).PlatformView?.LoadFromData(Array.Empty<byte>());
return;
}
IImageSourcePart virtualView2 = (IImageSourcePart)(object)((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).VirtualView;
if (virtualView2 != null)
{
virtualView2.UpdateIsLoading(true);
}
IFileImageSource val2 = (IFileImageSource)(object)((val is IFileImageSource) ? val : null);
if (val2 != null)
{
string file = val2.File;
if (!string.IsNullOrEmpty(file))
{
await ((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).PlatformView.LoadFromFileAsync(file);
}
return;
}
IUriImageSource val3 = (IUriImageSource)(object)((val is IUriImageSource) ? val : null);
if (val3 != null)
{
Uri uri = val3.Uri;
if (uri != null)
{
await ((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).PlatformView.LoadFromUriAsync(uri);
}
return;
}
IStreamImageSource val4 = (IStreamImageSource)(object)((val is IStreamImageSource) ? val : null);
if (val4 != null)
{
Stream stream = await val4.GetStreamAsync(token);
if (stream != null)
{
await ((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).PlatformView.LoadFromStreamAsync(stream);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception)
{
IImageSourcePart virtualView3 = (IImageSourcePart)(object)((ViewHandler<IImageButton, SkiaImageButton>)(object)_handler).VirtualView;
if (virtualView3 != null)
{
virtualView3.UpdateIsLoading(false);
}
}
}
}
public static IPropertyMapper<IImageButton, ImageButtonHandler> Mapper = (IPropertyMapper<IImageButton, ImageButtonHandler>)(object)new PropertyMapper<IImageButton, ImageButtonHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{
["Aspect"] = MapAspect,
["IsOpaque"] = MapIsOpaque,
["Source"] = MapSource,
["StrokeColor"] = MapStrokeColor,
["StrokeThickness"] = MapStrokeThickness,
["CornerRadius"] = MapCornerRadius,
["Padding"] = MapPadding,
["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor
}; };
public static CommandMapper<IImageButton, ImageButtonHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IImageButton, ImageButtonHandler> CommandMapper = new CommandMapper<IImageButton, ImageButtonHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ImageButtonHandler() : base(Mapper, CommandMapper) private ImageSourceServiceResultManager _sourceLoader;
private ImageSourceServiceResultManager SourceLoader => _sourceLoader ?? (_sourceLoader = new ImageSourceServiceResultManager(this));
public ImageButtonHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ImageButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ImageButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -66,167 +139,136 @@ public partial class ImageButtonHandler : ViewHandler<IImageButton, SkiaImageBut
private void OnClicked(object? sender, EventArgs e) private void OnClicked(object? sender, EventArgs e)
{ {
VirtualView?.Clicked(); IImageButton virtualView = base.VirtualView;
if (virtualView != null)
{
((IButton)virtualView).Clicked();
}
} }
private void OnPressed(object? sender, EventArgs e) private void OnPressed(object? sender, EventArgs e)
{ {
VirtualView?.Pressed(); IImageButton virtualView = base.VirtualView;
if (virtualView != null)
{
((IButton)virtualView).Pressed();
}
} }
private void OnReleased(object? sender, EventArgs e) private void OnReleased(object? sender, EventArgs e)
{ {
VirtualView?.Released(); IImageButton virtualView = base.VirtualView;
if (virtualView != null)
{
((IButton)virtualView).Released();
}
} }
private void OnImageLoaded(object? sender, EventArgs e) private void OnImageLoaded(object? sender, EventArgs e)
{ {
if (VirtualView is IImageSourcePart imageSourcePart) IImageSourcePart virtualView = (IImageSourcePart)(object)base.VirtualView;
if (virtualView != null)
{ {
imageSourcePart.UpdateIsLoading(false); virtualView.UpdateIsLoading(false);
} }
} }
private void OnImageLoadingError(object? sender, ImageLoadingErrorEventArgs e) private void OnImageLoadingError(object? sender, ImageLoadingErrorEventArgs e)
{ {
if (VirtualView is IImageSourcePart imageSourcePart) IImageSourcePart virtualView = (IImageSourcePart)(object)base.VirtualView;
if (virtualView != null)
{ {
imageSourcePart.UpdateIsLoading(false); virtualView.UpdateIsLoading(false);
} }
} }
public static void MapAspect(ImageButtonHandler handler, IImageButton imageButton) public static void MapAspect(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.Aspect = imageButton.Aspect; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
{
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.Aspect = ((IImage)imageButton).Aspect;
}
} }
public static void MapIsOpaque(ImageButtonHandler handler, IImageButton imageButton) public static void MapIsOpaque(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
handler.PlatformView.IsOpaque = imageButton.IsOpaque; {
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.IsOpaque = ((IImage)imageButton).IsOpaque;
}
} }
public static void MapSource(ImageButtonHandler handler, IImageButton imageButton) public static void MapSource(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
{
handler.SourceLoader.UpdateImageSourceAsync(); handler.SourceLoader.UpdateImageSourceAsync();
} }
}
public static void MapStrokeColor(ImageButtonHandler handler, IImageButton imageButton) public static void MapStrokeColor(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null && ((IButtonStroke)imageButton).StrokeColor != null)
if (imageButton.StrokeColor is not null) {
handler.PlatformView.StrokeColor = imageButton.StrokeColor.ToSKColor(); ((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.StrokeColor = ((IButtonStroke)imageButton).StrokeColor.ToSKColor();
}
} }
public static void MapStrokeThickness(ImageButtonHandler handler, IImageButton imageButton) public static void MapStrokeThickness(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
handler.PlatformView.StrokeThickness = (float)imageButton.StrokeThickness; {
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.StrokeThickness = (float)((IButtonStroke)imageButton).StrokeThickness;
}
} }
public static void MapCornerRadius(ImageButtonHandler handler, IImageButton imageButton) public static void MapCornerRadius(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
handler.PlatformView.CornerRadius = imageButton.CornerRadius; {
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.CornerRadius = ((IButtonStroke)imageButton).CornerRadius;
}
} }
public static void MapPadding(ImageButtonHandler handler, IImageButton imageButton) public static void MapPadding(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var padding = imageButton.Padding; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
handler.PlatformView.PaddingLeft = (float)padding.Left; {
handler.PlatformView.PaddingTop = (float)padding.Top; Thickness padding = ((IPadding)imageButton).Padding;
handler.PlatformView.PaddingRight = (float)padding.Right; ((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.PaddingLeft = (float)((Thickness)(ref padding)).Left;
handler.PlatformView.PaddingBottom = (float)padding.Bottom; ((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.PaddingTop = (float)((Thickness)(ref padding)).Top;
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.PaddingRight = (float)((Thickness)(ref padding)).Right;
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.PaddingBottom = (float)((Thickness)(ref padding)).Bottom;
}
} }
public static void MapBackground(ImageButtonHandler handler, IImageButton imageButton) public static void MapBackground(ImageButtonHandler handler, IImageButton imageButton)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
if (imageButton.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)imageButton).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
// Image source loading helper public static void MapBackgroundColor(ImageButtonHandler handler, IImageButton imageButton)
private ImageSourceServiceResultManager _sourceLoader = null!;
private ImageSourceServiceResultManager SourceLoader =>
_sourceLoader ??= new ImageSourceServiceResultManager(this);
internal class ImageSourceServiceResultManager
{ {
private readonly ImageButtonHandler _handler; //IL_0027: Unknown result type (might be due to invalid IL or missing references)
private CancellationTokenSource? _cts; if (((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView != null)
public ImageSourceServiceResultManager(ImageButtonHandler handler)
{ {
_handler = handler; ImageButton val = (ImageButton)(object)((imageButton is ImageButton) ? imageButton : null);
} if (val != null && ((VisualElement)val).BackgroundColor != null)
public async void UpdateImageSourceAsync()
{ {
_cts?.Cancel(); ((ViewHandler<IImageButton, SkiaImageButton>)(object)handler).PlatformView.BackgroundColor = ((VisualElement)val).BackgroundColor.ToSKColor();
_cts = new CancellationTokenSource();
var token = _cts.Token;
try
{
var source = _handler.VirtualView?.Source;
if (source == null)
{
_handler.PlatformView?.LoadFromData(Array.Empty<byte>());
return;
}
if (_handler.VirtualView is IImageSourcePart imageSourcePart)
{
imageSourcePart.UpdateIsLoading(true);
}
// Handle different image source types
if (source is IFileImageSource fileSource)
{
var file = fileSource.File;
if (!string.IsNullOrEmpty(file))
{
await _handler.PlatformView!.LoadFromFileAsync(file);
}
}
else if (source is IUriImageSource uriSource)
{
var uri = uriSource.Uri;
if (uri != null)
{
await _handler.PlatformView!.LoadFromUriAsync(uri);
}
}
else if (source is IStreamImageSource streamSource)
{
var stream = await streamSource.GetStreamAsync(token);
if (stream != null)
{
await _handler.PlatformView!.LoadFromStreamAsync(stream);
}
}
}
catch (OperationCanceledException)
{
// Loading was cancelled
}
catch (Exception)
{
// Handle error
if (_handler.VirtualView is IImageSourcePart imageSourcePart)
{
imageSourcePart.UpdateIsLoading(false);
}
} }
} }
} }

View File

@@ -1,37 +1,219 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.IO;
using System.Threading;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ImageHandler : ViewHandler<IImage, SkiaImage>
/// Handler for Image on Linux using Skia rendering.
/// Maps IImage interface to SkiaImage platform view.
/// IImage has: Aspect, IsOpaque (inherits from IImageSourcePart)
/// </summary>
public partial class ImageHandler : ViewHandler<IImage, SkiaImage>
{ {
public static IPropertyMapper<IImage, ImageHandler> Mapper = new PropertyMapper<IImage, ImageHandler>(ViewHandler.ViewMapper) internal class ImageSourceServiceResultManager
{ {
[nameof(IImage.Aspect)] = MapAspect, private readonly ImageHandler _handler;
[nameof(IImage.IsOpaque)] = MapIsOpaque,
[nameof(IImageSourcePart.Source)] = MapSource, private CancellationTokenSource? _cts;
[nameof(IView.Background)] = MapBackground,
public ImageSourceServiceResultManager(ImageHandler handler)
{
_handler = handler;
}
public async void UpdateImageSourceAsync()
{
_cts?.Cancel();
_cts = new CancellationTokenSource();
CancellationToken token = _cts.Token;
try
{
IImage virtualView = ((ViewHandler<IImage, SkiaImage>)(object)_handler).VirtualView;
IImageSource val = ((virtualView != null) ? ((IImageSourcePart)virtualView).Source : null);
if (val == null)
{
((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView?.LoadFromData(Array.Empty<byte>());
return;
}
IImageSourcePart virtualView2 = (IImageSourcePart)(object)((ViewHandler<IImage, SkiaImage>)(object)_handler).VirtualView;
if (virtualView2 != null)
{
virtualView2.UpdateIsLoading(true);
}
IFileImageSource val2 = (IFileImageSource)(object)((val is IFileImageSource) ? val : null);
if (val2 != null)
{
string file = val2.File;
if (!string.IsNullOrEmpty(file))
{
await ((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.LoadFromFileAsync(file);
}
return;
}
IUriImageSource val3 = (IUriImageSource)(object)((val is IUriImageSource) ? val : null);
if (val3 != null)
{
Uri uri = val3.Uri;
if (uri != null)
{
await ((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.LoadFromUriAsync(uri);
}
return;
}
IStreamImageSource val4 = (IStreamImageSource)(object)((val is IStreamImageSource) ? val : null);
if (val4 != null)
{
Stream stream = await val4.GetStreamAsync(token);
if (stream != null)
{
await ((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.LoadFromStreamAsync(stream);
}
return;
}
FontImageSource val5 = (FontImageSource)(object)((val is FontImageSource) ? val : null);
if (val5 != null)
{
SKBitmap val6 = RenderFontImageSource(val5, ((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.WidthRequest, ((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.HeightRequest);
if (val6 != null)
{
((ViewHandler<IImage, SkiaImage>)(object)_handler).PlatformView.LoadFromBitmap(val6);
}
}
}
catch (OperationCanceledException)
{
}
catch (Exception)
{
IImageSourcePart virtualView3 = (IImageSourcePart)(object)((ViewHandler<IImage, SkiaImage>)(object)_handler).VirtualView;
if (virtualView3 != null)
{
virtualView3.UpdateIsLoading(false);
}
}
}
private static SKBitmap? RenderFontImageSource(FontImageSource fontSource, double requestedWidth, double requestedHeight)
{
//IL_0062: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Expected O, but got Unknown
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0079: Expected O, but got Unknown
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_016a: Unknown result type (might be due to invalid IL or missing references)
//IL_0171: Expected O, but got Unknown
//IL_0173: Unknown result type (might be due to invalid IL or missing references)
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
//IL_0179: Unknown result type (might be due to invalid IL or missing references)
//IL_017f: Unknown result type (might be due to invalid IL or missing references)
//IL_0186: Unknown result type (might be due to invalid IL or missing references)
//IL_018f: Expected O, but got Unknown
//IL_0191: Unknown result type (might be due to invalid IL or missing references)
string glyph = fontSource.Glyph;
if (string.IsNullOrEmpty(glyph))
{
return null;
}
int val = (int)Math.Max((requestedWidth > 0.0) ? requestedWidth : 24.0, (requestedHeight > 0.0) ? requestedHeight : 24.0);
val = Math.Max(val, 16);
SKColor color = fontSource.Color?.ToSKColor() ?? SKColors.Black;
SKBitmap val2 = new SKBitmap(val, val, false);
SKCanvas val3 = new SKCanvas(val2);
try
{
val3.Clear(SKColors.Transparent);
SKTypeface val4 = null;
if (!string.IsNullOrEmpty(fontSource.FontFamily))
{
string[] array = new string[4]
{
"/usr/share/fonts/truetype/" + fontSource.FontFamily + ".ttf",
"/usr/share/fonts/opentype/" + fontSource.FontFamily + ".otf",
"/usr/local/share/fonts/" + fontSource.FontFamily + ".ttf",
Path.Combine(AppContext.BaseDirectory, fontSource.FontFamily + ".ttf")
};
foreach (string text in array)
{
if (File.Exists(text))
{
val4 = SKTypeface.FromFile(text, 0);
if (val4 != null)
{
break;
}
}
}
if (val4 == null)
{
val4 = SKTypeface.FromFamilyName(fontSource.FontFamily);
}
}
if (val4 == null)
{
val4 = SKTypeface.Default;
}
float num = (float)val * 0.8f;
SKFont val5 = new SKFont(val4, num, 1f, 0f);
try
{
SKPaint val6 = new SKPaint(val5)
{
Color = color,
IsAntialias = true,
TextAlign = (SKTextAlign)1
};
try
{
SKRect val7 = default(SKRect);
val6.MeasureText(glyph, ref val7);
float num2 = (float)val / 2f;
float num3 = ((float)val - ((SKRect)(ref val7)).Top - ((SKRect)(ref val7)).Bottom) / 2f;
val3.DrawText(glyph, num2, num3, val6);
return val2;
}
finally
{
((IDisposable)val6)?.Dispose();
}
}
finally
{
((IDisposable)val5)?.Dispose();
}
}
finally
{
((IDisposable)val3)?.Dispose();
}
}
}
public static IPropertyMapper<IImage, ImageHandler> Mapper = (IPropertyMapper<IImage, ImageHandler>)(object)new PropertyMapper<IImage, ImageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{
["Aspect"] = MapAspect,
["IsOpaque"] = MapIsOpaque,
["Source"] = MapSource,
["Background"] = MapBackground,
["Width"] = MapWidth,
["Height"] = MapHeight
}; };
public static CommandMapper<IImage, ImageHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IImage, ImageHandler> CommandMapper = new CommandMapper<IImage, ImageHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ImageHandler() : base(Mapper, CommandMapper) private ImageSourceServiceResultManager _sourceLoader;
private ImageSourceServiceResultManager SourceLoader => _sourceLoader ?? (_sourceLoader = new ImageSourceServiceResultManager(this));
public ImageHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ImageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ImageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -56,124 +238,104 @@ public partial class ImageHandler : ViewHandler<IImage, SkiaImage>
private void OnImageLoaded(object? sender, EventArgs e) private void OnImageLoaded(object? sender, EventArgs e)
{ {
// Notify that the image has been loaded IImageSourcePart virtualView = (IImageSourcePart)(object)base.VirtualView;
if (VirtualView is IImageSourcePart imageSourcePart) if (virtualView != null)
{ {
imageSourcePart.UpdateIsLoading(false); virtualView.UpdateIsLoading(false);
} }
} }
private void OnImageLoadingError(object? sender, ImageLoadingErrorEventArgs e) private void OnImageLoadingError(object? sender, ImageLoadingErrorEventArgs e)
{ {
// Handle loading error IImageSourcePart virtualView = (IImageSourcePart)(object)base.VirtualView;
if (VirtualView is IImageSourcePart imageSourcePart) if (virtualView != null)
{ {
imageSourcePart.UpdateIsLoading(false); virtualView.UpdateIsLoading(false);
} }
} }
public static void MapAspect(ImageHandler handler, IImage image) public static void MapAspect(ImageHandler handler, IImage image)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.Aspect = image.Aspect; if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView != null)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.Aspect = image.Aspect;
}
} }
public static void MapIsOpaque(ImageHandler handler, IImage image) public static void MapIsOpaque(ImageHandler handler, IImage image)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView != null)
handler.PlatformView.IsOpaque = image.IsOpaque; {
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.IsOpaque = image.IsOpaque;
}
} }
public static void MapSource(ImageHandler handler, IImage image) public static void MapSource(ImageHandler handler, IImage image)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView == null)
{
return;
}
Image val = (Image)(object)((image is Image) ? image : null);
if (val != null)
{
if (((VisualElement)val).WidthRequest > 0.0)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.WidthRequest = ((VisualElement)val).WidthRequest;
}
if (((VisualElement)val).HeightRequest > 0.0)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.HeightRequest = ((VisualElement)val).HeightRequest;
}
}
handler.SourceLoader.UpdateImageSourceAsync(); handler.SourceLoader.UpdateImageSourceAsync();
} }
public static void MapBackground(ImageHandler handler, IImage image) public static void MapBackground(ImageHandler handler, IImage image)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView != null)
if (image.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)image).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
// Image source loading helper public static void MapWidth(ImageHandler handler, IImage image)
private ImageSourceServiceResultManager _sourceLoader = null!;
private ImageSourceServiceResultManager SourceLoader =>
_sourceLoader ??= new ImageSourceServiceResultManager(this);
internal class ImageSourceServiceResultManager
{ {
private readonly ImageHandler _handler; if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView != null)
private CancellationTokenSource? _cts;
public ImageSourceServiceResultManager(ImageHandler handler)
{ {
_handler = handler; Image val = (Image)(object)((image is Image) ? image : null);
if (val != null && ((VisualElement)val).WidthRequest > 0.0)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.WidthRequest = ((VisualElement)val).WidthRequest;
Console.WriteLine($"[ImageHandler] MapWidth: {((VisualElement)val).WidthRequest}");
}
else if (((IView)image).Width > 0.0)
{
((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.WidthRequest = ((IView)image).Width;
}
}
} }
public async void UpdateImageSourceAsync() public static void MapHeight(ImageHandler handler, IImage image)
{ {
_cts?.Cancel(); if (((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView != null)
_cts = new CancellationTokenSource();
var token = _cts.Token;
try
{ {
var source = _handler.VirtualView?.Source; Image val = (Image)(object)((image is Image) ? image : null);
if (source == null) if (val != null && ((VisualElement)val).HeightRequest > 0.0)
{ {
_handler.PlatformView?.LoadFromData(Array.Empty<byte>()); ((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.HeightRequest = ((VisualElement)val).HeightRequest;
return; Console.WriteLine($"[ImageHandler] MapHeight: {((VisualElement)val).HeightRequest}");
} }
else if (((IView)image).Height > 0.0)
if (_handler.VirtualView is IImageSourcePart imageSourcePart)
{ {
imageSourcePart.UpdateIsLoading(true); ((ViewHandler<IImage, SkiaImage>)(object)handler).PlatformView.HeightRequest = ((IView)image).Height;
}
// Handle different image source types
if (source is IFileImageSource fileSource)
{
var file = fileSource.File;
if (!string.IsNullOrEmpty(file))
{
await _handler.PlatformView!.LoadFromFileAsync(file);
}
}
else if (source is IUriImageSource uriSource)
{
var uri = uriSource.Uri;
if (uri != null)
{
await _handler.PlatformView!.LoadFromUriAsync(uri);
}
}
else if (source is IStreamImageSource streamSource)
{
var stream = await streamSource.GetStreamAsync(token);
if (stream != null)
{
await _handler.PlatformView!.LoadFromStreamAsync(stream);
}
}
}
catch (OperationCanceledException)
{
// Loading was cancelled
}
catch (Exception)
{
// Handle error
if (_handler.VirtualView is IImageSourcePart imageSourcePart)
{
imageSourcePart.UpdateIsLoading(false);
}
} }
} }
} }

View File

@@ -1,45 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ItemsViewHandler<TItemsView> : ViewHandler<TItemsView, SkiaItemsView> where TItemsView : ItemsView
/// Base handler for ItemsView on Linux using Skia rendering.
/// Maps ItemsView to SkiaItemsView platform view.
/// </summary>
public partial class ItemsViewHandler<TItemsView> : ViewHandler<TItemsView, SkiaItemsView>
where TItemsView : ItemsView
{ {
public static IPropertyMapper<TItemsView, ItemsViewHandler<TItemsView>> ItemsViewMapper = public static IPropertyMapper<TItemsView, ItemsViewHandler<TItemsView>> ItemsViewMapper = (IPropertyMapper<TItemsView, ItemsViewHandler<TItemsView>>)(object)new PropertyMapper<TItemsView, ItemsViewHandler<TItemsView>>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<TItemsView, ItemsViewHandler<TItemsView>>(ViewHandler.ViewMapper)
{ {
[nameof(ItemsView.ItemsSource)] = MapItemsSource, ["ItemsSource"] = MapItemsSource,
[nameof(ItemsView.ItemTemplate)] = MapItemTemplate, ["ItemTemplate"] = MapItemTemplate,
[nameof(ItemsView.EmptyView)] = MapEmptyView, ["EmptyView"] = MapEmptyView,
[nameof(ItemsView.EmptyViewTemplate)] = MapEmptyViewTemplate, ["EmptyViewTemplate"] = MapEmptyViewTemplate,
[nameof(ItemsView.HorizontalScrollBarVisibility)] = MapHorizontalScrollBarVisibility, ["HorizontalScrollBarVisibility"] = MapHorizontalScrollBarVisibility,
[nameof(ItemsView.VerticalScrollBarVisibility)] = MapVerticalScrollBarVisibility, ["VerticalScrollBarVisibility"] = MapVerticalScrollBarVisibility,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<TItemsView, ItemsViewHandler<TItemsView>> ItemsViewCommandMapper = public static CommandMapper<TItemsView, ItemsViewHandler<TItemsView>> ItemsViewCommandMapper = new CommandMapper<TItemsView, ItemsViewHandler<TItemsView>>((CommandMapper)(object)ViewHandler.ViewCommandMapper) { ["ScrollTo"] = MapScrollTo };
new(ViewHandler.ViewCommandMapper)
{
["ScrollTo"] = MapScrollTo,
};
public ItemsViewHandler() : base(ItemsViewMapper, ItemsViewCommandMapper) public ItemsViewHandler()
: base((IPropertyMapper)(object)ItemsViewMapper, (CommandMapper)(object)ItemsViewCommandMapper)
{ {
} }
public ItemsViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ItemsViewHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? ItemsViewMapper, commandMapper ?? ItemsViewCommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)ItemsViewMapper)), (CommandMapper)(((object)commandMapper) ?? ((object)ItemsViewCommandMapper)))
{ {
} }
@@ -53,8 +39,6 @@ public partial class ItemsViewHandler<TItemsView> : ViewHandler<TItemsView, Skia
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.Scrolled += OnScrolled; platformView.Scrolled += OnScrolled;
platformView.ItemTapped += OnItemTapped; platformView.ItemTapped += OnItemTapped;
// Set up item renderer
platformView.ItemRenderer = RenderItem; platformView.ItemRenderer = RenderItem;
} }
@@ -68,97 +52,124 @@ public partial class ItemsViewHandler<TItemsView> : ViewHandler<TItemsView, Skia
private void OnScrolled(object? sender, ItemsScrolledEventArgs e) private void OnScrolled(object? sender, ItemsScrolledEventArgs e)
{ {
// Fire scrolled event on virtual view //IL_0010: Unknown result type (might be due to invalid IL or missing references)
VirtualView?.SendScrolled(new ItemsViewScrolledEventArgs //IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0040: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Expected O, but got Unknown
object obj = base.VirtualView;
if (obj != null)
{
((ItemsView)obj).SendScrolled(new ItemsViewScrolledEventArgs
{ {
VerticalOffset = e.ScrollOffset, VerticalOffset = e.ScrollOffset,
VerticalDelta = 0, VerticalDelta = 0.0,
HorizontalOffset = 0, HorizontalOffset = 0.0,
HorizontalDelta = 0 HorizontalDelta = 0.0
}); });
} }
}
private void OnItemTapped(object? sender, ItemsViewItemTappedEventArgs e) private void OnItemTapped(object? sender, ItemsViewItemTappedEventArgs e)
{ {
// Item tap handling - can be extended for selection
} }
protected virtual bool RenderItem(object item, int index, SKRect bounds, SKCanvas canvas, SKPaint paint) protected virtual bool RenderItem(object item, int index, SKRect bounds, SKCanvas canvas, SKPaint paint)
{ {
// Check if we have an ItemTemplate object obj = base.VirtualView;
var template = VirtualView?.ItemTemplate; if (obj != null)
if (template == null) {
return false; // Use default rendering _ = ((ItemsView)obj).ItemTemplate;
}
// For now, render based on item ToString else
// Full DataTemplate support would require creating actual views _ = null;
return false; return false;
} }
public static void MapItemsSource(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapItemsSource(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView != null)
handler.PlatformView.ItemsSource = itemsView.ItemsSource; {
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.ItemsSource = ((ItemsView)itemsView).ItemsSource;
}
} }
public static void MapItemTemplate(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapItemTemplate(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
// ItemTemplate affects how items are rendered ((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView?.Invalidate();
// The renderer will check this when drawing items
handler.PlatformView?.Invalidate();
} }
public static void MapEmptyView(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapEmptyView(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView != null)
handler.PlatformView.EmptyView = itemsView.EmptyView;
if (itemsView.EmptyView is string text)
{ {
handler.PlatformView.EmptyViewText = text; ((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.EmptyView = ((ItemsView)itemsView).EmptyView;
if (((ItemsView)itemsView).EmptyView is string emptyViewText)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.EmptyViewText = emptyViewText;
}
} }
} }
public static void MapEmptyViewTemplate(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapEmptyViewTemplate(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
// EmptyViewTemplate would be used to render custom empty view ((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView?.Invalidate();
handler.PlatformView?.Invalidate();
} }
public static void MapHorizontalScrollBarVisibility(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapHorizontalScrollBarVisibility(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
if (handler.PlatformView is null) return; //IL_0015: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalScrollBarVisibility = (ScrollBarVisibility)itemsView.HorizontalScrollBarVisibility; //IL_001f: Expected I4, but got Unknown
if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView != null)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.HorizontalScrollBarVisibility = (ScrollBarVisibility)((ItemsView)itemsView).HorizontalScrollBarVisibility;
}
} }
public static void MapVerticalScrollBarVisibility(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapVerticalScrollBarVisibility(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
if (handler.PlatformView is null) return; //IL_0015: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalScrollBarVisibility = (ScrollBarVisibility)itemsView.VerticalScrollBarVisibility; //IL_001f: Expected I4, but got Unknown
if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView != null)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.VerticalScrollBarVisibility = (ScrollBarVisibility)((ItemsView)itemsView).VerticalScrollBarVisibility;
}
} }
public static void MapBackground(ItemsViewHandler<TItemsView> handler, TItemsView itemsView) public static void MapBackground(ItemsViewHandler<TItemsView> handler, TItemsView itemsView)
{ {
if (handler.PlatformView is null) return; //IL_0029: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView != null)
if (itemsView.Background is SolidColorBrush solidBrush)
{ {
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor(); Brush background = ((VisualElement)(object)itemsView).Background;
SolidColorBrush val = (SolidColorBrush)(object)((background is SolidColorBrush) ? background : null);
if (val != null)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapScrollTo(ItemsViewHandler<TItemsView> handler, TItemsView itemsView, object? args) public static void MapScrollTo(ItemsViewHandler<TItemsView> handler, TItemsView itemsView, object? args)
{ {
if (handler.PlatformView is null || args is not ScrollToRequestEventArgs scrollArgs) //IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Invalid comparison between Unknown and I4
if (((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView == null)
{
return; return;
if (scrollArgs.Mode == ScrollToMode.Position)
{
handler.PlatformView.ScrollToIndex(scrollArgs.Index, scrollArgs.IsAnimated);
} }
else if (scrollArgs.Item != null) ScrollToRequestEventArgs e = (ScrollToRequestEventArgs)((args is ScrollToRequestEventArgs) ? args : null);
if (e != null)
{ {
handler.PlatformView.ScrollToItem(scrollArgs.Item, scrollArgs.IsAnimated); if ((int)e.Mode == 1)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.ScrollToIndex(e.Index, e.IsAnimated);
}
else if (e.Item != null)
{
((ViewHandler<TItemsView, SkiaItemsView>)(object)handler).PlatformView.ScrollToItem(e.Item, e.IsAnimated);
}
} }
} }
} }

View File

@@ -1,46 +1,45 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.Linq;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Window;
using Microsoft.Maui.Primitives;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class LabelHandler : ViewHandler<ILabel, SkiaLabel>
/// Handler for Label on Linux using Skia rendering.
/// Maps ILabel interface to SkiaLabel platform view.
/// </summary>
public partial class LabelHandler : ViewHandler<ILabel, SkiaLabel>
{ {
public static IPropertyMapper<ILabel, LabelHandler> Mapper = new PropertyMapper<ILabel, LabelHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ILabel, LabelHandler> Mapper = (IPropertyMapper<ILabel, LabelHandler>)(object)new PropertyMapper<ILabel, LabelHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IText.Text)] = MapText, ["Text"] = MapText,
[nameof(ITextStyle.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(ITextStyle.Font)] = MapFont, ["Font"] = MapFont,
[nameof(ITextStyle.CharacterSpacing)] = MapCharacterSpacing, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(ITextAlignment.HorizontalTextAlignment)] = MapHorizontalTextAlignment, ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
[nameof(ITextAlignment.VerticalTextAlignment)] = MapVerticalTextAlignment, ["VerticalTextAlignment"] = MapVerticalTextAlignment,
[nameof(ILabel.TextDecorations)] = MapTextDecorations, ["TextDecorations"] = MapTextDecorations,
[nameof(ILabel.LineHeight)] = MapLineHeight, ["LineHeight"] = MapLineHeight,
["LineBreakMode"] = MapLineBreakMode, ["LineBreakMode"] = MapLineBreakMode,
["MaxLines"] = MapMaxLines, ["MaxLines"] = MapMaxLines,
[nameof(IPadding.Padding)] = MapPadding, ["Padding"] = MapPadding,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
[nameof(IView.VerticalLayoutAlignment)] = MapVerticalLayoutAlignment, ["VerticalLayoutAlignment"] = MapVerticalLayoutAlignment,
[nameof(IView.HorizontalLayoutAlignment)] = MapHorizontalLayoutAlignment, ["HorizontalLayoutAlignment"] = MapHorizontalLayoutAlignment,
["FormattedText"] = MapFormattedText
}; };
public static CommandMapper<ILabel, LabelHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ILabel, LabelHandler> CommandMapper = new CommandMapper<ILabel, LabelHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public LabelHandler() : base(Mapper, CommandMapper) public LabelHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public LabelHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public LabelHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -49,160 +48,341 @@ public partial class LabelHandler : ViewHandler<ILabel, SkiaLabel>
return new SkiaLabel(); return new SkiaLabel();
} }
protected override void ConnectHandler(SkiaLabel platformView)
{
base.ConnectHandler(platformView);
ILabel virtualView = base.VirtualView;
View val = (View)(object)((virtualView is View) ? virtualView : null);
if (val != null)
{
platformView.MauiView = val;
if (val.GestureRecognizers.OfType<TapGestureRecognizer>().Any())
{
platformView.CursorType = CursorType.Hand;
}
}
platformView.Tapped += OnPlatformViewTapped;
}
protected override void DisconnectHandler(SkiaLabel platformView)
{
platformView.Tapped -= OnPlatformViewTapped;
platformView.MauiView = null;
base.DisconnectHandler(platformView);
}
private void OnPlatformViewTapped(object? sender, EventArgs e)
{
ILabel virtualView = base.VirtualView;
View val = (View)(object)((virtualView is View) ? virtualView : null);
if (val != null)
{
GestureManager.ProcessTap(val, 0.0, 0.0);
}
}
public static void MapText(LabelHandler handler, ILabel label) public static void MapText(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
handler.PlatformView.Text = label.Text ?? string.Empty; {
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.Text = ((IText)label).Text ?? string.Empty;
}
} }
public static void MapTextColor(LabelHandler handler, ILabel label) public static void MapTextColor(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null && ((ITextStyle)label).TextColor != null)
if (label.TextColor is not null) {
handler.PlatformView.TextColor = label.TextColor.ToSKColor(); ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.TextColor = ((ITextStyle)label).TextColor.ToSKColor();
}
} }
public static void MapFont(LabelHandler handler, ILabel label) public static void MapFont(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var font = label.Font; //IL_005d: Unknown result type (might be due to invalid IL or missing references)
if (font.Size > 0) //IL_0067: Invalid comparison between Unknown and I4
handler.PlatformView.FontSize = (float)font.Size; //IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Invalid comparison between Unknown and I4
if (!string.IsNullOrEmpty(font.Family)) //IL_0083: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.FontFamily = font.Family; //IL_0089: Invalid comparison between Unknown and I4
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
handler.PlatformView.IsBold = font.Weight >= FontWeight.Bold; {
handler.PlatformView.IsItalic = font.Slant == FontSlant.Italic || font.Slant == FontSlant.Oblique; Font font = ((ITextStyle)label).Font;
if (((Font)(ref font)).Size > 0.0)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.FontSize = (float)((Font)(ref font)).Size;
}
if (!string.IsNullOrEmpty(((Font)(ref font)).Family))
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.FontFamily = ((Font)(ref font)).Family;
}
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.IsBold = (int)((Font)(ref font)).Weight >= 700;
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.IsItalic = (int)((Font)(ref font)).Slant == 1 || (int)((Font)(ref font)).Slant == 2;
}
} }
public static void MapCharacterSpacing(LabelHandler handler, ILabel label) public static void MapCharacterSpacing(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
handler.PlatformView.CharacterSpacing = (float)label.CharacterSpacing; {
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.CharacterSpacing = (float)((ITextStyle)label).CharacterSpacing;
}
} }
public static void MapHorizontalTextAlignment(LabelHandler handler, ILabel label) public static void MapHorizontalTextAlignment(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
// Map MAUI TextAlignment to our internal TextAlignment //IL_0017: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalTextAlignment = label.HorizontalTextAlignment switch //IL_0029: Expected I4, but got Unknown
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{ {
Microsoft.Maui.TextAlignment.Start => Platform.TextAlignment.Start, SkiaLabel platformView = ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView;
Microsoft.Maui.TextAlignment.Center => Platform.TextAlignment.Center, TextAlignment horizontalTextAlignment = ((ITextAlignment)label).HorizontalTextAlignment;
Microsoft.Maui.TextAlignment.End => Platform.TextAlignment.End, platformView.HorizontalTextAlignment = (int)horizontalTextAlignment switch
_ => Platform.TextAlignment.Start {
0 => TextAlignment.Start,
1 => TextAlignment.Center,
2 => TextAlignment.End,
_ => TextAlignment.Start,
}; };
} }
}
public static void MapVerticalTextAlignment(LabelHandler handler, ILabel label) public static void MapVerticalTextAlignment(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalTextAlignment = label.VerticalTextAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Expected I4, but got Unknown
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{ {
Microsoft.Maui.TextAlignment.Start => Platform.TextAlignment.Start, SkiaLabel platformView = ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView;
Microsoft.Maui.TextAlignment.Center => Platform.TextAlignment.Center, TextAlignment verticalTextAlignment = ((ITextAlignment)label).VerticalTextAlignment;
Microsoft.Maui.TextAlignment.End => Platform.TextAlignment.End, platformView.VerticalTextAlignment = (int)verticalTextAlignment switch
_ => Platform.TextAlignment.Center {
0 => TextAlignment.Start,
1 => TextAlignment.Center,
2 => TextAlignment.End,
_ => TextAlignment.Center,
}; };
} }
}
public static void MapTextDecorations(LabelHandler handler, ILabel label) public static void MapTextDecorations(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.IsUnderline = (label.TextDecorations & TextDecorations.Underline) != 0; //IL_0018: Invalid comparison between Unknown and I4
handler.PlatformView.IsStrikethrough = (label.TextDecorations & TextDecorations.Strikethrough) != 0; //IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Invalid comparison between Unknown and I4
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.IsUnderline = (label.TextDecorations & 1) > 0;
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.IsStrikethrough = (label.TextDecorations & 2) > 0;
}
} }
public static void MapLineHeight(LabelHandler handler, ILabel label) public static void MapLineHeight(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
handler.PlatformView.LineHeight = (float)label.LineHeight; {
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.LineHeight = (float)label.LineHeight;
}
} }
public static void MapLineBreakMode(LabelHandler handler, ILabel label) public static void MapLineBreakMode(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
// LineBreakMode is on Label control, not ILabel interface //IL_0021: Unknown result type (might be due to invalid IL or missing references)
if (label is Microsoft.Maui.Controls.Label mauiLabel) //IL_003f: Expected I4, but got Unknown
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{ {
handler.PlatformView.LineBreakMode = mauiLabel.LineBreakMode switch Label val = (Label)(object)((label is Label) ? label : null);
if (val != null)
{ {
Microsoft.Maui.LineBreakMode.NoWrap => Platform.LineBreakMode.NoWrap, SkiaLabel platformView = ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView;
Microsoft.Maui.LineBreakMode.WordWrap => Platform.LineBreakMode.WordWrap, LineBreakMode lineBreakMode = val.LineBreakMode;
Microsoft.Maui.LineBreakMode.CharacterWrap => Platform.LineBreakMode.CharacterWrap, platformView.LineBreakMode = (int)lineBreakMode switch
Microsoft.Maui.LineBreakMode.HeadTruncation => Platform.LineBreakMode.HeadTruncation, {
Microsoft.Maui.LineBreakMode.TailTruncation => Platform.LineBreakMode.TailTruncation, 0 => LineBreakMode.NoWrap,
Microsoft.Maui.LineBreakMode.MiddleTruncation => Platform.LineBreakMode.MiddleTruncation, 1 => LineBreakMode.WordWrap,
_ => Platform.LineBreakMode.TailTruncation 2 => LineBreakMode.CharacterWrap,
3 => LineBreakMode.HeadTruncation,
4 => LineBreakMode.TailTruncation,
5 => LineBreakMode.MiddleTruncation,
_ => LineBreakMode.TailTruncation,
}; };
} }
} }
}
public static void MapMaxLines(LabelHandler handler, ILabel label) public static void MapMaxLines(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
// MaxLines is on Label control, not ILabel interface
if (label is Microsoft.Maui.Controls.Label mauiLabel)
{ {
handler.PlatformView.MaxLines = mauiLabel.MaxLines; Label val = (Label)(object)((label is Label) ? label : null);
if (val != null)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.MaxLines = val.MaxLines;
}
} }
} }
public static void MapPadding(LabelHandler handler, ILabel label) public static void MapPadding(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var padding = label.Padding; //IL_0036: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.Padding = new SKRect( if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
(float)padding.Left, {
(float)padding.Top, Thickness padding = ((IPadding)label).Padding;
(float)padding.Right, ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.Padding = new SKRect((float)((Thickness)(ref padding)).Left, (float)((Thickness)(ref padding)).Top, (float)((Thickness)(ref padding)).Right, (float)((Thickness)(ref padding)).Bottom);
(float)padding.Bottom); }
} }
public static void MapBackground(LabelHandler handler, ILabel label) public static void MapBackground(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
if (label.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)label).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapVerticalLayoutAlignment(LabelHandler handler, ILabel label) public static void MapVerticalLayoutAlignment(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.VerticalOptions = label.VerticalLayoutAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected I4, but got Unknown
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{ {
Primitives.LayoutAlignment.Start => LayoutOptions.Start, SkiaLabel platformView = ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView;
Primitives.LayoutAlignment.Center => LayoutOptions.Center, LayoutAlignment verticalLayoutAlignment = ((IView)label).VerticalLayoutAlignment;
Primitives.LayoutAlignment.End => LayoutOptions.End, platformView.VerticalOptions = (LayoutOptions)((int)verticalLayoutAlignment switch
Primitives.LayoutAlignment.Fill => LayoutOptions.Fill, {
_ => LayoutOptions.Start 1 => LayoutOptions.Start,
}; 2 => LayoutOptions.Center,
3 => LayoutOptions.End,
0 => LayoutOptions.Fill,
_ => LayoutOptions.Start,
});
}
} }
public static void MapHorizontalLayoutAlignment(LabelHandler handler, ILabel label) public static void MapHorizontalLayoutAlignment(LabelHandler handler, ILabel label)
{ {
if (handler.PlatformView is null) return; //IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
handler.PlatformView.HorizontalOptions = label.HorizontalLayoutAlignment switch //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected I4, but got Unknown
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView != null)
{ {
Primitives.LayoutAlignment.Start => LayoutOptions.Start, SkiaLabel platformView = ((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView;
Primitives.LayoutAlignment.Center => LayoutOptions.Center, LayoutAlignment horizontalLayoutAlignment = ((IView)label).HorizontalLayoutAlignment;
Primitives.LayoutAlignment.End => LayoutOptions.End, platformView.HorizontalOptions = (LayoutOptions)((int)horizontalLayoutAlignment switch
Primitives.LayoutAlignment.Fill => LayoutOptions.Fill, {
_ => LayoutOptions.Start 1 => LayoutOptions.Start,
2 => LayoutOptions.Center,
3 => LayoutOptions.End,
0 => LayoutOptions.Fill,
_ => LayoutOptions.Start,
});
}
}
public static void MapFormattedText(LabelHandler handler, ILabel label)
{
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Invalid comparison between Unknown and I4
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Invalid comparison between Unknown and I4
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView == null)
{
return;
}
Label val = (Label)(object)((label is Label) ? label : null);
if (val == null)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.FormattedSpans = null;
return;
}
FormattedString formattedText = val.FormattedText;
if (formattedText == null || formattedText.Spans.Count == 0)
{
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.FormattedSpans = null;
return;
}
List<SkiaTextSpan> list = new List<SkiaTextSpan>();
foreach (Span span in formattedText.Spans)
{
SkiaTextSpan skiaTextSpan = new SkiaTextSpan
{
Text = (span.Text ?? ""),
IsBold = ((Enum)span.FontAttributes).HasFlag((Enum)(object)(FontAttributes)1),
IsItalic = ((Enum)span.FontAttributes).HasFlag((Enum)(object)(FontAttributes)2),
IsUnderline = ((span.TextDecorations & 1) > 0),
IsStrikethrough = ((span.TextDecorations & 2) > 0),
CharacterSpacing = (float)span.CharacterSpacing,
LineHeight = (float)span.LineHeight
}; };
if (span.TextColor != null)
{
skiaTextSpan.TextColor = span.TextColor.ToSKColor();
}
if (span.BackgroundColor != null)
{
skiaTextSpan.BackgroundColor = span.BackgroundColor.ToSKColor();
}
if (!string.IsNullOrEmpty(span.FontFamily))
{
skiaTextSpan.FontFamily = span.FontFamily;
}
if (span.FontSize > 0.0)
{
skiaTextSpan.FontSize = (float)span.FontSize;
}
list.Add(skiaTextSpan);
}
((ViewHandler<ILabel, SkiaLabel>)(object)handler).PlatformView.FormattedSpans = list;
} }
} }

View File

@@ -1,40 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements. using System.Collections.Generic;
// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class LayoutHandler : ViewHandler<ILayout, SkiaLayoutView>
/// Handler for Layout on Linux using Skia rendering.
/// Maps ILayout interface to SkiaLayoutView platform view.
/// </summary>
public partial class LayoutHandler : ViewHandler<ILayout, SkiaLayoutView>
{ {
public static IPropertyMapper<ILayout, LayoutHandler> Mapper = new PropertyMapper<ILayout, LayoutHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ILayout, LayoutHandler> Mapper = (IPropertyMapper<ILayout, LayoutHandler>)(object)new PropertyMapper<ILayout, LayoutHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(ILayout.ClipsToBounds)] = MapClipsToBounds, ["ClipsToBounds"] = MapClipsToBounds,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
[nameof(IPadding.Padding)] = MapPadding, ["Padding"] = MapPadding
}; };
public static CommandMapper<ILayout, LayoutHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ILayout, LayoutHandler> CommandMapper = new CommandMapper<ILayout, LayoutHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper)
{ {
["Add"] = MapAdd, ["Add"] = MapAdd,
["Remove"] = MapRemove, ["Remove"] = MapRemove,
["Clear"] = MapClear, ["Clear"] = MapClear,
["Insert"] = MapInsert, ["Insert"] = MapInsert,
["Update"] = MapUpdate, ["Update"] = MapUpdate
}; };
public LayoutHandler() : base(Mapper, CommandMapper) public LayoutHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public LayoutHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null) public LayoutHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -45,84 +42,99 @@ public partial class LayoutHandler : ViewHandler<ILayout, SkiaLayoutView>
protected override void ConnectHandler(SkiaLayoutView platformView) protected override void ConnectHandler(SkiaLayoutView platformView)
{ {
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
if (base.VirtualView == null || ((ElementHandler)this).MauiContext == null)
// Create handlers for all children and add them to the platform view
if (VirtualView == null || MauiContext == null) return;
// Explicitly map BackgroundColor since it may be set before handler creation
if (VirtualView is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
{ {
platformView.BackgroundColor = ve.BackgroundColor.ToSKColor(); return;
} }
ILayout virtualView = base.VirtualView;
for (int i = 0; i < VirtualView.Count; i++) VisualElement val = (VisualElement)(object)((virtualView is VisualElement) ? virtualView : null);
if (val != null && val.BackgroundColor != null)
{ {
var child = VirtualView[i]; platformView.BackgroundColor = val.BackgroundColor.ToSKColor();
if (child == null) continue;
// Create handler for child if it doesn't exist
if (child.Handler == null)
{
child.Handler = child.ToHandler(MauiContext);
} }
for (int i = 0; i < ((ICollection<IView>)base.VirtualView).Count; i++)
// Add child's platform view to our layout
if (child.Handler?.PlatformView is SkiaView skiaChild)
{ {
platformView.AddChild(skiaChild); IView val2 = ((IList<IView>)base.VirtualView)[i];
if (val2 != null)
{
if (val2.Handler == null)
{
val2.Handler = val2.ToViewHandler(((ElementHandler)this).MauiContext);
}
IViewHandler handler = val2.Handler;
if (((handler != null) ? ((IElementHandler)handler).PlatformView : null) is SkiaView child)
{
platformView.AddChild(child);
}
} }
} }
} }
public static void MapClipsToBounds(LayoutHandler handler, ILayout layout) public static void MapClipsToBounds(LayoutHandler handler, ILayout layout)
{ {
if (handler.PlatformView == null) return; if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView != null)
handler.PlatformView.ClipToBounds = layout.ClipsToBounds; {
((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.ClipToBounds = layout.ClipsToBounds;
}
} }
public static void MapBackground(LayoutHandler handler, ILayout layout) public static void MapBackground(LayoutHandler handler, ILayout layout)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView != null)
if (layout.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)layout).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapAdd(LayoutHandler handler, ILayout layout, object? arg) public static void MapAdd(LayoutHandler handler, ILayout layout, object? arg)
{ {
if (handler.PlatformView == null || arg is not LayoutHandlerUpdate update) if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView == null || !(arg is LayoutHandlerUpdate { Index: var index } layoutHandlerUpdate))
return;
var index = update.Index;
var child = update.View;
if (child?.Handler?.PlatformView is SkiaView skiaView)
{ {
if (index >= 0 && index < handler.PlatformView.Children.Count) return;
handler.PlatformView.InsertChild(index, skiaView); }
IView? view = layoutHandlerUpdate.View;
object obj;
if (view == null)
{
obj = null;
}
else else
handler.PlatformView.AddChild(skiaView); {
IViewHandler handler2 = view.Handler;
obj = ((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null);
}
if (obj is SkiaView child)
{
if (index >= 0 && index < ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.Children.Count)
{
((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.InsertChild(index, child);
}
else
{
((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.AddChild(child);
}
} }
} }
public static void MapRemove(LayoutHandler handler, ILayout layout, object? arg) public static void MapRemove(LayoutHandler handler, ILayout layout, object? arg)
{ {
if (handler.PlatformView == null || arg is not LayoutHandlerUpdate update) if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView != null && arg is LayoutHandlerUpdate { Index: var index } && index >= 0 && index < ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.Children.Count)
return;
var index = update.Index;
if (index >= 0 && index < handler.PlatformView.Children.Count)
{ {
handler.PlatformView.RemoveChildAt(index); ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.RemoveChildAt(index);
} }
} }
public static void MapClear(LayoutHandler handler, ILayout layout, object? arg) public static void MapClear(LayoutHandler handler, ILayout layout, object? arg)
{ {
handler.PlatformView?.ClearChildren(); ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView?.ClearChildren();
} }
public static void MapInsert(LayoutHandler handler, ILayout layout, object? arg) public static void MapInsert(LayoutHandler handler, ILayout layout, object? arg)
@@ -132,237 +144,23 @@ public partial class LayoutHandler : ViewHandler<ILayout, SkiaLayoutView>
public static void MapUpdate(LayoutHandler handler, ILayout layout, object? arg) public static void MapUpdate(LayoutHandler handler, ILayout layout, object? arg)
{ {
// Force re-layout ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView?.InvalidateMeasure();
handler.PlatformView?.InvalidateMeasure();
} }
public static void MapPadding(LayoutHandler handler, ILayout layout) public static void MapPadding(LayoutHandler handler, ILayout layout)
{ {
if (handler.PlatformView == null) return; //IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
if (layout is IPadding paddable) //IL_003b: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView != null)
{ {
var padding = paddable.Padding; if (layout != null)
handler.PlatformView.Padding = new SKRect( {
(float)padding.Left, Thickness padding = ((IPadding)layout).Padding;
(float)padding.Top, ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.Padding = new SKRect((float)((Thickness)(ref padding)).Left, (float)((Thickness)(ref padding)).Top, (float)((Thickness)(ref padding)).Right, (float)((Thickness)(ref padding)).Bottom);
(float)padding.Right, ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.InvalidateMeasure();
(float)padding.Bottom); ((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView.Invalidate();
handler.PlatformView.InvalidateMeasure(); }
handler.PlatformView.Invalidate();
}
}
}
/// <summary>
/// Update payload for layout changes.
/// </summary>
public class LayoutHandlerUpdate
{
public int Index { get; }
public IView? View { get; }
public LayoutHandlerUpdate(int index, IView? view)
{
Index = index;
View = view;
}
}
/// <summary>
/// Handler for StackLayout on Linux.
/// </summary>
public partial class StackLayoutHandler : LayoutHandler
{
public static new IPropertyMapper<IStackLayout, StackLayoutHandler> Mapper = new PropertyMapper<IStackLayout, StackLayoutHandler>(LayoutHandler.Mapper)
{
[nameof(IStackLayout.Spacing)] = MapSpacing,
};
public StackLayoutHandler() : base(Mapper)
{
}
protected override SkiaLayoutView CreatePlatformView()
{
return new SkiaStackLayout();
}
protected override void ConnectHandler(SkiaLayoutView platformView)
{
// Set orientation first
if (platformView is SkiaStackLayout stackLayout && VirtualView is IStackLayout stackView)
{
// Determine orientation based on view type
if (VirtualView is Microsoft.Maui.Controls.HorizontalStackLayout)
{
stackLayout.Orientation = StackOrientation.Horizontal;
}
else if (VirtualView is Microsoft.Maui.Controls.VerticalStackLayout ||
VirtualView is Microsoft.Maui.Controls.StackLayout)
{
stackLayout.Orientation = StackOrientation.Vertical;
}
stackLayout.Spacing = (float)stackView.Spacing;
}
// Let base handle children
base.ConnectHandler(platformView);
}
public static void MapSpacing(StackLayoutHandler handler, IStackLayout layout)
{
if (handler.PlatformView is SkiaStackLayout stackLayout)
{
stackLayout.Spacing = (float)layout.Spacing;
}
}
}
/// <summary>
/// Handler for Grid on Linux.
/// </summary>
public partial class GridHandler : LayoutHandler
{
public static new IPropertyMapper<IGridLayout, GridHandler> Mapper = new PropertyMapper<IGridLayout, GridHandler>(LayoutHandler.Mapper)
{
[nameof(IGridLayout.RowSpacing)] = MapRowSpacing,
[nameof(IGridLayout.ColumnSpacing)] = MapColumnSpacing,
[nameof(IGridLayout.RowDefinitions)] = MapRowDefinitions,
[nameof(IGridLayout.ColumnDefinitions)] = MapColumnDefinitions,
};
public GridHandler() : base(Mapper)
{
}
protected override SkiaLayoutView CreatePlatformView()
{
return new SkiaGrid();
}
protected override void ConnectHandler(SkiaLayoutView platformView)
{
try
{
// Don't call base - we handle children specially for Grid
if (VirtualView is not IGridLayout gridLayout || MauiContext == null || platformView is not SkiaGrid grid) return;
Console.WriteLine($"[GridHandler] ConnectHandler: {gridLayout.Count} children, {gridLayout.RowDefinitions.Count} rows, {gridLayout.ColumnDefinitions.Count} cols");
// Explicitly map BackgroundColor since it may be set before handler creation
if (VirtualView is Microsoft.Maui.Controls.VisualElement ve && ve.BackgroundColor != null)
{
platformView.BackgroundColor = ve.BackgroundColor.ToSKColor();
}
// Explicitly map Padding since it may be set before handler creation
if (VirtualView is IPadding paddable)
{
var padding = paddable.Padding;
platformView.Padding = new SKRect(
(float)padding.Left,
(float)padding.Top,
(float)padding.Right,
(float)padding.Bottom);
Console.WriteLine($"[GridHandler] Applied Padding: L={padding.Left}, T={padding.Top}, R={padding.Right}, B={padding.Bottom}");
}
// Map row/column definitions first
MapRowDefinitions(this, gridLayout);
MapColumnDefinitions(this, gridLayout);
// Add each child with its row/column position
for (int i = 0; i < gridLayout.Count; i++)
{
var child = gridLayout[i];
if (child == null) continue;
Console.WriteLine($"[GridHandler] Processing child {i}: {child.GetType().Name}");
// Create handler for child if it doesn't exist
if (child.Handler == null)
{
child.Handler = child.ToHandler(MauiContext);
}
// Get grid position from attached properties
int row = 0, column = 0, rowSpan = 1, columnSpan = 1;
if (child is Microsoft.Maui.Controls.View mauiView)
{
row = Microsoft.Maui.Controls.Grid.GetRow(mauiView);
column = Microsoft.Maui.Controls.Grid.GetColumn(mauiView);
rowSpan = Microsoft.Maui.Controls.Grid.GetRowSpan(mauiView);
columnSpan = Microsoft.Maui.Controls.Grid.GetColumnSpan(mauiView);
}
Console.WriteLine($"[GridHandler] Child {i} at row={row}, col={column}, handler={child.Handler?.GetType().Name}");
// Add child's platform view to our grid
if (child.Handler?.PlatformView is SkiaView skiaChild)
{
grid.AddChild(skiaChild, row, column, rowSpan, columnSpan);
Console.WriteLine($"[GridHandler] Added child {i} to grid");
}
}
Console.WriteLine($"[GridHandler] ConnectHandler complete");
}
catch (Exception ex)
{
Console.WriteLine($"[GridHandler] EXCEPTION in ConnectHandler: {ex.GetType().Name}: {ex.Message}");
Console.WriteLine($"[GridHandler] Stack trace: {ex.StackTrace}");
throw;
}
}
public static void MapRowSpacing(GridHandler handler, IGridLayout layout)
{
if (handler.PlatformView is SkiaGrid grid)
{
grid.RowSpacing = (float)layout.RowSpacing;
}
}
public static void MapColumnSpacing(GridHandler handler, IGridLayout layout)
{
if (handler.PlatformView is SkiaGrid grid)
{
grid.ColumnSpacing = (float)layout.ColumnSpacing;
}
}
public static void MapRowDefinitions(GridHandler handler, IGridLayout layout)
{
if (handler.PlatformView is not SkiaGrid grid) return;
grid.RowDefinitions.Clear();
foreach (var rowDef in layout.RowDefinitions)
{
var height = rowDef.Height;
if (height.IsAbsolute)
grid.RowDefinitions.Add(new Microsoft.Maui.Platform.GridLength((float)height.Value, Microsoft.Maui.Platform.GridUnitType.Absolute));
else if (height.IsAuto)
grid.RowDefinitions.Add(Microsoft.Maui.Platform.GridLength.Auto);
else // Star
grid.RowDefinitions.Add(new Microsoft.Maui.Platform.GridLength((float)height.Value, Microsoft.Maui.Platform.GridUnitType.Star));
}
}
public static void MapColumnDefinitions(GridHandler handler, IGridLayout layout)
{
if (handler.PlatformView is not SkiaGrid grid) return;
grid.ColumnDefinitions.Clear();
foreach (var colDef in layout.ColumnDefinitions)
{
var width = colDef.Width;
if (width.IsAbsolute)
grid.ColumnDefinitions.Add(new Microsoft.Maui.Platform.GridLength((float)width.Value, Microsoft.Maui.Platform.GridUnitType.Absolute));
else if (width.IsAuto)
grid.ColumnDefinitions.Add(Microsoft.Maui.Platform.GridLength.Auto);
else // Star
grid.ColumnDefinitions.Add(new Microsoft.Maui.Platform.GridLength((float)width.Value, Microsoft.Maui.Platform.GridUnitType.Star));
} }
} }
} }

View File

@@ -0,0 +1,14 @@
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class LayoutHandlerUpdate
{
public int Index { get; }
public IView? View { get; }
public LayoutHandlerUpdate(int index, IView? view)
{
Index = index;
View = view;
}
}

View File

@@ -0,0 +1,64 @@
using System.Collections.Generic;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class LinuxApplicationContext
{
private readonly List<IWindow> _windows = new List<IWindow>();
private IApplication? _application;
public IApplication? Application
{
get
{
return _application;
}
set
{
_application = value;
if (_application == null)
{
return;
}
foreach (IWindow window in _application.Windows)
{
if (!_windows.Contains(window))
{
_windows.Add(window);
}
}
}
}
public IReadOnlyList<IWindow> Windows => _windows;
public IWindow? MainWindow
{
get
{
if (_windows.Count <= 0)
{
return null;
}
return _windows[0];
}
}
public void OpenWindow(IWindow window)
{
if (!_windows.Contains(window))
{
_windows.Add(window);
}
}
public void CloseWindow(IWindow window)
{
_windows.Remove(window);
if (_windows.Count == 0)
{
LinuxApplication.Current?.MainWindow?.Stop();
}
}
}

View File

@@ -1,41 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using SkiaSharp;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.IO;
using System.Linq;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
using SkiaSharp;
using Svg.Skia;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNavigationPage>
/// Handler for NavigationPage on Linux using Skia rendering.
/// </summary>
public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNavigationPage>
{ {
public static IPropertyMapper<NavigationPage, NavigationPageHandler> Mapper = public static IPropertyMapper<NavigationPage, NavigationPageHandler> Mapper = (IPropertyMapper<NavigationPage, NavigationPageHandler>)(object)new PropertyMapper<NavigationPage, NavigationPageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<NavigationPage, NavigationPageHandler>(ViewHandler.ViewMapper)
{ {
[nameof(NavigationPage.BarBackgroundColor)] = MapBarBackgroundColor, ["BarBackgroundColor"] = MapBarBackgroundColor,
[nameof(NavigationPage.BarBackground)] = MapBarBackground, ["BarBackground"] = MapBarBackground,
[nameof(NavigationPage.BarTextColor)] = MapBarTextColor, ["BarTextColor"] = MapBarTextColor,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<NavigationPage, NavigationPageHandler> CommandMapper = public static CommandMapper<NavigationPage, NavigationPageHandler> CommandMapper = new CommandMapper<NavigationPage, NavigationPageHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper) { ["RequestNavigation"] = MapRequestNavigation };
new(ViewHandler.ViewCommandMapper)
{
[nameof(IStackNavigationView.RequestNavigation)] = MapRequestNavigation,
};
public NavigationPageHandler() : base(Mapper, CommandMapper) private readonly Dictionary<Page, (SkiaPage, INotifyCollectionChanged)> _toolbarSubscriptions = new Dictionary<Page, (SkiaPage, INotifyCollectionChanged)>();
public NavigationPageHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public NavigationPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public NavigationPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -50,15 +46,11 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
platformView.Pushed += OnPushed; platformView.Pushed += OnPushed;
platformView.Popped += OnPopped; platformView.Popped += OnPopped;
platformView.PoppedToRoot += OnPoppedToRoot; platformView.PoppedToRoot += OnPoppedToRoot;
if (base.VirtualView != null)
// Subscribe to navigation events from virtual view
if (VirtualView != null)
{ {
VirtualView.Pushed += OnVirtualViewPushed; base.VirtualView.Pushed += OnVirtualViewPushed;
VirtualView.Popped += OnVirtualViewPopped; base.VirtualView.Popped += OnVirtualViewPopped;
VirtualView.PoppedToRoot += OnVirtualViewPoppedToRoot; base.VirtualView.PoppedToRoot += OnVirtualViewPoppedToRoot;
// Set up initial navigation stack
SetupNavigationStack(); SetupNavigationStack();
} }
} }
@@ -68,314 +60,389 @@ public partial class NavigationPageHandler : ViewHandler<NavigationPage, SkiaNav
platformView.Pushed -= OnPushed; platformView.Pushed -= OnPushed;
platformView.Popped -= OnPopped; platformView.Popped -= OnPopped;
platformView.PoppedToRoot -= OnPoppedToRoot; platformView.PoppedToRoot -= OnPoppedToRoot;
if (base.VirtualView != null)
if (VirtualView != null)
{ {
VirtualView.Pushed -= OnVirtualViewPushed; base.VirtualView.Pushed -= OnVirtualViewPushed;
VirtualView.Popped -= OnVirtualViewPopped; base.VirtualView.Popped -= OnVirtualViewPopped;
VirtualView.PoppedToRoot -= OnVirtualViewPoppedToRoot; base.VirtualView.PoppedToRoot -= OnVirtualViewPoppedToRoot;
} }
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void SetupNavigationStack() private void SetupNavigationStack()
{ {
if (VirtualView == null || PlatformView == null || MauiContext == null) return; //IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
// Get all pages in the navigation stack if (base.VirtualView == null || base.PlatformView == null || ((ElementHandler)this).MauiContext == null)
var pages = VirtualView.Navigation.NavigationStack.ToList();
Console.WriteLine($"[NavigationPageHandler] Setting up {pages.Count} pages");
// If no pages in stack, check CurrentPage
if (pages.Count == 0 && VirtualView.CurrentPage != null)
{ {
Console.WriteLine($"[NavigationPageHandler] No pages in stack, using CurrentPage: {VirtualView.CurrentPage.Title}"); return;
pages.Add(VirtualView.CurrentPage);
} }
List<Page> list = ((NavigableElement)base.VirtualView).Navigation.NavigationStack.ToList();
foreach (var page in pages) Console.WriteLine($"[NavigationPageHandler] Setting up {list.Count} pages");
if (list.Count == 0 && base.VirtualView.CurrentPage != null)
{ {
// Ensure the page has a handler Console.WriteLine("[NavigationPageHandler] No pages in stack, using CurrentPage: " + base.VirtualView.CurrentPage.Title);
if (page.Handler == null) list.Add(base.VirtualView.CurrentPage);
{
Console.WriteLine($"[NavigationPageHandler] Creating handler for: {page.Title}");
page.Handler = page.ToHandler(MauiContext);
} }
foreach (Page item in list)
Console.WriteLine($"[NavigationPageHandler] Page handler type: {page.Handler?.GetType().Name}"); {
Console.WriteLine($"[NavigationPageHandler] Page PlatformView type: {page.Handler?.PlatformView?.GetType().Name}"); if (((VisualElement)item).Handler == null)
{
if (page.Handler?.PlatformView is SkiaPage skiaPage) Console.WriteLine("[NavigationPageHandler] Creating handler for: " + item.Title);
((VisualElement)item).Handler = ((IView)(object)item).ToViewHandler(((ElementHandler)this).MauiContext);
}
Console.WriteLine("[NavigationPageHandler] Page handler type: " + ((object)((VisualElement)item).Handler)?.GetType().Name);
IViewHandler handler = ((VisualElement)item).Handler;
Console.WriteLine("[NavigationPageHandler] Page PlatformView type: " + ((handler == null) ? null : ((IElementHandler)handler).PlatformView?.GetType().Name));
IViewHandler handler2 = ((VisualElement)item).Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaPage skiaPage)
{ {
// Set navigation bar properties
skiaPage.ShowNavigationBar = true; skiaPage.ShowNavigationBar = true;
skiaPage.TitleBarColor = PlatformView.BarBackgroundColor; skiaPage.TitleBarColor = base.PlatformView.BarBackgroundColor;
skiaPage.TitleTextColor = PlatformView.BarTextColor; skiaPage.TitleTextColor = base.PlatformView.BarTextColor;
skiaPage.Title = page.Title ?? ""; skiaPage.Title = item.Title ?? "";
Console.WriteLine("[NavigationPageHandler] SkiaPage content: " + (((object)skiaPage.Content)?.GetType().Name ?? "null"));
Console.WriteLine($"[NavigationPageHandler] SkiaPage content: {skiaPage.Content?.GetType().Name ?? "null"}"); if (skiaPage.Content == null)
// If content is null, try to get it from ContentPage
if (skiaPage.Content == null && page is ContentPage contentPage && contentPage.Content != null)
{ {
Console.WriteLine($"[NavigationPageHandler] Content is null, manually creating handler for: {contentPage.Content.GetType().Name}"); ContentPage val = (ContentPage)(object)((item is ContentPage) ? item : null);
if (contentPage.Content.Handler == null) if (val != null && val.Content != null)
{ {
contentPage.Content.Handler = contentPage.Content.ToHandler(MauiContext); Console.WriteLine("[NavigationPageHandler] Content is null, manually creating handler for: " + ((object)val.Content).GetType().Name);
if (((VisualElement)val.Content).Handler == null)
{
((VisualElement)val.Content).Handler = ((IView)(object)val.Content).ToViewHandler(((ElementHandler)this).MauiContext);
} }
if (contentPage.Content.Handler?.PlatformView is SkiaView skiaContent) IViewHandler handler3 = ((VisualElement)val.Content).Handler;
if (((handler3 != null) ? ((IElementHandler)handler3).PlatformView : null) is SkiaView skiaView)
{ {
skiaPage.Content = skiaContent; skiaPage.Content = skiaView;
Console.WriteLine($"[NavigationPageHandler] Set content to: {skiaContent.GetType().Name}"); Console.WriteLine("[NavigationPageHandler] Set content to: " + ((object)skiaView).GetType().Name);
} }
} }
}
// Map toolbar items MapToolbarItems(skiaPage, item);
MapToolbarItems(skiaPage, page); if (base.PlatformView.StackDepth == 0)
if (PlatformView.StackDepth == 0)
{ {
Console.WriteLine($"[NavigationPageHandler] Setting root page: {page.Title}"); Console.WriteLine("[NavigationPageHandler] Setting root page: " + item.Title);
PlatformView.SetRootPage(skiaPage); base.PlatformView.SetRootPage(skiaPage);
} }
else else
{ {
Console.WriteLine($"[NavigationPageHandler] Pushing page: {page.Title}"); Console.WriteLine("[NavigationPageHandler] Pushing page: " + item.Title);
PlatformView.Push(skiaPage, false); base.PlatformView.Push(skiaPage, animated: false);
} }
} }
else else
{ {
Console.WriteLine($"[NavigationPageHandler] Failed to get SkiaPage for: {page.Title}"); Console.WriteLine("[NavigationPageHandler] Failed to get SkiaPage for: " + item.Title);
} }
} }
} }
private readonly Dictionary<Page, (SkiaPage, INotifyCollectionChanged)> _toolbarSubscriptions = new();
private void MapToolbarItems(SkiaPage skiaPage, Page page) private void MapToolbarItems(SkiaPage skiaPage, Page page)
{ {
if (skiaPage is SkiaContentPage contentPage) //IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_011f: Invalid comparison between Unknown and I4
if (!(skiaPage is SkiaContentPage skiaContentPage))
{ {
return;
}
Console.WriteLine($"[NavigationPageHandler] MapToolbarItems for '{page.Title}', count={page.ToolbarItems.Count}"); Console.WriteLine($"[NavigationPageHandler] MapToolbarItems for '{page.Title}', count={page.ToolbarItems.Count}");
skiaContentPage.ToolbarItems.Clear();
contentPage.ToolbarItems.Clear(); foreach (ToolbarItem toolbarItem2 in page.ToolbarItems)
foreach (var item in page.ToolbarItems)
{ {
Console.WriteLine($"[NavigationPageHandler] Adding toolbar item: '{item.Text}', Order={item.Order}"); Console.WriteLine($"[NavigationPageHandler] Adding toolbar item: '{((MenuItem)toolbarItem2).Text}', IconImageSource={((MenuItem)toolbarItem2).IconImageSource}, Order={toolbarItem2.Order}");
// Default and Primary should both be treated as Primary (shown in toolbar) SkiaToolbarItemOrder order = (((int)toolbarItem2.Order == 2) ? SkiaToolbarItemOrder.Secondary : SkiaToolbarItemOrder.Primary);
// Only Secondary goes to overflow menu ToolbarItem toolbarItem = toolbarItem2;
var order = item.Order == ToolbarItemOrder.Secondary RelayCommand command = new RelayCommand(delegate
? SkiaToolbarItemOrder.Secondary
: SkiaToolbarItemOrder.Primary;
// Create a command that invokes the Clicked event
var toolbarItem = item; // Capture for closure
var clickCommand = new RelayCommand(() =>
{ {
Console.WriteLine($"[NavigationPageHandler] ToolbarItem '{toolbarItem.Text}' clicked, invoking..."); Console.WriteLine("[NavigationPageHandler] ToolbarItem '" + ((MenuItem)toolbarItem).Text + "' clicked, invoking...");
// Use IMenuItemController to send the click IMenuItemController val2 = (IMenuItemController)(object)toolbarItem;
if (toolbarItem is IMenuItemController menuController) if (val2 != null)
{ {
menuController.Activate(); val2.Activate();
} }
else else
{ {
// Fallback: invoke Command if set ((MenuItem)toolbarItem).Command?.Execute(((MenuItem)toolbarItem).CommandParameter);
toolbarItem.Command?.Execute(toolbarItem.CommandParameter);
} }
}); });
SKBitmap icon = null;
contentPage.ToolbarItems.Add(new SkiaToolbarItem ImageSource iconImageSource = ((MenuItem)toolbarItem2).IconImageSource;
FileImageSource val = (FileImageSource)(object)((iconImageSource is FileImageSource) ? iconImageSource : null);
if (val != null && !string.IsNullOrEmpty(val.File))
{ {
Text = item.Text ?? "", icon = LoadToolbarIcon(val.File);
}
skiaContentPage.ToolbarItems.Add(new SkiaToolbarItem
{
Text = (((MenuItem)toolbarItem2).Text ?? ""),
Icon = icon,
Order = order, Order = order,
Command = clickCommand Command = command
}); });
} }
if (page.ToolbarItems is INotifyCollectionChanged notifyCollectionChanged && !_toolbarSubscriptions.ContainsKey(page))
// Subscribe to ToolbarItems changes if not already subscribed
if (page.ToolbarItems is INotifyCollectionChanged notifyCollection && !_toolbarSubscriptions.ContainsKey(page))
{ {
Console.WriteLine($"[NavigationPageHandler] Subscribing to ToolbarItems changes for '{page.Title}'"); Console.WriteLine("[NavigationPageHandler] Subscribing to ToolbarItems changes for '" + page.Title + "'");
notifyCollection.CollectionChanged += (s, e) => notifyCollectionChanged.CollectionChanged += delegate(object? s, NotifyCollectionChangedEventArgs e)
{ {
Console.WriteLine($"[NavigationPageHandler] ToolbarItems changed for '{page.Title}', action={e.Action}"); Console.WriteLine($"[NavigationPageHandler] ToolbarItems changed for '{page.Title}', action={e.Action}");
MapToolbarItems(skiaPage, page); MapToolbarItems(skiaPage, page);
skiaPage.Invalidate(); skiaPage.Invalidate();
}; };
_toolbarSubscriptions[page] = (skiaPage, notifyCollection); _toolbarSubscriptions[page] = (skiaPage, notifyCollectionChanged);
}
} }
} }
private void OnVirtualViewPushed(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e) private SKBitmap? LoadToolbarIcon(string fileName)
{ {
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Expected O, but got Unknown
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Expected O, but got Unknown
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_012b: Expected O, but got Unknown
//IL_012d: Unknown result type (might be due to invalid IL or missing references)
try try
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView Pushed: {e.Page?.Title}"); string baseDirectory = AppContext.BaseDirectory;
if (e.Page == null || PlatformView == null || MauiContext == null) return; string text = Path.Combine(baseDirectory, fileName);
string text2 = Path.Combine(baseDirectory, Path.ChangeExtension(fileName, ".svg"));
// Ensure the page has a handler Console.WriteLine("[NavigationPageHandler] LoadToolbarIcon: Looking for " + fileName);
if (e.Page.Handler == null) Console.WriteLine($"[NavigationPageHandler] Trying PNG: {text} (exists: {File.Exists(text)})");
Console.WriteLine($"[NavigationPageHandler] Trying SVG: {text2} (exists: {File.Exists(text2)})");
if (File.Exists(text2))
{ {
Console.WriteLine($"[NavigationPageHandler] Creating handler for page: {e.Page.GetType().Name}"); SKSvg val = new SKSvg();
e.Page.Handler = e.Page.ToHandler(MauiContext); try
Console.WriteLine($"[NavigationPageHandler] Handler created: {e.Page.Handler?.GetType().Name}");
}
if (e.Page.Handler?.PlatformView is SkiaPage skiaPage)
{ {
Console.WriteLine($"[NavigationPageHandler] Setting up skiaPage, content: {skiaPage.Content?.GetType().Name ?? "null"}"); val.Load(text2);
skiaPage.ShowNavigationBar = true; if (val.Picture != null)
skiaPage.TitleBarColor = PlatformView.BarBackgroundColor; {
skiaPage.TitleTextColor = PlatformView.BarTextColor; SKRect cullRect = val.Picture.CullRect;
Console.WriteLine($"[NavigationPageHandler] Mapping toolbar items"); float num = 24f / Math.Max(((SKRect)(ref cullRect)).Width, ((SKRect)(ref cullRect)).Height);
MapToolbarItems(skiaPage, e.Page); SKBitmap val2 = new SKBitmap(24, 24, false);
Console.WriteLine($"[NavigationPageHandler] Pushing page to platform"); SKCanvas val3 = new SKCanvas(val2);
PlatformView.Push(skiaPage, true); try
Console.WriteLine($"[NavigationPageHandler] Push complete"); {
val3.Clear(SKColors.Transparent);
val3.Scale(num);
val3.DrawPicture(val.Picture, (SKPaint)null);
Console.WriteLine("[NavigationPageHandler] Loaded SVG icon: " + text2);
return val2;
} }
finally
{
((IDisposable)val3)?.Dispose();
}
}
}
finally
{
((IDisposable)val)?.Dispose();
}
}
if (File.Exists(text))
{
using (FileStream fileStream = File.OpenRead(text))
{
SKBitmap result = SKBitmap.Decode((Stream)fileStream);
Console.WriteLine("[NavigationPageHandler] Loaded PNG icon: " + text);
return result;
}
}
Console.WriteLine("[NavigationPageHandler] Icon not found: " + fileName);
return null;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[NavigationPageHandler] EXCEPTION in OnVirtualViewPushed: {ex.GetType().Name}: {ex.Message}"); Console.WriteLine("[NavigationPageHandler] Error loading icon " + fileName + ": " + ex.Message);
Console.WriteLine($"[NavigationPageHandler] Stack trace: {ex.StackTrace}"); return null;
}
}
private void OnVirtualViewPushed(object? sender, NavigationEventArgs e)
{
//IL_0111: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
try
{
Page page = e.Page;
Console.WriteLine("[NavigationPageHandler] VirtualView Pushed: " + ((page != null) ? page.Title : null));
if (e.Page == null || base.PlatformView == null || ((ElementHandler)this).MauiContext == null)
{
return;
}
if (((VisualElement)e.Page).Handler == null)
{
Console.WriteLine("[NavigationPageHandler] Creating handler for page: " + ((object)e.Page).GetType().Name);
((VisualElement)e.Page).Handler = ((IView)(object)e.Page).ToViewHandler(((ElementHandler)this).MauiContext);
Console.WriteLine("[NavigationPageHandler] Handler created: " + ((object)((VisualElement)e.Page).Handler)?.GetType().Name);
}
IViewHandler handler = ((VisualElement)e.Page).Handler;
if (((handler != null) ? ((IElementHandler)handler).PlatformView : null) is SkiaPage skiaPage)
{
Console.WriteLine("[NavigationPageHandler] Setting up skiaPage, content: " + (((object)skiaPage.Content)?.GetType().Name ?? "null"));
skiaPage.ShowNavigationBar = true;
skiaPage.TitleBarColor = base.PlatformView.BarBackgroundColor;
skiaPage.TitleTextColor = base.PlatformView.BarTextColor;
skiaPage.Title = e.Page.Title ?? "";
if (skiaPage.Content == null)
{
Page page2 = e.Page;
ContentPage val = (ContentPage)(object)((page2 is ContentPage) ? page2 : null);
if (val != null && val.Content != null)
{
Console.WriteLine("[NavigationPageHandler] Content is null, creating handler for: " + ((object)val.Content).GetType().Name);
if (((VisualElement)val.Content).Handler == null)
{
((VisualElement)val.Content).Handler = ((IView)(object)val.Content).ToViewHandler(((ElementHandler)this).MauiContext);
}
IViewHandler handler2 = ((VisualElement)val.Content).Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView skiaView)
{
skiaPage.Content = skiaView;
Console.WriteLine("[NavigationPageHandler] Set content to: " + ((object)skiaView).GetType().Name);
}
}
}
Console.WriteLine("[NavigationPageHandler] Mapping toolbar items");
MapToolbarItems(skiaPage, e.Page);
Console.WriteLine("[NavigationPageHandler] Pushing page to platform");
base.PlatformView.Push(skiaPage, animated: false);
Console.WriteLine($"[NavigationPageHandler] Push complete, thread={Environment.CurrentManagedThreadId}");
}
Console.WriteLine("[NavigationPageHandler] OnVirtualViewPushed returning");
}
catch (Exception ex)
{
Console.WriteLine("[NavigationPageHandler] EXCEPTION in OnVirtualViewPushed: " + ex.GetType().Name + ": " + ex.Message);
Console.WriteLine("[NavigationPageHandler] Stack trace: " + ex.StackTrace);
throw; throw;
} }
} }
private void OnVirtualViewPopped(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e) private void OnVirtualViewPopped(object? sender, NavigationEventArgs e)
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView Popped: {e.Page?.Title}"); Page page = e.Page;
// Pop on the platform side to sync with MAUI navigation Console.WriteLine("[NavigationPageHandler] VirtualView Popped: " + ((page != null) ? page.Title : null));
PlatformView?.Pop(true); base.PlatformView?.Pop();
} }
private void OnVirtualViewPoppedToRoot(object? sender, Microsoft.Maui.Controls.NavigationEventArgs e) private void OnVirtualViewPoppedToRoot(object? sender, NavigationEventArgs e)
{ {
Console.WriteLine($"[NavigationPageHandler] VirtualView PoppedToRoot"); Console.WriteLine("[NavigationPageHandler] VirtualView PoppedToRoot");
PlatformView?.PopToRoot(true); base.PlatformView?.PopToRoot();
} }
private void OnPushed(object? sender, NavigationEventArgs e) private void OnPushed(object? sender, NavigationEventArgs e)
{ {
// Navigation was completed on platform side
} }
private void OnPopped(object? sender, NavigationEventArgs e) private void OnPopped(object? sender, NavigationEventArgs e)
{ {
// Sync back to virtual view - pop from MAUI navigation stack NavigationPage virtualView = base.VirtualView;
if (VirtualView?.Navigation.NavigationStack.Count > 1) if (virtualView != null && ((NavigableElement)virtualView).Navigation.NavigationStack.Count > 1)
{ {
// Don't trigger another pop on platform side ((NavigableElement)base.VirtualView).Navigation.RemovePage(((NavigableElement)base.VirtualView).Navigation.NavigationStack.Last());
VirtualView.Navigation.RemovePage(VirtualView.Navigation.NavigationStack.Last());
} }
} }
private void OnPoppedToRoot(object? sender, NavigationEventArgs e) private void OnPoppedToRoot(object? sender, NavigationEventArgs e)
{ {
// Navigation was reset
} }
public static void MapBarBackgroundColor(NavigationPageHandler handler, NavigationPage navigationPage) public static void MapBarBackgroundColor(NavigationPageHandler handler, NavigationPage navigationPage)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView != null && navigationPage.BarBackgroundColor != null)
if (navigationPage.BarBackgroundColor is not null)
{ {
handler.PlatformView.BarBackgroundColor = navigationPage.BarBackgroundColor.ToSKColor(); ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BarBackgroundColor = navigationPage.BarBackgroundColor.ToSKColor();
} }
} }
public static void MapBarBackground(NavigationPageHandler handler, NavigationPage navigationPage) public static void MapBarBackground(NavigationPageHandler handler, NavigationPage navigationPage)
{ {
if (handler.PlatformView is null) return; //IL_0024: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView != null)
if (navigationPage.BarBackground is SolidColorBrush solidBrush)
{ {
handler.PlatformView.BarBackgroundColor = solidBrush.Color.ToSKColor(); Brush barBackground = navigationPage.BarBackground;
SolidColorBrush val = (SolidColorBrush)(object)((barBackground is SolidColorBrush) ? barBackground : null);
if (val != null)
{
((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BarBackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage navigationPage) public static void MapBarTextColor(NavigationPageHandler handler, NavigationPage navigationPage)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView != null && navigationPage.BarTextColor != null)
if (navigationPage.BarTextColor is not null)
{ {
handler.PlatformView.BarTextColor = navigationPage.BarTextColor.ToSKColor(); ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BarTextColor = navigationPage.BarTextColor.ToSKColor();
} }
} }
public static void MapBackground(NavigationPageHandler handler, NavigationPage navigationPage) public static void MapBackground(NavigationPageHandler handler, NavigationPage navigationPage)
{ {
if (handler.PlatformView is null) return; //IL_0024: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView != null)
if (navigationPage.Background is SolidColorBrush solidBrush)
{ {
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor(); Brush background = ((VisualElement)navigationPage).Background;
SolidColorBrush val = (SolidColorBrush)(object)((background is SolidColorBrush) ? background : null);
if (val != null)
{
((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapRequestNavigation(NavigationPageHandler handler, NavigationPage navigationPage, object? args) public static void MapRequestNavigation(NavigationPageHandler handler, NavigationPage navigationPage, object? args)
{ {
if (handler.PlatformView is null || handler.MauiContext is null || args is not NavigationRequest request) //IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
{
return; return;
Console.WriteLine($"[NavigationPageHandler] MapRequestNavigation: {request.NavigationStack.Count} pages");
// Handle navigation request
foreach (var view in request.NavigationStack)
{
if (view is not Page page) continue;
// Ensure handler exists
if (page.Handler == null)
{
page.Handler = page.ToHandler(handler.MauiContext);
} }
NavigationRequest val = (NavigationRequest)((args is NavigationRequest) ? args : null);
if (page.Handler?.PlatformView is SkiaPage skiaPage) if (val == null)
{
return;
}
Console.WriteLine($"[NavigationPageHandler] MapRequestNavigation: {val.NavigationStack.Count} pages");
foreach (IView item in val.NavigationStack)
{
Page val2 = (Page)(object)((item is Page) ? item : null);
if (val2 == null)
{
continue;
}
if (((VisualElement)val2).Handler == null)
{
((VisualElement)val2).Handler = ((IView)(object)val2).ToViewHandler(((ElementHandler)handler).MauiContext);
}
IViewHandler handler2 = ((VisualElement)val2).Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaPage skiaPage)
{ {
skiaPage.ShowNavigationBar = true; skiaPage.ShowNavigationBar = true;
skiaPage.TitleBarColor = handler.PlatformView.BarBackgroundColor; skiaPage.TitleBarColor = ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BarBackgroundColor;
skiaPage.TitleTextColor = handler.PlatformView.BarTextColor; skiaPage.TitleTextColor = ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.BarTextColor;
handler.MapToolbarItems(skiaPage, page); handler.MapToolbarItems(skiaPage, val2);
if (((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.StackDepth == 0)
if (handler.PlatformView.StackDepth == 0)
{ {
handler.PlatformView.SetRootPage(skiaPage); ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.SetRootPage(skiaPage);
} }
else else
{ {
handler.PlatformView.Push(skiaPage, request.Animated); ((ViewHandler<NavigationPage, SkiaNavigationPage>)(object)handler).PlatformView.Push(skiaPage, val.Animated);
} }
} }
} }
} }
} }
/// <summary>
/// Simple relay command for invoking actions.
/// </summary>
internal class RelayCommand : System.Windows.Input.ICommand
{
private readonly Action _execute;
private readonly Func<bool>? _canExecute;
public RelayCommand(Action execute, Func<bool>? canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object? parameter) => _execute();
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

View File

@@ -1,39 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class PageHandler : ViewHandler<Page, SkiaPage>
/// Base handler for Page on Linux using Skia rendering.
/// </summary>
public partial class PageHandler : ViewHandler<Page, SkiaPage>
{ {
public static IPropertyMapper<Page, PageHandler> Mapper = public static IPropertyMapper<Page, PageHandler> Mapper = (IPropertyMapper<Page, PageHandler>)(object)new PropertyMapper<Page, PageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<Page, PageHandler>(ViewHandler.ViewMapper)
{ {
[nameof(Page.Title)] = MapTitle, ["Title"] = MapTitle,
[nameof(Page.BackgroundImageSource)] = MapBackgroundImageSource, ["BackgroundImageSource"] = MapBackgroundImageSource,
[nameof(Page.Padding)] = MapPadding, ["Padding"] = MapPadding,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor
}; };
public static CommandMapper<Page, PageHandler> CommandMapper = public static CommandMapper<Page, PageHandler> CommandMapper = new CommandMapper<Page, PageHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public PageHandler() : base(Mapper, CommandMapper) public PageHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public PageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public PageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -58,109 +49,76 @@ public partial class PageHandler : ViewHandler<Page, SkiaPage>
private void OnAppearing(object? sender, EventArgs e) private void OnAppearing(object? sender, EventArgs e)
{ {
Console.WriteLine($"[PageHandler] OnAppearing received for: {VirtualView?.Title}"); Page virtualView = base.VirtualView;
(VirtualView as IPageController)?.SendAppearing(); Console.WriteLine("[PageHandler] OnAppearing received for: " + ((virtualView != null) ? virtualView.Title : null));
Page virtualView2 = base.VirtualView;
if (virtualView2 != null)
{
((IPageController)virtualView2).SendAppearing();
}
} }
private void OnDisappearing(object? sender, EventArgs e) private void OnDisappearing(object? sender, EventArgs e)
{ {
(VirtualView as IPageController)?.SendDisappearing(); Page virtualView = base.VirtualView;
if (virtualView != null)
{
((IPageController)virtualView).SendDisappearing();
}
} }
public static void MapTitle(PageHandler handler, Page page) public static void MapTitle(PageHandler handler, Page page)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView != null)
handler.PlatformView.Title = page.Title ?? ""; {
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.Title = page.Title ?? "";
}
} }
public static void MapBackgroundImageSource(PageHandler handler, Page page) public static void MapBackgroundImageSource(PageHandler handler, Page page)
{ {
// Background image would be loaded and set here ((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView?.Invalidate();
// For now, we just invalidate
handler.PlatformView?.Invalidate();
} }
public static void MapPadding(PageHandler handler, Page page) public static void MapPadding(PageHandler handler, Page page)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var padding = page.Padding; if (((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView != null)
handler.PlatformView.PaddingLeft = (float)padding.Left; {
handler.PlatformView.PaddingTop = (float)padding.Top; Thickness padding = page.Padding;
handler.PlatformView.PaddingRight = (float)padding.Right; ((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.PaddingLeft = (float)((Thickness)(ref padding)).Left;
handler.PlatformView.PaddingBottom = (float)padding.Bottom; ((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.PaddingTop = (float)((Thickness)(ref padding)).Top;
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.PaddingRight = (float)((Thickness)(ref padding)).Right;
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.PaddingBottom = (float)((Thickness)(ref padding)).Bottom;
}
} }
public static void MapBackground(PageHandler handler, Page page) public static void MapBackground(PageHandler handler, Page page)
{ {
if (handler.PlatformView is null) return; //IL_0024: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView != null)
if (page.Background is SolidColorBrush solidBrush)
{ {
handler.PlatformView.BackgroundColor = solidBrush.Color.ToSKColor(); Brush background = ((VisualElement)page).Background;
} SolidColorBrush val = (SolidColorBrush)(object)((background is SolidColorBrush) ? background : null);
} if (val != null)
} {
((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
/// <summary> }
/// Handler for ContentPage on Linux using Skia rendering. }
/// </summary> }
public partial class ContentPageHandler : PageHandler
{ public static void MapBackgroundColor(PageHandler handler, Page page)
public static new IPropertyMapper<ContentPage, ContentPageHandler> Mapper = {
new PropertyMapper<ContentPage, ContentPageHandler>(PageHandler.Mapper) //IL_0022: Unknown result type (might be due to invalid IL or missing references)
{ if (((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView != null)
[nameof(ContentPage.Content)] = MapContent, {
}; Color backgroundColor = ((VisualElement)page).BackgroundColor;
if (backgroundColor != null && backgroundColor != Colors.Transparent)
public static new CommandMapper<ContentPage, ContentPageHandler> CommandMapper = {
new(PageHandler.CommandMapper) ((ViewHandler<Page, SkiaPage>)(object)handler).PlatformView.BackgroundColor = backgroundColor.ToSKColor();
{ Console.WriteLine($"[PageHandler] MapBackgroundColor: {backgroundColor}");
}; }
public ContentPageHandler() : base(Mapper, CommandMapper)
{
}
public ContentPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{
}
protected override SkiaPage CreatePlatformView()
{
return new SkiaContentPage();
}
public static void MapContent(ContentPageHandler handler, ContentPage page)
{
if (handler.PlatformView is null || handler.MauiContext is null) return;
// Get the platform view for the content
var content = page.Content;
if (content != null)
{
// Create handler for content if it doesn't exist
if (content.Handler == null)
{
Console.WriteLine($"[ContentPageHandler] Creating handler for content: {content.GetType().Name}");
content.Handler = content.ToHandler(handler.MauiContext);
}
// The content's handler should provide the platform view
if (content.Handler?.PlatformView is SkiaView skiaContent)
{
Console.WriteLine($"[ContentPageHandler] Setting content: {skiaContent.GetType().Name}");
handler.PlatformView.Content = skiaContent;
}
else
{
Console.WriteLine($"[ContentPageHandler] Content handler PlatformView is not SkiaView: {content.Handler?.PlatformView?.GetType().Name ?? "null"}");
}
}
else
{
handler.PlatformView.Content = null;
} }
} }
} }

View File

@@ -1,47 +1,39 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform;
using SkiaSharp;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class PickerHandler : ViewHandler<IPicker, SkiaPicker>
/// Handler for Picker on Linux using Skia rendering.
/// </summary>
public partial class PickerHandler : ViewHandler<IPicker, SkiaPicker>
{ {
public static IPropertyMapper<IPicker, PickerHandler> Mapper = public static IPropertyMapper<IPicker, PickerHandler> Mapper = (IPropertyMapper<IPicker, PickerHandler>)(object)new PropertyMapper<IPicker, PickerHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IPicker, PickerHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IPicker.Title)] = MapTitle, ["Title"] = MapTitle,
[nameof(IPicker.TitleColor)] = MapTitleColor, ["TitleColor"] = MapTitleColor,
[nameof(IPicker.SelectedIndex)] = MapSelectedIndex, ["SelectedIndex"] = MapSelectedIndex,
[nameof(IPicker.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(IPicker.CharacterSpacing)] = MapCharacterSpacing, ["Font"] = MapFont,
[nameof(IPicker.HorizontalTextAlignment)] = MapHorizontalTextAlignment, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(IPicker.VerticalTextAlignment)] = MapVerticalTextAlignment, ["HorizontalTextAlignment"] = MapHorizontalTextAlignment,
[nameof(IView.Background)] = MapBackground, ["VerticalTextAlignment"] = MapVerticalTextAlignment,
[nameof(Picker.ItemsSource)] = MapItemsSource, ["Background"] = MapBackground,
["ItemsSource"] = MapItemsSource
}; };
public static CommandMapper<IPicker, PickerHandler> CommandMapper = public static CommandMapper<IPicker, PickerHandler> CommandMapper = new CommandMapper<IPicker, PickerHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
private INotifyCollectionChanged? _itemsCollection; private INotifyCollectionChanged? _itemsCollection;
public PickerHandler() : base(Mapper, CommandMapper) public PickerHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public PickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public PickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -54,28 +46,24 @@ public partial class PickerHandler : ViewHandler<IPicker, SkiaPicker>
{ {
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.SelectedIndexChanged += OnSelectedIndexChanged; platformView.SelectedIndexChanged += OnSelectedIndexChanged;
IPicker virtualView = base.VirtualView;
// Subscribe to items collection changes Picker val = (Picker)(object)((virtualView is Picker) ? virtualView : null);
if (VirtualView is Picker picker && picker.Items is INotifyCollectionChanged items) if (val != null && val.Items is INotifyCollectionChanged itemsCollection)
{ {
_itemsCollection = items; _itemsCollection = itemsCollection;
_itemsCollection.CollectionChanged += OnItemsCollectionChanged; _itemsCollection.CollectionChanged += OnItemsCollectionChanged;
} }
// Load items
ReloadItems(); ReloadItems();
} }
protected override void DisconnectHandler(SkiaPicker platformView) protected override void DisconnectHandler(SkiaPicker platformView)
{ {
platformView.SelectedIndexChanged -= OnSelectedIndexChanged; platformView.SelectedIndexChanged -= OnSelectedIndexChanged;
if (_itemsCollection != null) if (_itemsCollection != null)
{ {
_itemsCollection.CollectionChanged -= OnItemsCollectionChanged; _itemsCollection.CollectionChanged -= OnItemsCollectionChanged;
_itemsCollection = null; _itemsCollection = null;
} }
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
@@ -86,71 +74,97 @@ public partial class PickerHandler : ViewHandler<IPicker, SkiaPicker>
private void OnSelectedIndexChanged(object? sender, EventArgs e) private void OnSelectedIndexChanged(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
{
VirtualView.SelectedIndex = PlatformView.SelectedIndex; base.VirtualView.SelectedIndex = base.PlatformView.SelectedIndex;
}
} }
private void ReloadItems() private void ReloadItems()
{ {
if (PlatformView is null || VirtualView is null) return; if (base.PlatformView != null && base.VirtualView != null)
{
var items = VirtualView.GetItemsAsArray(); string[] itemsAsArray = IPickerExtension.GetItemsAsArray(base.VirtualView);
PlatformView.SetItems(items.Select(i => i?.ToString() ?? "")); base.PlatformView.SetItems(itemsAsArray.Select((string i) => i?.ToString() ?? ""));
}
} }
public static void MapTitle(PickerHandler handler, IPicker picker) public static void MapTitle(PickerHandler handler, IPicker picker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null)
handler.PlatformView.Title = picker.Title ?? ""; {
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.Title = picker.Title ?? "";
}
} }
public static void MapTitleColor(PickerHandler handler, IPicker picker) public static void MapTitleColor(PickerHandler handler, IPicker picker)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (picker.TitleColor is not null) if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null && picker.TitleColor != null)
{ {
handler.PlatformView.TitleColor = picker.TitleColor.ToSKColor(); ((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.TitleColor = picker.TitleColor.ToSKColor();
} }
} }
public static void MapSelectedIndex(PickerHandler handler, IPicker picker) public static void MapSelectedIndex(PickerHandler handler, IPicker picker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null)
handler.PlatformView.SelectedIndex = picker.SelectedIndex; {
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.SelectedIndex = picker.SelectedIndex;
}
} }
public static void MapTextColor(PickerHandler handler, IPicker picker) public static void MapTextColor(PickerHandler handler, IPicker picker)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (picker.TextColor is not null) if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null && ((ITextStyle)picker).TextColor != null)
{ {
handler.PlatformView.TextColor = picker.TextColor.ToSKColor(); ((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.TextColor = ((ITextStyle)picker).TextColor.ToSKColor();
}
}
public static void MapFont(PickerHandler handler, IPicker picker)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null)
{
Font font = ((ITextStyle)picker).Font;
if (!string.IsNullOrEmpty(((Font)(ref font)).Family))
{
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.FontFamily = ((Font)(ref font)).Family;
}
if (((Font)(ref font)).Size > 0.0)
{
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.FontSize = (float)((Font)(ref font)).Size;
}
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.Invalidate();
} }
} }
public static void MapCharacterSpacing(PickerHandler handler, IPicker picker) public static void MapCharacterSpacing(PickerHandler handler, IPicker picker)
{ {
// Character spacing could be implemented with custom text rendering
} }
public static void MapHorizontalTextAlignment(PickerHandler handler, IPicker picker) public static void MapHorizontalTextAlignment(PickerHandler handler, IPicker picker)
{ {
// Text alignment would require changes to SkiaPicker drawing
} }
public static void MapVerticalTextAlignment(PickerHandler handler, IPicker picker) public static void MapVerticalTextAlignment(PickerHandler handler, IPicker picker)
{ {
// Text alignment would require changes to SkiaPicker drawing
} }
public static void MapBackground(PickerHandler handler, IPicker picker) public static void MapBackground(PickerHandler handler, IPicker picker)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView != null)
if (picker.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)picker).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IPicker, SkiaPicker>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }

View File

@@ -1,36 +1,25 @@
// Licensed to the .NET Foundation under one or more agreements. using System.ComponentModel;
// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar>
/// Handler for ProgressBar on Linux using Skia rendering.
/// Maps IProgress interface to SkiaProgressBar platform view.
/// IProgress has: Progress (0-1), ProgressColor
/// </summary>
public partial class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar>
{ {
public static IPropertyMapper<IProgress, ProgressBarHandler> Mapper = new PropertyMapper<IProgress, ProgressBarHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IProgress, ProgressBarHandler> Mapper = (IPropertyMapper<IProgress, ProgressBarHandler>)(object)new PropertyMapper<IProgress, ProgressBarHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IProgress.Progress)] = MapProgress, ["Progress"] = MapProgress,
[nameof(IProgress.ProgressColor)] = MapProgressColor, ["ProgressColor"] = MapProgressColor,
[nameof(IView.Background)] = MapBackground, ["IsEnabled"] = MapIsEnabled,
["Background"] = MapBackground,
["BackgroundColor"] = MapBackgroundColor
}; };
public static CommandMapper<IProgress, ProgressBarHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<IProgress, ProgressBarHandler> CommandMapper = new CommandMapper<IProgress, ProgressBarHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ProgressBarHandler() : base(Mapper, CommandMapper) public ProgressBarHandler()
{ : base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
}
public ProgressBarHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
{ {
} }
@@ -39,27 +28,86 @@ public partial class ProgressBarHandler : ViewHandler<IProgress, SkiaProgressBar
return new SkiaProgressBar(); return new SkiaProgressBar();
} }
protected override void ConnectHandler(SkiaProgressBar platformView)
{
base.ConnectHandler(platformView);
IProgress virtualView = base.VirtualView;
BindableObject val = (BindableObject)(object)((virtualView is BindableObject) ? virtualView : null);
if (val != null)
{
val.PropertyChanged += OnVirtualViewPropertyChanged;
}
IProgress virtualView2 = base.VirtualView;
VisualElement val2 = (VisualElement)(object)((virtualView2 is VisualElement) ? virtualView2 : null);
if (val2 != null)
{
platformView.IsVisible = val2.IsVisible;
}
}
protected override void DisconnectHandler(SkiaProgressBar platformView)
{
IProgress virtualView = base.VirtualView;
BindableObject val = (BindableObject)(object)((virtualView is BindableObject) ? virtualView : null);
if (val != null)
{
val.PropertyChanged -= OnVirtualViewPropertyChanged;
}
base.DisconnectHandler(platformView);
}
private void OnVirtualViewPropertyChanged(object? sender, PropertyChangedEventArgs e)
{
IProgress virtualView = base.VirtualView;
VisualElement val = (VisualElement)(object)((virtualView is VisualElement) ? virtualView : null);
if (val != null && e.PropertyName == "IsVisible")
{
base.PlatformView.IsVisible = val.IsVisible;
base.PlatformView.Invalidate();
}
}
public static void MapProgress(ProgressBarHandler handler, IProgress progress) public static void MapProgress(ProgressBarHandler handler, IProgress progress)
{ {
if (handler.PlatformView is null) return; ((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.Progress = progress.Progress;
handler.PlatformView.Progress = Math.Clamp(progress.Progress, 0.0, 1.0);
} }
public static void MapProgressColor(ProgressBarHandler handler, IProgress progress) public static void MapProgressColor(ProgressBarHandler handler, IProgress progress)
{ {
if (handler.PlatformView is null) return; //IL_0014: Unknown result type (might be due to invalid IL or missing references)
if (progress.ProgressColor != null)
{
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.ProgressColor = progress.ProgressColor.ToSKColor();
}
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.Invalidate();
}
if (progress.ProgressColor is not null) public static void MapIsEnabled(ProgressBarHandler handler, IProgress progress)
handler.PlatformView.ProgressColor = progress.ProgressColor.ToSKColor(); {
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.IsEnabled = ((IView)progress).IsEnabled;
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.Invalidate();
} }
public static void MapBackground(ProgressBarHandler handler, IProgress progress) public static void MapBackground(ProgressBarHandler handler, IProgress progress)
{ {
if (handler.PlatformView is null) return; //IL_0023: Unknown result type (might be due to invalid IL or missing references)
Paint background = ((IView)progress).Background;
if (progress.Background is SolidPaint solidPaint && solidPaint.Color is not null) SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); ((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.Invalidate();
}
}
public static void MapBackgroundColor(ProgressBarHandler handler, IProgress progress)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
VisualElement val = (VisualElement)(object)((progress is VisualElement) ? progress : null);
if (val != null && val.BackgroundColor != null)
{
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.BackgroundColor = val.BackgroundColor.ToSKColor();
((ViewHandler<IProgress, SkiaProgressBar>)(object)handler).PlatformView.Invalidate();
} }
} }
} }

View File

@@ -1,39 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class RadioButtonHandler : ViewHandler<IRadioButton, SkiaRadioButton>
/// Handler for RadioButton on Linux using Skia rendering.
/// </summary>
public partial class RadioButtonHandler : ViewHandler<IRadioButton, SkiaRadioButton>
{ {
public static IPropertyMapper<IRadioButton, RadioButtonHandler> Mapper = public static IPropertyMapper<IRadioButton, RadioButtonHandler> Mapper = (IPropertyMapper<IRadioButton, RadioButtonHandler>)(object)new PropertyMapper<IRadioButton, RadioButtonHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IRadioButton, RadioButtonHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IRadioButton.IsChecked)] = MapIsChecked, ["IsChecked"] = MapIsChecked,
[nameof(ITextStyle.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(ITextStyle.Font)] = MapFont, ["Font"] = MapFont,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<IRadioButton, RadioButtonHandler> CommandMapper = public static CommandMapper<IRadioButton, RadioButtonHandler> CommandMapper = new CommandMapper<IRadioButton, RadioButtonHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public RadioButtonHandler() : base(Mapper, CommandMapper) public RadioButtonHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public RadioButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public RadioButtonHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -46,13 +36,13 @@ public partial class RadioButtonHandler : ViewHandler<IRadioButton, SkiaRadioBut
{ {
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.CheckedChanged += OnCheckedChanged; platformView.CheckedChanged += OnCheckedChanged;
IRadioButton virtualView = base.VirtualView;
// Set content if available RadioButton val = (RadioButton)(object)((virtualView is RadioButton) ? virtualView : null);
if (VirtualView is RadioButton rb) if (val != null)
{ {
platformView.Content = rb.Content?.ToString() ?? ""; platformView.Content = val.Content?.ToString() ?? "";
platformView.GroupName = rb.GroupName; platformView.GroupName = val.GroupName;
platformView.Value = rb.Value; platformView.Value = val.Value;
} }
} }
@@ -64,43 +54,58 @@ public partial class RadioButtonHandler : ViewHandler<IRadioButton, SkiaRadioBut
private void OnCheckedChanged(object? sender, EventArgs e) private void OnCheckedChanged(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
VirtualView.IsChecked = PlatformView.IsChecked; {
base.VirtualView.IsChecked = base.PlatformView.IsChecked;
}
} }
public static void MapIsChecked(RadioButtonHandler handler, IRadioButton radioButton) public static void MapIsChecked(RadioButtonHandler handler, IRadioButton radioButton)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView != null)
handler.PlatformView.IsChecked = radioButton.IsChecked; {
((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView.IsChecked = radioButton.IsChecked;
}
} }
public static void MapTextColor(RadioButtonHandler handler, IRadioButton radioButton) public static void MapTextColor(RadioButtonHandler handler, IRadioButton radioButton)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView != null && ((ITextStyle)radioButton).TextColor != null)
if (radioButton.TextColor is not null)
{ {
handler.PlatformView.TextColor = radioButton.TextColor.ToSKColor(); ((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView.TextColor = ((ITextStyle)radioButton).TextColor.ToSKColor();
} }
} }
public static void MapFont(RadioButtonHandler handler, IRadioButton radioButton) public static void MapFont(RadioButtonHandler handler, IRadioButton radioButton)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
if (radioButton.Font.Size > 0) //IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView != null)
{ {
handler.PlatformView.FontSize = (float)radioButton.Font.Size; Font font = ((ITextStyle)radioButton).Font;
if (((Font)(ref font)).Size > 0.0)
{
SkiaRadioButton platformView = ((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView;
font = ((ITextStyle)radioButton).Font;
platformView.FontSize = (float)((Font)(ref font)).Size;
}
} }
} }
public static void MapBackground(RadioButtonHandler handler, IRadioButton radioButton) public static void MapBackground(RadioButtonHandler handler, IRadioButton radioButton)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView != null)
if (radioButton.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)radioButton).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IRadioButton, SkiaRadioButton>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
} }

34
Handlers/RelayCommand.cs Normal file
View File

@@ -0,0 +1,34 @@
using System;
using System.Windows.Input;
namespace Microsoft.Maui.Platform.Linux.Handlers;
internal class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool>? _canExecute;
public event EventHandler? CanExecuteChanged;
public RelayCommand(Action execute, Func<bool>? canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return _canExecute?.Invoke() ?? true;
}
public void Execute(object? parameter)
{
_execute();
}
public void RaiseCanExecuteChanged()
{
this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}

View File

@@ -1,36 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform.Linux.Hosting;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ScrollViewHandler : ViewHandler<IScrollView, SkiaScrollView>
/// Handler for ScrollView on Linux using SkiaScrollView.
/// </summary>
public partial class ScrollViewHandler : ViewHandler<IScrollView, SkiaScrollView>
{ {
public static IPropertyMapper<IScrollView, ScrollViewHandler> Mapper = public static IPropertyMapper<IScrollView, ScrollViewHandler> Mapper = (IPropertyMapper<IScrollView, ScrollViewHandler>)(object)new PropertyMapper<IScrollView, ScrollViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IScrollView, ScrollViewHandler>(ViewMapper)
{ {
[nameof(IScrollView.Content)] = MapContent, ["Content"] = MapContent,
[nameof(IScrollView.HorizontalScrollBarVisibility)] = MapHorizontalScrollBarVisibility, ["HorizontalScrollBarVisibility"] = MapHorizontalScrollBarVisibility,
[nameof(IScrollView.VerticalScrollBarVisibility)] = MapVerticalScrollBarVisibility, ["VerticalScrollBarVisibility"] = MapVerticalScrollBarVisibility,
[nameof(IScrollView.Orientation)] = MapOrientation, ["Orientation"] = MapOrientation
}; };
public static CommandMapper<IScrollView, ScrollViewHandler> CommandMapper = public static CommandMapper<IScrollView, ScrollViewHandler> CommandMapper = new CommandMapper<IScrollView, ScrollViewHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper) { ["RequestScrollTo"] = MapRequestScrollTo };
new(ViewCommandMapper)
{
[nameof(IScrollView.RequestScrollTo)] = MapRequestScrollTo
};
public ScrollViewHandler() : base(Mapper, CommandMapper) public ScrollViewHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ScrollViewHandler(IPropertyMapper? mapper) public ScrollViewHandler(IPropertyMapper? mapper)
: base(mapper ?? Mapper, CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(object)CommandMapper)
{ {
} }
@@ -41,69 +33,83 @@ public partial class ScrollViewHandler : ViewHandler<IScrollView, SkiaScrollView
public static void MapContent(ScrollViewHandler handler, IScrollView scrollView) public static void MapContent(ScrollViewHandler handler, IScrollView scrollView)
{ {
if (handler.PlatformView == null || handler.MauiContext == null) if (((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView == null || ((ElementHandler)handler).MauiContext == null)
{
return; return;
var content = scrollView.PresentedContent;
if (content != null)
{
Console.WriteLine($"[ScrollViewHandler] MapContent: {content.GetType().Name}");
// Create handler for content if it doesn't exist
if (content.Handler == null)
{
content.Handler = content.ToHandler(handler.MauiContext);
} }
IView presentedContent = ((IContentView)scrollView).PresentedContent;
if (content.Handler?.PlatformView is SkiaView skiaContent) if (presentedContent != null)
{ {
Console.WriteLine($"[ScrollViewHandler] Setting content: {skiaContent.GetType().Name}"); Console.WriteLine("[ScrollViewHandler] MapContent: " + ((object)presentedContent).GetType().Name);
handler.PlatformView.Content = skiaContent; if (presentedContent.Handler == null)
{
presentedContent.Handler = presentedContent.ToViewHandler(((ElementHandler)handler).MauiContext);
}
IViewHandler handler2 = presentedContent.Handler;
if (((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null) is SkiaView skiaView)
{
Console.WriteLine("[ScrollViewHandler] Setting content: " + ((object)skiaView).GetType().Name);
((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView.Content = skiaView;
} }
} }
else else
{ {
handler.PlatformView.Content = null; ((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView.Content = null;
} }
} }
public static void MapHorizontalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) public static void MapHorizontalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView)
{ {
handler.PlatformView.HorizontalScrollBarVisibility = scrollView.HorizontalScrollBarVisibility switch //IL_0008: Unknown result type (might be due to invalid IL or missing references)
{ //IL_000d: Unknown result type (might be due to invalid IL or missing references)
Microsoft.Maui.ScrollBarVisibility.Always => ScrollBarVisibility.Always, //IL_000e: Unknown result type (might be due to invalid IL or missing references)
Microsoft.Maui.ScrollBarVisibility.Never => ScrollBarVisibility.Never, //IL_0010: Invalid comparison between Unknown and I4
_ => ScrollBarVisibility.Default //IL_0012: Unknown result type (might be due to invalid IL or missing references)
}; //IL_0014: Invalid comparison between Unknown and I4
SkiaScrollView platformView = ((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView;
ScrollBarVisibility horizontalScrollBarVisibility = scrollView.HorizontalScrollBarVisibility;
ScrollBarVisibility horizontalScrollBarVisibility2 = (((int)horizontalScrollBarVisibility == 1) ? ScrollBarVisibility.Always : (((int)horizontalScrollBarVisibility == 2) ? ScrollBarVisibility.Never : ScrollBarVisibility.Default));
platformView.HorizontalScrollBarVisibility = horizontalScrollBarVisibility2;
} }
public static void MapVerticalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView) public static void MapVerticalScrollBarVisibility(ScrollViewHandler handler, IScrollView scrollView)
{ {
handler.PlatformView.VerticalScrollBarVisibility = scrollView.VerticalScrollBarVisibility switch //IL_0008: Unknown result type (might be due to invalid IL or missing references)
{ //IL_000d: Unknown result type (might be due to invalid IL or missing references)
Microsoft.Maui.ScrollBarVisibility.Always => ScrollBarVisibility.Always, //IL_000e: Unknown result type (might be due to invalid IL or missing references)
Microsoft.Maui.ScrollBarVisibility.Never => ScrollBarVisibility.Never, //IL_0010: Invalid comparison between Unknown and I4
_ => ScrollBarVisibility.Default //IL_0012: Unknown result type (might be due to invalid IL or missing references)
}; //IL_0014: Invalid comparison between Unknown and I4
SkiaScrollView platformView = ((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView;
ScrollBarVisibility verticalScrollBarVisibility = scrollView.VerticalScrollBarVisibility;
ScrollBarVisibility verticalScrollBarVisibility2 = (((int)verticalScrollBarVisibility == 1) ? ScrollBarVisibility.Always : (((int)verticalScrollBarVisibility == 2) ? ScrollBarVisibility.Never : ScrollBarVisibility.Default));
platformView.VerticalScrollBarVisibility = verticalScrollBarVisibility2;
} }
public static void MapOrientation(ScrollViewHandler handler, IScrollView scrollView) public static void MapOrientation(ScrollViewHandler handler, IScrollView scrollView)
{ {
handler.PlatformView.Orientation = scrollView.Orientation switch //IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected I4, but got Unknown
SkiaScrollView platformView = ((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView;
ScrollOrientation orientation = scrollView.Orientation;
platformView.Orientation = (orientation - 1) switch
{ {
Microsoft.Maui.ScrollOrientation.Horizontal => ScrollOrientation.Horizontal, 0 => ScrollOrientation.Horizontal,
Microsoft.Maui.ScrollOrientation.Both => ScrollOrientation.Both, 1 => ScrollOrientation.Both,
Microsoft.Maui.ScrollOrientation.Neither => ScrollOrientation.Neither, 2 => ScrollOrientation.Neither,
_ => ScrollOrientation.Vertical _ => ScrollOrientation.Vertical,
}; };
} }
public static void MapRequestScrollTo(ScrollViewHandler handler, IScrollView scrollView, object? args) public static void MapRequestScrollTo(ScrollViewHandler handler, IScrollView scrollView, object? args)
{ {
if (args is ScrollToRequest request) ScrollToRequest val = (ScrollToRequest)((args is ScrollToRequest) ? args : null);
if (val != null)
{ {
// Instant means no animation, so we pass !Instant for animated parameter ((ViewHandler<IScrollView, SkiaScrollView>)(object)handler).PlatformView.ScrollTo((float)val.HorizontalOffset, (float)val.VerticalOffset, !val.Instant);
handler.PlatformView.ScrollTo((float)request.HorizontalOffset, (float)request.VerticalOffset, !request.Instant);
} }
} }
} }

View File

@@ -1,39 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class SearchBarHandler : ViewHandler<ISearchBar, SkiaSearchBar>
/// Handler for SearchBar on Linux using Skia rendering.
/// Maps ISearchBar interface to SkiaSearchBar platform view.
/// </summary>
public partial class SearchBarHandler : ViewHandler<ISearchBar, SkiaSearchBar>
{ {
public static IPropertyMapper<ISearchBar, SearchBarHandler> Mapper = new PropertyMapper<ISearchBar, SearchBarHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ISearchBar, SearchBarHandler> Mapper = (IPropertyMapper<ISearchBar, SearchBarHandler>)(object)new PropertyMapper<ISearchBar, SearchBarHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(ITextInput.Text)] = MapText, ["Text"] = MapText,
[nameof(ITextStyle.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(ITextStyle.Font)] = MapFont, ["Font"] = MapFont,
[nameof(IPlaceholder.Placeholder)] = MapPlaceholder, ["Placeholder"] = MapPlaceholder,
[nameof(IPlaceholder.PlaceholderColor)] = MapPlaceholderColor, ["PlaceholderColor"] = MapPlaceholderColor,
[nameof(ISearchBar.CancelButtonColor)] = MapCancelButtonColor, ["CancelButtonColor"] = MapCancelButtonColor,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<ISearchBar, SearchBarHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ISearchBar, SearchBarHandler> CommandMapper = new CommandMapper<ISearchBar, SearchBarHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public SearchBarHandler() : base(Mapper, CommandMapper) public SearchBarHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public SearchBarHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public SearchBarHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -58,78 +50,93 @@ public partial class SearchBarHandler : ViewHandler<ISearchBar, SkiaSearchBar>
private void OnTextChanged(object? sender, TextChangedEventArgs e) private void OnTextChanged(object? sender, TextChangedEventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null && ((ITextInput)base.VirtualView).Text != e.NewTextValue)
if (VirtualView.Text != e.NewTextValue)
{ {
VirtualView.Text = e.NewTextValue ?? string.Empty; ((ITextInput)base.VirtualView).Text = e.NewTextValue ?? string.Empty;
} }
} }
private void OnSearchButtonPressed(object? sender, EventArgs e) private void OnSearchButtonPressed(object? sender, EventArgs e)
{ {
VirtualView?.SearchButtonPressed(); ISearchBar virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.SearchButtonPressed();
}
} }
public static void MapText(SearchBarHandler handler, ISearchBar searchBar) public static void MapText(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null && ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.Text != ((ITextInput)searchBar).Text)
{
if (handler.PlatformView.Text != searchBar.Text) ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.Text = ((ITextInput)searchBar).Text ?? string.Empty;
handler.PlatformView.Text = searchBar.Text ?? string.Empty; }
} }
public static void MapTextColor(SearchBarHandler handler, ISearchBar searchBar) public static void MapTextColor(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null && ((ITextStyle)searchBar).TextColor != null)
if (searchBar.TextColor is not null) {
handler.PlatformView.TextColor = searchBar.TextColor.ToSKColor(); ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.TextColor = ((ITextStyle)searchBar).TextColor.ToSKColor();
}
} }
public static void MapFont(SearchBarHandler handler, ISearchBar searchBar) public static void MapFont(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; //IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
var font = searchBar.Font; if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null)
if (font.Size > 0) {
handler.PlatformView.FontSize = (float)font.Size; Font font = ((ITextStyle)searchBar).Font;
if (((Font)(ref font)).Size > 0.0)
if (!string.IsNullOrEmpty(font.Family)) {
handler.PlatformView.FontFamily = font.Family; ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.FontSize = (float)((Font)(ref font)).Size;
}
if (!string.IsNullOrEmpty(((Font)(ref font)).Family))
{
((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.FontFamily = ((Font)(ref font)).Family;
}
}
} }
public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar) public static void MapPlaceholder(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null)
handler.PlatformView.Placeholder = searchBar.Placeholder ?? string.Empty; {
((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.Placeholder = ((IPlaceholder)searchBar).Placeholder ?? string.Empty;
}
} }
public static void MapPlaceholderColor(SearchBarHandler handler, ISearchBar searchBar) public static void MapPlaceholderColor(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null && ((IPlaceholder)searchBar).PlaceholderColor != null)
if (searchBar.PlaceholderColor is not null) {
handler.PlatformView.PlaceholderColor = searchBar.PlaceholderColor.ToSKColor(); ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.PlaceholderColor = ((IPlaceholder)searchBar).PlaceholderColor.ToSKColor();
}
} }
public static void MapCancelButtonColor(SearchBarHandler handler, ISearchBar searchBar) public static void MapCancelButtonColor(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null && searchBar.CancelButtonColor != null)
// CancelButtonColor maps to ClearButtonColor {
if (searchBar.CancelButtonColor is not null) ((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.ClearButtonColor = searchBar.CancelButtonColor.ToSKColor();
handler.PlatformView.ClearButtonColor = searchBar.CancelButtonColor.ToSKColor(); }
} }
public static void MapBackground(SearchBarHandler handler, ISearchBar searchBar) public static void MapBackground(SearchBarHandler handler, ISearchBar searchBar)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView != null)
if (searchBar.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)searchBar).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ISearchBar, SkiaSearchBar>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
} }

View File

@@ -1,32 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class ShellHandler : ViewHandler<Shell, SkiaShell>
/// Handler for Shell on Linux using Skia rendering.
/// </summary>
public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
{ {
public static IPropertyMapper<Shell, ShellHandler> Mapper = new PropertyMapper<Shell, ShellHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<Shell, ShellHandler> Mapper = (IPropertyMapper<Shell, ShellHandler>)(object)new PropertyMapper<Shell, ShellHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper });
{
};
public static CommandMapper<Shell, ShellHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<Shell, ShellHandler> CommandMapper = new CommandMapper<Shell, ShellHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public ShellHandler() : base(Mapper, CommandMapper) public ShellHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public ShellHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public ShellHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -40,12 +31,10 @@ public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.FlyoutIsPresentedChanged += OnFlyoutIsPresentedChanged; platformView.FlyoutIsPresentedChanged += OnFlyoutIsPresentedChanged;
platformView.Navigated += OnNavigated; platformView.Navigated += OnNavigated;
if (base.VirtualView != null)
// Subscribe to Shell navigation events
if (VirtualView != null)
{ {
VirtualView.Navigating += OnShellNavigating; base.VirtualView.Navigating += OnShellNavigating;
VirtualView.Navigated += OnShellNavigated; base.VirtualView.Navigated += OnShellNavigated;
} }
} }
@@ -53,41 +42,47 @@ public partial class ShellHandler : ViewHandler<Shell, SkiaShell>
{ {
platformView.FlyoutIsPresentedChanged -= OnFlyoutIsPresentedChanged; platformView.FlyoutIsPresentedChanged -= OnFlyoutIsPresentedChanged;
platformView.Navigated -= OnNavigated; platformView.Navigated -= OnNavigated;
if (base.VirtualView != null)
if (VirtualView != null)
{ {
VirtualView.Navigating -= OnShellNavigating; base.VirtualView.Navigating -= OnShellNavigating;
VirtualView.Navigated -= OnShellNavigated; base.VirtualView.Navigated -= OnShellNavigated;
} }
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnFlyoutIsPresentedChanged(object? sender, EventArgs e) private void OnFlyoutIsPresentedChanged(object? sender, EventArgs e)
{ {
// Sync flyout state to virtual view
} }
private void OnNavigated(object? sender, ShellNavigationEventArgs e) private void OnNavigated(object? sender, ShellNavigationEventArgs e)
{ {
// Handle platform navigation events
} }
private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e) private void OnShellNavigating(object? sender, ShellNavigatingEventArgs e)
{ {
Console.WriteLine($"[ShellHandler] Shell Navigating to: {e.Target?.Location}"); DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(36, 1);
defaultInterpolatedStringHandler.AppendLiteral("[ShellHandler] Shell Navigating to: ");
// Route to platform view ShellNavigationState target = e.Target;
if (PlatformView != null && e.Target?.Location != null) defaultInterpolatedStringHandler.AppendFormatted((target != null) ? target.Location : null);
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
if (base.PlatformView != null)
{ {
var route = e.Target.Location.ToString().TrimStart('/'); ShellNavigationState target2 = e.Target;
Console.WriteLine($"[ShellHandler] Routing to: {route}"); if (((target2 != null) ? target2.Location : null) != null)
PlatformView.GoToAsync(route); {
string text = e.Target.Location.ToString().TrimStart('/');
Console.WriteLine("[ShellHandler] Routing to: " + text);
base.PlatformView.GoToAsync(text);
}
} }
} }
private void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) private void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
{ {
Console.WriteLine($"[ShellHandler] Shell Navigated to: {e.Current?.Location}"); DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(35, 1);
defaultInterpolatedStringHandler.AppendLiteral("[ShellHandler] Shell Navigated to: ");
ShellNavigationState current = e.Current;
defaultInterpolatedStringHandler.AppendFormatted((current != null) ? current.Location : null);
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
} }
} }

View File

@@ -0,0 +1,16 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class SizeChangedEventArgs : EventArgs
{
public int Width { get; }
public int Height { get; }
public SizeChangedEventArgs(int width, int height)
{
Width = width;
Height = height;
}
}

184
Handlers/SkiaWindow.cs Normal file
View File

@@ -0,0 +1,184 @@
using System;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class SkiaWindow
{
private SkiaView? _content;
private string _title = "MAUI Application";
private int _x;
private int _y;
private int _width = 800;
private int _height = 600;
private int _minWidth = 100;
private int _minHeight = 100;
private int _maxWidth = int.MaxValue;
private int _maxHeight = int.MaxValue;
public SkiaView? Content
{
get
{
return _content;
}
set
{
_content = value;
this.ContentChanged?.Invoke(this, EventArgs.Empty);
}
}
public string Title
{
get
{
return _title;
}
set
{
_title = value;
this.TitleChanged?.Invoke(this, EventArgs.Empty);
}
}
public int X
{
get
{
return _x;
}
set
{
_x = value;
this.PositionChanged?.Invoke(this, EventArgs.Empty);
}
}
public int Y
{
get
{
return _y;
}
set
{
_y = value;
this.PositionChanged?.Invoke(this, EventArgs.Empty);
}
}
public int Width
{
get
{
return _width;
}
set
{
_width = Math.Clamp(value, _minWidth, _maxWidth);
this.SizeChanged?.Invoke(this, new SizeChangedEventArgs(_width, _height));
}
}
public int Height
{
get
{
return _height;
}
set
{
_height = Math.Clamp(value, _minHeight, _maxHeight);
this.SizeChanged?.Invoke(this, new SizeChangedEventArgs(_width, _height));
}
}
public int MinWidth
{
get
{
return _minWidth;
}
set
{
_minWidth = value;
}
}
public int MinHeight
{
get
{
return _minHeight;
}
set
{
_minHeight = value;
}
}
public int MaxWidth
{
get
{
return _maxWidth;
}
set
{
_maxWidth = value;
}
}
public int MaxHeight
{
get
{
return _maxHeight;
}
set
{
_maxHeight = value;
}
}
public event EventHandler? ContentChanged;
public event EventHandler? TitleChanged;
public event EventHandler? PositionChanged;
public event EventHandler<SizeChangedEventArgs>? SizeChanged;
public event EventHandler? CloseRequested;
public void Render(SKCanvas canvas)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
canvas.Clear(SKColors.White);
if (_content != null)
{
_content.Measure(new SKSize((float)_width, (float)_height));
_content.Arrange(new SKRect(0f, 0f, (float)_width, (float)_height));
_content.Draw(canvas);
}
SkiaView.DrawPopupOverlays(canvas);
}
public void Close()
{
this.CloseRequested?.Invoke(this, EventArgs.Empty);
}
}

View File

@@ -1,40 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using SkiaSharp; using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class SliderHandler : ViewHandler<ISlider, SkiaSlider>
/// Handler for Slider on Linux using Skia rendering.
/// Maps ISlider interface to SkiaSlider platform view.
/// </summary>
public partial class SliderHandler : ViewHandler<ISlider, SkiaSlider>
{ {
public static IPropertyMapper<ISlider, SliderHandler> Mapper = new PropertyMapper<ISlider, SliderHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ISlider, SliderHandler> Mapper = (IPropertyMapper<ISlider, SliderHandler>)(object)new PropertyMapper<ISlider, SliderHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(IRange.Minimum)] = MapMinimum, ["Minimum"] = MapMinimum,
[nameof(IRange.Maximum)] = MapMaximum, ["Maximum"] = MapMaximum,
[nameof(IRange.Value)] = MapValue, ["Value"] = MapValue,
[nameof(ISlider.MinimumTrackColor)] = MapMinimumTrackColor, ["MinimumTrackColor"] = MapMinimumTrackColor,
[nameof(ISlider.MaximumTrackColor)] = MapMaximumTrackColor, ["MaximumTrackColor"] = MapMaximumTrackColor,
[nameof(ISlider.ThumbColor)] = MapThumbColor, ["ThumbColor"] = MapThumbColor,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
[nameof(IView.IsEnabled)] = MapIsEnabled, ["IsEnabled"] = MapIsEnabled
}; };
public static CommandMapper<ISlider, SliderHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ISlider, SliderHandler> CommandMapper = new CommandMapper<ISlider, SliderHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public SliderHandler() : base(Mapper, CommandMapper) public SliderHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public SliderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public SliderHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -49,14 +41,12 @@ public partial class SliderHandler : ViewHandler<ISlider, SkiaSlider>
platformView.ValueChanged += OnValueChanged; platformView.ValueChanged += OnValueChanged;
platformView.DragStarted += OnDragStarted; platformView.DragStarted += OnDragStarted;
platformView.DragCompleted += OnDragCompleted; platformView.DragCompleted += OnDragCompleted;
if (base.VirtualView != null)
// Sync properties that may have been set before handler connection
if (VirtualView != null)
{ {
MapMinimum(this, VirtualView); MapMinimum(this, base.VirtualView);
MapMaximum(this, VirtualView); MapMaximum(this, base.VirtualView);
MapValue(this, VirtualView); MapValue(this, base.VirtualView);
MapIsEnabled(this, VirtualView); MapIsEnabled(this, base.VirtualView);
} }
} }
@@ -70,84 +60,101 @@ public partial class SliderHandler : ViewHandler<ISlider, SkiaSlider>
private void OnValueChanged(object? sender, SliderValueChangedEventArgs e) private void OnValueChanged(object? sender, SliderValueChangedEventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null && Math.Abs(((IRange)base.VirtualView).Value - e.NewValue) > 0.0001)
if (Math.Abs(VirtualView.Value - e.NewValue) > 0.0001)
{ {
VirtualView.Value = e.NewValue; ((IRange)base.VirtualView).Value = e.NewValue;
} }
} }
private void OnDragStarted(object? sender, EventArgs e) private void OnDragStarted(object? sender, EventArgs e)
{ {
VirtualView?.DragStarted(); ISlider virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.DragStarted();
}
} }
private void OnDragCompleted(object? sender, EventArgs e) private void OnDragCompleted(object? sender, EventArgs e)
{ {
VirtualView?.DragCompleted(); ISlider virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.DragCompleted();
}
} }
public static void MapMinimum(SliderHandler handler, ISlider slider) public static void MapMinimum(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null)
handler.PlatformView.Minimum = slider.Minimum; {
((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.Minimum = ((IRange)slider).Minimum;
}
} }
public static void MapMaximum(SliderHandler handler, ISlider slider) public static void MapMaximum(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null)
handler.PlatformView.Maximum = slider.Maximum; {
((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.Maximum = ((IRange)slider).Maximum;
}
} }
public static void MapValue(SliderHandler handler, ISlider slider) public static void MapValue(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null && Math.Abs(((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.Value - ((IRange)slider).Value) > 0.0001)
{
if (Math.Abs(handler.PlatformView.Value - slider.Value) > 0.0001) ((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.Value = ((IRange)slider).Value;
handler.PlatformView.Value = slider.Value; }
} }
public static void MapMinimumTrackColor(SliderHandler handler, ISlider slider) public static void MapMinimumTrackColor(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null && slider.MinimumTrackColor != null)
// MinimumTrackColor maps to ActiveTrackColor (the filled portion) {
if (slider.MinimumTrackColor is not null) ((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.ActiveTrackColor = slider.MinimumTrackColor.ToSKColor();
handler.PlatformView.ActiveTrackColor = slider.MinimumTrackColor.ToSKColor(); }
} }
public static void MapMaximumTrackColor(SliderHandler handler, ISlider slider) public static void MapMaximumTrackColor(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null && slider.MaximumTrackColor != null)
// MaximumTrackColor maps to TrackColor (the unfilled portion) {
if (slider.MaximumTrackColor is not null) ((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.TrackColor = slider.MaximumTrackColor.ToSKColor();
handler.PlatformView.TrackColor = slider.MaximumTrackColor.ToSKColor(); }
} }
public static void MapThumbColor(SliderHandler handler, ISlider slider) public static void MapThumbColor(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null && slider.ThumbColor != null)
if (slider.ThumbColor is not null) {
handler.PlatformView.ThumbColor = slider.ThumbColor.ToSKColor(); ((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.ThumbColor = slider.ThumbColor.ToSKColor();
}
} }
public static void MapBackground(SliderHandler handler, ISlider slider) public static void MapBackground(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null)
if (slider.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)slider).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
public static void MapIsEnabled(SliderHandler handler, ISlider slider) public static void MapIsEnabled(SliderHandler handler, ISlider slider)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView != null)
handler.PlatformView.IsEnabled = slider.IsEnabled; {
handler.PlatformView.Invalidate(); ((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.IsEnabled = ((IView)slider).IsEnabled;
((ViewHandler<ISlider, SkiaSlider>)(object)handler).PlatformView.Invalidate();
}
} }
} }

View File

@@ -0,0 +1,49 @@
using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class StackLayoutHandler : LayoutHandler
{
public new static IPropertyMapper<IStackLayout, StackLayoutHandler> Mapper = (IPropertyMapper<IStackLayout, StackLayoutHandler>)(object)new PropertyMapper<IStackLayout, StackLayoutHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)LayoutHandler.Mapper }) { ["Spacing"] = MapSpacing };
public StackLayoutHandler()
: base((IPropertyMapper?)(object)Mapper)
{
}
protected override SkiaLayoutView CreatePlatformView()
{
return new SkiaStackLayout();
}
protected override void ConnectHandler(SkiaLayoutView platformView)
{
if (platformView is SkiaStackLayout skiaStackLayout)
{
ILayout virtualView = ((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView;
IStackLayout val = (IStackLayout)(object)((virtualView is IStackLayout) ? virtualView : null);
if (val != null)
{
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView is HorizontalStackLayout)
{
skiaStackLayout.Orientation = StackOrientation.Horizontal;
}
else if (((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView is VerticalStackLayout || ((ViewHandler<ILayout, SkiaLayoutView>)(object)this).VirtualView is StackLayout)
{
skiaStackLayout.Orientation = StackOrientation.Vertical;
}
skiaStackLayout.Spacing = (float)val.Spacing;
}
}
base.ConnectHandler(platformView);
}
public static void MapSpacing(StackLayoutHandler handler, IStackLayout layout)
{
if (((ViewHandler<ILayout, SkiaLayoutView>)(object)handler).PlatformView is SkiaStackLayout skiaStackLayout)
{
skiaStackLayout.Spacing = (float)layout.Spacing;
}
}
}

View File

@@ -1,38 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Platform; using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class StepperHandler : ViewHandler<IStepper, SkiaStepper>
/// Handler for Stepper on Linux using Skia rendering.
/// </summary>
public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
{ {
public static IPropertyMapper<IStepper, StepperHandler> Mapper = public static IPropertyMapper<IStepper, StepperHandler> Mapper = (IPropertyMapper<IStepper, StepperHandler>)(object)new PropertyMapper<IStepper, StepperHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<IStepper, StepperHandler>(ViewHandler.ViewMapper)
{ {
[nameof(IStepper.Value)] = MapValue, ["Value"] = MapValue,
[nameof(IStepper.Minimum)] = MapMinimum, ["Minimum"] = MapMinimum,
[nameof(IStepper.Maximum)] = MapMaximum, ["Maximum"] = MapMaximum,
[nameof(IView.Background)] = MapBackground, ["Increment"] = MapIncrement,
["Background"] = MapBackground,
["IsEnabled"] = MapIsEnabled
}; };
public static CommandMapper<IStepper, StepperHandler> CommandMapper = public static CommandMapper<IStepper, StepperHandler> CommandMapper = new CommandMapper<IStepper, StepperHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public StepperHandler() : base(Mapper, CommandMapper) public StepperHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public StepperHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public StepperHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -43,8 +37,26 @@ public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
protected override void ConnectHandler(SkiaStepper platformView) protected override void ConnectHandler(SkiaStepper platformView)
{ {
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Invalid comparison between Unknown and I4
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.ValueChanged += OnValueChanged; platformView.ValueChanged += OnValueChanged;
Application current = Application.Current;
if (current != null && (int)current.UserAppTheme == 2)
{
platformView.ButtonBackgroundColor = new SKColor((byte)66, (byte)66, (byte)66);
platformView.ButtonPressedColor = new SKColor((byte)97, (byte)97, (byte)97);
platformView.ButtonDisabledColor = new SKColor((byte)48, (byte)48, (byte)48);
platformView.SymbolColor = new SKColor((byte)224, (byte)224, (byte)224);
platformView.SymbolDisabledColor = new SKColor((byte)97, (byte)97, (byte)97);
platformView.BorderColor = new SKColor((byte)97, (byte)97, (byte)97);
}
} }
protected override void DisconnectHandler(SkiaStepper platformView) protected override void DisconnectHandler(SkiaStepper platformView)
@@ -55,35 +67,67 @@ public partial class StepperHandler : ViewHandler<IStepper, SkiaStepper>
private void OnValueChanged(object? sender, EventArgs e) private void OnValueChanged(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
VirtualView.Value = PlatformView.Value; {
((IRange)base.VirtualView).Value = base.PlatformView.Value;
}
} }
public static void MapValue(StepperHandler handler, IStepper stepper) public static void MapValue(StepperHandler handler, IStepper stepper)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
handler.PlatformView.Value = stepper.Value; {
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.Value = ((IRange)stepper).Value;
}
} }
public static void MapMinimum(StepperHandler handler, IStepper stepper) public static void MapMinimum(StepperHandler handler, IStepper stepper)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
handler.PlatformView.Minimum = stepper.Minimum; {
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.Minimum = ((IRange)stepper).Minimum;
}
} }
public static void MapMaximum(StepperHandler handler, IStepper stepper) public static void MapMaximum(StepperHandler handler, IStepper stepper)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
handler.PlatformView.Maximum = stepper.Maximum; {
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.Maximum = ((IRange)stepper).Maximum;
}
} }
public static void MapBackground(StepperHandler handler, IStepper stepper) public static void MapBackground(StepperHandler handler, IStepper stepper)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
if (stepper.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)stepper).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
}
}
public static void MapIncrement(StepperHandler handler, IStepper stepper)
{
if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
{
Stepper val = (Stepper)(object)((stepper is Stepper) ? stepper : null);
if (val != null)
{
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.Increment = val.Increment;
}
}
}
public static void MapIsEnabled(StepperHandler handler, IStepper stepper)
{
if (((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView != null)
{
((ViewHandler<IStepper, SkiaStepper>)(object)handler).PlatformView.IsEnabled = ((IView)stepper).IsEnabled;
} }
} }
} }

View File

@@ -1,36 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class SwitchHandler : ViewHandler<ISwitch, SkiaSwitch>
/// Handler for Switch on Linux using Skia rendering.
/// Maps ISwitch interface to SkiaSwitch platform view.
/// </summary>
public partial class SwitchHandler : ViewHandler<ISwitch, SkiaSwitch>
{ {
public static IPropertyMapper<ISwitch, SwitchHandler> Mapper = new PropertyMapper<ISwitch, SwitchHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ISwitch, SwitchHandler> Mapper = (IPropertyMapper<ISwitch, SwitchHandler>)(object)new PropertyMapper<ISwitch, SwitchHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
{ {
[nameof(ISwitch.IsOn)] = MapIsOn, ["IsOn"] = MapIsOn,
[nameof(ISwitch.TrackColor)] = MapTrackColor, ["TrackColor"] = MapTrackColor,
[nameof(ISwitch.ThumbColor)] = MapThumbColor, ["ThumbColor"] = MapThumbColor,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground,
["IsEnabled"] = MapIsEnabled
}; };
public static CommandMapper<ISwitch, SwitchHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ISwitch, SwitchHandler> CommandMapper = new CommandMapper<ISwitch, SwitchHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public SwitchHandler() : base(Mapper, CommandMapper) public SwitchHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public SwitchHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public SwitchHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -51,49 +44,64 @@ public partial class SwitchHandler : ViewHandler<ISwitch, SkiaSwitch>
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnToggled(object? sender, Platform.ToggledEventArgs e) private void OnToggled(object? sender, ToggledEventArgs e)
{ {
if (VirtualView is not null && VirtualView.IsOn != e.Value) if (base.VirtualView != null && base.VirtualView.IsOn != e.Value)
{ {
VirtualView.IsOn = e.Value; base.VirtualView.IsOn = e.Value;
} }
} }
public static void MapIsOn(SwitchHandler handler, ISwitch @switch) public static void MapIsOn(SwitchHandler handler, ISwitch @switch)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView != null)
handler.PlatformView.IsOn = @switch.IsOn; {
((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.IsOn = @switch.IsOn;
}
} }
public static void MapTrackColor(SwitchHandler handler, ISwitch @switch) public static void MapTrackColor(SwitchHandler handler, ISwitch @switch)
{ {
if (handler.PlatformView is null) return; //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
// TrackColor sets both On and Off track colors //IL_0023: Unknown result type (might be due to invalid IL or missing references)
if (@switch.TrackColor is not null) //IL_0036: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView != null && @switch.TrackColor != null)
{ {
var color = @switch.TrackColor.ToSKColor(); SKColor onTrackColor = @switch.TrackColor.ToSKColor();
handler.PlatformView.OnTrackColor = color; ((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.OnTrackColor = onTrackColor;
// Off track could be a lighter version ((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.OffTrackColor = ((SKColor)(ref onTrackColor)).WithAlpha((byte)128);
handler.PlatformView.OffTrackColor = color.WithAlpha(128);
} }
} }
public static void MapThumbColor(SwitchHandler handler, ISwitch @switch) public static void MapThumbColor(SwitchHandler handler, ISwitch @switch)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView != null && @switch.ThumbColor != null)
if (@switch.ThumbColor is not null) {
handler.PlatformView.ThumbColor = @switch.ThumbColor.ToSKColor(); ((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.ThumbColor = @switch.ThumbColor.ToSKColor();
}
} }
public static void MapBackground(SwitchHandler handler, ISwitch @switch) public static void MapBackground(SwitchHandler handler, ISwitch @switch)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView != null)
if (@switch.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)@switch).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
}
}
public static void MapIsEnabled(SwitchHandler handler, ISwitch @switch)
{
if (((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView != null)
{
((ViewHandler<ISwitch, SkiaSwitch>)(object)handler).PlatformView.IsEnabled = ((IView)@switch).IsEnabled;
} }
} }
} }

View File

@@ -1,32 +1,21 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class TabbedPageHandler : ViewHandler<ITabbedView, SkiaTabbedPage>
/// Handler for TabbedPage on Linux using Skia rendering.
/// Maps ITabbedView interface to SkiaTabbedPage platform view.
/// </summary>
public partial class TabbedPageHandler : ViewHandler<ITabbedView, SkiaTabbedPage>
{ {
public static IPropertyMapper<ITabbedView, TabbedPageHandler> Mapper = new PropertyMapper<ITabbedView, TabbedPageHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<ITabbedView, TabbedPageHandler> Mapper = (IPropertyMapper<ITabbedView, TabbedPageHandler>)(object)new PropertyMapper<ITabbedView, TabbedPageHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper });
{
};
public static CommandMapper<ITabbedView, TabbedPageHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public static CommandMapper<ITabbedView, TabbedPageHandler> CommandMapper = new CommandMapper<ITabbedView, TabbedPageHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
{
};
public TabbedPageHandler() : base(Mapper, CommandMapper) public TabbedPageHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public TabbedPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public TabbedPageHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -50,6 +39,5 @@ public partial class TabbedPageHandler : ViewHandler<ITabbedView, SkiaTabbedPage
private void OnSelectedIndexChanged(object? sender, EventArgs e) private void OnSelectedIndexChanged(object? sender, EventArgs e)
{ {
// Notify the virtual view of selection change
} }
} }

View File

@@ -0,0 +1,84 @@
using Microsoft.Maui.Handlers;
namespace Microsoft.Maui.Platform.Linux.Handlers;
public class TextButtonHandler : ButtonHandler
{
public new static IPropertyMapper<ITextButton, TextButtonHandler> Mapper = (IPropertyMapper<ITextButton, TextButtonHandler>)(object)new PropertyMapper<ITextButton, TextButtonHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ButtonHandler.Mapper })
{
["Text"] = MapText,
["TextColor"] = MapTextColor,
["Font"] = MapFont,
["CharacterSpacing"] = MapCharacterSpacing
};
public TextButtonHandler()
: base((IPropertyMapper?)(object)Mapper)
{
}
protected override void ConnectHandler(SkiaButton platformView)
{
base.ConnectHandler(platformView);
IButton virtualView = ((ViewHandler<IButton, SkiaButton>)(object)this).VirtualView;
ITextButton val = (ITextButton)(object)((virtualView is ITextButton) ? virtualView : null);
if (val != null)
{
MapText(this, val);
MapTextColor(this, val);
MapFont(this, val);
MapCharacterSpacing(this, val);
}
}
public static void MapText(TextButtonHandler handler, ITextButton button)
{
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.Text = ((IText)button).Text ?? string.Empty;
}
}
public static void MapTextColor(TextButtonHandler handler, ITextButton button)
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null && ((ITextStyle)button).TextColor != null)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.TextColor = ((ITextStyle)button).TextColor.ToSKColor();
}
}
public static void MapFont(TextButtonHandler handler, ITextButton button)
{
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Invalid comparison between Unknown and I4
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Invalid comparison between Unknown and I4
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Invalid comparison between Unknown and I4
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
{
Font font = ((ITextStyle)button).Font;
if (((Font)(ref font)).Size > 0.0)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.FontSize = (float)((Font)(ref font)).Size;
}
if (!string.IsNullOrEmpty(((Font)(ref font)).Family))
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.FontFamily = ((Font)(ref font)).Family;
}
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.IsBold = (int)((Font)(ref font)).Weight >= 700;
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.IsItalic = (int)((Font)(ref font)).Slant == 1 || (int)((Font)(ref font)).Slant == 2;
}
}
public static void MapCharacterSpacing(TextButtonHandler handler, ITextButton button)
{
if (((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView != null)
{
((ViewHandler<IButton, SkiaButton>)(object)handler).PlatformView.CharacterSpacing = (float)((ITextStyle)button).CharacterSpacing;
}
}
}

View File

@@ -1,40 +1,31 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class TimePickerHandler : ViewHandler<ITimePicker, SkiaTimePicker>
/// Handler for TimePicker on Linux using Skia rendering.
/// </summary>
public partial class TimePickerHandler : ViewHandler<ITimePicker, SkiaTimePicker>
{ {
public static IPropertyMapper<ITimePicker, TimePickerHandler> Mapper = public static IPropertyMapper<ITimePicker, TimePickerHandler> Mapper = (IPropertyMapper<ITimePicker, TimePickerHandler>)(object)new PropertyMapper<ITimePicker, TimePickerHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper })
new PropertyMapper<ITimePicker, TimePickerHandler>(ViewHandler.ViewMapper)
{ {
[nameof(ITimePicker.Time)] = MapTime, ["Time"] = MapTime,
[nameof(ITimePicker.Format)] = MapFormat, ["Format"] = MapFormat,
[nameof(ITimePicker.TextColor)] = MapTextColor, ["TextColor"] = MapTextColor,
[nameof(ITimePicker.CharacterSpacing)] = MapCharacterSpacing, ["CharacterSpacing"] = MapCharacterSpacing,
[nameof(IView.Background)] = MapBackground, ["Background"] = MapBackground
}; };
public static CommandMapper<ITimePicker, TimePickerHandler> CommandMapper = public static CommandMapper<ITimePicker, TimePickerHandler> CommandMapper = new CommandMapper<ITimePicker, TimePickerHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper);
new(ViewHandler.ViewCommandMapper)
{
};
public TimePickerHandler() : base(Mapper, CommandMapper) public TimePickerHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public TimePickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public TimePickerHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -45,8 +36,24 @@ public partial class TimePickerHandler : ViewHandler<ITimePicker, SkiaTimePicker
protected override void ConnectHandler(SkiaTimePicker platformView) protected override void ConnectHandler(SkiaTimePicker platformView)
{ {
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Invalid comparison between Unknown and I4
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.TimeSelected += OnTimeSelected; platformView.TimeSelected += OnTimeSelected;
Application current = Application.Current;
if (current != null && (int)current.UserAppTheme == 2)
{
platformView.ClockBackgroundColor = new SKColor((byte)30, (byte)30, (byte)30);
platformView.ClockFaceColor = new SKColor((byte)45, (byte)45, (byte)45);
platformView.TextColor = new SKColor((byte)224, (byte)224, (byte)224);
platformView.BorderColor = new SKColor((byte)97, (byte)97, (byte)97);
platformView.BackgroundColor = new SKColor((byte)45, (byte)45, (byte)45);
}
} }
protected override void DisconnectHandler(SkiaTimePicker platformView) protected override void DisconnectHandler(SkiaTimePicker platformView)
@@ -57,44 +64,52 @@ public partial class TimePickerHandler : ViewHandler<ITimePicker, SkiaTimePicker
private void OnTimeSelected(object? sender, EventArgs e) private void OnTimeSelected(object? sender, EventArgs e)
{ {
if (VirtualView is null || PlatformView is null) return; if (base.VirtualView != null && base.PlatformView != null)
{
VirtualView.Time = PlatformView.Time; base.VirtualView.Time = base.PlatformView.Time;
}
} }
public static void MapTime(TimePickerHandler handler, ITimePicker timePicker) public static void MapTime(TimePickerHandler handler, ITimePicker timePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView != null)
handler.PlatformView.Time = timePicker.Time; {
((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView.Time = timePicker.Time;
}
} }
public static void MapFormat(TimePickerHandler handler, ITimePicker timePicker) public static void MapFormat(TimePickerHandler handler, ITimePicker timePicker)
{ {
if (handler.PlatformView is null) return; if (((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView != null)
handler.PlatformView.Format = timePicker.Format ?? "t"; {
((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView.Format = timePicker.Format ?? "t";
}
} }
public static void MapTextColor(TimePickerHandler handler, ITimePicker timePicker) public static void MapTextColor(TimePickerHandler handler, ITimePicker timePicker)
{ {
if (handler.PlatformView is null) return; //IL_001d: Unknown result type (might be due to invalid IL or missing references)
if (timePicker.TextColor is not null) if (((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView != null && ((ITextStyle)timePicker).TextColor != null)
{ {
handler.PlatformView.TextColor = timePicker.TextColor.ToSKColor(); ((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView.TextColor = ((ITextStyle)timePicker).TextColor.ToSKColor();
} }
} }
public static void MapCharacterSpacing(TimePickerHandler handler, ITimePicker timePicker) public static void MapCharacterSpacing(TimePickerHandler handler, ITimePicker timePicker)
{ {
// Character spacing would require custom text rendering
} }
public static void MapBackground(TimePickerHandler handler, ITimePicker timePicker) public static void MapBackground(TimePickerHandler handler, ITimePicker timePicker)
{ {
if (handler.PlatformView is null) return; //IL_002c: Unknown result type (might be due to invalid IL or missing references)
if (((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView != null)
if (timePicker.Background is SolidPaint solidPaint && solidPaint.Color is not null)
{ {
handler.PlatformView.BackgroundColor = solidPaint.Color.ToSKColor(); Paint background = ((IView)timePicker).Background;
SolidPaint val = (SolidPaint)(object)((background is SolidPaint) ? background : null);
if (val != null && val.Color != null)
{
((ViewHandler<ITimePicker, SkiaTimePicker>)(object)handler).PlatformView.BackgroundColor = val.Color.ToSKColor();
}
} }
} }
} }

View File

@@ -1,35 +1,27 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Handlers; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class WebViewHandler : ViewHandler<IWebView, SkiaWebView>
/// Handler for WebView control on Linux using WebKitGTK.
/// </summary>
public partial class WebViewHandler : ViewHandler<IWebView, SkiaWebView>
{ {
public static IPropertyMapper<IWebView, WebViewHandler> Mapper = new PropertyMapper<IWebView, WebViewHandler>(ViewHandler.ViewMapper) public static IPropertyMapper<IWebView, WebViewHandler> Mapper = (IPropertyMapper<IWebView, WebViewHandler>)(object)new PropertyMapper<IWebView, WebViewHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ViewHandler.ViewMapper }) { ["Source"] = MapSource };
public static CommandMapper<IWebView, WebViewHandler> CommandMapper = new CommandMapper<IWebView, WebViewHandler>((CommandMapper)(object)ViewHandler.ViewCommandMapper)
{ {
[nameof(IWebView.Source)] = MapSource, ["GoBack"] = MapGoBack,
["GoForward"] = MapGoForward,
["Reload"] = MapReload
}; };
public static CommandMapper<IWebView, WebViewHandler> CommandMapper = new(ViewHandler.ViewCommandMapper) public WebViewHandler()
{ : base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
[nameof(IWebView.GoBack)] = MapGoBack,
[nameof(IWebView.GoForward)] = MapGoForward,
[nameof(IWebView.Reload)] = MapReload,
};
public WebViewHandler() : base(Mapper, CommandMapper)
{ {
} }
public WebViewHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null) public WebViewHandler(IPropertyMapper? mapper = null, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -41,7 +33,6 @@ public partial class WebViewHandler : ViewHandler<IWebView, SkiaWebView>
protected override void ConnectHandler(SkiaWebView platformView) protected override void ConnectHandler(SkiaWebView platformView)
{ {
base.ConnectHandler(platformView); base.ConnectHandler(platformView);
platformView.Navigating += OnNavigating; platformView.Navigating += OnNavigating;
platformView.Navigated += OnNavigated; platformView.Navigated += OnNavigated;
} }
@@ -50,47 +41,80 @@ public partial class WebViewHandler : ViewHandler<IWebView, SkiaWebView>
{ {
platformView.Navigating -= OnNavigating; platformView.Navigating -= OnNavigating;
platformView.Navigated -= OnNavigated; platformView.Navigated -= OnNavigated;
base.DisconnectHandler(platformView); base.DisconnectHandler(platformView);
} }
private void OnNavigating(object? sender, WebNavigatingEventArgs e) private void OnNavigating(object? sender, WebNavigatingEventArgs e)
{ {
// Forward to virtual view if needed //IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_001d: Expected O, but got Unknown
IWebView virtualView = base.VirtualView;
IWebViewController val = (IWebViewController)(object)((virtualView is IWebViewController) ? virtualView : null);
if (val != null)
{
WebNavigatingEventArgs e2 = new WebNavigatingEventArgs((WebNavigationEvent)3, (WebViewSource)null, e.Url);
val.SendNavigating(e2);
}
} }
private void OnNavigated(object? sender, WebNavigatedEventArgs e) private void OnNavigated(object? sender, WebNavigatedEventArgs e)
{ {
// Forward to virtual view if needed //IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Expected O, but got Unknown
IWebView virtualView = base.VirtualView;
IWebViewController val = (IWebViewController)(object)((virtualView is IWebViewController) ? virtualView : null);
if (val != null)
{
WebNavigationResult val2 = (WebNavigationResult)(e.Success ? 1 : 4);
WebNavigatedEventArgs e2 = new WebNavigatedEventArgs((WebNavigationEvent)3, (WebViewSource)null, e.Url, val2);
val.SendNavigated(e2);
}
} }
public static void MapSource(WebViewHandler handler, IWebView webView) public static void MapSource(WebViewHandler handler, IWebView webView)
{ {
if (handler.PlatformView == null) return; Console.WriteLine("[WebViewHandler] MapSource called");
if (((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView == null)
var source = webView.Source;
if (source is UrlWebViewSource urlSource)
{ {
handler.PlatformView.Source = urlSource.Url ?? ""; Console.WriteLine("[WebViewHandler] PlatformView is null!");
return;
} }
else if (source is HtmlWebViewSource htmlSource) IWebViewSource source = webView.Source;
Console.WriteLine("[WebViewHandler] Source type: " + (((object)source)?.GetType().Name ?? "null"));
UrlWebViewSource val = (UrlWebViewSource)(object)((source is UrlWebViewSource) ? source : null);
if (val != null)
{ {
handler.PlatformView.Html = htmlSource.Html ?? ""; Console.WriteLine("[WebViewHandler] Loading URL: " + val.Url);
((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView.Source = val.Url ?? "";
return;
}
HtmlWebViewSource val2 = (HtmlWebViewSource)(object)((source is HtmlWebViewSource) ? source : null);
if (val2 != null)
{
Console.WriteLine($"[WebViewHandler] Loading HTML ({val2.Html?.Length ?? 0} chars)");
Console.WriteLine("[WebViewHandler] HTML preview: " + val2.Html?.Substring(0, Math.Min(100, val2.Html?.Length ?? 0)) + "...");
((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView.Html = val2.Html ?? "";
}
else
{
Console.WriteLine("[WebViewHandler] Unknown source type or null");
} }
} }
public static void MapGoBack(WebViewHandler handler, IWebView webView, object? args) public static void MapGoBack(WebViewHandler handler, IWebView webView, object? args)
{ {
handler.PlatformView?.GoBack(); ((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView?.GoBack();
} }
public static void MapGoForward(WebViewHandler handler, IWebView webView, object? args) public static void MapGoForward(WebViewHandler handler, IWebView webView, object? args)
{ {
handler.PlatformView?.GoForward(); ((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView?.GoForward();
} }
public static void MapReload(WebViewHandler handler, IWebView webView, object? args) public static void MapReload(WebViewHandler handler, IWebView webView, object? args)
{ {
handler.PlatformView?.Reload(); ((ViewHandler<IWebView, SkiaWebView>)(object)handler).PlatformView?.Reload();
} }
} }

View File

@@ -1,46 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Maui.Handlers;
using Microsoft.Maui.Graphics; using Microsoft.Maui.Graphics;
using Microsoft.Maui.Controls; using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Handlers; namespace Microsoft.Maui.Platform.Linux.Handlers;
/// <summary> public class WindowHandler : ElementHandler<IWindow, SkiaWindow>
/// Handler for Window on Linux.
/// Maps IWindow to the Linux display window system.
/// </summary>
public partial class WindowHandler : ElementHandler<IWindow, SkiaWindow>
{ {
public static IPropertyMapper<IWindow, WindowHandler> Mapper = public static IPropertyMapper<IWindow, WindowHandler> Mapper = (IPropertyMapper<IWindow, WindowHandler>)(object)new PropertyMapper<IWindow, WindowHandler>((IPropertyMapper[])(object)new IPropertyMapper[1] { (IPropertyMapper)ElementHandler.ElementMapper })
new PropertyMapper<IWindow, WindowHandler>(ElementHandler.ElementMapper)
{ {
[nameof(IWindow.Title)] = MapTitle, ["Title"] = MapTitle,
[nameof(IWindow.Content)] = MapContent, ["Content"] = MapContent,
[nameof(IWindow.X)] = MapX, ["X"] = MapX,
[nameof(IWindow.Y)] = MapY, ["Y"] = MapY,
[nameof(IWindow.Width)] = MapWidth, ["Width"] = MapWidth,
[nameof(IWindow.Height)] = MapHeight, ["Height"] = MapHeight,
[nameof(IWindow.MinimumWidth)] = MapMinimumWidth, ["MinimumWidth"] = MapMinimumWidth,
[nameof(IWindow.MinimumHeight)] = MapMinimumHeight, ["MinimumHeight"] = MapMinimumHeight,
[nameof(IWindow.MaximumWidth)] = MapMaximumWidth, ["MaximumWidth"] = MapMaximumWidth,
[nameof(IWindow.MaximumHeight)] = MapMaximumHeight, ["MaximumHeight"] = MapMaximumHeight
}; };
public static CommandMapper<IWindow, WindowHandler> CommandMapper = public static CommandMapper<IWindow, WindowHandler> CommandMapper = new CommandMapper<IWindow, WindowHandler>((CommandMapper)(object)ElementHandler.ElementCommandMapper);
new(ElementHandler.ElementCommandMapper)
{
};
public WindowHandler() : base(Mapper, CommandMapper) public WindowHandler()
: base((IPropertyMapper)(object)Mapper, (CommandMapper)(object)CommandMapper)
{ {
} }
public WindowHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null) public WindowHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
: base(mapper ?? Mapper, commandMapper ?? CommandMapper) : base((IPropertyMapper)(((object)mapper) ?? ((object)Mapper)), (CommandMapper)(((object)commandMapper) ?? ((object)CommandMapper)))
{ {
} }
@@ -65,217 +53,114 @@ public partial class WindowHandler : ElementHandler<IWindow, SkiaWindow>
private void OnCloseRequested(object? sender, EventArgs e) private void OnCloseRequested(object? sender, EventArgs e)
{ {
VirtualView?.Destroying(); IWindow virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.Destroying();
}
} }
private void OnSizeChanged(object? sender, SizeChangedEventArgs e) private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
{ {
VirtualView?.FrameChanged(new Rect(0, 0, e.Width, e.Height)); //IL_002b: Unknown result type (might be due to invalid IL or missing references)
IWindow virtualView = base.VirtualView;
if (virtualView != null)
{
virtualView.FrameChanged(new Rect(0.0, 0.0, (double)e.Width, (double)e.Height));
}
} }
public static void MapTitle(WindowHandler handler, IWindow window) public static void MapTitle(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.Title = window.Title ?? "MAUI Application"; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.Title = ((ITitledElement)window).Title ?? "MAUI Application";
}
} }
public static void MapContent(WindowHandler handler, IWindow window) public static void MapContent(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
var content = window.Content;
if (content?.Handler?.PlatformView is SkiaView skiaContent)
{ {
handler.PlatformView.Content = skiaContent; IView content = window.Content;
object obj;
if (content == null)
{
obj = null;
}
else
{
IViewHandler handler2 = content.Handler;
obj = ((handler2 != null) ? ((IElementHandler)handler2).PlatformView : null);
}
if (obj is SkiaView content2)
{
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.Content = content2;
}
} }
} }
public static void MapX(WindowHandler handler, IWindow window) public static void MapX(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.X = (int)window.X; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.X = (int)window.X;
}
} }
public static void MapY(WindowHandler handler, IWindow window) public static void MapY(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.Y = (int)window.Y; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.Y = (int)window.Y;
}
} }
public static void MapWidth(WindowHandler handler, IWindow window) public static void MapWidth(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.Width = (int)window.Width; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.Width = (int)window.Width;
}
} }
public static void MapHeight(WindowHandler handler, IWindow window) public static void MapHeight(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.Height = (int)window.Height; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.Height = (int)window.Height;
}
} }
public static void MapMinimumWidth(WindowHandler handler, IWindow window) public static void MapMinimumWidth(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.MinWidth = (int)window.MinimumWidth; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.MinWidth = (int)window.MinimumWidth;
}
} }
public static void MapMinimumHeight(WindowHandler handler, IWindow window) public static void MapMinimumHeight(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.MinHeight = (int)window.MinimumHeight; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.MinHeight = (int)window.MinimumHeight;
}
} }
public static void MapMaximumWidth(WindowHandler handler, IWindow window) public static void MapMaximumWidth(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.MaxWidth = (int)window.MaximumWidth; {
((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.MaxWidth = (int)window.MaximumWidth;
}
} }
public static void MapMaximumHeight(WindowHandler handler, IWindow window) public static void MapMaximumHeight(WindowHandler handler, IWindow window)
{ {
if (handler.PlatformView is null) return; if (((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView != null)
handler.PlatformView.MaxHeight = (int)window.MaximumHeight; {
} ((ElementHandler<IWindow, SkiaWindow>)(object)handler).PlatformView.MaxHeight = (int)window.MaximumHeight;
} }
/// <summary>
/// Skia window wrapper for Linux display servers.
/// Handles rendering of content and popup overlays automatically.
/// </summary>
public class SkiaWindow
{
private SkiaView? _content;
private string _title = "MAUI Application";
private int _x, _y;
private int _width = 800;
private int _height = 600;
private int _minWidth = 100;
private int _minHeight = 100;
private int _maxWidth = int.MaxValue;
private int _maxHeight = int.MaxValue;
public SkiaView? Content
{
get => _content;
set
{
_content = value;
ContentChanged?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Renders the window content and popup overlays to the canvas.
/// This should be called by the platform rendering loop.
/// </summary>
public void Render(SKCanvas canvas)
{
// Clear background
canvas.Clear(SKColors.White);
// Draw main content
if (_content != null)
{
_content.Measure(new SKSize(_width, _height));
_content.Arrange(new SKRect(0, 0, _width, _height));
_content.Draw(canvas);
}
// Draw popup overlays on top (dropdowns, date pickers, etc.)
// This ensures popups always render above all other content
SkiaView.DrawPopupOverlays(canvas);
}
public string Title
{
get => _title;
set
{
_title = value;
TitleChanged?.Invoke(this, EventArgs.Empty);
}
}
public int X
{
get => _x;
set { _x = value; PositionChanged?.Invoke(this, EventArgs.Empty); }
}
public int Y
{
get => _y;
set { _y = value; PositionChanged?.Invoke(this, EventArgs.Empty); }
}
public int Width
{
get => _width;
set
{
_width = Math.Clamp(value, _minWidth, _maxWidth);
SizeChanged?.Invoke(this, new SizeChangedEventArgs(_width, _height));
}
}
public int Height
{
get => _height;
set
{
_height = Math.Clamp(value, _minHeight, _maxHeight);
SizeChanged?.Invoke(this, new SizeChangedEventArgs(_width, _height));
}
}
public int MinWidth
{
get => _minWidth;
set { _minWidth = value; }
}
public int MinHeight
{
get => _minHeight;
set { _minHeight = value; }
}
public int MaxWidth
{
get => _maxWidth;
set { _maxWidth = value; }
}
public int MaxHeight
{
get => _maxHeight;
set { _maxHeight = value; }
}
public event EventHandler? ContentChanged;
public event EventHandler? TitleChanged;
public event EventHandler? PositionChanged;
public event EventHandler<SizeChangedEventArgs>? SizeChanged;
public event EventHandler? CloseRequested;
public void Close()
{
CloseRequested?.Invoke(this, EventArgs.Empty);
}
}
/// <summary>
/// Event args for window size changes.
/// </summary>
public class SizeChangedEventArgs : EventArgs
{
public int Width { get; }
public int Height { get; }
public SizeChangedEventArgs(int width, int height)
{
Width = width;
Height = height;
} }
} }

55
Hosting/GtkMauiContext.cs Normal file
View File

@@ -0,0 +1,55 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Animations;
using Microsoft.Maui.Dispatching;
namespace Microsoft.Maui.Platform.Linux.Hosting;
public class GtkMauiContext : IMauiContext
{
private readonly IServiceProvider _services;
private readonly IMauiHandlersFactory _handlers;
private IAnimationManager? _animationManager;
private IDispatcher? _dispatcher;
public IServiceProvider Services => _services;
public IMauiHandlersFactory Handlers => _handlers;
public IAnimationManager AnimationManager
{
get
{
if (_animationManager == null)
{
_animationManager = (IAnimationManager?)(((object)_services.GetService<IAnimationManager>()) ?? ((object)new LinuxAnimationManager((ITicker)(object)new LinuxTicker())));
}
return _animationManager;
}
}
public IDispatcher Dispatcher
{
get
{
if (_dispatcher == null)
{
_dispatcher = (IDispatcher?)(((object)_services.GetService<IDispatcher>()) ?? ((object)new LinuxDispatcher()));
}
return _dispatcher;
}
}
public GtkMauiContext(IServiceProvider services)
{
_services = services ?? throw new ArgumentNullException("services");
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
if (LinuxApplication.Current == null)
{
new LinuxApplication();
}
}
}

View File

@@ -0,0 +1,12 @@
using Microsoft.Maui.Hosting;
namespace Microsoft.Maui.Platform.Linux.Hosting;
public static class HandlerMappingExtensions
{
public static IMauiHandlersCollection AddHandler<TView, THandler>(this IMauiHandlersCollection handlers) where TView : class where THandler : class
{
MauiHandlersCollectionExtensions.AddHandler(handlers, typeof(TView), typeof(THandler));
return handlers;
}
}

View File

@@ -0,0 +1,54 @@
using System.Collections.Generic;
using Microsoft.Maui.Animations;
namespace Microsoft.Maui.Platform.Linux.Hosting;
internal class LinuxAnimationManager : IAnimationManager
{
private readonly List<Animation> _animations = new List<Animation>();
private readonly ITicker _ticker;
public double SpeedModifier { get; set; } = 1.0;
public bool AutoStartTicker { get; set; } = true;
public ITicker Ticker => _ticker;
public LinuxAnimationManager(ITicker ticker)
{
_ticker = ticker;
_ticker.Fire = OnTickerFire;
}
public void Add(Animation animation)
{
_animations.Add(animation);
if (AutoStartTicker && !_ticker.IsRunning)
{
_ticker.Start();
}
}
public void Remove(Animation animation)
{
_animations.Remove(animation);
if (_animations.Count == 0 && _ticker.IsRunning)
{
_ticker.Stop();
}
}
private void OnTickerFire()
{
Animation[] array = _animations.ToArray();
foreach (Animation val in array)
{
val.Tick(0.016 * SpeedModifier);
if (val.HasFinished)
{
Remove(val);
}
}
}
}

View File

@@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Maui.Dispatching;
namespace Microsoft.Maui.Platform.Linux.Hosting;
internal class LinuxDispatcher : IDispatcher
{
private readonly object _lock = new object();
private readonly Queue<Action> _queue = new Queue<Action>();
private bool _isDispatching;
public bool IsDispatchRequired => false;
public IDispatcherTimer CreateTimer()
{
return (IDispatcherTimer)(object)new LinuxDispatcherTimer();
}
public bool Dispatch(Action action)
{
if (action == null)
{
return false;
}
lock (_lock)
{
_queue.Enqueue(action);
}
ProcessQueue();
return true;
}
public bool DispatchDelayed(TimeSpan delay, Action action)
{
if (action == null)
{
return false;
}
Task.Delay(delay).ContinueWith((Task _) => Dispatch(action));
return true;
}
private void ProcessQueue()
{
if (_isDispatching)
{
return;
}
_isDispatching = true;
try
{
while (true)
{
Action action;
lock (_lock)
{
if (_queue.Count == 0)
{
break;
}
action = _queue.Dequeue();
}
action?.Invoke();
}
}
finally
{
_isDispatching = false;
}
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Threading;
using Microsoft.Maui.Dispatching;
namespace Microsoft.Maui.Platform.Linux.Hosting;
internal class LinuxDispatcherTimer : IDispatcherTimer
{
private Timer? _timer;
private TimeSpan _interval = TimeSpan.FromMilliseconds(16L, 0L);
private bool _isRunning;
private bool _isRepeating = true;
public TimeSpan Interval
{
get
{
return _interval;
}
set
{
_interval = value;
}
}
public bool IsRunning => _isRunning;
public bool IsRepeating
{
get
{
return _isRepeating;
}
set
{
_isRepeating = value;
}
}
public event EventHandler? Tick;
public void Start()
{
if (!_isRunning)
{
_isRunning = true;
_timer = new Timer(OnTimerCallback, null, _interval, _isRepeating ? _interval : Timeout.InfiniteTimeSpan);
}
}
public void Stop()
{
_isRunning = false;
_timer?.Dispose();
_timer = null;
}
private void OnTimerCallback(object? state)
{
this.Tick?.Invoke(this, EventArgs.Empty);
if (!_isRepeating)
{
Stop();
}
}
}

View File

@@ -1,44 +1,40 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel; using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.ApplicationModel.Communication; using Microsoft.Maui.ApplicationModel.Communication;
using Microsoft.Maui.ApplicationModel.DataTransfer; using Microsoft.Maui.ApplicationModel.DataTransfer;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Platform.Linux.Services;
using Microsoft.Maui.Platform.Linux.Converters;
using Microsoft.Maui.Storage;
using Microsoft.Maui.Platform.Linux.Handlers;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Networking;
using Microsoft.Maui.Platform.Linux.Converters;
using Microsoft.Maui.Platform.Linux.Dispatching;
using Microsoft.Maui.Platform.Linux.Handlers;
using Microsoft.Maui.Platform.Linux.Services;
using Microsoft.Maui.Storage;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Hosting; namespace Microsoft.Maui.Platform.Linux.Hosting;
/// <summary>
/// Extension methods for configuring MAUI applications for Linux.
/// </summary>
public static class LinuxMauiAppBuilderExtensions public static class LinuxMauiAppBuilderExtensions
{ {
/// <summary>
/// Configures the MAUI application to run on Linux.
/// </summary>
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder) public static MauiAppBuilder UseLinux(this MauiAppBuilder builder)
{ {
return builder.UseLinux(configure: null); return builder.UseLinux(null);
} }
/// <summary>
/// Configures the MAUI application to run on Linux with options.
/// </summary>
public static MauiAppBuilder UseLinux(this MauiAppBuilder builder, Action<LinuxApplicationOptions>? configure) public static MauiAppBuilder UseLinux(this MauiAppBuilder builder, Action<LinuxApplicationOptions>? configure)
{ {
var options = new LinuxApplicationOptions(); LinuxApplicationOptions linuxApplicationOptions = new LinuxApplicationOptions();
configure?.Invoke(options); configure?.Invoke(linuxApplicationOptions);
builder.Services.TryAddSingleton<IDispatcherProvider>((IDispatcherProvider)(object)LinuxDispatcherProvider.Instance);
// Register platform services builder.Services.TryAddSingleton<IDeviceInfo>((IDeviceInfo)(object)DeviceInfoService.Instance);
builder.Services.TryAddSingleton<IDeviceDisplay>((IDeviceDisplay)(object)DeviceDisplayService.Instance);
builder.Services.TryAddSingleton<IAppInfo>((IAppInfo)(object)AppInfoService.Instance);
builder.Services.TryAddSingleton<IConnectivity>((IConnectivity)(object)ConnectivityService.Instance);
builder.Services.TryAddSingleton<ILauncher, LauncherService>(); builder.Services.TryAddSingleton<ILauncher, LauncherService>();
builder.Services.TryAddSingleton<IPreferences, PreferencesService>(); builder.Services.TryAddSingleton<IPreferences, PreferencesService>();
builder.Services.TryAddSingleton<IFilePicker, FilePickerService>(); builder.Services.TryAddSingleton<IFilePicker, FilePickerService>();
@@ -49,17 +45,11 @@ public static class LinuxMauiAppBuilderExtensions
builder.Services.TryAddSingleton<IAppActions, AppActionsService>(); builder.Services.TryAddSingleton<IAppActions, AppActionsService>();
builder.Services.TryAddSingleton<IBrowser, BrowserService>(); builder.Services.TryAddSingleton<IBrowser, BrowserService>();
builder.Services.TryAddSingleton<IEmail, EmailService>(); builder.Services.TryAddSingleton<IEmail, EmailService>();
builder.Services.TryAddSingleton((IServiceProvider _) => GtkHostService.Instance);
// Register type converters for XAML support
RegisterTypeConverters(); RegisterTypeConverters();
HandlerMauiAppBuilderExtensions.ConfigureMauiHandlers(builder, (Action<IMauiHandlersCollection>)delegate(IMauiHandlersCollection handlers)
// Register Linux-specific handlers
builder.ConfigureMauiHandlers(handlers =>
{ {
// Application handler
handlers.AddHandler<IApplication, ApplicationHandler>(); handlers.AddHandler<IApplication, ApplicationHandler>();
// Core controls
handlers.AddHandler<BoxView, BoxViewHandler>(); handlers.AddHandler<BoxView, BoxViewHandler>();
handlers.AddHandler<Button, TextButtonHandler>(); handlers.AddHandler<Button, TextButtonHandler>();
handlers.AddHandler<Label, LabelHandler>(); handlers.AddHandler<Label, LabelHandler>();
@@ -70,8 +60,6 @@ public static class LinuxMauiAppBuilderExtensions
handlers.AddHandler<Slider, SliderHandler>(); handlers.AddHandler<Slider, SliderHandler>();
handlers.AddHandler<Stepper, StepperHandler>(); handlers.AddHandler<Stepper, StepperHandler>();
handlers.AddHandler<RadioButton, RadioButtonHandler>(); handlers.AddHandler<RadioButton, RadioButtonHandler>();
// Layout controls
handlers.AddHandler<Grid, GridHandler>(); handlers.AddHandler<Grid, GridHandler>();
handlers.AddHandler<StackLayout, StackLayoutHandler>(); handlers.AddHandler<StackLayout, StackLayoutHandler>();
handlers.AddHandler<VerticalStackLayout, StackLayoutHandler>(); handlers.AddHandler<VerticalStackLayout, StackLayoutHandler>();
@@ -82,75 +70,36 @@ public static class LinuxMauiAppBuilderExtensions
handlers.AddHandler<Frame, FrameHandler>(); handlers.AddHandler<Frame, FrameHandler>();
handlers.AddHandler<Border, BorderHandler>(); handlers.AddHandler<Border, BorderHandler>();
handlers.AddHandler<ContentView, BorderHandler>(); handlers.AddHandler<ContentView, BorderHandler>();
// Picker controls
handlers.AddHandler<Picker, PickerHandler>(); handlers.AddHandler<Picker, PickerHandler>();
handlers.AddHandler<DatePicker, DatePickerHandler>(); handlers.AddHandler<DatePicker, DatePickerHandler>();
handlers.AddHandler<TimePicker, TimePickerHandler>(); handlers.AddHandler<TimePicker, TimePickerHandler>();
handlers.AddHandler<SearchBar, SearchBarHandler>(); handlers.AddHandler<SearchBar, SearchBarHandler>();
// Progress & Activity
handlers.AddHandler<ProgressBar, ProgressBarHandler>(); handlers.AddHandler<ProgressBar, ProgressBarHandler>();
handlers.AddHandler<ActivityIndicator, ActivityIndicatorHandler>(); handlers.AddHandler<ActivityIndicator, ActivityIndicatorHandler>();
// Image & Graphics
handlers.AddHandler<Image, ImageHandler>(); handlers.AddHandler<Image, ImageHandler>();
handlers.AddHandler<ImageButton, ImageButtonHandler>(); handlers.AddHandler<ImageButton, ImageButtonHandler>();
handlers.AddHandler<GraphicsView, GraphicsViewHandler>(); handlers.AddHandler<GraphicsView, GraphicsViewHandler>();
handlers.AddHandler<WebView, GtkWebViewHandler>();
// Web
handlers.AddHandler<WebView, WebViewHandler>();
// Collection Views
handlers.AddHandler<CollectionView, CollectionViewHandler>(); handlers.AddHandler<CollectionView, CollectionViewHandler>();
handlers.AddHandler<ListView, CollectionViewHandler>(); handlers.AddHandler<ListView, CollectionViewHandler>();
// Pages & Navigation
handlers.AddHandler<Page, PageHandler>(); handlers.AddHandler<Page, PageHandler>();
handlers.AddHandler<ContentPage, ContentPageHandler>(); handlers.AddHandler<ContentPage, ContentPageHandler>();
handlers.AddHandler<NavigationPage, NavigationPageHandler>(); handlers.AddHandler<NavigationPage, NavigationPageHandler>();
handlers.AddHandler<Shell, ShellHandler>(); handlers.AddHandler<Shell, ShellHandler>();
handlers.AddHandler<FlyoutPage, FlyoutPageHandler>(); handlers.AddHandler<FlyoutPage, FlyoutPageHandler>();
handlers.AddHandler<TabbedPage, TabbedPageHandler>(); handlers.AddHandler<TabbedPage, TabbedPageHandler>();
// Application & Window
handlers.AddHandler<Application, ApplicationHandler>(); handlers.AddHandler<Application, ApplicationHandler>();
handlers.AddHandler<Microsoft.Maui.Controls.Window, WindowHandler>(); handlers.AddHandler<Window, WindowHandler>();
}); });
builder.Services.AddSingleton(linuxApplicationOptions);
// Store options for later use
builder.Services.AddSingleton(options);
return builder; return builder;
} }
/// <summary>
/// Registers custom type converters for Linux platform.
/// </summary>
private static void RegisterTypeConverters() private static void RegisterTypeConverters()
{ {
// Register SkiaSharp type converters for XAML styling support
TypeDescriptor.AddAttributes(typeof(SKColor), new TypeConverterAttribute(typeof(SKColorTypeConverter))); TypeDescriptor.AddAttributes(typeof(SKColor), new TypeConverterAttribute(typeof(SKColorTypeConverter)));
TypeDescriptor.AddAttributes(typeof(SKRect), new TypeConverterAttribute(typeof(SKRectTypeConverter))); TypeDescriptor.AddAttributes(typeof(SKRect), new TypeConverterAttribute(typeof(SKRectTypeConverter)));
TypeDescriptor.AddAttributes(typeof(SKSize), new TypeConverterAttribute(typeof(SKSizeTypeConverter))); TypeDescriptor.AddAttributes(typeof(SKSize), new TypeConverterAttribute(typeof(SKSizeTypeConverter)));
TypeDescriptor.AddAttributes(typeof(SKPoint), new TypeConverterAttribute(typeof(SKPointTypeConverter))); TypeDescriptor.AddAttributes(typeof(SKPoint), new TypeConverterAttribute(typeof(SKPointTypeConverter)));
} }
} }
/// <summary>
/// Handler registration extensions.
/// </summary>
public static class HandlerMappingExtensions
{
/// <summary>
/// Adds a handler for the specified view type.
/// </summary>
public static IMauiHandlersCollection AddHandler<TView, THandler>(
this IMauiHandlersCollection handlers)
where TView : class
where THandler : class
{
handlers.AddHandler(typeof(TView), typeof(THandler));
return handlers;
}
}

View File

@@ -1,299 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Animations; using Microsoft.Maui.Animations;
using Microsoft.Maui.Dispatching; using Microsoft.Maui.Dispatching;
using Microsoft.Maui.Platform;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Hosting; namespace Microsoft.Maui.Platform.Linux.Hosting;
/// <summary>
/// Linux-specific implementation of IMauiContext.
/// Provides the infrastructure for creating handlers and accessing platform services.
/// </summary>
public class LinuxMauiContext : IMauiContext public class LinuxMauiContext : IMauiContext
{ {
private readonly IServiceProvider _services; private readonly IServiceProvider _services;
private readonly IMauiHandlersFactory _handlers; private readonly IMauiHandlersFactory _handlers;
private readonly LinuxApplication _linuxApp; private readonly LinuxApplication _linuxApp;
private IAnimationManager? _animationManager; private IAnimationManager? _animationManager;
private IDispatcher? _dispatcher; private IDispatcher? _dispatcher;
public LinuxMauiContext(IServiceProvider services, LinuxApplication linuxApp)
{
_services = services ?? throw new ArgumentNullException(nameof(services));
_linuxApp = linuxApp ?? throw new ArgumentNullException(nameof(linuxApp));
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
}
/// <inheritdoc />
public IServiceProvider Services => _services; public IServiceProvider Services => _services;
/// <inheritdoc />
public IMauiHandlersFactory Handlers => _handlers; public IMauiHandlersFactory Handlers => _handlers;
/// <summary>
/// Gets the Linux application instance.
/// </summary>
public LinuxApplication LinuxApp => _linuxApp; public LinuxApplication LinuxApp => _linuxApp;
/// <summary>
/// Gets the animation manager.
/// </summary>
public IAnimationManager AnimationManager public IAnimationManager AnimationManager
{ {
get get
{ {
_animationManager ??= _services.GetService<IAnimationManager>() if (_animationManager == null)
?? new LinuxAnimationManager(new LinuxTicker()); {
_animationManager = (IAnimationManager?)(((object)_services.GetService<IAnimationManager>()) ?? ((object)new LinuxAnimationManager((ITicker)(object)new LinuxTicker())));
}
return _animationManager; return _animationManager;
} }
} }
/// <summary>
/// Gets the dispatcher for UI thread operations.
/// </summary>
public IDispatcher Dispatcher public IDispatcher Dispatcher
{ {
get get
{ {
_dispatcher ??= _services.GetService<IDispatcher>() if (_dispatcher == null)
?? new LinuxDispatcher(); {
_dispatcher = (IDispatcher?)(((object)_services.GetService<IDispatcher>()) ?? ((object)new LinuxDispatcher()));
}
return _dispatcher; return _dispatcher;
} }
} }
}
/// <summary> public LinuxMauiContext(IServiceProvider services, LinuxApplication linuxApp)
/// Scoped MAUI context for a specific window or view hierarchy.
/// </summary>
public class ScopedLinuxMauiContext : IMauiContext
{
private readonly LinuxMauiContext _parent;
public ScopedLinuxMauiContext(LinuxMauiContext parent)
{ {
_parent = parent ?? throw new ArgumentNullException(nameof(parent)); _services = services ?? throw new ArgumentNullException("services");
} _linuxApp = linuxApp ?? throw new ArgumentNullException("linuxApp");
_handlers = services.GetRequiredService<IMauiHandlersFactory>();
public IServiceProvider Services => _parent.Services;
public IMauiHandlersFactory Handlers => _parent.Handlers;
}
/// <summary>
/// Linux dispatcher for UI thread operations.
/// </summary>
internal class LinuxDispatcher : IDispatcher
{
private readonly object _lock = new();
private readonly Queue<Action> _queue = new();
private bool _isDispatching;
public bool IsDispatchRequired => false; // Linux uses single-threaded event loop
public IDispatcherTimer CreateTimer()
{
return new LinuxDispatcherTimer();
}
public bool Dispatch(Action action)
{
if (action == null)
return false;
lock (_lock)
{
_queue.Enqueue(action);
}
ProcessQueue();
return true;
}
public bool DispatchDelayed(TimeSpan delay, Action action)
{
if (action == null)
return false;
Task.Delay(delay).ContinueWith(_ => Dispatch(action));
return true;
}
private void ProcessQueue()
{
if (_isDispatching)
return;
_isDispatching = true;
try
{
while (true)
{
Action? action;
lock (_lock)
{
if (_queue.Count == 0)
break;
action = _queue.Dequeue();
}
action?.Invoke();
}
}
finally
{
_isDispatching = false;
}
}
}
/// <summary>
/// Linux dispatcher timer implementation.
/// </summary>
internal class LinuxDispatcherTimer : IDispatcherTimer
{
private Timer? _timer;
private TimeSpan _interval = TimeSpan.FromMilliseconds(16); // ~60fps default
private bool _isRunning;
private bool _isRepeating = true;
public TimeSpan Interval
{
get => _interval;
set => _interval = value;
}
public bool IsRunning => _isRunning;
public bool IsRepeating
{
get => _isRepeating;
set => _isRepeating = value;
}
public event EventHandler? Tick;
public void Start()
{
if (_isRunning)
return;
_isRunning = true;
_timer = new Timer(OnTimerCallback, null, _interval, _isRepeating ? _interval : Timeout.InfiniteTimeSpan);
}
public void Stop()
{
_isRunning = false;
_timer?.Dispose();
_timer = null;
}
private void OnTimerCallback(object? state)
{
Tick?.Invoke(this, EventArgs.Empty);
if (!_isRepeating)
{
Stop();
}
}
}
/// <summary>
/// Linux animation manager.
/// </summary>
internal class LinuxAnimationManager : IAnimationManager
{
private readonly List<Microsoft.Maui.Animations.Animation> _animations = new();
private readonly ITicker _ticker;
public LinuxAnimationManager(ITicker ticker)
{
_ticker = ticker;
_ticker.Fire = OnTickerFire;
}
public double SpeedModifier { get; set; } = 1.0;
public bool AutoStartTicker { get; set; } = true;
public ITicker Ticker => _ticker;
public void Add(Microsoft.Maui.Animations.Animation animation)
{
_animations.Add(animation);
if (AutoStartTicker && !_ticker.IsRunning)
{
_ticker.Start();
}
}
public void Remove(Microsoft.Maui.Animations.Animation animation)
{
_animations.Remove(animation);
if (_animations.Count == 0 && _ticker.IsRunning)
{
_ticker.Stop();
}
}
private void OnTickerFire()
{
var animations = _animations.ToArray();
foreach (var animation in animations)
{
animation.Tick(16.0 / 1000.0 * SpeedModifier); // ~60fps
if (animation.HasFinished)
{
Remove(animation);
}
}
}
}
/// <summary>
/// Linux ticker for animation timing.
/// </summary>
internal class LinuxTicker : ITicker
{
private Timer? _timer;
private bool _isRunning;
private int _maxFps = 60;
public bool IsRunning => _isRunning;
public bool SystemEnabled => true;
public int MaxFps
{
get => _maxFps;
set => _maxFps = Math.Max(1, Math.Min(120, value));
}
public Action? Fire { get; set; }
public void Start()
{
if (_isRunning)
return;
_isRunning = true;
var interval = TimeSpan.FromMilliseconds(1000.0 / _maxFps);
_timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, interval);
}
public void Stop()
{
_isRunning = false;
_timer?.Dispose();
_timer = null;
}
private void OnTimerCallback(object? state)
{
Fire?.Invoke();
} }
} }

View File

File diff suppressed because it is too large Load Diff

54
Hosting/LinuxTicker.cs Normal file
View File

@@ -0,0 +1,54 @@
using System;
using System.Threading;
using Microsoft.Maui.Animations;
namespace Microsoft.Maui.Platform.Linux.Hosting;
internal class LinuxTicker : ITicker
{
private Timer? _timer;
private bool _isRunning;
private int _maxFps = 60;
public bool IsRunning => _isRunning;
public bool SystemEnabled => true;
public int MaxFps
{
get
{
return _maxFps;
}
set
{
_maxFps = Math.Max(1, Math.Min(120, value));
}
}
public Action? Fire { get; set; }
public void Start()
{
if (!_isRunning)
{
_isRunning = true;
TimeSpan period = TimeSpan.FromMilliseconds(1000.0 / (double)_maxFps);
_timer = new Timer(OnTimerCallback, null, TimeSpan.Zero, period);
}
}
public void Stop()
{
_isRunning = false;
_timer?.Dispose();
_timer = null;
}
private void OnTimerCallback(object? state)
{
Fire?.Invoke();
}
}

View File

@@ -1,252 +1,264 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Reflection;
using System.Runtime.CompilerServices;
using Microsoft.Maui.Controls; using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform; using Microsoft.Maui.Graphics;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Hosting; namespace Microsoft.Maui.Platform.Linux.Hosting;
/// <summary>
/// Renders MAUI views to Skia platform views.
/// Handles the conversion of the view hierarchy.
/// </summary>
public class LinuxViewRenderer public class LinuxViewRenderer
{ {
private readonly IMauiContext _mauiContext; private readonly IMauiContext _mauiContext;
/// <summary>
/// Static reference to the current MAUI Shell for navigation support.
/// Used when Shell.Current is not available through normal lifecycle.
/// </summary>
public static Shell? CurrentMauiShell { get; private set; } public static Shell? CurrentMauiShell { get; private set; }
/// <summary>
/// Static reference to the current SkiaShell for navigation updates.
/// </summary>
public static SkiaShell? CurrentSkiaShell { get; private set; } public static SkiaShell? CurrentSkiaShell { get; private set; }
/// <summary> public static LinuxViewRenderer? CurrentRenderer { get; set; }
/// Navigate to a route using the SkiaShell directly.
/// Use this instead of Shell.Current.GoToAsync on Linux.
/// </summary>
/// <param name="route">The route to navigate to (e.g., "Buttons" or "//Buttons")</param>
/// <returns>True if navigation succeeded</returns>
public static bool NavigateToRoute(string route) public static bool NavigateToRoute(string route)
{ {
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[NavigateToRoute] CurrentSkiaShell is null"); Console.WriteLine("[NavigateToRoute] CurrentSkiaShell is null");
return false; return false;
} }
string text = route.TrimStart('/');
// Clean up the route - remove leading // or / Console.WriteLine("[NavigateToRoute] Navigating to: " + text);
var cleanRoute = route.TrimStart('/');
Console.WriteLine($"[NavigateToRoute] Navigating to: {cleanRoute}");
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
{ {
var section = CurrentSkiaShell.Sections[i]; ShellSection shellSection = CurrentSkiaShell.Sections[i];
if (section.Route.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase) || if (shellSection.Route.Equals(text, StringComparison.OrdinalIgnoreCase) || shellSection.Title.Equals(text, StringComparison.OrdinalIgnoreCase))
section.Title.Equals(cleanRoute, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[NavigateToRoute] Found section {i}: {section.Title}"); Console.WriteLine($"[NavigateToRoute] Found section {i}: {shellSection.Title}");
CurrentSkiaShell.NavigateToSection(i); CurrentSkiaShell.NavigateToSection(i);
return true; return true;
} }
} }
Console.WriteLine("[NavigateToRoute] Route not found: " + text);
Console.WriteLine($"[NavigateToRoute] Route not found: {cleanRoute}");
return false; return false;
} }
/// <summary>
/// Current renderer instance for page rendering.
/// </summary>
public static LinuxViewRenderer? CurrentRenderer { get; set; }
/// <summary>
/// Pushes a page onto the navigation stack.
/// </summary>
/// <param name="page">The page to push</param>
/// <returns>True if successful</returns>
public static bool PushPage(Page page) public static bool PushPage(Page page)
{ {
Console.WriteLine($"[PushPage] Pushing page: {page.GetType().Name}"); Console.WriteLine("[PushPage] Pushing page: " + ((object)page).GetType().Name);
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[PushPage] CurrentSkiaShell is null"); Console.WriteLine("[PushPage] CurrentSkiaShell is null");
return false; return false;
} }
if (CurrentRenderer == null) if (CurrentRenderer == null)
{ {
Console.WriteLine($"[PushPage] CurrentRenderer is null"); Console.WriteLine("[PushPage] CurrentRenderer is null");
return false; return false;
} }
try try
{ {
// Render the page content SkiaView skiaView = null;
SkiaView? pageContent = null; ContentPage val = (ContentPage)(object)((page is ContentPage) ? page : null);
if (page is ContentPage contentPage && contentPage.Content != null) if (val != null && val.Content != null)
{ {
pageContent = CurrentRenderer.RenderView(contentPage.Content); skiaView = CurrentRenderer.RenderView((IView)(object)val.Content);
} }
if (skiaView == null)
if (pageContent == null)
{ {
Console.WriteLine($"[PushPage] Failed to render page content"); Console.WriteLine("[PushPage] Failed to render page content");
return false; return false;
} }
if (!(skiaView is SkiaScrollView))
// Wrap in ScrollView if needed
if (pageContent is not SkiaScrollView)
{ {
var scrollView = new SkiaScrollView { Content = pageContent }; skiaView = new SkiaScrollView
pageContent = scrollView; {
Content = skiaView
};
} }
CurrentSkiaShell.PushAsync(skiaView, page.Title ?? "Detail");
// Push onto SkiaShell's navigation stack Console.WriteLine("[PushPage] Successfully pushed page");
CurrentSkiaShell.PushAsync(pageContent, page.Title ?? "Detail");
Console.WriteLine($"[PushPage] Successfully pushed page");
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[PushPage] Error: {ex.Message}"); Console.WriteLine("[PushPage] Error: " + ex.Message);
return false; return false;
} }
} }
/// <summary>
/// Pops the current page from the navigation stack.
/// </summary>
/// <returns>True if successful</returns>
public static bool PopPage() public static bool PopPage()
{ {
Console.WriteLine($"[PopPage] Popping page"); Console.WriteLine("[PopPage] Popping page");
if (CurrentSkiaShell == null) if (CurrentSkiaShell == null)
{ {
Console.WriteLine($"[PopPage] CurrentSkiaShell is null"); Console.WriteLine("[PopPage] CurrentSkiaShell is null");
return false; return false;
} }
return CurrentSkiaShell.PopAsync(); return CurrentSkiaShell.PopAsync();
} }
public LinuxViewRenderer(IMauiContext mauiContext) public LinuxViewRenderer(IMauiContext mauiContext)
{ {
_mauiContext = mauiContext ?? throw new ArgumentNullException(nameof(mauiContext)); _mauiContext = mauiContext ?? throw new ArgumentNullException("mauiContext");
// Store reference for push/pop navigation
CurrentRenderer = this; CurrentRenderer = this;
} }
/// <summary>
/// Renders a MAUI page and returns the corresponding SkiaView.
/// </summary>
public SkiaView? RenderPage(Page page) public SkiaView? RenderPage(Page page)
{ {
if (page == null) if (page == null)
{
return null; return null;
// Special handling for Shell - Shell is our navigation container
if (page is Shell shell)
{
return RenderShell(shell);
} }
Shell val = (Shell)(object)((page is Shell) ? page : null);
// Set handler context if (val != null)
page.Handler?.DisconnectHandler();
var handler = page.ToHandler(_mauiContext);
if (handler.PlatformView is SkiaView skiaPage)
{ {
// For ContentPage, render the content return RenderShell(val);
if (page is ContentPage contentPage && contentPage.Content != null) }
IViewHandler handler = ((VisualElement)page).Handler;
if (handler != null)
{ {
var contentView = RenderView(contentPage.Content); ((IElementHandler)handler).DisconnectHandler();
if (skiaPage is SkiaPage sp && contentView != null) }
if (((IElement)(object)page).ToHandler(_mauiContext).PlatformView is SkiaView skiaView)
{ {
sp.Content = contentView; ContentPage val2 = (ContentPage)(object)((page is ContentPage) ? page : null);
if (val2 != null && val2.Content != null)
{
SkiaView skiaView2 = RenderView((IView)(object)val2.Content);
if (skiaView is SkiaPage skiaPage && skiaView2 != null)
{
skiaPage.Content = skiaView2;
} }
} }
return skiaView;
return skiaPage;
} }
return null; return null;
} }
/// <summary>
/// Renders a MAUI Shell with all its navigation structure.
/// </summary>
private SkiaShell RenderShell(Shell shell) private SkiaShell RenderShell(Shell shell)
{ {
// Store reference for navigation - Shell.Current is computed from Application.Current.Windows //IL_0027: Unknown result type (might be due to invalid IL or missing references)
// Our platform handles navigation through SkiaShell directly //IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Expected I4, but got Unknown
CurrentMauiShell = shell; CurrentMauiShell = shell;
SkiaShell skiaShell = new SkiaShell();
var skiaShell = new SkiaShell skiaShell.Title = ((Page)shell).Title ?? "App";
SkiaShell skiaShell2 = skiaShell;
FlyoutBehavior flyoutBehavior = shell.FlyoutBehavior;
skiaShell2.FlyoutBehavior = (int)flyoutBehavior switch
{ {
Title = shell.Title ?? "App", 1 => ShellFlyoutBehavior.Flyout,
FlyoutBehavior = shell.FlyoutBehavior switch 2 => ShellFlyoutBehavior.Locked,
{ 0 => ShellFlyoutBehavior.Disabled,
FlyoutBehavior.Flyout => ShellFlyoutBehavior.Flyout, _ => ShellFlyoutBehavior.Flyout,
FlyoutBehavior.Locked => ShellFlyoutBehavior.Locked,
FlyoutBehavior.Disabled => ShellFlyoutBehavior.Disabled,
_ => ShellFlyoutBehavior.Flyout
}
}; };
skiaShell.MauiShell = shell;
// Process shell items into sections SkiaShell skiaShell3 = skiaShell;
foreach (var item in shell.Items) ApplyShellColors(skiaShell3, shell);
object flyoutHeader = shell.FlyoutHeader;
View val = (View)((flyoutHeader is View) ? flyoutHeader : null);
if (val != null)
{ {
ProcessShellItem(skiaShell, item); SkiaView skiaView = RenderView((IView)(object)val);
if (skiaView != null)
{
skiaShell3.FlyoutHeaderView = skiaView;
skiaShell3.FlyoutHeaderHeight = (float)((((VisualElement)val).HeightRequest > 0.0) ? ((VisualElement)val).HeightRequest : 140.0);
} }
}
// Store reference to SkiaShell for navigation Version version = Assembly.GetEntryAssembly()?.GetName().Version;
CurrentSkiaShell = skiaShell; skiaShell3.FlyoutFooterText = $"Version {version?.Major ?? 1}.{version?.Minor ?? 0}.{version?.Build ?? 0}";
foreach (ShellItem item in shell.Items)
// Subscribe to MAUI Shell navigation events to update SkiaShell {
ProcessShellItem(skiaShell3, item);
}
CurrentSkiaShell = skiaShell3;
skiaShell3.ContentRenderer = CreateShellContentPage;
skiaShell3.ColorRefresher = ApplyShellColors;
shell.Navigated += OnShellNavigated; shell.Navigated += OnShellNavigated;
shell.Navigating += (s, e) => Console.WriteLine($"[Navigation] Navigating: {e.Target}"); shell.Navigating += delegate(object? s, ShellNavigatingEventArgs e)
Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell.Sections.Count}");
for (int i = 0; i < skiaShell.Sections.Count; i++)
{ {
Console.WriteLine($"[Navigation] Section {i}: Route='{skiaShell.Sections[i].Route}', Title='{skiaShell.Sections[i].Title}'"); Console.WriteLine($"[Navigation] Navigating: {e.Target}");
};
Console.WriteLine($"[Navigation] Shell navigation events subscribed. Sections: {skiaShell3.Sections.Count}");
for (int num = 0; num < skiaShell3.Sections.Count; num++)
{
Console.WriteLine($"[Navigation] Section {num}: Route='{skiaShell3.Sections[num].Route}', Title='{skiaShell3.Sections[num].Title}'");
}
return skiaShell3;
} }
return skiaShell; private static void ApplyShellColors(SkiaShell skiaShell, Shell shell)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Invalid comparison between Unknown and I4
//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_013b: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
//IL_0236: Unknown result type (might be due to invalid IL or missing references)
//IL_021e: Unknown result type (might be due to invalid IL or missing references)
Application current = Application.Current;
bool flag = current != null && (int)current.UserAppTheme == 2;
Console.WriteLine("[ApplyShellColors] Theme is: " + (flag ? "Dark" : "Light"));
if (shell.FlyoutBackgroundColor != null && shell.FlyoutBackgroundColor != Colors.Transparent)
{
Color flyoutBackgroundColor = shell.FlyoutBackgroundColor;
skiaShell.FlyoutBackgroundColor = new SKColor((byte)(flyoutBackgroundColor.Red * 255f), (byte)(flyoutBackgroundColor.Green * 255f), (byte)(flyoutBackgroundColor.Blue * 255f), (byte)(flyoutBackgroundColor.Alpha * 255f));
Console.WriteLine($"[ApplyShellColors] FlyoutBackgroundColor from MAUI: {skiaShell.FlyoutBackgroundColor}");
}
else
{
skiaShell.FlyoutBackgroundColor = (flag ? new SKColor((byte)30, (byte)30, (byte)30) : new SKColor(byte.MaxValue, byte.MaxValue, byte.MaxValue));
Console.WriteLine($"[ApplyShellColors] Using default FlyoutBackgroundColor: {skiaShell.FlyoutBackgroundColor}");
}
skiaShell.FlyoutTextColor = (flag ? new SKColor((byte)224, (byte)224, (byte)224) : new SKColor((byte)33, (byte)33, (byte)33));
Console.WriteLine($"[ApplyShellColors] FlyoutTextColor: {skiaShell.FlyoutTextColor}");
skiaShell.ContentBackgroundColor = (flag ? new SKColor((byte)18, (byte)18, (byte)18) : new SKColor((byte)250, (byte)250, (byte)250));
Console.WriteLine($"[ApplyShellColors] ContentBackgroundColor: {skiaShell.ContentBackgroundColor}");
if (((VisualElement)shell).BackgroundColor != null && ((VisualElement)shell).BackgroundColor != Colors.Transparent)
{
Color backgroundColor = ((VisualElement)shell).BackgroundColor;
skiaShell.NavBarBackgroundColor = new SKColor((byte)(backgroundColor.Red * 255f), (byte)(backgroundColor.Green * 255f), (byte)(backgroundColor.Blue * 255f), (byte)(backgroundColor.Alpha * 255f));
}
else
{
skiaShell.NavBarBackgroundColor = new SKColor((byte)33, (byte)150, (byte)243);
}
} }
/// <summary>
/// Handles MAUI Shell navigation events and updates SkiaShell accordingly.
/// </summary>
private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e) private static void OnShellNavigated(object? sender, ShellNavigatedEventArgs e)
{ {
Console.WriteLine($"[Navigation] OnShellNavigated called - Source: {e.Source}, Current: {e.Current?.Location}, Previous: {e.Previous?.Location}"); //IL_0019: Unknown result type (might be due to invalid IL or missing references)
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(70, 3);
defaultInterpolatedStringHandler.AppendLiteral("[Navigation] OnShellNavigated called - Source: ");
defaultInterpolatedStringHandler.AppendFormatted<ShellNavigationSource>(e.Source);
defaultInterpolatedStringHandler.AppendLiteral(", Current: ");
ShellNavigationState current = e.Current;
defaultInterpolatedStringHandler.AppendFormatted((current != null) ? current.Location : null);
defaultInterpolatedStringHandler.AppendLiteral(", Previous: ");
ShellNavigationState previous = e.Previous;
defaultInterpolatedStringHandler.AppendFormatted((previous != null) ? previous.Location : null);
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
if (CurrentSkiaShell == null || CurrentMauiShell == null) if (CurrentSkiaShell == null || CurrentMauiShell == null)
{ {
Console.WriteLine($"[Navigation] CurrentSkiaShell or CurrentMauiShell is null"); Console.WriteLine("[Navigation] CurrentSkiaShell or CurrentMauiShell is null");
return; return;
} }
ShellNavigationState currentState = CurrentMauiShell.CurrentState;
// Get the current route from the Shell string text = ((currentState == null) ? null : currentState.Location?.OriginalString) ?? "";
var currentState = CurrentMauiShell.CurrentState; Console.WriteLine($"[Navigation] Location: {text}, Sections: {CurrentSkiaShell.Sections.Count}");
var location = currentState?.Location?.OriginalString ?? "";
Console.WriteLine($"[Navigation] Location: {location}, Sections: {CurrentSkiaShell.Sections.Count}");
// Find the matching section in SkiaShell by route
for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++) for (int i = 0; i < CurrentSkiaShell.Sections.Count; i++)
{ {
var section = CurrentSkiaShell.Sections[i]; ShellSection shellSection = CurrentSkiaShell.Sections[i];
Console.WriteLine($"[Navigation] Checking section {i}: Route='{section.Route}', Title='{section.Title}'"); Console.WriteLine($"[Navigation] Checking section {i}: Route='{shellSection.Route}', Title='{shellSection.Title}'");
if (!string.IsNullOrEmpty(section.Route) && location.Contains(section.Route, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(shellSection.Route) && text.Contains(shellSection.Route, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}"); Console.WriteLine($"[Navigation] Match found by route! Navigating to section {i}");
if (i != CurrentSkiaShell.CurrentSectionIndex) if (i != CurrentSkiaShell.CurrentSectionIndex)
@@ -255,7 +267,7 @@ public class LinuxViewRenderer
} }
return; return;
} }
if (!string.IsNullOrEmpty(section.Title) && location.Contains(section.Title, StringComparison.OrdinalIgnoreCase)) if (!string.IsNullOrEmpty(shellSection.Title) && text.Contains(shellSection.Title, StringComparison.OrdinalIgnoreCase))
{ {
Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}"); Console.WriteLine($"[Navigation] Match found by title! Navigating to section {i}");
if (i != CurrentSkiaShell.CurrentSectionIndex) if (i != CurrentSkiaShell.CurrentSectionIndex)
@@ -265,195 +277,179 @@ public class LinuxViewRenderer
return; return;
} }
} }
Console.WriteLine($"[Navigation] No matching section found for location: {location}"); Console.WriteLine("[Navigation] No matching section found for location: " + text);
} }
/// <summary>
/// Process a ShellItem (FlyoutItem, TabBar, etc.) into SkiaShell sections.
/// </summary>
private void ProcessShellItem(SkiaShell skiaShell, ShellItem item) private void ProcessShellItem(SkiaShell skiaShell, ShellItem item)
{ {
if (item is FlyoutItem flyoutItem) FlyoutItem val = (FlyoutItem)(object)((item is FlyoutItem) ? item : null);
if (val != null)
{ {
// Each FlyoutItem becomes a section ShellSection shellSection = new ShellSection
var section = new ShellSection
{ {
Title = flyoutItem.Title ?? "", Title = (((BaseShellItem)val).Title ?? ""),
Route = flyoutItem.Route ?? flyoutItem.Title ?? "" Route = (((BaseShellItem)val).Route ?? ((BaseShellItem)val).Title ?? "")
}; };
foreach (ShellSection item2 in ((ShellItem)val).Items)
// Process the items within the FlyoutItem
foreach (var shellSection in flyoutItem.Items)
{ {
foreach (var content in shellSection.Items) foreach (ShellContent item3 in item2.Items)
{ {
var shellContent = new ShellContent ShellContent shellContent = new ShellContent
{ {
Title = content.Title ?? shellSection.Title ?? flyoutItem.Title ?? "", Title = (((BaseShellItem)item3).Title ?? ((BaseShellItem)item2).Title ?? ((BaseShellItem)val).Title ?? ""),
Route = content.Route ?? "" Route = (((BaseShellItem)item3).Route ?? ""),
MauiShellContent = item3
}; };
SkiaView skiaView = CreateShellContentPage(item3);
// Create the page content if (skiaView != null)
var pageContent = CreateShellContentPage(content);
if (pageContent != null)
{ {
shellContent.Content = pageContent; shellContent.Content = skiaView;
} }
shellSection.Items.Add(shellContent);
section.Items.Add(shellContent);
} }
} }
if (shellSection.Items.Count == 1)
// If there's only one item, use it as the main section content
if (section.Items.Count == 1)
{ {
section.Title = section.Items[0].Title; shellSection.Title = shellSection.Items[0].Title;
} }
skiaShell.AddSection(shellSection);
skiaShell.AddSection(section); return;
} }
else if (item is TabBar tabBar) TabBar val2 = (TabBar)(object)((item is TabBar) ? item : null);
if (val2 != null)
{ {
// TabBar items get their own sections foreach (ShellSection item4 in ((ShellItem)val2).Items)
foreach (var tab in tabBar.Items)
{ {
var section = new ShellSection ShellSection shellSection2 = new ShellSection
{ {
Title = tab.Title ?? "", Title = (((BaseShellItem)item4).Title ?? ""),
Route = tab.Route ?? "" Route = (((BaseShellItem)item4).Route ?? "")
}; };
foreach (ShellContent item5 in item4.Items)
foreach (var content in tab.Items)
{ {
var shellContent = new ShellContent ShellContent shellContent2 = new ShellContent
{ {
Title = content.Title ?? tab.Title ?? "", Title = (((BaseShellItem)item5).Title ?? ((BaseShellItem)item4).Title ?? ""),
Route = content.Route ?? "" Route = (((BaseShellItem)item5).Route ?? ""),
MauiShellContent = item5
}; };
SkiaView skiaView2 = CreateShellContentPage(item5);
var pageContent = CreateShellContentPage(content); if (skiaView2 != null)
if (pageContent != null)
{ {
shellContent.Content = pageContent; shellContent2.Content = skiaView2;
} }
shellSection2.Items.Add(shellContent2);
section.Items.Add(shellContent);
} }
skiaShell.AddSection(shellSection2);
skiaShell.AddSection(section);
} }
return;
} }
else ShellSection shellSection3 = new ShellSection
{ {
// Generic ShellItem Title = (((BaseShellItem)item).Title ?? ""),
var section = new ShellSection Route = (((BaseShellItem)item).Route ?? "")
{
Title = item.Title ?? "",
Route = item.Route ?? ""
}; };
foreach (ShellSection item6 in item.Items)
foreach (var shellSection in item.Items)
{ {
foreach (var content in shellSection.Items) foreach (ShellContent item7 in item6.Items)
{ {
var shellContent = new ShellContent ShellContent shellContent3 = new ShellContent
{ {
Title = content.Title ?? "", Title = (((BaseShellItem)item7).Title ?? ""),
Route = content.Route ?? "" Route = (((BaseShellItem)item7).Route ?? ""),
MauiShellContent = item7
}; };
SkiaView skiaView3 = CreateShellContentPage(item7);
var pageContent = CreateShellContentPage(content); if (skiaView3 != null)
if (pageContent != null)
{ {
shellContent.Content = pageContent; shellContent3.Content = skiaView3;
}
shellSection3.Items.Add(shellContent3);
}
}
skiaShell.AddSection(shellSection3);
} }
section.Items.Add(shellContent); private SkiaView? CreateShellContentPage(ShellContent content)
}
}
skiaShell.AddSection(section);
}
}
/// <summary>
/// Creates the page content for a ShellContent.
/// </summary>
private SkiaView? CreateShellContentPage(Controls.ShellContent content)
{ {
//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
try try
{ {
// Try to create the page from the content template Page val = null;
Page? page = null;
if (content.ContentTemplate != null) if (content.ContentTemplate != null)
{ {
page = content.ContentTemplate.CreateContent() as Page; object obj = ((ElementTemplate)content.ContentTemplate).CreateContent();
val = (Page)((obj is Page) ? obj : null);
} }
if (val == null)
if (page == null && content.Content is Page contentPage)
{ {
page = contentPage; object content2 = content.Content;
Page val2 = (Page)((content2 is Page) ? content2 : null);
if (val2 != null)
{
val = val2;
} }
if (page is ContentPage cp && cp.Content != null)
{
// Wrap in a scroll view if not already scrollable
var contentView = RenderView(cp.Content);
if (contentView != null)
{
if (contentView is SkiaScrollView)
{
return contentView;
} }
else ContentPage val3 = (ContentPage)(object)((val is ContentPage) ? val : null);
if (val3 != null && val3.Content != null)
{ {
var scrollView = new SkiaScrollView SkiaView skiaView = RenderView((IView)(object)val3.Content);
if (skiaView != null)
{ {
Content = contentView SKColor? value = null;
if (((VisualElement)val3).BackgroundColor != null && ((VisualElement)val3).BackgroundColor != Colors.Transparent)
{
Color backgroundColor = ((VisualElement)val3).BackgroundColor;
value = new SKColor((byte)(backgroundColor.Red * 255f), (byte)(backgroundColor.Green * 255f), (byte)(backgroundColor.Blue * 255f), (byte)(backgroundColor.Alpha * 255f));
Console.WriteLine($"[CreateShellContentPage] Page BackgroundColor: {value}");
}
if (skiaView is SkiaScrollView skiaScrollView)
{
if (value.HasValue)
{
skiaScrollView.BackgroundColor = value.Value;
}
return skiaScrollView;
}
SkiaScrollView skiaScrollView2 = new SkiaScrollView
{
Content = skiaView
}; };
return scrollView; if (value.HasValue)
{
skiaScrollView2.BackgroundColor = value.Value;
} }
return skiaScrollView2;
} }
} }
} }
catch (Exception) catch (Exception)
{ {
// Silently handle template creation errors
} }
return null; return null;
} }
/// <summary>
/// Renders a MAUI view and returns the corresponding SkiaView.
/// </summary>
public SkiaView? RenderView(IView view) public SkiaView? RenderView(IView view)
{ {
if (view == null) if (view == null)
{
return null; return null;
}
try try
{ {
// Disconnect any existing handler Element val = (Element)(object)((view is Element) ? view : null);
if (view is Element element && element.Handler != null) if (val != null && val.Handler != null)
{ {
element.Handler.DisconnectHandler(); val.Handler.DisconnectHandler();
} }
IElementHandler obj = ((IElement)(object)view).ToHandler(_mauiContext);
// Create handler for the view if (!(((obj != null) ? obj.PlatformView : null) is SkiaView result))
// The handler's ConnectHandler and property mappers handle child views automatically
var handler = view.ToHandler(_mauiContext);
if (handler?.PlatformView is not SkiaView skiaView)
{ {
// If no Skia handler, create a fallback
return CreateFallbackView(view); return CreateFallbackView(view);
} }
return result;
// Handlers manage their own children via ConnectHandler and property mappers
// No manual child rendering needed here - that caused "View already has a parent" errors
return skiaView;
} }
catch (Exception) catch (Exception)
{ {
@@ -461,37 +457,14 @@ public class LinuxViewRenderer
} }
} }
/// <summary>
/// Creates a fallback view for unsupported view types.
/// </summary>
private SkiaView CreateFallbackView(IView view) private SkiaView CreateFallbackView(IView view)
{ {
// For views without handlers, create a placeholder //IL_0026: Unknown result type (might be due to invalid IL or missing references)
return new SkiaLabel return new SkiaLabel
{ {
Text = $"[{view.GetType().Name}]", Text = "[" + ((object)view).GetType().Name + "]",
TextColor = SKColors.Gray, TextColor = SKColors.Gray,
FontSize = 12 FontSize = 12f
}; };
} }
} }
/// <summary>
/// Extension methods for MAUI handler creation.
/// </summary>
public static class MauiHandlerExtensions
{
/// <summary>
/// Creates a handler for the view and returns it.
/// </summary>
public static IElementHandler ToHandler(this IElement element, IMauiContext mauiContext)
{
var handler = mauiContext.Handlers.GetHandler(element.GetType());
if (handler != null)
{
handler.SetMauiContext(mauiContext);
handler.SetVirtualView(element);
}
return handler!;
}
}

View File

@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Platform.Linux.Handlers;
namespace Microsoft.Maui.Platform.Linux.Hosting;
public static class MauiHandlerExtensions
{
private static readonly Dictionary<Type, Func<IElementHandler>> LinuxHandlerMap = new Dictionary<Type, Func<IElementHandler>>
{
[typeof(Button)] = () => (IElementHandler)(object)new TextButtonHandler(),
[typeof(Label)] = () => (IElementHandler)(object)new LabelHandler(),
[typeof(Entry)] = () => (IElementHandler)(object)new EntryHandler(),
[typeof(Editor)] = () => (IElementHandler)(object)new EditorHandler(),
[typeof(CheckBox)] = () => (IElementHandler)(object)new CheckBoxHandler(),
[typeof(Switch)] = () => (IElementHandler)(object)new SwitchHandler(),
[typeof(Slider)] = () => (IElementHandler)(object)new SliderHandler(),
[typeof(Stepper)] = () => (IElementHandler)(object)new StepperHandler(),
[typeof(ProgressBar)] = () => (IElementHandler)(object)new ProgressBarHandler(),
[typeof(ActivityIndicator)] = () => (IElementHandler)(object)new ActivityIndicatorHandler(),
[typeof(Picker)] = () => (IElementHandler)(object)new PickerHandler(),
[typeof(DatePicker)] = () => (IElementHandler)(object)new DatePickerHandler(),
[typeof(TimePicker)] = () => (IElementHandler)(object)new TimePickerHandler(),
[typeof(SearchBar)] = () => (IElementHandler)(object)new SearchBarHandler(),
[typeof(RadioButton)] = () => (IElementHandler)(object)new RadioButtonHandler(),
[typeof(WebView)] = () => (IElementHandler)(object)new GtkWebViewHandler(),
[typeof(Image)] = () => (IElementHandler)(object)new ImageHandler(),
[typeof(ImageButton)] = () => (IElementHandler)(object)new ImageButtonHandler(),
[typeof(BoxView)] = () => (IElementHandler)(object)new BoxViewHandler(),
[typeof(Frame)] = () => (IElementHandler)(object)new FrameHandler(),
[typeof(Border)] = () => (IElementHandler)(object)new BorderHandler(),
[typeof(ContentView)] = () => (IElementHandler)(object)new BorderHandler(),
[typeof(ScrollView)] = () => (IElementHandler)(object)new ScrollViewHandler(),
[typeof(Grid)] = () => (IElementHandler)(object)new GridHandler(),
[typeof(StackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
[typeof(VerticalStackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
[typeof(HorizontalStackLayout)] = () => (IElementHandler)(object)new StackLayoutHandler(),
[typeof(AbsoluteLayout)] = () => (IElementHandler)(object)new LayoutHandler(),
[typeof(FlexLayout)] = () => (IElementHandler)(object)new LayoutHandler(),
[typeof(CollectionView)] = () => (IElementHandler)(object)new CollectionViewHandler(),
[typeof(ListView)] = () => (IElementHandler)(object)new CollectionViewHandler(),
[typeof(Page)] = () => (IElementHandler)(object)new PageHandler(),
[typeof(ContentPage)] = () => (IElementHandler)(object)new ContentPageHandler(),
[typeof(NavigationPage)] = () => (IElementHandler)(object)new NavigationPageHandler(),
[typeof(Shell)] = () => (IElementHandler)(object)new ShellHandler(),
[typeof(FlyoutPage)] = () => (IElementHandler)(object)new FlyoutPageHandler(),
[typeof(TabbedPage)] = () => (IElementHandler)(object)new TabbedPageHandler(),
[typeof(Application)] = () => (IElementHandler)(object)new ApplicationHandler(),
[typeof(Window)] = () => (IElementHandler)(object)new WindowHandler(),
[typeof(GraphicsView)] = () => (IElementHandler)(object)new GraphicsViewHandler()
};
public static IElementHandler ToHandler(this IElement element, IMauiContext mauiContext)
{
return CreateHandler(element, mauiContext);
}
public static IViewHandler? ToViewHandler(this IView view, IMauiContext mauiContext)
{
IElementHandler? obj = CreateHandler((IElement)(object)view, mauiContext);
return (IViewHandler?)(object)((obj is IViewHandler) ? obj : null);
}
private static IElementHandler? CreateHandler(IElement element, IMauiContext mauiContext)
{
Type type = ((object)element).GetType();
IElementHandler val = null;
if (LinuxHandlerMap.TryGetValue(type, out Func<IElementHandler> value))
{
val = value();
Console.WriteLine("[ToHandler] Using Linux handler for " + type.Name + ": " + ((object)val).GetType().Name);
}
else
{
Type type2 = null;
Func<IElementHandler> func = null;
foreach (KeyValuePair<Type, Func<IElementHandler>> item in LinuxHandlerMap)
{
if (item.Key.IsAssignableFrom(type) && (type2 == null || type2.IsAssignableFrom(item.Key)))
{
type2 = item.Key;
func = item.Value;
}
}
if (func != null)
{
val = func();
Console.WriteLine($"[ToHandler] Using Linux handler (via base {type2.Name}) for {type.Name}: {((object)val).GetType().Name}");
}
}
if (val == null)
{
val = mauiContext.Handlers.GetHandler(type);
Console.WriteLine("[ToHandler] Using MAUI handler for " + type.Name + ": " + (((object)val)?.GetType().Name ?? "null"));
}
if (val != null)
{
val.SetMauiContext(mauiContext);
val.SetVirtualView(element);
}
return val;
}
}

View File

@@ -0,0 +1,17 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Hosting;
public class ScopedLinuxMauiContext : IMauiContext
{
private readonly LinuxMauiContext _parent;
public IServiceProvider Services => _parent.Services;
public IMauiHandlersFactory Handlers => _parent.Handlers;
public ScopedLinuxMauiContext(LinuxMauiContext parent)
{
_parent = parent ?? throw new ArgumentNullException("parent");
}
}

View File

@@ -0,0 +1,22 @@
using System.Runtime.InteropServices;
namespace Microsoft.Maui.Platform.Linux.Interop;
[StructLayout(LayoutKind.Explicit)]
public struct ClientMessageData
{
[FieldOffset(0)]
public long L0;
[FieldOffset(8)]
public long L1;
[FieldOffset(16)]
public long L2;
[FieldOffset(24)]
public long L3;
[FieldOffset(32)]
public long L4;
}

View File

@@ -1,268 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Microsoft.Maui.Platform.Linux.Interop; namespace Microsoft.Maui.Platform.Linux.Interop;
/// <summary>
/// P/Invoke bindings for WebKitGTK library.
/// WebKitGTK provides a full-featured web browser engine for Linux.
/// </summary>
public static class WebKitGtk public static class WebKitGtk
{ {
private const string WebKit2Lib = "libwebkit2gtk-4.1.so.0";
private const string GtkLib = "libgtk-3.so.0";
private const string GObjectLib = "libgobject-2.0.so.0";
private const string GLibLib = "libglib-2.0.so.0";
#region GTK Initialization
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_init_check(ref int argc, ref IntPtr argv);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main();
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main_quit();
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_events_pending();
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main_iteration();
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_main_iteration_do(bool blocking);
#endregion
#region GTK Window
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_window_new(int type);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_set_default_size(IntPtr window, int width, int height);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_set_decorated(IntPtr window, bool decorated);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_move(IntPtr window, int x, int y);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_resize(IntPtr window, int width, int height);
#endregion
#region GTK Widget
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_show_all(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_show(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_hide(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_destroy(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_set_size_request(IntPtr widget, int width, int height);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_realize(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_widget_get_window(IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_set_can_focus(IntPtr widget, bool canFocus);
#endregion
#region GTK Container
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_container_add(IntPtr container, IntPtr widget);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_container_remove(IntPtr container, IntPtr widget);
#endregion
#region GTK Plug (for embedding in X11 windows)
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_plug_new(ulong socketId);
[DllImport(GtkLib, CallingConvention = CallingConvention.Cdecl)]
public static extern ulong gtk_plug_get_id(IntPtr plug);
#endregion
#region WebKitWebView
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_new();
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_new_with_context(IntPtr context);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_load_uri(IntPtr webView, [MarshalAs(UnmanagedType.LPUTF8Str)] string uri);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_load_html(IntPtr webView,
[MarshalAs(UnmanagedType.LPUTF8Str)] string content,
[MarshalAs(UnmanagedType.LPUTF8Str)] string? baseUri);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_reload(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_stop_loading(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_go_back(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_go_forward(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_can_go_back(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_can_go_forward(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_uri(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_title(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern double webkit_web_view_get_estimated_load_progress(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_is_loading(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_run_javascript(IntPtr webView,
[MarshalAs(UnmanagedType.LPUTF8Str)] string script,
IntPtr cancellable,
IntPtr callback,
IntPtr userData);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_run_javascript_finish(IntPtr webView,
IntPtr result,
out IntPtr error);
#endregion
#region WebKitSettings
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_settings(IntPtr webView);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_javascript(IntPtr settings, bool enabled);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_user_agent(IntPtr settings,
[MarshalAs(UnmanagedType.LPUTF8Str)] string userAgent);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_settings_get_user_agent(IntPtr settings);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_developer_extras(IntPtr settings, bool enabled);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_javascript_can_access_clipboard(IntPtr settings, bool enabled);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_webgl(IntPtr settings, bool enabled);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_allow_file_access_from_file_urls(IntPtr settings, bool enabled);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_allow_universal_access_from_file_urls(IntPtr settings, bool enabled);
#endregion
#region WebKitWebContext
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_get_default();
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_new();
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_get_cookie_manager(IntPtr context);
#endregion
#region WebKitCookieManager
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_cookie_manager_set_accept_policy(IntPtr cookieManager, int policy);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_cookie_manager_set_persistent_storage(IntPtr cookieManager,
[MarshalAs(UnmanagedType.LPUTF8Str)] string filename,
int storage);
// Cookie accept policies
public const int WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS = 0;
public const int WEBKIT_COOKIE_POLICY_ACCEPT_NEVER = 1;
public const int WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY = 2;
// Cookie persistent storage types
public const int WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT = 0;
public const int WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE = 1;
#endregion
#region WebKitNavigationAction
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_navigation_action_get_request(IntPtr action);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern int webkit_navigation_action_get_navigation_type(IntPtr action);
#endregion
#region WebKitURIRequest
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_uri_request_get_uri(IntPtr request);
#endregion
#region WebKitPolicyDecision
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_use(IntPtr decision);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_ignore(IntPtr decision);
[DllImport(WebKit2Lib, CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_download(IntPtr decision);
#endregion
#region GObject Signal Connection
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void GCallback(); public delegate void GCallback();
@@ -278,68 +20,241 @@ public static class WebKitGtk
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void NotifyCallback(IntPtr webView, IntPtr paramSpec, IntPtr userData); public delegate void NotifyCallback(IntPtr webView, IntPtr paramSpec, IntPtr userData);
[DllImport(GObjectLib, CallingConvention = CallingConvention.Cdecl)] private const string WebKit2Lib = "libwebkit2gtk-4.1.so.0";
public static extern ulong g_signal_connect_data(IntPtr instance,
[MarshalAs(UnmanagedType.LPUTF8Str)] string detailedSignal,
Delegate handler,
IntPtr data,
IntPtr destroyData,
int connectFlags);
[DllImport(GObjectLib, CallingConvention = CallingConvention.Cdecl)] private const string GtkLib = "libgtk-3.so.0";
public static extern void g_signal_handler_disconnect(IntPtr instance, ulong handlerId);
[DllImport(GObjectLib, CallingConvention = CallingConvention.Cdecl)] private const string GObjectLib = "libgobject-2.0.so.0";
public static extern void g_object_unref(IntPtr obj);
#endregion private const string GLibLib = "libglib-2.0.so.0";
#region GLib Memory public const int WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS = 0;
[DllImport(GLibLib, CallingConvention = CallingConvention.Cdecl)] public const int WEBKIT_COOKIE_POLICY_ACCEPT_NEVER = 1;
public static extern void g_free(IntPtr mem);
#endregion public const int WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY = 2;
#region WebKit Load Events public const int WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT = 0;
public const int WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE = 1;
public const int WEBKIT_LOAD_STARTED = 0; public const int WEBKIT_LOAD_STARTED = 0;
public const int WEBKIT_LOAD_REDIRECTED = 1; public const int WEBKIT_LOAD_REDIRECTED = 1;
public const int WEBKIT_LOAD_COMMITTED = 2; public const int WEBKIT_LOAD_COMMITTED = 2;
public const int WEBKIT_LOAD_FINISHED = 3; public const int WEBKIT_LOAD_FINISHED = 3;
#endregion
#region WebKit Policy Decision Types
public const int WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION = 0; public const int WEBKIT_POLICY_DECISION_TYPE_NAVIGATION_ACTION = 0;
public const int WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION = 1; public const int WEBKIT_POLICY_DECISION_TYPE_NEW_WINDOW_ACTION = 1;
public const int WEBKIT_POLICY_DECISION_TYPE_RESPONSE = 2; public const int WEBKIT_POLICY_DECISION_TYPE_RESPONSE = 2;
#endregion [DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_init_check(ref int argc, ref IntPtr argv);
#region Helper Methods [DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main();
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main_quit();
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_events_pending();
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_main_iteration();
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool gtk_main_iteration_do(bool blocking);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_window_new(int type);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_set_default_size(IntPtr window, int width, int height);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_set_decorated(IntPtr window, bool decorated);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_move(IntPtr window, int x, int y);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_window_resize(IntPtr window, int width, int height);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_show_all(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_show(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_hide(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_destroy(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_set_size_request(IntPtr widget, int width, int height);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_realize(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_widget_get_window(IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_widget_set_can_focus(IntPtr widget, bool canFocus);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_container_add(IntPtr container, IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void gtk_container_remove(IntPtr container, IntPtr widget);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr gtk_plug_new(ulong socketId);
[DllImport("libgtk-3.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong gtk_plug_get_id(IntPtr plug);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_new();
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_new_with_context(IntPtr context);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_load_uri(IntPtr webView, [MarshalAs(UnmanagedType.LPUTF8Str)] string uri);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_load_html(IntPtr webView, [MarshalAs(UnmanagedType.LPUTF8Str)] string content, [MarshalAs(UnmanagedType.LPUTF8Str)] string? baseUri);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_reload(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_stop_loading(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_go_back(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_go_forward(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_can_go_back(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_can_go_forward(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_uri(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_title(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern double webkit_web_view_get_estimated_load_progress(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern bool webkit_web_view_is_loading(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_web_view_run_javascript(IntPtr webView, [MarshalAs(UnmanagedType.LPUTF8Str)] string script, IntPtr cancellable, IntPtr callback, IntPtr userData);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_run_javascript_finish(IntPtr webView, IntPtr result, out IntPtr error);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_view_get_settings(IntPtr webView);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_javascript(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_user_agent(IntPtr settings, [MarshalAs(UnmanagedType.LPUTF8Str)] string userAgent);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_settings_get_user_agent(IntPtr settings);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_developer_extras(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_javascript_can_access_clipboard(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_enable_webgl(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_allow_file_access_from_file_urls(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_settings_set_allow_universal_access_from_file_urls(IntPtr settings, bool enabled);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_get_default();
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_new();
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_web_context_get_cookie_manager(IntPtr context);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_cookie_manager_set_accept_policy(IntPtr cookieManager, int policy);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_cookie_manager_set_persistent_storage(IntPtr cookieManager, [MarshalAs(UnmanagedType.LPUTF8Str)] string filename, int storage);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_navigation_action_get_request(IntPtr action);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern int webkit_navigation_action_get_navigation_type(IntPtr action);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr webkit_uri_request_get_uri(IntPtr request);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_use(IntPtr decision);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_ignore(IntPtr decision);
[DllImport("libwebkit2gtk-4.1.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void webkit_policy_decision_download(IntPtr decision);
[DllImport("libgobject-2.0.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong g_signal_connect_data(IntPtr instance, [MarshalAs(UnmanagedType.LPUTF8Str)] string detailedSignal, Delegate handler, IntPtr data, IntPtr destroyData, int connectFlags);
[DllImport("libgobject-2.0.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void g_signal_handler_disconnect(IntPtr instance, ulong handlerId);
[DllImport("libgobject-2.0.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void g_object_unref(IntPtr obj);
[DllImport("libglib-2.0.so.0", CallingConvention = CallingConvention.Cdecl)]
public static extern void g_free(IntPtr mem);
/// <summary>
/// Converts a native UTF-8 string pointer to a managed string.
/// </summary>
public static string? PtrToStringUtf8(IntPtr ptr) public static string? PtrToStringUtf8(IntPtr ptr)
{ {
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero)
{
return null; return null;
}
return Marshal.PtrToStringUTF8(ptr); return Marshal.PtrToStringUTF8(ptr);
} }
/// <summary>
/// Processes pending GTK events without blocking.
/// </summary>
public static void ProcessGtkEvents() public static void ProcessGtkEvents()
{ {
while (gtk_events_pending()) while (gtk_events_pending())
{ {
gtk_main_iteration_do(false); gtk_main_iteration_do(blocking: false);
} }
} }
#endregion
} }

442
Interop/X11.cs Normal file
View File

@@ -0,0 +1,442 @@
using System;
using System.CodeDom.Compiler;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
namespace Microsoft.Maui.Platform.Linux.Interop;
internal static class X11
{
private const string LibX11 = "libX11.so.6";
private const string LibXext = "libXext.so.6";
public const int ZPixmap = 2;
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XOpenDisplay(IntPtr displayName);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XCloseDisplay(IntPtr display);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDefaultScreen(IntPtr display);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XRootWindow(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDisplayWidth(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDisplayHeight(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDefaultDepth(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XDefaultVisual(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XDefaultColormap(IntPtr display, int screenNumber);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XFlush(IntPtr display);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public static int XSync(IntPtr display, [MarshalAs(UnmanagedType.Bool)] bool discard)
{
int _discard_native = (discard ? 1 : 0);
return __PInvoke(display, _discard_native);
[DllImport("libX11.so.6", EntryPoint = "XSync", ExactSpelling = true)]
static extern int __PInvoke(IntPtr __display_native, int __discard_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, uint width, uint height, uint borderWidth, ulong border, ulong background);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, uint width, uint height, uint borderWidth, int depth, uint windowClass, IntPtr visual, ulong valueMask, ref XSetWindowAttributes attributes)
{
IntPtr result;
fixed (XSetWindowAttributes* _attributes_native = &attributes)
{
result = __PInvoke(display, parent, x, y, width, height, borderWidth, depth, windowClass, visual, valueMask, _attributes_native);
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XCreateWindow", ExactSpelling = true)]
static extern unsafe IntPtr __PInvoke(IntPtr __display_native, IntPtr __parent_native, int __x_native, int __y_native, uint __width_native, uint __height_native, uint __borderWidth_native, int __depth_native, uint __windowClass_native, IntPtr __visual_native, ulong __valueMask_native, XSetWindowAttributes* __attributes_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDestroyWindow(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XMapWindow(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XUnmapWindow(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XMoveWindow(IntPtr display, IntPtr window, int x, int y);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XResizeWindow(IntPtr display, IntPtr window, uint width, uint height);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, uint width, uint height);
[LibraryImport("libX11.so.6", StringMarshalling = StringMarshalling.Utf8)]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XStoreName(IntPtr display, IntPtr window, string windowName)
{
byte* ptr = default(byte*);
int num = 0;
Utf8StringMarshaller.ManagedToUnmanagedIn managedToUnmanagedIn = default(Utf8StringMarshaller.ManagedToUnmanagedIn);
try
{
Span<byte> buffer = stackalloc byte[Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize];
managedToUnmanagedIn.FromManaged(windowName, buffer);
ptr = managedToUnmanagedIn.ToUnmanaged();
return __PInvoke(display, window, ptr);
}
finally
{
managedToUnmanagedIn.Free();
}
[DllImport("libX11.so.6", EntryPoint = "XStoreName", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, IntPtr __window_native, byte* __windowName_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XRaiseWindow(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XLowerWindow(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XSelectInput(IntPtr display, IntPtr window, long eventMask);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XNextEvent(IntPtr display, out XEvent eventReturn)
{
eventReturn = default(XEvent);
int result;
fixed (XEvent* _eventReturn_native = &eventReturn)
{
result = __PInvoke(display, _eventReturn_native);
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XNextEvent", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, XEvent* __eventReturn_native);
}
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XPeekEvent(IntPtr display, out XEvent eventReturn)
{
eventReturn = default(XEvent);
int result;
fixed (XEvent* _eventReturn_native = &eventReturn)
{
result = __PInvoke(display, _eventReturn_native);
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XPeekEvent", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, XEvent* __eventReturn_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XPending(IntPtr display);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
[return: MarshalAs(UnmanagedType.Bool)]
public unsafe static bool XCheckTypedWindowEvent(IntPtr display, IntPtr window, int eventType, out XEvent eventReturn)
{
eventReturn = default(XEvent);
int num;
fixed (XEvent* _eventReturn_native = &eventReturn)
{
num = __PInvoke(display, window, eventType, _eventReturn_native);
}
return num != 0;
[DllImport("libX11.so.6", EntryPoint = "XCheckTypedWindowEvent", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, IntPtr __window_native, int __eventType_native, XEvent* __eventReturn_native);
}
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XSendEvent(IntPtr display, IntPtr window, [MarshalAs(UnmanagedType.Bool)] bool propagate, long eventMask, ref XEvent eventSend)
{
int _propagate_native = (propagate ? 1 : 0);
int result;
fixed (XEvent* _eventSend_native = &eventSend)
{
result = __PInvoke(display, window, _propagate_native, eventMask, _eventSend_native);
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XSendEvent", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, IntPtr __window_native, int __propagate_native, long __eventMask_native, XEvent* __eventSend_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern ulong XKeycodeToKeysym(IntPtr display, int keycode, int index);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XLookupString(ref XKeyEvent keyEvent, IntPtr bufferReturn, int bytesBuffer, out ulong keysymReturn, IntPtr statusInOut)
{
keysymReturn = 0uL;
int result;
fixed (ulong* _keysymReturn_native = &keysymReturn)
{
fixed (XKeyEvent* _keyEvent_native = &keyEvent)
{
result = __PInvoke(_keyEvent_native, bufferReturn, bytesBuffer, _keysymReturn_native, statusInOut);
}
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XLookupString", ExactSpelling = true)]
static extern unsafe int __PInvoke(XKeyEvent* __keyEvent_native, IntPtr __bufferReturn_native, int __bytesBuffer_native, ulong* __keysymReturn_native, IntPtr __statusInOut_native);
}
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public static int XGrabKeyboard(IntPtr display, IntPtr grabWindow, [MarshalAs(UnmanagedType.Bool)] bool ownerEvents, int pointerMode, int keyboardMode, ulong time)
{
int _ownerEvents_native = (ownerEvents ? 1 : 0);
return __PInvoke(display, grabWindow, _ownerEvents_native, pointerMode, keyboardMode, time);
[DllImport("libX11.so.6", EntryPoint = "XGrabKeyboard", ExactSpelling = true)]
static extern int __PInvoke(IntPtr __display_native, IntPtr __grabWindow_native, int __ownerEvents_native, int __pointerMode_native, int __keyboardMode_native, ulong __time_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XUngrabKeyboard(IntPtr display, ulong time);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public static int XGrabPointer(IntPtr display, IntPtr grabWindow, [MarshalAs(UnmanagedType.Bool)] bool ownerEvents, uint eventMask, int pointerMode, int keyboardMode, IntPtr confineTo, IntPtr cursor, ulong time)
{
int _ownerEvents_native = (ownerEvents ? 1 : 0);
return __PInvoke(display, grabWindow, _ownerEvents_native, eventMask, pointerMode, keyboardMode, confineTo, cursor, time);
[DllImport("libX11.so.6", EntryPoint = "XGrabPointer", ExactSpelling = true)]
static extern int __PInvoke(IntPtr __display_native, IntPtr __grabWindow_native, int __ownerEvents_native, uint __eventMask_native, int __pointerMode_native, int __keyboardMode_native, IntPtr __confineTo_native, IntPtr __cursor_native, ulong __time_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XUngrabPointer(IntPtr display, ulong time);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
[return: MarshalAs(UnmanagedType.Bool)]
public unsafe static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr rootReturn, out IntPtr childReturn, out int rootX, out int rootY, out int winX, out int winY, out uint maskReturn)
{
rootReturn = (IntPtr)0;
childReturn = (IntPtr)0;
rootX = 0;
rootY = 0;
winX = 0;
winY = 0;
maskReturn = 0u;
int num;
fixed (uint* _maskReturn_native = &maskReturn)
{
fixed (int* _winY_native = &winY)
{
fixed (int* _winX_native = &winX)
{
fixed (int* _rootY_native = &rootY)
{
fixed (int* _rootX_native = &rootX)
{
fixed (IntPtr* _childReturn_native = &childReturn)
{
fixed (IntPtr* _rootReturn_native = &rootReturn)
{
num = __PInvoke(display, window, _rootReturn_native, _childReturn_native, _rootX_native, _rootY_native, _winX_native, _winY_native, _maskReturn_native);
}
}
}
}
}
}
}
return num != 0;
[DllImport("libX11.so.6", EntryPoint = "XQueryPointer", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, IntPtr __window_native, IntPtr* __rootReturn_native, IntPtr* __childReturn_native, int* __rootX_native, int* __rootY_native, int* __winX_native, int* __winY_native, uint* __maskReturn_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XWarpPointer(IntPtr display, IntPtr srcWindow, IntPtr destWindow, int srcX, int srcY, uint srcWidth, uint srcHeight, int destX, int destY);
[LibraryImport("libX11.so.6", StringMarshalling = StringMarshalling.Utf8)]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static IntPtr XInternAtom(IntPtr display, string atomName, [MarshalAs(UnmanagedType.Bool)] bool onlyIfExists)
{
byte* ptr = default(byte*);
int num = 0;
nint num2 = 0;
Utf8StringMarshaller.ManagedToUnmanagedIn managedToUnmanagedIn = default(Utf8StringMarshaller.ManagedToUnmanagedIn);
try
{
num = (onlyIfExists ? 1 : 0);
Span<byte> buffer = stackalloc byte[Utf8StringMarshaller.ManagedToUnmanagedIn.BufferSize];
managedToUnmanagedIn.FromManaged(atomName, buffer);
ptr = managedToUnmanagedIn.ToUnmanaged();
return __PInvoke(display, ptr, num);
}
finally
{
managedToUnmanagedIn.Free();
}
[DllImport("libX11.so.6", EntryPoint = "XInternAtom", ExactSpelling = true)]
static extern unsafe IntPtr __PInvoke(IntPtr __display_native, byte* __atomName_native, int __onlyIfExists_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, int mode, IntPtr data, int nelements);
[LibraryImport("libX11.so.6")]
[GeneratedCode("Microsoft.Interop.LibraryImportGenerator", "9.0.11.2809")]
[SkipLocalsInit]
public unsafe static int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr property, long longOffset, long longLength, [MarshalAs(UnmanagedType.Bool)] bool delete, IntPtr reqType, out IntPtr actualTypeReturn, out int actualFormatReturn, out IntPtr nitemsReturn, out IntPtr bytesAfterReturn, out IntPtr propReturn)
{
actualTypeReturn = (IntPtr)0;
actualFormatReturn = 0;
nitemsReturn = (IntPtr)0;
bytesAfterReturn = (IntPtr)0;
propReturn = (IntPtr)0;
int _delete_native = (delete ? 1 : 0);
int result;
fixed (IntPtr* _propReturn_native = &propReturn)
{
fixed (IntPtr* _bytesAfterReturn_native = &bytesAfterReturn)
{
fixed (IntPtr* _nitemsReturn_native = &nitemsReturn)
{
fixed (int* _actualFormatReturn_native = &actualFormatReturn)
{
fixed (IntPtr* _actualTypeReturn_native = &actualTypeReturn)
{
result = __PInvoke(display, window, property, longOffset, longLength, _delete_native, reqType, _actualTypeReturn_native, _actualFormatReturn_native, _nitemsReturn_native, _bytesAfterReturn_native, _propReturn_native);
}
}
}
}
}
return result;
[DllImport("libX11.so.6", EntryPoint = "XGetWindowProperty", ExactSpelling = true)]
static extern unsafe int __PInvoke(IntPtr __display_native, IntPtr __window_native, IntPtr __property_native, long __longOffset_native, long __longLength_native, int __delete_native, IntPtr __reqType_native, IntPtr* __actualTypeReturn_native, int* __actualFormatReturn_native, IntPtr* __nitemsReturn_native, IntPtr* __bytesAfterReturn_native, IntPtr* __propReturn_native);
}
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, ulong time);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, IntPtr requestor, ulong time);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XFree(IntPtr data);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XCreateGC(IntPtr display, IntPtr drawable, ulong valueMask, IntPtr values);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XFreeGC(IntPtr display, IntPtr gc);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int srcX, int srcY, uint width, uint height, int destX, int destY);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XCreateFontCursor(IntPtr display, uint shape);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XFreeCursor(IntPtr display, IntPtr cursor);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XUndefineCursor(IntPtr display, IntPtr window);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XConnectionNumber(IntPtr display);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XCreateImage(IntPtr display, IntPtr visual, uint depth, int format, int offset, IntPtr data, uint width, uint height, int bitmapPad, int bytesPerLine);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XPutImage(IntPtr display, IntPtr drawable, IntPtr gc, IntPtr image, int srcX, int srcY, int destX, int destY, uint width, uint height);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern int XDestroyImage(IntPtr image);
[DllImport("libX11.so.6", ExactSpelling = true)]
[LibraryImport("libX11.so.6")]
public static extern IntPtr XDefaultGC(IntPtr display, int screen);
}

36
Interop/XButtonEvent.cs Normal file
View File

@@ -0,0 +1,36 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XButtonEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public IntPtr Root;
public IntPtr Subwindow;
public ulong Time;
public int X;
public int Y;
public int XRoot;
public int YRoot;
public uint State;
public uint Button;
public int SameScreen;
}

View File

@@ -0,0 +1,22 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XClientMessageEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public IntPtr MessageType;
public int Format;
public ClientMessageData Data;
}

View File

@@ -0,0 +1,32 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XConfigureEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Event;
public IntPtr Window;
public int X;
public int Y;
public int Width;
public int Height;
public int BorderWidth;
public IntPtr Above;
public int OverrideRedirect;
}

40
Interop/XCrossingEvent.cs Normal file
View File

@@ -0,0 +1,40 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XCrossingEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public IntPtr Root;
public IntPtr Subwindow;
public ulong Time;
public int X;
public int Y;
public int XRoot;
public int YRoot;
public int Mode;
public int Detail;
public int SameScreen;
public int Focus;
public uint State;
}

14
Interop/XCursorShape.cs Normal file
View File

@@ -0,0 +1,14 @@
namespace Microsoft.Maui.Platform.Linux.Interop;
public static class XCursorShape
{
public const uint XC_left_ptr = 68u;
public const uint XC_hand2 = 60u;
public const uint XC_xterm = 152u;
public const uint XC_watch = 150u;
public const uint XC_crosshair = 34u;
}

34
Interop/XEvent.cs Normal file
View File

@@ -0,0 +1,34 @@
using System.Runtime.InteropServices;
namespace Microsoft.Maui.Platform.Linux.Interop;
[StructLayout(LayoutKind.Explicit, Size = 192)]
public struct XEvent
{
[FieldOffset(0)]
public int Type;
[FieldOffset(0)]
public XKeyEvent KeyEvent;
[FieldOffset(0)]
public XButtonEvent ButtonEvent;
[FieldOffset(0)]
public XMotionEvent MotionEvent;
[FieldOffset(0)]
public XConfigureEvent ConfigureEvent;
[FieldOffset(0)]
public XExposeEvent ExposeEvent;
[FieldOffset(0)]
public XClientMessageEvent ClientMessageEvent;
[FieldOffset(0)]
public XCrossingEvent CrossingEvent;
[FieldOffset(0)]
public XFocusChangeEvent FocusChangeEvent;
}

24
Interop/XEventMask.cs Normal file
View File

@@ -0,0 +1,24 @@
namespace Microsoft.Maui.Platform.Linux.Interop;
public static class XEventMask
{
public const long KeyPressMask = 1L;
public const long KeyReleaseMask = 2L;
public const long ButtonPressMask = 4L;
public const long ButtonReleaseMask = 8L;
public const long EnterWindowMask = 16L;
public const long LeaveWindowMask = 32L;
public const long PointerMotionMask = 64L;
public const long ExposureMask = 32768L;
public const long StructureNotifyMask = 131072L;
public const long FocusChangeMask = 2097152L;
}

28
Interop/XEventType.cs Normal file
View File

@@ -0,0 +1,28 @@
namespace Microsoft.Maui.Platform.Linux.Interop;
public static class XEventType
{
public const int KeyPress = 2;
public const int KeyRelease = 3;
public const int ButtonPress = 4;
public const int ButtonRelease = 5;
public const int MotionNotify = 6;
public const int EnterNotify = 7;
public const int LeaveNotify = 8;
public const int FocusIn = 9;
public const int FocusOut = 10;
public const int Expose = 12;
public const int ConfigureNotify = 22;
public const int ClientMessage = 33;
}

26
Interop/XExposeEvent.cs Normal file
View File

@@ -0,0 +1,26 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XExposeEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public int X;
public int Y;
public int Width;
public int Height;
public int Count;
}

View File

@@ -0,0 +1,20 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XFocusChangeEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public int Mode;
public int Detail;
}

36
Interop/XKeyEvent.cs Normal file
View File

@@ -0,0 +1,36 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XKeyEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public IntPtr Root;
public IntPtr Subwindow;
public ulong Time;
public int X;
public int Y;
public int XRoot;
public int YRoot;
public uint State;
public uint Keycode;
public int SameScreen;
}

36
Interop/XMotionEvent.cs Normal file
View File

@@ -0,0 +1,36 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XMotionEvent
{
public int Type;
public ulong Serial;
public int SendEvent;
public IntPtr Display;
public IntPtr Window;
public IntPtr Root;
public IntPtr Subwindow;
public ulong Time;
public int X;
public int Y;
public int XRoot;
public int YRoot;
public uint State;
public byte IsHint;
public int SameScreen;
}

View File

@@ -0,0 +1,36 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Interop;
public struct XSetWindowAttributes
{
public IntPtr BackgroundPixmap;
public ulong BackgroundPixel;
public IntPtr BorderPixmap;
public ulong BorderPixel;
public int BitGravity;
public int WinGravity;
public int BackingStore;
public ulong BackingPlanes;
public ulong BackingPixel;
public int SaveUnder;
public long EventMask;
public long DoNotPropagateMask;
public int OverrideRedirect;
public IntPtr Colormap;
public IntPtr Cursor;
}

8
Interop/XWindowClass.cs Normal file
View File

@@ -0,0 +1,8 @@
namespace Microsoft.Maui.Platform.Linux.Interop;
public static class XWindowClass
{
public const uint InputOutput = 1u;
public const uint InputOnly = 2u;
}

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,20 @@
namespace Microsoft.Maui.Platform.Linux;
public class LinuxApplicationOptions
{
public string? Title { get; set; } = "MAUI Application";
public int Width { get; set; } = 800;
public int Height { get; set; } = 600;
public bool UseHardwareAcceleration { get; set; } = true;
public DisplayServerType DisplayServer { get; set; }
public bool ForceDemo { get; set; }
public string? IconPath { get; set; }
public bool UseGtk { get; set; }
}

View File

@@ -1,40 +1,36 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.Linq;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
/// <summary>
/// Manages dirty rectangles for optimized rendering.
/// Only redraws areas that have been invalidated.
/// </summary>
public class DirtyRectManager public class DirtyRectManager
{ {
private readonly List<SKRect> _dirtyRects = new(); private readonly List<SKRect> _dirtyRects = new List<SKRect>();
private readonly object _lock = new();
private readonly object _lock = new object();
private bool _fullRedrawNeeded = true; private bool _fullRedrawNeeded = true;
private SKRect _bounds; private SKRect _bounds;
private int _maxDirtyRects = 10; private int _maxDirtyRects = 10;
/// <summary>
/// Gets or sets the maximum number of dirty rectangles to track before
/// falling back to a full redraw.
/// </summary>
public int MaxDirtyRects public int MaxDirtyRects
{ {
get => _maxDirtyRects; get
set => _maxDirtyRects = Math.Max(1, value); {
return _maxDirtyRects;
}
set
{
_maxDirtyRects = Math.Max(1, value);
}
} }
/// <summary>
/// Gets whether a full redraw is needed.
/// </summary>
public bool NeedsFullRedraw => _fullRedrawNeeded; public bool NeedsFullRedraw => _fullRedrawNeeded;
/// <summary>
/// Gets the current dirty rectangles.
/// </summary>
public IReadOnlyList<SKRect> DirtyRects public IReadOnlyList<SKRect> DirtyRects
{ {
get get
@@ -46,9 +42,6 @@ public class DirtyRectManager
} }
} }
/// <summary>
/// Gets whether there are any dirty regions.
/// </summary>
public bool HasDirtyRegions public bool HasDirtyRegions
{ {
get get
@@ -60,11 +53,12 @@ public class DirtyRectManager
} }
} }
/// <summary>
/// Sets the rendering bounds.
/// </summary>
public void SetBounds(SKRect bounds) public void SetBounds(SKRect bounds)
{ {
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
if (_bounds != bounds) if (_bounds != bounds)
{ {
_bounds = bounds; _bounds = bounds;
@@ -72,73 +66,82 @@ public class DirtyRectManager
} }
} }
/// <summary>
/// Invalidates a specific region.
/// </summary>
public void Invalidate(SKRect rect) public void Invalidate(SKRect rect)
{ {
if (rect.IsEmpty) return; //IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
lock (_lock) //IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00df: Unknown result type (might be due to invalid IL or missing references)
//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
//IL_011c: Unknown result type (might be due to invalid IL or missing references)
//IL_0121: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
if (((SKRect)(ref rect)).IsEmpty)
{ {
if (_fullRedrawNeeded) return;
// Clamp to bounds
rect = SKRect.Intersect(rect, _bounds);
if (rect.IsEmpty) return;
// Try to merge with existing dirty rects
for (int i = 0; i < _dirtyRects.Count; i++)
{
if (_dirtyRects[i].Contains(rect))
{
// Already covered
return; return;
} }
lock (_lock)
if (rect.Contains(_dirtyRects[i])) {
if (_fullRedrawNeeded)
{
return;
}
rect = SKRect.Intersect(rect, _bounds);
if (((SKRect)(ref rect)).IsEmpty)
{
return;
}
for (int i = 0; i < _dirtyRects.Count; i++)
{
SKRect val = _dirtyRects[i];
if (((SKRect)(ref val)).Contains(rect))
{
return;
}
if (((SKRect)(ref rect)).Contains(_dirtyRects[i]))
{ {
// New rect covers existing
_dirtyRects[i] = rect; _dirtyRects[i] = rect;
MergeDirtyRects(); MergeDirtyRects();
return; return;
} }
SKRect val2 = SKRect.Intersect(_dirtyRects[i], rect);
// Check if they overlap significantly (50% overlap) if (!((SKRect)(ref val2)).IsEmpty)
var intersection = SKRect.Intersect(_dirtyRects[i], rect);
if (!intersection.IsEmpty)
{ {
float intersectArea = intersection.Width * intersection.Height; float num = ((SKRect)(ref val2)).Width * ((SKRect)(ref val2)).Height;
float smallerArea = Math.Min( val = _dirtyRects[i];
_dirtyRects[i].Width * _dirtyRects[i].Height, float width = ((SKRect)(ref val)).Width;
rect.Width * rect.Height); val = _dirtyRects[i];
float num2 = Math.Min(width * ((SKRect)(ref val)).Height, ((SKRect)(ref rect)).Width * ((SKRect)(ref rect)).Height);
if (intersectArea > smallerArea * 0.5f) if (num > num2 * 0.5f)
{ {
// Merge the rectangles
_dirtyRects[i] = SKRect.Union(_dirtyRects[i], rect); _dirtyRects[i] = SKRect.Union(_dirtyRects[i], rect);
MergeDirtyRects(); MergeDirtyRects();
return; return;
} }
} }
} }
// Add as new dirty rect
_dirtyRects.Add(rect); _dirtyRects.Add(rect);
// Check if we have too many dirty rects
if (_dirtyRects.Count > _maxDirtyRects) if (_dirtyRects.Count > _maxDirtyRects)
{ {
// Fall back to full redraw
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
_dirtyRects.Clear(); _dirtyRects.Clear();
} }
} }
} }
/// <summary>
/// Invalidates the entire rendering area.
/// </summary>
public void InvalidateAll() public void InvalidateAll()
{ {
lock (_lock) lock (_lock)
@@ -148,9 +151,6 @@ public class DirtyRectManager
} }
} }
/// <summary>
/// Clears all dirty regions after rendering.
/// </summary>
public void Clear() public void Clear()
{ {
lock (_lock) lock (_lock)
@@ -160,73 +160,95 @@ public class DirtyRectManager
} }
} }
/// <summary>
/// Gets the combined dirty region as a single rectangle.
/// </summary>
public SKRect GetCombinedDirtyRect() public SKRect GetCombinedDirtyRect()
{ {
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0054: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
lock (_lock) lock (_lock)
{ {
if (_fullRedrawNeeded || _dirtyRects.Count == 0) if (_fullRedrawNeeded || _dirtyRects.Count == 0)
{ {
return _bounds; return _bounds;
} }
SKRect val = _dirtyRects[0];
var combined = _dirtyRects[0];
for (int i = 1; i < _dirtyRects.Count; i++) for (int i = 1; i < _dirtyRects.Count; i++)
{ {
combined = SKRect.Union(combined, _dirtyRects[i]); val = SKRect.Union(val, _dirtyRects[i]);
} }
return combined; return val;
} }
} }
/// <summary>
/// Applies dirty region clipping to a canvas.
/// </summary>
public void ApplyClipping(SKCanvas canvas) public void ApplyClipping(SKCanvas canvas)
{ {
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Expected O, but got Unknown
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
lock (_lock) lock (_lock)
{ {
if (_fullRedrawNeeded || _dirtyRects.Count == 0) if (_fullRedrawNeeded || _dirtyRects.Count == 0)
{ {
// No clipping needed for full redraw
return; return;
} }
SKPath val = new SKPath();
// Create a path from all dirty rects try
using var path = new SKPath();
foreach (var rect in _dirtyRects)
{ {
path.AddRect(rect); foreach (SKRect dirtyRect in _dirtyRects)
{
val.AddRect(dirtyRect, (SKPathDirection)0);
}
canvas.ClipPath(val, (SKClipOperation)1, false);
}
finally
{
((IDisposable)val)?.Dispose();
} }
canvas.ClipPath(path);
} }
} }
private void MergeDirtyRects() private void MergeDirtyRects()
{ {
// Simple merge pass - could be optimized //IL_0013: Unknown result type (might be due to invalid IL or missing references)
bool merged; //IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
bool flag;
do do
{ {
merged = false; flag = false;
for (int i = 0; i < _dirtyRects.Count - 1; i++) for (int i = 0; i < _dirtyRects.Count - 1; i++)
{ {
for (int j = i + 1; j < _dirtyRects.Count; j++) for (int j = i + 1; j < _dirtyRects.Count; j++)
{ {
var intersection = SKRect.Intersect(_dirtyRects[i], _dirtyRects[j]); SKRect val = SKRect.Intersect(_dirtyRects[i], _dirtyRects[j]);
if (!intersection.IsEmpty) if (!((SKRect)(ref val)).IsEmpty)
{ {
_dirtyRects[i] = SKRect.Union(_dirtyRects[i], _dirtyRects[j]); _dirtyRects[i] = SKRect.Union(_dirtyRects[i], _dirtyRects[j]);
_dirtyRects.RemoveAt(j); _dirtyRects.RemoveAt(j);
merged = true; flag = true;
break; break;
} }
} }
if (merged) break; if (flag)
{
break;
} }
} while (merged); }
}
while (flag);
} }
} }

View File

@@ -1,49 +1,68 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using SkiaSharp;
using Microsoft.Maui.Platform.Linux.Window; using Microsoft.Maui.Platform.Linux.Window;
using System.Runtime.InteropServices; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
/// <summary>
/// GPU-accelerated rendering engine using OpenGL.
/// Falls back to software rendering if GPU initialization fails.
/// </summary>
public class GpuRenderingEngine : IDisposable public class GpuRenderingEngine : IDisposable
{ {
private readonly X11Window _window; private readonly X11Window _window;
private GRContext? _grContext; private GRContext? _grContext;
private GRBackendRenderTarget? _renderTarget; private GRBackendRenderTarget? _renderTarget;
private SKSurface? _surface; private SKSurface? _surface;
private SKCanvas? _canvas; private SKCanvas? _canvas;
private bool _disposed; private bool _disposed;
private bool _gpuAvailable; private bool _gpuAvailable;
private int _width; private int _width;
private int _height; private int _height;
// Fallback to software rendering
private SKBitmap? _softwareBitmap; private SKBitmap? _softwareBitmap;
private SKCanvas? _softwareCanvas; private SKCanvas? _softwareCanvas;
// Dirty region tracking private readonly List<SKRect> _dirtyRegions = new List<SKRect>();
private readonly List<SKRect> _dirtyRegions = new();
private readonly object _dirtyLock = new(); private readonly object _dirtyLock = new object();
private bool _fullRedrawNeeded = true; private bool _fullRedrawNeeded = true;
private const int MaxDirtyRegions = 32; private const int MaxDirtyRegions = 32;
/// <summary> public bool IsGpuAccelerated
/// Gets whether GPU acceleration is available and active. {
/// </summary> get
public bool IsGpuAccelerated => _gpuAvailable && _grContext != null; {
if (_gpuAvailable)
{
return _grContext != null;
}
return false;
}
}
/// <summary> public string BackendName
/// Gets the current rendering backend name. {
/// </summary> get
public string BackendName => IsGpuAccelerated ? "OpenGL" : "Software"; {
if (!IsGpuAccelerated)
{
return "Software";
}
return "OpenGL";
}
}
public int Width => _width; public int Width => _width;
public int Height => _height; public int Height => _height;
public GpuRenderingEngine(X11Window window) public GpuRenderingEngine(X11Window window)
@@ -51,16 +70,12 @@ public class GpuRenderingEngine : IDisposable
_window = window; _window = window;
_width = window.Width; _width = window.Width;
_height = window.Height; _height = window.Height;
// Try to initialize GPU rendering
_gpuAvailable = TryInitializeGpu(); _gpuAvailable = TryInitializeGpu();
if (!_gpuAvailable) if (!_gpuAvailable)
{ {
Console.WriteLine("[GpuRenderingEngine] GPU not available, using software rendering"); Console.WriteLine("[GpuRenderingEngine] GPU not available, using software rendering");
InitializeSoftwareRendering(); InitializeSoftwareRendering();
} }
_window.Resized += OnWindowResized; _window.Resized += OnWindowResized;
_window.Exposed += OnWindowExposed; _window.Exposed += OnWindowExposed;
} }
@@ -69,88 +84,95 @@ public class GpuRenderingEngine : IDisposable
{ {
try try
{ {
// Check if we can create an OpenGL context GRGlInterface val = GRGlInterface.Create();
var glInterface = GRGlInterface.Create(); if (val == null)
if (glInterface == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GL interface"); Console.WriteLine("[GpuRenderingEngine] Failed to create GL interface");
return false; return false;
} }
_grContext = GRContext.CreateGl(val);
_grContext = GRContext.CreateGl(glInterface);
if (_grContext == null) if (_grContext == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GR context"); Console.WriteLine("[GpuRenderingEngine] Failed to create GR context");
glInterface.Dispose(); ((SKNativeObject)val).Dispose();
return false; return false;
} }
CreateGpuSurface(); CreateGpuSurface();
Console.WriteLine("[GpuRenderingEngine] GPU acceleration enabled"); Console.WriteLine("[GpuRenderingEngine] GPU acceleration enabled");
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"[GpuRenderingEngine] GPU initialization failed: {ex.Message}"); Console.WriteLine("[GpuRenderingEngine] GPU initialization failed: " + ex.Message);
return false; return false;
} }
} }
private void CreateGpuSurface() private void CreateGpuSurface()
{ {
if (_grContext == null) return; //IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
_renderTarget?.Dispose(); //IL_0063: Expected O, but got Unknown
_surface?.Dispose(); if (_grContext != null)
{
var width = Math.Max(1, _width); GRBackendRenderTarget? renderTarget = _renderTarget;
var height = Math.Max(1, _height); if (renderTarget != null)
{
// Create framebuffer info (assuming default framebuffer 0) ((SKNativeObject)renderTarget).Dispose();
var framebufferInfo = new GRGlFramebufferInfo(0, SKColorType.Rgba8888.ToGlSizedFormat()); }
SKSurface? surface = _surface;
_renderTarget = new GRBackendRenderTarget( if (surface != null)
width, height, {
0, // sample count ((SKNativeObject)surface).Dispose();
8, // stencil bits }
framebufferInfo); int num = Math.Max(1, _width);
int num2 = Math.Max(1, _height);
_surface = SKSurface.Create( GRGlFramebufferInfo val = default(GRGlFramebufferInfo);
_grContext, ((GRGlFramebufferInfo)(ref val))._002Ector(0u, SkiaExtensions.ToGlSizedFormat((SKColorType)4));
_renderTarget, _renderTarget = new GRBackendRenderTarget(num, num2, 0, 8, val);
GRSurfaceOrigin.BottomLeft, _surface = SKSurface.Create(_grContext, _renderTarget, (GRSurfaceOrigin)1, (SKColorType)4);
SKColorType.Rgba8888);
if (_surface == null) if (_surface == null)
{ {
Console.WriteLine("[GpuRenderingEngine] Failed to create GPU surface, falling back to software"); Console.WriteLine("[GpuRenderingEngine] Failed to create GPU surface, falling back to software");
_gpuAvailable = false; _gpuAvailable = false;
InitializeSoftwareRendering(); InitializeSoftwareRendering();
return;
} }
else
{
_canvas = _surface.Canvas; _canvas = _surface.Canvas;
} }
}
}
private void InitializeSoftwareRendering() private void InitializeSoftwareRendering()
{ {
var width = Math.Max(1, _width); //IL_0048: Unknown result type (might be due to invalid IL or missing references)
var height = Math.Max(1, _height); //IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Expected O, but got Unknown
_softwareBitmap?.Dispose(); //IL_005a: Unknown result type (might be due to invalid IL or missing references)
_softwareCanvas?.Dispose(); //IL_0064: Expected O, but got Unknown
int num = Math.Max(1, _width);
var imageInfo = new SKImageInfo(width, height, SKColorType.Bgra8888, SKAlphaType.Premul); int num2 = Math.Max(1, _height);
_softwareBitmap = new SKBitmap(imageInfo); SKBitmap? softwareBitmap = _softwareBitmap;
if (softwareBitmap != null)
{
((SKNativeObject)softwareBitmap).Dispose();
}
SKCanvas? softwareCanvas = _softwareCanvas;
if (softwareCanvas != null)
{
((SKNativeObject)softwareCanvas).Dispose();
}
SKImageInfo val = default(SKImageInfo);
((SKImageInfo)(ref val))._002Ector(num, num2, (SKColorType)6, (SKAlphaType)2);
_softwareBitmap = new SKBitmap(val);
_softwareCanvas = new SKCanvas(_softwareBitmap); _softwareCanvas = new SKCanvas(_softwareBitmap);
_canvas = _softwareCanvas; _canvas = _softwareCanvas;
} }
private void OnWindowResized(object? sender, (int Width, int Height) size) private void OnWindowResized(object? sender, (int Width, int Height) size)
{ {
_width = size.Width; (_width, _height) = size;
_height = size.Height;
if (_gpuAvailable && _grContext != null) if (_gpuAvailable && _grContext != null)
{ {
CreateGpuSurface(); CreateGpuSurface();
@@ -159,7 +181,6 @@ public class GpuRenderingEngine : IDisposable
{ {
InitializeSoftwareRendering(); InitializeSoftwareRendering();
} }
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
} }
@@ -168,107 +189,104 @@ public class GpuRenderingEngine : IDisposable
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
} }
/// <summary>
/// Marks a region as needing redraw.
/// </summary>
public void InvalidateRegion(SKRect region) public void InvalidateRegion(SKRect region)
{ {
if (region.IsEmpty || region.Width <= 0 || region.Height <= 0) //IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
if (((SKRect)(ref region)).IsEmpty || ((SKRect)(ref region)).Width <= 0f || ((SKRect)(ref region)).Height <= 0f)
{
return; return;
}
region = SKRect.Intersect(region, new SKRect(0, 0, Width, Height)); region = SKRect.Intersect(region, new SKRect(0f, 0f, (float)Width, (float)Height));
if (region.IsEmpty) return; if (((SKRect)(ref region)).IsEmpty)
{
return;
}
lock (_dirtyLock) lock (_dirtyLock)
{ {
if (_dirtyRegions.Count >= MaxDirtyRegions) if (_dirtyRegions.Count >= 32)
{ {
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
_dirtyRegions.Clear(); _dirtyRegions.Clear();
return;
} }
else
{
_dirtyRegions.Add(region); _dirtyRegions.Add(region);
} }
} }
}
/// <summary>
/// Marks the entire surface as needing redraw.
/// </summary>
public void InvalidateAll() public void InvalidateAll()
{ {
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
} }
/// <summary>
/// Renders the view tree with dirty region optimization.
/// </summary>
public void Render(SkiaView rootView) public void Render(SkiaView rootView)
{ {
if (_canvas == null) return; //IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
// Measure and arrange //IL_003f: Unknown result type (might be due to invalid IL or missing references)
var availableSize = new SKSize(Width, Height); //IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
if (_canvas == null)
{
return;
}
SKSize availableSize = default(SKSize);
((SKSize)(ref availableSize))._002Ector((float)Width, (float)Height);
rootView.Measure(availableSize); rootView.Measure(availableSize);
rootView.Arrange(new SKRect(0, 0, Width, Height)); rootView.Arrange(new SKRect(0f, 0f, (float)Width, (float)Height));
bool flag;
// Determine regions to redraw List<SKRect> list;
List<SKRect> regionsToRedraw;
bool isFullRedraw;
lock (_dirtyLock) lock (_dirtyLock)
{ {
isFullRedraw = _fullRedrawNeeded || _dirtyRegions.Count == 0; flag = _fullRedrawNeeded || _dirtyRegions.Count == 0;
if (isFullRedraw) if (flag)
{ {
regionsToRedraw = new List<SKRect> { new SKRect(0, 0, Width, Height) }; list = new List<SKRect>
{
new SKRect(0f, 0f, (float)Width, (float)Height)
};
_dirtyRegions.Clear(); _dirtyRegions.Clear();
_fullRedrawNeeded = false; _fullRedrawNeeded = false;
} }
else else
{ {
regionsToRedraw = new List<SKRect>(_dirtyRegions); list = new List<SKRect>(_dirtyRegions);
_dirtyRegions.Clear(); _dirtyRegions.Clear();
} }
} }
foreach (SKRect item in list)
// Render each dirty region
foreach (var region in regionsToRedraw)
{ {
_canvas.Save(); _canvas.Save();
if (!isFullRedraw) if (!flag)
{ {
_canvas.ClipRect(region); _canvas.ClipRect(item, (SKClipOperation)1, false);
} }
// Clear region
_canvas.Clear(SKColors.White); _canvas.Clear(SKColors.White);
// Draw view tree
rootView.Draw(_canvas); rootView.Draw(_canvas);
_canvas.Restore(); _canvas.Restore();
} }
// Draw popup overlays
SkiaView.DrawPopupOverlays(_canvas); SkiaView.DrawPopupOverlays(_canvas);
// Draw modal dialogs
if (LinuxDialogService.HasActiveDialog) if (LinuxDialogService.HasActiveDialog)
{ {
LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); LinuxDialogService.DrawDialogs(_canvas, new SKRect(0f, 0f, (float)Width, (float)Height));
} }
_canvas.Flush(); _canvas.Flush();
// Present to window
if (_gpuAvailable && _grContext != null) if (_gpuAvailable && _grContext != null)
{ {
_grContext.Submit(); _grContext.Submit(false);
// Swap buffers would happen here via GLX/EGL
} }
else if (_softwareBitmap != null) else if (_softwareBitmap != null)
{ {
var pixels = _softwareBitmap.GetPixels(); IntPtr pixels = _softwareBitmap.GetPixels();
if (pixels != IntPtr.Zero) if (pixels != IntPtr.Zero)
{ {
_window.DrawPixels(pixels, Width, Height, _softwareBitmap.RowBytes); _window.DrawPixels(pixels, Width, Height, _softwareBitmap.RowBytes);
@@ -276,74 +294,83 @@ public class GpuRenderingEngine : IDisposable
} }
} }
/// <summary>
/// Gets performance statistics for the GPU context.
/// </summary>
public GpuStats GetStats() public GpuStats GetStats()
{ {
if (_grContext == null) if (_grContext == null)
{ {
return new GpuStats { IsGpuAccelerated = false }; return new GpuStats
{
IsGpuAccelerated = false
};
} }
int num = default(int);
// Get resource cache limits from GRContext long resourceCacheLimitBytes = default(long);
_grContext.GetResourceCacheLimits(out var maxResources, out var maxBytes); _grContext.GetResourceCacheLimits(ref num, ref resourceCacheLimitBytes);
return new GpuStats return new GpuStats
{ {
IsGpuAccelerated = true, IsGpuAccelerated = true,
MaxTextureSize = 4096, // Common default, SkiaSharp doesn't expose this directly MaxTextureSize = 4096,
ResourceCacheUsedBytes = 0, // Would need to track manually ResourceCacheUsedBytes = 0L,
ResourceCacheLimitBytes = maxBytes ResourceCacheLimitBytes = resourceCacheLimitBytes
}; };
} }
/// <summary>
/// Purges unused GPU resources to free memory.
/// </summary>
public void PurgeResources() public void PurgeResources()
{ {
_grContext?.PurgeResources(); GRContext? grContext = _grContext;
if (grContext != null)
{
grContext.PurgeResources();
}
} }
public SKCanvas? GetCanvas() => _canvas; public SKCanvas? GetCanvas()
{
return _canvas;
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!_disposed) if (_disposed)
{ {
return;
}
if (disposing) if (disposing)
{ {
_window.Resized -= OnWindowResized; _window.Resized -= OnWindowResized;
_window.Exposed -= OnWindowExposed; _window.Exposed -= OnWindowExposed;
SKSurface? surface = _surface;
_surface?.Dispose(); if (surface != null)
_renderTarget?.Dispose(); {
_grContext?.Dispose(); ((SKNativeObject)surface).Dispose();
_softwareBitmap?.Dispose(); }
_softwareCanvas?.Dispose(); GRBackendRenderTarget? renderTarget = _renderTarget;
if (renderTarget != null)
{
((SKNativeObject)renderTarget).Dispose();
}
GRContext? grContext = _grContext;
if (grContext != null)
{
((SKNativeObject)grContext).Dispose();
}
SKBitmap? softwareBitmap = _softwareBitmap;
if (softwareBitmap != null)
{
((SKNativeObject)softwareBitmap).Dispose();
}
SKCanvas? softwareCanvas = _softwareCanvas;
if (softwareCanvas != null)
{
((SKNativeObject)softwareCanvas).Dispose();
}
} }
_disposed = true; _disposed = true;
} }
}
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(disposing: true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }
/// <summary>
/// GPU performance statistics.
/// </summary>
public class GpuStats
{
public bool IsGpuAccelerated { get; init; }
public int MaxTextureSize { get; init; }
public long ResourceCacheUsedBytes { get; init; }
public long ResourceCacheLimitBytes { get; init; }
public double ResourceCacheUsedMB => ResourceCacheUsedBytes / (1024.0 * 1024.0);
public double ResourceCacheLimitMB => ResourceCacheLimitBytes / (1024.0 * 1024.0);
}

16
Rendering/GpuStats.cs Normal file
View File

@@ -0,0 +1,16 @@
namespace Microsoft.Maui.Platform.Linux.Rendering;
public class GpuStats
{
public bool IsGpuAccelerated { get; init; }
public int MaxTextureSize { get; init; }
public long ResourceCacheUsedBytes { get; init; }
public long ResourceCacheLimitBytes { get; init; }
public double ResourceCacheUsedMB => (double)ResourceCacheUsedBytes / 1048576.0;
public double ResourceCacheLimitMB => (double)ResourceCacheLimitBytes / 1048576.0;
}

View File

@@ -0,0 +1,455 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.Maui.Platform.Linux.Native;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering;
public sealed class GtkSkiaSurfaceWidget : IDisposable
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool DrawCallback(IntPtr widget, IntPtr cairoContext, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool ConfigureCallback(IntPtr widget, IntPtr eventData, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool ButtonEventCallback(IntPtr widget, IntPtr eventData, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool MotionEventCallback(IntPtr widget, IntPtr eventData, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool KeyEventCallback(IntPtr widget, IntPtr eventData, IntPtr userData);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate bool ScrollEventCallback(IntPtr widget, IntPtr eventData, IntPtr userData);
private struct GdkEventButton
{
public int type;
public IntPtr window;
public sbyte send_event;
public uint time;
public double x;
public double y;
public IntPtr axes;
public uint state;
public uint button;
}
private struct GdkEventMotion
{
public int type;
public IntPtr window;
public sbyte send_event;
public uint time;
public double x;
public double y;
}
private struct GdkEventKey
{
public int type;
public IntPtr window;
public sbyte send_event;
public uint time;
public uint state;
public uint keyval;
public int length;
public IntPtr str;
public ushort hardware_keycode;
}
private struct GdkEventScroll
{
public int type;
public IntPtr window;
public sbyte send_event;
public uint time;
public double x;
public double y;
public uint state;
public int direction;
public IntPtr device;
public double x_root;
public double y_root;
public double delta_x;
public double delta_y;
}
private IntPtr _widget;
private SKImageInfo _imageInfo;
private SKBitmap? _bitmap;
private SKCanvas? _canvas;
private IntPtr _cairoSurface;
private readonly DrawCallback _drawCallback;
private readonly ConfigureCallback _configureCallback;
private ulong _drawSignalId;
private ulong _configureSignalId;
private bool _isTransparent;
private readonly ButtonEventCallback _buttonPressCallback;
private readonly ButtonEventCallback _buttonReleaseCallback;
private readonly MotionEventCallback _motionCallback;
private readonly KeyEventCallback _keyPressCallback;
private readonly KeyEventCallback _keyReleaseCallback;
private readonly ScrollEventCallback _scrollCallback;
public IntPtr Widget => _widget;
public SKCanvas? Canvas => _canvas;
public SKImageInfo ImageInfo => _imageInfo;
public int Width => ((SKImageInfo)(ref _imageInfo)).Width;
public int Height => ((SKImageInfo)(ref _imageInfo)).Height;
public bool IsTransparent => _isTransparent;
public event EventHandler? DrawRequested;
public event EventHandler<(int Width, int Height)>? Resized;
public event EventHandler<(double X, double Y, int Button)>? PointerPressed;
public event EventHandler<(double X, double Y, int Button)>? PointerReleased;
public event EventHandler<(double X, double Y)>? PointerMoved;
public event EventHandler<(uint KeyVal, uint KeyCode, uint State)>? KeyPressed;
public event EventHandler<(uint KeyVal, uint KeyCode, uint State)>? KeyReleased;
public event EventHandler<(double X, double Y, double DeltaX, double DeltaY)>? Scrolled;
public event EventHandler<string>? TextInput;
public GtkSkiaSurfaceWidget(int width, int height)
{
_widget = GtkNative.gtk_drawing_area_new();
if (_widget == IntPtr.Zero)
{
throw new InvalidOperationException("Failed to create GTK drawing area");
}
GtkNative.gtk_widget_set_size_request(_widget, width, height);
GtkNative.gtk_widget_add_events(_widget, 10551046);
GtkNative.gtk_widget_set_can_focus(_widget, canFocus: true);
CreateBuffer(width, height);
_drawCallback = OnDraw;
_configureCallback = OnConfigure;
_buttonPressCallback = OnButtonPress;
_buttonReleaseCallback = OnButtonRelease;
_motionCallback = OnMotion;
_keyPressCallback = OnKeyPress;
_keyReleaseCallback = OnKeyRelease;
_scrollCallback = OnScroll;
_drawSignalId = GtkNative.g_signal_connect_data(_widget, "draw", Marshal.GetFunctionPointerForDelegate(_drawCallback), IntPtr.Zero, IntPtr.Zero, 0);
_configureSignalId = GtkNative.g_signal_connect_data(_widget, "configure-event", Marshal.GetFunctionPointerForDelegate(_configureCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "button-press-event", Marshal.GetFunctionPointerForDelegate(_buttonPressCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "button-release-event", Marshal.GetFunctionPointerForDelegate(_buttonReleaseCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "motion-notify-event", Marshal.GetFunctionPointerForDelegate(_motionCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "key-press-event", Marshal.GetFunctionPointerForDelegate(_keyPressCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "key-release-event", Marshal.GetFunctionPointerForDelegate(_keyReleaseCallback), IntPtr.Zero, IntPtr.Zero, 0);
GtkNative.g_signal_connect_data(_widget, "scroll-event", Marshal.GetFunctionPointerForDelegate(_scrollCallback), IntPtr.Zero, IntPtr.Zero, 0);
Console.WriteLine($"[GtkSkiaSurfaceWidget] Created with size {width}x{height}");
}
private void CreateBuffer(int width, int height)
{
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Expected O, but got Unknown
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Expected O, but got Unknown
width = Math.Max(1, width);
height = Math.Max(1, height);
SKCanvas? canvas = _canvas;
if (canvas != null)
{
((SKNativeObject)canvas).Dispose();
}
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
if (_cairoSurface != IntPtr.Zero)
{
CairoNative.cairo_surface_destroy(_cairoSurface);
_cairoSurface = IntPtr.Zero;
}
_imageInfo = new SKImageInfo(width, height, (SKColorType)6, (SKAlphaType)2);
_bitmap = new SKBitmap(_imageInfo);
_canvas = new SKCanvas(_bitmap);
IntPtr pixels = _bitmap.GetPixels();
_cairoSurface = CairoNative.cairo_image_surface_create_for_data(pixels, CairoNative.cairo_format_t.CAIRO_FORMAT_ARGB32, ((SKImageInfo)(ref _imageInfo)).Width, ((SKImageInfo)(ref _imageInfo)).Height, ((SKImageInfo)(ref _imageInfo)).RowBytes);
Console.WriteLine($"[GtkSkiaSurfaceWidget] Created buffer {width}x{height}, stride={((SKImageInfo)(ref _imageInfo)).RowBytes}");
}
public void Resize(int width, int height)
{
if (width != ((SKImageInfo)(ref _imageInfo)).Width || height != ((SKImageInfo)(ref _imageInfo)).Height)
{
CreateBuffer(width, height);
this.Resized?.Invoke(this, (width, height));
}
}
public void RenderFrame(Action<SKCanvas, SKImageInfo> render)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
if (_canvas != null && _bitmap != null)
{
render(_canvas, _imageInfo);
_canvas.Flush();
CairoNative.cairo_surface_flush(_cairoSurface);
CairoNative.cairo_surface_mark_dirty(_cairoSurface);
GtkNative.gtk_widget_queue_draw(_widget);
}
}
public void Invalidate()
{
GtkNative.gtk_widget_queue_draw(_widget);
}
public void SetTransparent(bool transparent)
{
_isTransparent = transparent;
}
private bool OnDraw(IntPtr widget, IntPtr cairoContext, IntPtr userData)
{
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
if (_cairoSurface == IntPtr.Zero || cairoContext == IntPtr.Zero)
{
return false;
}
if (_isTransparent)
{
SKCanvas? canvas = _canvas;
if (canvas != null)
{
canvas.Clear(SKColors.Transparent);
}
}
this.DrawRequested?.Invoke(this, EventArgs.Empty);
SKCanvas? canvas2 = _canvas;
if (canvas2 != null)
{
canvas2.Flush();
}
CairoNative.cairo_surface_flush(_cairoSurface);
CairoNative.cairo_surface_mark_dirty(_cairoSurface);
CairoNative.cairo_set_source_surface(cairoContext, _cairoSurface, 0.0, 0.0);
CairoNative.cairo_paint(cairoContext);
return true;
}
private bool OnConfigure(IntPtr widget, IntPtr eventData, IntPtr userData)
{
GtkNative.gtk_widget_get_allocation(widget, out var allocation);
if (allocation.Width > 0 && allocation.Height > 0 && (allocation.Width != ((SKImageInfo)(ref _imageInfo)).Width || allocation.Height != ((SKImageInfo)(ref _imageInfo)).Height))
{
Resize(allocation.Width, allocation.Height);
}
return false;
}
private bool OnButtonPress(IntPtr widget, IntPtr eventData, IntPtr userData)
{
GtkNative.gtk_widget_grab_focus(_widget);
var (num, num2, num3) = ParseButtonEvent(eventData);
Console.WriteLine($"[GtkSkiaSurfaceWidget] ButtonPress at ({num}, {num2}), button={num3}");
this.PointerPressed?.Invoke(this, (num, num2, num3));
return true;
}
private bool OnButtonRelease(IntPtr widget, IntPtr eventData, IntPtr userData)
{
var (item, item2, item3) = ParseButtonEvent(eventData);
this.PointerReleased?.Invoke(this, (item, item2, item3));
return true;
}
private bool OnMotion(IntPtr widget, IntPtr eventData, IntPtr userData)
{
var (item, item2) = ParseMotionEvent(eventData);
this.PointerMoved?.Invoke(this, (item, item2));
return true;
}
public void RaisePointerPressed(double x, double y, int button)
{
Console.WriteLine($"[GtkSkiaSurfaceWidget] RaisePointerPressed at ({x}, {y}), button={button}");
this.PointerPressed?.Invoke(this, (x, y, button));
}
public void RaisePointerReleased(double x, double y, int button)
{
this.PointerReleased?.Invoke(this, (x, y, button));
}
public void RaisePointerMoved(double x, double y)
{
this.PointerMoved?.Invoke(this, (x, y));
}
private bool OnKeyPress(IntPtr widget, IntPtr eventData, IntPtr userData)
{
var (num, item, item2) = ParseKeyEvent(eventData);
this.KeyPressed?.Invoke(this, (num, item, item2));
uint num2 = GdkNative.gdk_keyval_to_unicode(num);
if (num2 != 0 && num2 < 65536)
{
char c = (char)num2;
if (!char.IsControl(c) || c == '\r' || c == '\n' || c == '\t')
{
string text = c.ToString();
Console.WriteLine($"[GtkSkiaSurfaceWidget] TextInput: '{text}' (keyval={num}, unicode={num2})");
this.TextInput?.Invoke(this, text);
}
}
return true;
}
private bool OnKeyRelease(IntPtr widget, IntPtr eventData, IntPtr userData)
{
var (item, item2, item3) = ParseKeyEvent(eventData);
this.KeyReleased?.Invoke(this, (item, item2, item3));
return true;
}
private bool OnScroll(IntPtr widget, IntPtr eventData, IntPtr userData)
{
var (item, item2, item3, item4) = ParseScrollEvent(eventData);
this.Scrolled?.Invoke(this, (item, item2, item3, item4));
return true;
}
private static (double x, double y, int button) ParseButtonEvent(IntPtr eventData)
{
GdkEventButton gdkEventButton = Marshal.PtrToStructure<GdkEventButton>(eventData);
return (x: gdkEventButton.x, y: gdkEventButton.y, button: (int)gdkEventButton.button);
}
private static (double x, double y) ParseMotionEvent(IntPtr eventData)
{
GdkEventMotion gdkEventMotion = Marshal.PtrToStructure<GdkEventMotion>(eventData);
return (x: gdkEventMotion.x, y: gdkEventMotion.y);
}
private static (uint keyval, uint keycode, uint state) ParseKeyEvent(IntPtr eventData)
{
GdkEventKey gdkEventKey = Marshal.PtrToStructure<GdkEventKey>(eventData);
return (keyval: gdkEventKey.keyval, keycode: gdkEventKey.hardware_keycode, state: gdkEventKey.state);
}
private static (double x, double y, double deltaX, double deltaY) ParseScrollEvent(IntPtr eventData)
{
GdkEventScroll gdkEventScroll = Marshal.PtrToStructure<GdkEventScroll>(eventData);
double item = 0.0;
double item2 = 0.0;
if (gdkEventScroll.direction == 4)
{
item = gdkEventScroll.delta_x;
item2 = gdkEventScroll.delta_y;
}
else
{
switch (gdkEventScroll.direction)
{
case 0:
item2 = -1.0;
break;
case 1:
item2 = 1.0;
break;
case 2:
item = -1.0;
break;
case 3:
item = 1.0;
break;
}
}
return (x: gdkEventScroll.x, y: gdkEventScroll.y, deltaX: item, deltaY: item2);
}
public void GrabFocus()
{
GtkNative.gtk_widget_grab_focus(_widget);
}
public void Dispose()
{
SKCanvas? canvas = _canvas;
if (canvas != null)
{
((SKNativeObject)canvas).Dispose();
}
_canvas = null;
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
_bitmap = null;
if (_cairoSurface != IntPtr.Zero)
{
CairoNative.cairo_surface_destroy(_cairoSurface);
_cairoSurface = IntPtr.Zero;
}
}
}

View File

@@ -0,0 +1,80 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering;
public class LayeredRenderer : IDisposable
{
private readonly Dictionary<int, RenderLayer> _layers = new Dictionary<int, RenderLayer>();
private readonly object _lock = new object();
private bool _disposed;
public RenderLayer GetLayer(int zIndex)
{
lock (_lock)
{
if (!_layers.TryGetValue(zIndex, out RenderLayer value))
{
value = new RenderLayer(zIndex);
_layers[zIndex] = value;
}
return value;
}
}
public void RemoveLayer(int zIndex)
{
lock (_lock)
{
if (_layers.TryGetValue(zIndex, out RenderLayer value))
{
value.Dispose();
_layers.Remove(zIndex);
}
}
}
public void Composite(SKCanvas canvas, SKRect bounds)
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
lock (_lock)
{
foreach (RenderLayer item in _layers.Values.OrderBy((RenderLayer l) => l.ZIndex))
{
item.DrawTo(canvas, bounds);
}
}
}
public void InvalidateAll()
{
lock (_lock)
{
foreach (RenderLayer value in _layers.Values)
{
value.Invalidate();
}
}
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
lock (_lock)
{
foreach (RenderLayer value in _layers.Values)
{
value.Dispose();
}
_layers.Clear();
}
}
}

View File

@@ -1,43 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.Linq;
using SkiaSharp; using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
/// <summary>
/// Caches rendered content for views that don't change frequently.
/// Improves performance by avoiding redundant rendering.
/// </summary>
public class RenderCache : IDisposable public class RenderCache : IDisposable
{ {
private readonly Dictionary<string, CacheEntry> _cache = new(); private class CacheEntry
private readonly object _lock = new(); {
private long _maxCacheSize = 50 * 1024 * 1024; // 50 MB default public string Key { get; set; } = string.Empty;
public SKBitmap? Bitmap { get; set; }
public long Size { get; set; }
public DateTime Created { get; set; }
public DateTime LastAccessed { get; set; }
public int AccessCount { get; set; }
}
private readonly Dictionary<string, CacheEntry> _cache = new Dictionary<string, CacheEntry>();
private readonly object _lock = new object();
private long _maxCacheSize = 52428800L;
private long _currentCacheSize; private long _currentCacheSize;
private bool _disposed; private bool _disposed;
/// <summary>
/// Gets or sets the maximum cache size in bytes.
/// </summary>
public long MaxCacheSize public long MaxCacheSize
{ {
get => _maxCacheSize; get
{
return _maxCacheSize;
}
set set
{ {
_maxCacheSize = Math.Max(1024 * 1024, value); // Minimum 1 MB _maxCacheSize = Math.Max(1048576L, value);
TrimCache(); TrimCache();
} }
} }
/// <summary>
/// Gets the current cache size in bytes.
/// </summary>
public long CurrentCacheSize => _currentCacheSize; public long CurrentCacheSize => _currentCacheSize;
/// <summary>
/// Gets the number of cached items.
/// </summary>
public int CachedItemCount public int CachedItemCount
{ {
get get
@@ -49,478 +58,173 @@ public class RenderCache : IDisposable
} }
} }
/// <summary>
/// Tries to get a cached bitmap for the given key.
/// </summary>
public bool TryGet(string key, out SKBitmap? bitmap) public bool TryGet(string key, out SKBitmap? bitmap)
{ {
lock (_lock) lock (_lock)
{ {
if (_cache.TryGetValue(key, out var entry)) if (_cache.TryGetValue(key, out CacheEntry value))
{ {
entry.LastAccessed = DateTime.UtcNow; value.LastAccessed = DateTime.UtcNow;
entry.AccessCount++; value.AccessCount++;
bitmap = entry.Bitmap; bitmap = value.Bitmap;
return true; return true;
} }
} }
bitmap = null; bitmap = null;
return false; return false;
} }
/// <summary>
/// Caches a bitmap with the given key.
/// </summary>
public void Set(string key, SKBitmap bitmap) public void Set(string key, SKBitmap bitmap)
{ {
if (bitmap == null) return; if (bitmap == null)
{
long bitmapSize = bitmap.ByteCount; return;
}
// Don't cache if bitmap is larger than max size long num = bitmap.ByteCount;
if (bitmapSize > _maxCacheSize) if (num > _maxCacheSize)
{ {
return; return;
} }
lock (_lock) lock (_lock)
{ {
// Remove existing entry if present if (_cache.TryGetValue(key, out CacheEntry value))
if (_cache.TryGetValue(key, out var existing))
{ {
_currentCacheSize -= existing.Size; _currentCacheSize -= value.Size;
existing.Bitmap?.Dispose(); SKBitmap? bitmap2 = value.Bitmap;
if (bitmap2 != null)
{
((SKNativeObject)bitmap2).Dispose();
} }
}
// Create copy of bitmap for cache SKBitmap val = bitmap.Copy();
var cachedBitmap = bitmap.Copy(); if (val != null)
if (cachedBitmap == null) return; {
CacheEntry value2 = new CacheEntry
var entry = new CacheEntry
{ {
Key = key, Key = key,
Bitmap = cachedBitmap, Bitmap = val,
Size = bitmapSize, Size = num,
Created = DateTime.UtcNow, Created = DateTime.UtcNow,
LastAccessed = DateTime.UtcNow, LastAccessed = DateTime.UtcNow,
AccessCount = 1 AccessCount = 1
}; };
_cache[key] = value2;
_cache[key] = entry; _currentCacheSize += num;
_currentCacheSize += bitmapSize;
// Trim cache if needed
TrimCache(); TrimCache();
} }
} }
}
/// <summary>
/// Invalidates a cached entry.
/// </summary>
public void Invalidate(string key) public void Invalidate(string key)
{ {
lock (_lock) lock (_lock)
{ {
if (_cache.TryGetValue(key, out var entry)) if (_cache.TryGetValue(key, out CacheEntry value))
{ {
_currentCacheSize -= entry.Size; _currentCacheSize -= value.Size;
entry.Bitmap?.Dispose(); SKBitmap? bitmap = value.Bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
_cache.Remove(key); _cache.Remove(key);
} }
} }
} }
/// <summary>
/// Invalidates all entries matching a prefix.
/// </summary>
public void InvalidatePrefix(string prefix) public void InvalidatePrefix(string prefix)
{ {
lock (_lock) lock (_lock)
{ {
var keysToRemove = _cache.Keys foreach (string item in _cache.Keys.Where((string k) => k.StartsWith(prefix, StringComparison.Ordinal)).ToList())
.Where(k => k.StartsWith(prefix, StringComparison.Ordinal))
.ToList();
foreach (var key in keysToRemove)
{ {
if (_cache.TryGetValue(key, out var entry)) if (_cache.TryGetValue(item, out CacheEntry value))
{ {
_currentCacheSize -= entry.Size; _currentCacheSize -= value.Size;
entry.Bitmap?.Dispose(); SKBitmap? bitmap = value.Bitmap;
_cache.Remove(key); if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
_cache.Remove(item);
} }
} }
} }
} }
/// <summary>
/// Clears all cached content.
/// </summary>
public void Clear() public void Clear()
{ {
lock (_lock) lock (_lock)
{ {
foreach (var entry in _cache.Values) foreach (CacheEntry value in _cache.Values)
{ {
entry.Bitmap?.Dispose(); SKBitmap? bitmap = value.Bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
} }
_cache.Clear(); _cache.Clear();
_currentCacheSize = 0; _currentCacheSize = 0L;
} }
} }
/// <summary>
/// Renders content with caching.
/// </summary>
public SKBitmap GetOrCreate(string key, int width, int height, Action<SKCanvas> render) public SKBitmap GetOrCreate(string key, int width, int height, Action<SKCanvas> render)
{ {
// Check cache first //IL_0026: Unknown result type (might be due to invalid IL or missing references)
if (TryGet(key, out var cached) && cached != null && //IL_002c: Expected O, but got Unknown
cached.Width == width && cached.Height == height) //IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected O, but got Unknown
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (TryGet(key, out SKBitmap bitmap) && bitmap != null && bitmap.Width == width && bitmap.Height == height)
{ {
return cached;
}
// Create new bitmap
var bitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
using (var canvas = new SKCanvas(bitmap))
{
canvas.Clear(SKColors.Transparent);
render(canvas);
}
// Cache it
Set(key, bitmap);
return bitmap; return bitmap;
} }
SKBitmap val = new SKBitmap(width, height, (SKColorType)4, (SKAlphaType)2);
SKCanvas val2 = new SKCanvas(val);
try
{
val2.Clear(SKColors.Transparent);
render(val2);
}
finally
{
((IDisposable)val2)?.Dispose();
}
Set(key, val);
return val;
}
private void TrimCache() private void TrimCache()
{ {
if (_currentCacheSize <= _maxCacheSize) return; if (_currentCacheSize <= _maxCacheSize)
// Remove least recently used entries until under limit
var entries = _cache.Values
.OrderBy(e => e.LastAccessed)
.ThenBy(e => e.AccessCount)
.ToList();
foreach (var entry in entries)
{ {
if (_currentCacheSize <= _maxCacheSize * 0.8) // Target 80% usage return;
}
foreach (CacheEntry item in (from e in _cache.Values
orderby e.LastAccessed, e.AccessCount
select e).ToList())
{
if ((double)_currentCacheSize <= (double)_maxCacheSize * 0.8)
{ {
break; break;
} }
_currentCacheSize -= item.Size;
_currentCacheSize -= entry.Size; SKBitmap? bitmap = item.Bitmap;
entry.Bitmap?.Dispose(); if (bitmap != null)
_cache.Remove(entry.Key); {
((SKNativeObject)bitmap).Dispose();
}
_cache.Remove(item.Key);
} }
} }
public void Dispose() public void Dispose()
{ {
if (_disposed) return; if (!_disposed)
{
_disposed = true; _disposed = true;
Clear(); Clear();
} }
private class CacheEntry
{
public string Key { get; set; } = string.Empty;
public SKBitmap? Bitmap { get; set; }
public long Size { get; set; }
public DateTime Created { get; set; }
public DateTime LastAccessed { get; set; }
public int AccessCount { get; set; }
}
}
/// <summary>
/// Provides layered rendering for separating static and dynamic content.
/// </summary>
public class LayeredRenderer : IDisposable
{
private readonly Dictionary<int, RenderLayer> _layers = new();
private readonly object _lock = new();
private bool _disposed;
/// <summary>
/// Gets or creates a render layer.
/// </summary>
public RenderLayer GetLayer(int zIndex)
{
lock (_lock)
{
if (!_layers.TryGetValue(zIndex, out var layer))
{
layer = new RenderLayer(zIndex);
_layers[zIndex] = layer;
}
return layer;
}
}
/// <summary>
/// Removes a render layer.
/// </summary>
public void RemoveLayer(int zIndex)
{
lock (_lock)
{
if (_layers.TryGetValue(zIndex, out var layer))
{
layer.Dispose();
_layers.Remove(zIndex);
}
}
}
/// <summary>
/// Composites all layers onto the target canvas.
/// </summary>
public void Composite(SKCanvas canvas, SKRect bounds)
{
lock (_lock)
{
foreach (var layer in _layers.Values.OrderBy(l => l.ZIndex))
{
layer.DrawTo(canvas, bounds);
}
}
}
/// <summary>
/// Invalidates all layers.
/// </summary>
public void InvalidateAll()
{
lock (_lock)
{
foreach (var layer in _layers.Values)
{
layer.Invalidate();
}
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
lock (_lock)
{
foreach (var layer in _layers.Values)
{
layer.Dispose();
}
_layers.Clear();
}
}
}
/// <summary>
/// Represents a single render layer with its own bitmap buffer.
/// </summary>
public class RenderLayer : IDisposable
{
private SKBitmap? _bitmap;
private SKCanvas? _canvas;
private bool _isDirty = true;
private SKRect _bounds;
private bool _disposed;
/// <summary>
/// Gets the Z-index of this layer.
/// </summary>
public int ZIndex { get; }
/// <summary>
/// Gets whether this layer needs to be redrawn.
/// </summary>
public bool IsDirty => _isDirty;
/// <summary>
/// Gets or sets whether this layer is visible.
/// </summary>
public bool IsVisible { get; set; } = true;
/// <summary>
/// Gets or sets the layer opacity (0-1).
/// </summary>
public float Opacity { get; set; } = 1f;
public RenderLayer(int zIndex)
{
ZIndex = zIndex;
}
/// <summary>
/// Prepares the layer for rendering.
/// </summary>
public SKCanvas BeginDraw(SKRect bounds)
{
if (_bitmap == null || _bounds != bounds)
{
_bitmap?.Dispose();
_canvas?.Dispose();
int width = Math.Max(1, (int)bounds.Width);
int height = Math.Max(1, (int)bounds.Height);
_bitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
_canvas = new SKCanvas(_bitmap);
_bounds = bounds;
}
_canvas!.Clear(SKColors.Transparent);
_isDirty = false;
return _canvas;
}
/// <summary>
/// Marks the layer as needing redraw.
/// </summary>
public void Invalidate()
{
_isDirty = true;
}
/// <summary>
/// Draws this layer to the target canvas.
/// </summary>
public void DrawTo(SKCanvas canvas, SKRect bounds)
{
if (!IsVisible || _bitmap == null) return;
using var paint = new SKPaint
{
Color = SKColors.White.WithAlpha((byte)(Opacity * 255))
};
canvas.DrawBitmap(_bitmap, bounds.Left, bounds.Top, paint);
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_canvas?.Dispose();
_bitmap?.Dispose();
}
}
/// <summary>
/// Provides text rendering optimization with glyph caching.
/// </summary>
public class TextRenderCache : IDisposable
{
private readonly Dictionary<TextCacheKey, SKBitmap> _cache = new();
private readonly object _lock = new();
private int _maxEntries = 500;
private bool _disposed;
/// <summary>
/// Gets or sets the maximum number of cached text entries.
/// </summary>
public int MaxEntries
{
get => _maxEntries;
set => _maxEntries = Math.Max(10, value);
}
/// <summary>
/// Gets a cached text bitmap or creates one.
/// </summary>
public SKBitmap GetOrCreate(string text, SKPaint paint)
{
var key = new TextCacheKey(text, paint);
lock (_lock)
{
if (_cache.TryGetValue(key, out var cached))
{
return cached;
}
// Create text bitmap
var bounds = new SKRect();
paint.MeasureText(text, ref bounds);
int width = Math.Max(1, (int)Math.Ceiling(bounds.Width) + 2);
int height = Math.Max(1, (int)Math.Ceiling(bounds.Height) + 2);
var bitmap = new SKBitmap(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
using (var canvas = new SKCanvas(bitmap))
{
canvas.Clear(SKColors.Transparent);
canvas.DrawText(text, -bounds.Left + 1, -bounds.Top + 1, paint);
}
// Trim cache if needed
if (_cache.Count >= _maxEntries)
{
var oldest = _cache.First();
oldest.Value.Dispose();
_cache.Remove(oldest.Key);
}
_cache[key] = bitmap;
return bitmap;
}
}
/// <summary>
/// Clears all cached text.
/// </summary>
public void Clear()
{
lock (_lock)
{
foreach (var entry in _cache.Values)
{
entry.Dispose();
}
_cache.Clear();
}
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
Clear();
}
private readonly struct TextCacheKey : IEquatable<TextCacheKey>
{
private readonly string _text;
private readonly float _textSize;
private readonly SKColor _color;
private readonly int _weight;
private readonly int _hashCode;
public TextCacheKey(string text, SKPaint paint)
{
_text = text;
_textSize = paint.TextSize;
_color = paint.Color;
_weight = paint.Typeface?.FontWeight ?? (int)SKFontStyleWeight.Normal;
_hashCode = HashCode.Combine(_text, _textSize, _color, _weight);
}
public bool Equals(TextCacheKey other)
{
return _text == other._text &&
Math.Abs(_textSize - other._textSize) < 0.001f &&
_color == other._color &&
_weight == other._weight;
}
public override bool Equals(object? obj) => obj is TextCacheKey other && Equals(other);
public override int GetHashCode() => _hashCode;
} }
} }

111
Rendering/RenderLayer.cs Normal file
View File

@@ -0,0 +1,111 @@
using System;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering;
public class RenderLayer : IDisposable
{
private SKBitmap? _bitmap;
private SKCanvas? _canvas;
private bool _isDirty = true;
private SKRect _bounds;
private bool _disposed;
public int ZIndex { get; }
public bool IsDirty => _isDirty;
public bool IsVisible { get; set; } = true;
public float Opacity { get; set; } = 1f;
public RenderLayer(int zIndex)
{
ZIndex = zIndex;
}
public SKCanvas BeginDraw(SKRect bounds)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Expected O, but got Unknown
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Expected O, but got Unknown
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
if (_bitmap == null || _bounds != bounds)
{
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
SKCanvas? canvas = _canvas;
if (canvas != null)
{
((SKNativeObject)canvas).Dispose();
}
int num = Math.Max(1, (int)((SKRect)(ref bounds)).Width);
int num2 = Math.Max(1, (int)((SKRect)(ref bounds)).Height);
_bitmap = new SKBitmap(num, num2, (SKColorType)4, (SKAlphaType)2);
_canvas = new SKCanvas(_bitmap);
_bounds = bounds;
}
_canvas.Clear(SKColors.Transparent);
_isDirty = false;
return _canvas;
}
public void Invalidate()
{
_isDirty = true;
}
public void DrawTo(SKCanvas canvas, SKRect bounds)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Expected O, but got Unknown
if (!IsVisible || _bitmap == null)
{
return;
}
SKPaint val = new SKPaint
{
Color = ((SKColor)(ref SKColors.White)).WithAlpha((byte)(Opacity * 255f))
};
try
{
canvas.DrawBitmap(_bitmap, ((SKRect)(ref bounds)).Left, ((SKRect)(ref bounds)).Top, val);
}
finally
{
((IDisposable)val)?.Dispose();
}
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
SKCanvas? canvas = _canvas;
if (canvas != null)
{
((SKNativeObject)canvas).Dispose();
}
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering;
public class ResourceCache : IDisposable
{
private readonly Dictionary<string, SKTypeface> _typefaces = new Dictionary<string, SKTypeface>();
private bool _disposed;
public SKTypeface GetTypeface(string fontFamily, SKFontStyle style)
{
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
string key = $"{fontFamily}_{style.Weight}_{style.Width}_{style.Slant}";
if (!_typefaces.TryGetValue(key, out SKTypeface value))
{
value = SKTypeface.FromFamilyName(fontFamily, style) ?? SKTypeface.Default;
_typefaces[key] = value;
}
return value;
}
public void Clear()
{
foreach (SKTypeface value in _typefaces.Values)
{
((SKNativeObject)value).Dispose();
}
_typefaces.Clear();
}
public void Dispose()
{
if (!_disposed)
{
Clear();
_disposed = true;
}
}
}

View File

@@ -1,49 +1,54 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.Linq;
using SkiaSharp;
using Microsoft.Maui.Platform.Linux.Window; using Microsoft.Maui.Platform.Linux.Window;
using Microsoft.Maui.Platform; using SkiaSharp;
using System.Runtime.InteropServices;
namespace Microsoft.Maui.Platform.Linux.Rendering; namespace Microsoft.Maui.Platform.Linux.Rendering;
/// <summary>
/// Manages Skia rendering to an X11 window with dirty region optimization.
/// </summary>
public class SkiaRenderingEngine : IDisposable public class SkiaRenderingEngine : IDisposable
{ {
private readonly X11Window _window; private readonly X11Window _window;
private SKBitmap? _bitmap; private SKBitmap? _bitmap;
private SKBitmap? _backBuffer; private SKBitmap? _backBuffer;
private SKCanvas? _canvas; private SKCanvas? _canvas;
private SKImageInfo _imageInfo; private SKImageInfo _imageInfo;
private bool _disposed; private bool _disposed;
private bool _fullRedrawNeeded = true; private bool _fullRedrawNeeded = true;
// Dirty region tracking for optimized rendering private readonly List<SKRect> _dirtyRegions = new List<SKRect>();
private readonly List<SKRect> _dirtyRegions = new();
private readonly object _dirtyLock = new(); private readonly object _dirtyLock = new object();
private const int MaxDirtyRegions = 32; private const int MaxDirtyRegions = 32;
private const float RegionMergeThreshold = 0.3f; // Merge if overlap > 30%
private const float RegionMergeThreshold = 0.3f;
public static SkiaRenderingEngine? Current { get; private set; } public static SkiaRenderingEngine? Current { get; private set; }
public ResourceCache ResourceCache { get; }
public int Width => _imageInfo.Width;
public int Height => _imageInfo.Height;
/// <summary> public ResourceCache ResourceCache { get; }
/// Gets or sets whether dirty region optimization is enabled.
/// When disabled, full redraws occur (useful for debugging). public int Width => ((SKImageInfo)(ref _imageInfo)).Width;
/// </summary>
public int Height => ((SKImageInfo)(ref _imageInfo)).Height;
public bool EnableDirtyRegionOptimization { get; set; } = true; public bool EnableDirtyRegionOptimization { get; set; } = true;
/// <summary>
/// Gets the number of dirty regions in the current frame.
/// </summary>
public int DirtyRegionCount public int DirtyRegionCount
{ {
get { lock (_dirtyLock) return _dirtyRegions.Count; } get
{
lock (_dirtyLock)
{
return _dirtyRegions.Count;
}
}
} }
public SkiaRenderingEngine(X11Window window) public SkiaRenderingEngine(X11Window window)
@@ -51,30 +56,43 @@ public class SkiaRenderingEngine : IDisposable
_window = window; _window = window;
ResourceCache = new ResourceCache(); ResourceCache = new ResourceCache();
Current = this; Current = this;
CreateSurface(window.Width, window.Height); CreateSurface(window.Width, window.Height);
_window.Resized += OnWindowResized; _window.Resized += OnWindowResized;
_window.Exposed += OnWindowExposed; _window.Exposed += OnWindowExposed;
} }
private void CreateSurface(int width, int height) private void CreateSurface(int width, int height)
{ {
_bitmap?.Dispose(); //IL_0044: Unknown result type (might be due to invalid IL or missing references)
_backBuffer?.Dispose(); //IL_0049: Unknown result type (might be due to invalid IL or missing references)
_canvas?.Dispose(); //IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
_imageInfo = new SKImageInfo( //IL_005f: Expected O, but got Unknown
Math.Max(1, width), //IL_0061: Unknown result type (might be due to invalid IL or missing references)
Math.Max(1, height), //IL_0066: Unknown result type (might be due to invalid IL or missing references)
SKColorType.Bgra8888, //IL_0070: Expected O, but got Unknown
SKAlphaType.Premul); //IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_0081: Expected O, but got Unknown
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
SKBitmap? backBuffer = _backBuffer;
if (backBuffer != null)
{
((SKNativeObject)backBuffer).Dispose();
}
SKCanvas? canvas = _canvas;
if (canvas != null)
{
((SKNativeObject)canvas).Dispose();
}
_imageInfo = new SKImageInfo(Math.Max(1, width), Math.Max(1, height), (SKColorType)6, (SKAlphaType)2);
_bitmap = new SKBitmap(_imageInfo); _bitmap = new SKBitmap(_imageInfo);
_backBuffer = new SKBitmap(_imageInfo); _backBuffer = new SKBitmap(_imageInfo);
_canvas = new SKCanvas(_bitmap); _canvas = new SKCanvas(_bitmap);
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
lock (_dirtyLock) lock (_dirtyLock)
{ {
_dirtyRegions.Clear(); _dirtyRegions.Clear();
@@ -91,253 +109,264 @@ public class SkiaRenderingEngine : IDisposable
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
} }
/// <summary>
/// Marks the entire surface as needing redraw.
/// </summary>
public void InvalidateAll() public void InvalidateAll()
{ {
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
} }
/// <summary>
/// Marks a specific region as needing redraw.
/// Multiple regions are tracked and merged for efficiency.
/// </summary>
public void InvalidateRegion(SKRect region) public void InvalidateRegion(SKRect region)
{ {
if (region.IsEmpty || region.Width <= 0 || region.Height <= 0) //IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
if (((SKRect)(ref region)).IsEmpty || ((SKRect)(ref region)).Width <= 0f || ((SKRect)(ref region)).Height <= 0f)
{
return; return;
}
// Clamp to surface bounds region = SKRect.Intersect(region, new SKRect(0f, 0f, (float)Width, (float)Height));
region = SKRect.Intersect(region, new SKRect(0, 0, Width, Height)); if (((SKRect)(ref region)).IsEmpty)
if (region.IsEmpty) {
return; return;
}
lock (_dirtyLock) lock (_dirtyLock)
{ {
// If we have too many regions, just do a full redraw if (_dirtyRegions.Count >= 32)
if (_dirtyRegions.Count >= MaxDirtyRegions)
{ {
_fullRedrawNeeded = true; _fullRedrawNeeded = true;
_dirtyRegions.Clear(); _dirtyRegions.Clear();
return; return;
} }
// Try to merge with existing regions
for (int i = 0; i < _dirtyRegions.Count; i++) for (int i = 0; i < _dirtyRegions.Count; i++)
{ {
var existing = _dirtyRegions[i]; SKRect val = _dirtyRegions[i];
if (ShouldMergeRegions(existing, region)) if (ShouldMergeRegions(val, region))
{ {
_dirtyRegions[i] = SKRect.Union(existing, region); _dirtyRegions[i] = SKRect.Union(val, region);
return; return;
} }
} }
_dirtyRegions.Add(region); _dirtyRegions.Add(region);
} }
} }
private bool ShouldMergeRegions(SKRect a, SKRect b) private bool ShouldMergeRegions(SKRect a, SKRect b)
{ {
// Check if regions overlap //IL_0000: Unknown result type (might be due to invalid IL or missing references)
var intersection = SKRect.Intersect(a, b); //IL_0001: Unknown result type (might be due to invalid IL or missing references)
if (intersection.IsEmpty) //IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
SKRect val = SKRect.Intersect(a, b);
if (((SKRect)(ref val)).IsEmpty)
{ {
// Check if they're adjacent (within a few pixels) SKRect val2 = default(SKRect);
var expanded = new SKRect(a.Left - 4, a.Top - 4, a.Right + 4, a.Bottom + 4); ((SKRect)(ref val2))._002Ector(((SKRect)(ref a)).Left - 4f, ((SKRect)(ref a)).Top - 4f, ((SKRect)(ref a)).Right + 4f, ((SKRect)(ref a)).Bottom + 4f);
return expanded.IntersectsWith(b); return ((SKRect)(ref val2)).IntersectsWith(b);
}
float num = ((SKRect)(ref val)).Width * ((SKRect)(ref val)).Height;
float val3 = ((SKRect)(ref a)).Width * ((SKRect)(ref a)).Height;
float val4 = ((SKRect)(ref b)).Width * ((SKRect)(ref b)).Height;
float num2 = Math.Min(val3, val4);
return num / num2 >= 0.3f;
} }
// Merge if intersection is significant relative to either region
var intersectionArea = intersection.Width * intersection.Height;
var aArea = a.Width * a.Height;
var bArea = b.Width * b.Height;
var minArea = Math.Min(aArea, bArea);
return intersectionArea / minArea >= RegionMergeThreshold;
}
/// <summary>
/// Renders the view tree, optionally using dirty region optimization.
/// </summary>
public void Render(SkiaView rootView) public void Render(SkiaView rootView)
{ {
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_0099: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Unknown result type (might be due to invalid IL or missing references)
if (_canvas == null || _bitmap == null) if (_canvas == null || _bitmap == null)
{
return; return;
}
// Measure and arrange SKSize availableSize = default(SKSize);
var availableSize = new SKSize(Width, Height); ((SKSize)(ref availableSize))._002Ector((float)Width, (float)Height);
rootView.Measure(availableSize); rootView.Measure(availableSize);
rootView.Arrange(new SKRect(0, 0, Width, Height)); rootView.Arrange(new SKRect(0f, 0f, (float)Width, (float)Height));
bool flag = _fullRedrawNeeded || !EnableDirtyRegionOptimization;
// Determine what to redraw List<SKRect> list;
List<SKRect> regionsToRedraw;
bool isFullRedraw = _fullRedrawNeeded || !EnableDirtyRegionOptimization;
lock (_dirtyLock) lock (_dirtyLock)
{ {
if (isFullRedraw) if (flag)
{ {
regionsToRedraw = new List<SKRect> { new SKRect(0, 0, Width, Height) }; list = new List<SKRect>
{
new SKRect(0f, 0f, (float)Width, (float)Height)
};
_dirtyRegions.Clear(); _dirtyRegions.Clear();
_fullRedrawNeeded = false; _fullRedrawNeeded = false;
} }
else if (_dirtyRegions.Count == 0)
{
// Nothing to redraw
return;
}
else else
{ {
regionsToRedraw = MergeOverlappingRegions(_dirtyRegions.ToList()); if (_dirtyRegions.Count == 0)
{
return;
}
list = MergeOverlappingRegions(_dirtyRegions.ToList());
_dirtyRegions.Clear(); _dirtyRegions.Clear();
} }
} }
foreach (SKRect item in list)
// Render dirty regions
foreach (var region in regionsToRedraw)
{ {
RenderRegion(rootView, region, isFullRedraw); RenderRegion(rootView, item, flag);
} }
// Draw popup overlays (always on top, full redraw)
SkiaView.DrawPopupOverlays(_canvas); SkiaView.DrawPopupOverlays(_canvas);
// Draw modal dialogs on top of everything
if (LinuxDialogService.HasActiveDialog) if (LinuxDialogService.HasActiveDialog)
{ {
LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); LinuxDialogService.DrawDialogs(_canvas, new SKRect(0f, 0f, (float)Width, (float)Height));
} }
_canvas.Flush(); _canvas.Flush();
// Present to X11 window
PresentToWindow(); PresentToWindow();
} }
private void RenderRegion(SkiaView rootView, SKRect region, bool isFullRedraw) private void RenderRegion(SkiaView rootView, SKRect region, bool isFullRedraw)
{ {
if (_canvas == null) return; //IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_003e: Expected O, but got Unknown
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
if (_canvas == null)
{
return;
}
_canvas.Save(); _canvas.Save();
if (!isFullRedraw) if (!isFullRedraw)
{ {
// Clip to dirty region for partial updates _canvas.ClipRect(region, (SKClipOperation)1, false);
_canvas.ClipRect(region);
} }
SKPaint val = new SKPaint
// Clear the region {
using var clearPaint = new SKPaint { Color = SKColors.White, Style = SKPaintStyle.Fill }; Color = SKColors.White,
_canvas.DrawRect(region, clearPaint); Style = (SKPaintStyle)0
};
// Draw the view tree (views will naturally clip to their bounds) try
{
_canvas.DrawRect(region, val);
rootView.Draw(_canvas); rootView.Draw(_canvas);
_canvas.Restore(); _canvas.Restore();
} }
finally
{
((IDisposable)val)?.Dispose();
}
}
private List<SKRect> MergeOverlappingRegions(List<SKRect> regions) private List<SKRect> MergeOverlappingRegions(List<SKRect> regions)
{ {
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
if (regions.Count <= 1) if (regions.Count <= 1)
{
return regions; return regions;
}
var merged = new List<SKRect>(); List<SKRect> list = new List<SKRect>();
var used = new bool[regions.Count]; bool[] array = new bool[regions.Count];
for (int i = 0; i < regions.Count; i++) for (int i = 0; i < regions.Count; i++)
{ {
if (used[i]) continue; if (array[i])
{
var current = regions[i]; continue;
used[i] = true; }
SKRect val = regions[i];
// Keep merging until no more merges possible array[i] = true;
bool didMerge; bool flag;
do do
{ {
didMerge = false; flag = false;
for (int j = i + 1; j < regions.Count; j++) for (int j = i + 1; j < regions.Count; j++)
{ {
if (used[j]) continue; if (!array[j] && ShouldMergeRegions(val, regions[j]))
if (ShouldMergeRegions(current, regions[j]))
{ {
current = SKRect.Union(current, regions[j]); val = SKRect.Union(val, regions[j]);
used[j] = true; array[j] = true;
didMerge = true; flag = true;
} }
} }
} while (didMerge);
merged.Add(current);
} }
while (flag);
return merged; list.Add(val);
}
return list;
} }
private void PresentToWindow() private void PresentToWindow()
{ {
if (_bitmap == null) return; if (_bitmap != null)
{
var pixels = _bitmap.GetPixels(); IntPtr pixels = _bitmap.GetPixels();
if (pixels == IntPtr.Zero) return; if (pixels != IntPtr.Zero)
{
_window.DrawPixels(pixels, _imageInfo.Width, _imageInfo.Height, _imageInfo.RowBytes); _window.DrawPixels(pixels, ((SKImageInfo)(ref _imageInfo)).Width, ((SKImageInfo)(ref _imageInfo)).Height, ((SKImageInfo)(ref _imageInfo)).RowBytes);
}
}
} }
public SKCanvas? GetCanvas() => _canvas; public SKCanvas? GetCanvas()
{
return _canvas;
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!_disposed) if (_disposed)
{ {
return;
}
if (disposing) if (disposing)
{ {
_window.Resized -= OnWindowResized; _window.Resized -= OnWindowResized;
_window.Exposed -= OnWindowExposed; _window.Exposed -= OnWindowExposed;
_canvas?.Dispose(); SKCanvas? canvas = _canvas;
_bitmap?.Dispose(); if (canvas != null)
_backBuffer?.Dispose(); {
((SKNativeObject)canvas).Dispose();
}
SKBitmap? bitmap = _bitmap;
if (bitmap != null)
{
((SKNativeObject)bitmap).Dispose();
}
SKBitmap? backBuffer = _backBuffer;
if (backBuffer != null)
{
((SKNativeObject)backBuffer).Dispose();
}
ResourceCache.Dispose(); ResourceCache.Dispose();
if (Current == this) Current = null; if (Current == this)
{
Current = null;
}
} }
_disposed = true; _disposed = true;
} }
}
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(disposing: true);
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
} }
public class ResourceCache : IDisposable
{
private readonly Dictionary<string, SKTypeface> _typefaces = new();
private bool _disposed;
public SKTypeface GetTypeface(string fontFamily, SKFontStyle style)
{
var key = $"{fontFamily}_{style.Weight}_{style.Width}_{style.Slant}";
if (!_typefaces.TryGetValue(key, out var typeface))
{
typeface = SKTypeface.FromFamilyName(fontFamily, style) ?? SKTypeface.Default;
_typefaces[key] = typeface;
}
return typeface;
}
public void Clear()
{
foreach (var tf in _typefaces.Values) tf.Dispose();
_typefaces.Clear();
}
public void Dispose()
{
if (!_disposed) { Clear(); _disposed = true; }
}
}

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SkiaSharp;
namespace Microsoft.Maui.Platform.Linux.Rendering;
public class TextRenderCache : IDisposable
{
private readonly struct TextCacheKey : IEquatable<TextCacheKey>
{
private readonly string _text;
private readonly float _textSize;
private readonly SKColor _color;
private readonly int _weight;
private readonly int _hashCode;
public TextCacheKey(string text, SKPaint paint)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
_text = text;
_textSize = paint.TextSize;
_color = paint.Color;
SKTypeface typeface = paint.Typeface;
_weight = ((typeface != null) ? typeface.FontWeight : 400);
_hashCode = HashCode.Combine<string, float, SKColor, int>(_text, _textSize, _color, _weight);
}
public bool Equals(TextCacheKey other)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
if (_text == other._text && Math.Abs(_textSize - other._textSize) < 0.001f && _color == other._color)
{
return _weight == other._weight;
}
return false;
}
public override bool Equals(object? obj)
{
if (obj is TextCacheKey other)
{
return Equals(other);
}
return false;
}
public override int GetHashCode()
{
return _hashCode;
}
}
private readonly Dictionary<TextCacheKey, SKBitmap> _cache = new Dictionary<TextCacheKey, SKBitmap>();
private readonly object _lock = new object();
private int _maxEntries = 500;
private bool _disposed;
public int MaxEntries
{
get
{
return _maxEntries;
}
set
{
_maxEntries = Math.Max(10, value);
}
}
public SKBitmap GetOrCreate(string text, SKPaint paint)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
//IL_007f: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Expected O, but got Unknown
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
TextCacheKey key = new TextCacheKey(text, paint);
lock (_lock)
{
if (_cache.TryGetValue(key, out SKBitmap value))
{
return value;
}
SKRect val = default(SKRect);
paint.MeasureText(text, ref val);
int num = Math.Max(1, (int)Math.Ceiling(((SKRect)(ref val)).Width) + 2);
int num2 = Math.Max(1, (int)Math.Ceiling(((SKRect)(ref val)).Height) + 2);
SKBitmap val2 = new SKBitmap(num, num2, (SKColorType)4, (SKAlphaType)2);
SKCanvas val3 = new SKCanvas(val2);
try
{
val3.Clear(SKColors.Transparent);
val3.DrawText(text, 0f - ((SKRect)(ref val)).Left + 1f, 0f - ((SKRect)(ref val)).Top + 1f, paint);
}
finally
{
((IDisposable)val3)?.Dispose();
}
if (_cache.Count >= _maxEntries)
{
KeyValuePair<TextCacheKey, SKBitmap> keyValuePair = _cache.First();
((SKNativeObject)keyValuePair.Value).Dispose();
_cache.Remove(keyValuePair.Key);
}
_cache[key] = val2;
return val2;
}
}
public void Clear()
{
lock (_lock)
{
foreach (SKBitmap value in _cache.Values)
{
((SKNativeObject)value).Dispose();
}
_cache.Clear();
}
}
public void Dispose()
{
if (!_disposed)
{
_disposed = true;
Clear();
}
}
}

View File

@@ -0,0 +1,25 @@
using System.Collections.Generic;
namespace Microsoft.Maui.Platform.Linux.Services;
internal class _003CFontFallbackManager_003EFAC9D2911A2850E174CCA7662C668F37C2FBBA325CAF5C11AFE3FA59C16CC64ED__StringBuilder
{
private readonly List<char> _chars = new List<char>();
public int Length => _chars.Count;
public void Append(string s)
{
_chars.AddRange(s);
}
public void Clear()
{
_chars.Clear();
}
public override string ToString()
{
return new string(_chars.ToArray());
}
}

View File

@@ -0,0 +1,52 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Services;
public static class AccessibilityServiceFactory
{
private static IAccessibilityService? _instance;
private static readonly object _lock = new object();
public static IAccessibilityService Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = CreateService();
}
}
}
return _instance;
}
}
private static IAccessibilityService CreateService()
{
try
{
AtSpi2AccessibilityService atSpi2AccessibilityService = new AtSpi2AccessibilityService();
atSpi2AccessibilityService.Initialize();
return atSpi2AccessibilityService;
}
catch (Exception ex)
{
Console.WriteLine("AccessibilityServiceFactory: Failed to create AT-SPI2 service - " + ex.Message);
return new NullAccessibilityService();
}
}
public static void Reset()
{
lock (_lock)
{
_instance?.Shutdown();
_instance = null;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public class AccessibleAction
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string? KeyBinding { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public enum AccessibleProperty
{
Name,
Description,
Role,
Value,
Parent,
Children
}

View File

@@ -0,0 +1,20 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public struct AccessibleRect
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public AccessibleRect(int x, int y, int width, int height)
{
X = x;
Y = y;
Width = width;
Height = height;
}
}

View File

@@ -0,0 +1,49 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public enum AccessibleRole
{
Unknown,
Window,
Application,
Panel,
Frame,
Button,
CheckBox,
RadioButton,
ComboBox,
Entry,
Label,
List,
ListItem,
Menu,
MenuBar,
MenuItem,
ScrollBar,
Slider,
SpinButton,
StatusBar,
Tab,
TabPanel,
Text,
ToggleButton,
ToolBar,
ToolTip,
Tree,
TreeItem,
Image,
ProgressBar,
Separator,
Link,
Table,
TableCell,
TableRow,
TableColumnHeader,
TableRowHeader,
PageTab,
PageTabList,
Dialog,
Alert,
Filler,
Icon,
Canvas
}

View File

@@ -0,0 +1,38 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public enum AccessibleState
{
Active,
Armed,
Busy,
Checked,
Collapsed,
Defunct,
Editable,
Enabled,
Expandable,
Expanded,
Focusable,
Focused,
Horizontal,
Iconified,
Modal,
MultiLine,
Opaque,
Pressed,
Resizable,
Selectable,
Selected,
Sensitive,
Showing,
SingleLine,
Stale,
Transient,
Vertical,
Visible,
ManagesDescendants,
Indeterminate,
Required,
InvalidEntry,
ReadOnly
}

View File

@@ -0,0 +1,50 @@
using System;
namespace Microsoft.Maui.Platform.Linux.Services;
[Flags]
public enum AccessibleStates : long
{
None = 0L,
Active = 1L,
Armed = 2L,
Busy = 4L,
Checked = 8L,
Collapsed = 0x10L,
Defunct = 0x20L,
Editable = 0x40L,
Enabled = 0x80L,
Expandable = 0x100L,
Expanded = 0x200L,
Focusable = 0x400L,
Focused = 0x800L,
HasToolTip = 0x1000L,
Horizontal = 0x2000L,
Iconified = 0x4000L,
Modal = 0x8000L,
MultiLine = 0x10000L,
MultiSelectable = 0x20000L,
Opaque = 0x40000L,
Pressed = 0x80000L,
Resizable = 0x100000L,
Selectable = 0x200000L,
Selected = 0x400000L,
Sensitive = 0x800000L,
Showing = 0x1000000L,
SingleLine = 0x2000000L,
Stale = 0x4000000L,
Transient = 0x8000000L,
Vertical = 0x10000000L,
Visible = 0x20000000L,
ManagesDescendants = 0x40000000L,
Indeterminate = 0x80000000L,
Required = 0x100000000L,
Truncated = 0x200000000L,
Animated = 0x400000000L,
InvalidEntry = 0x800000000L,
SupportsAutocompletion = 0x1000000000L,
SelectableText = 0x2000000000L,
IsDefault = 0x4000000000L,
Visited = 0x8000000000L,
ReadOnly = 0x10000000000L
}

View File

@@ -0,0 +1,7 @@
namespace Microsoft.Maui.Platform.Linux.Services;
public enum AnnouncementPriority
{
Polite,
Assertive
}

View File

@@ -1,78 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements. using System;
// The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.ApplicationModel;
namespace Microsoft.Maui.Platform.Linux.Services; namespace Microsoft.Maui.Platform.Linux.Services;
/// <summary>
/// Linux app actions implementation using desktop file actions.
/// </summary>
public class AppActionsService : IAppActions public class AppActionsService : IAppActions
{ {
private readonly List<AppAction> _actions = new(); private readonly List<AppAction> _actions = new List<AppAction>();
private static readonly string DesktopFilesPath;
static AppActionsService() private static readonly string DesktopFilesPath;
{
DesktopFilesPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"applications");
}
public bool IsSupported => true; public bool IsSupported => true;
public event EventHandler<AppActionEventArgs>? AppActionActivated; public event EventHandler<AppActionEventArgs>? AppActionActivated;
static AppActionsService()
{
DesktopFilesPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "applications");
}
public Task<IEnumerable<AppAction>> GetAsync() public Task<IEnumerable<AppAction>> GetAsync()
{ {
return Task.FromResult<IEnumerable<AppAction>>(_actions.AsReadOnly()); return Task.FromResult((IEnumerable<AppAction>)_actions.AsReadOnly());
} }
public Task SetAsync(IEnumerable<AppAction> actions) public Task SetAsync(IEnumerable<AppAction> actions)
{ {
_actions.Clear(); _actions.Clear();
_actions.AddRange(actions); _actions.AddRange(actions);
// On Linux, app actions can be exposed via .desktop file Actions
// This would require modifying the application's .desktop file
UpdateDesktopActions(); UpdateDesktopActions();
return Task.CompletedTask; return Task.CompletedTask;
} }
private void UpdateDesktopActions() private void UpdateDesktopActions()
{ {
// Desktop actions are defined in the .desktop file
// Example:
// [Desktop Action new-window]
// Name=New Window
// Exec=myapp --action=new-window
// For a proper implementation, we would need to:
// 1. Find or create the application's .desktop file
// 2. Add [Desktop Action] sections for each action
// 3. The actions would then appear in the dock/launcher right-click menu
// This is a simplified implementation that logs actions
// A full implementation would require more system integration
} }
/// <summary>
/// Call this method to handle command-line action arguments.
/// </summary>
public void HandleActionArgument(string actionId) public void HandleActionArgument(string actionId)
{ {
var action = _actions.FirstOrDefault(a => a.Id == actionId); //IL_0035: Unknown result type (might be due to invalid IL or missing references)
if (action != null) //IL_003f: Expected O, but got Unknown
AppAction val = ((IEnumerable<AppAction>)_actions).FirstOrDefault((Func<AppAction, bool>)((AppAction a) => a.Id == actionId));
if (val != null)
{ {
AppActionActivated?.Invoke(this, new AppActionEventArgs(action)); this.AppActionActivated?.Invoke(this, new AppActionEventArgs(val));
} }
} }
/// <summary>
/// Creates a .desktop file for the application with the defined actions.
/// </summary>
public void CreateDesktopFile(string appName, string execPath, string? iconPath = null) public void CreateDesktopFile(string appName, string execPath, string? iconPath = null)
{ {
try try
@@ -81,67 +59,91 @@ public class AppActionsService : IAppActions
{ {
Directory.CreateDirectory(DesktopFilesPath); Directory.CreateDirectory(DesktopFilesPath);
} }
string contents = GenerateDesktopFileContent(appName, execPath, iconPath);
var desktopContent = GenerateDesktopFileContent(appName, execPath, iconPath); string path = Path.Combine(DesktopFilesPath, appName.ToLowerInvariant().Replace(" ", "-") + ".desktop");
var desktopFilePath = Path.Combine(DesktopFilesPath, $"{appName.ToLowerInvariant().Replace(" ", "-")}.desktop"); File.WriteAllText(path, contents);
File.SetUnixFileMode(path, UnixFileMode.OtherRead | UnixFileMode.GroupRead | UnixFileMode.UserExecute | UnixFileMode.UserWrite | UnixFileMode.UserRead);
File.WriteAllText(desktopFilePath, desktopContent);
// Make it executable
File.SetUnixFileMode(desktopFilePath,
UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute |
UnixFileMode.GroupRead | UnixFileMode.OtherRead);
} }
catch catch
{ {
// Silently fail - desktop file creation is optional
} }
} }
private string GenerateDesktopFileContent(string appName, string execPath, string? iconPath) private string GenerateDesktopFileContent(string appName, string execPath, string? iconPath)
{ {
var content = new System.Text.StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("[Desktop Entry]");
content.AppendLine("[Desktop Entry]"); stringBuilder.AppendLine("Type=Application");
content.AppendLine("Type=Application"); StringBuilder stringBuilder2 = stringBuilder;
content.AppendLine($"Name={appName}"); StringBuilder stringBuilder3 = stringBuilder2;
content.AppendLine($"Exec={execPath} %U"); StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(5, 1, stringBuilder2);
handler.AppendLiteral("Name=");
handler.AppendFormatted(appName);
stringBuilder3.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder4 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
handler.AppendLiteral("Exec=");
handler.AppendFormatted(execPath);
handler.AppendLiteral(" %U");
stringBuilder4.AppendLine(ref handler);
if (!string.IsNullOrEmpty(iconPath) && File.Exists(iconPath)) if (!string.IsNullOrEmpty(iconPath) && File.Exists(iconPath))
{ {
content.AppendLine($"Icon={iconPath}"); stringBuilder2 = stringBuilder;
StringBuilder stringBuilder5 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(5, 1, stringBuilder2);
handler.AppendLiteral("Icon=");
handler.AppendFormatted(iconPath);
stringBuilder5.AppendLine(ref handler);
} }
stringBuilder.AppendLine("Terminal=false");
content.AppendLine("Terminal=false"); stringBuilder.AppendLine("Categories=Utility;");
content.AppendLine("Categories=Utility;");
// Add actions list
if (_actions.Count > 0) if (_actions.Count > 0)
{ {
var actionIds = string.Join(";", _actions.Select(a => a.Id)); string value = string.Join(";", _actions.Select((AppAction a) => a.Id));
content.AppendLine($"Actions={actionIds};"); stringBuilder2 = stringBuilder;
content.AppendLine(); StringBuilder stringBuilder6 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder2);
// Add each action section handler.AppendLiteral("Actions=");
foreach (var action in _actions) handler.AppendFormatted(value);
handler.AppendLiteral(";");
stringBuilder6.AppendLine(ref handler);
stringBuilder.AppendLine();
foreach (AppAction action in _actions)
{ {
content.AppendLine($"[Desktop Action {action.Id}]"); stringBuilder2 = stringBuilder;
content.AppendLine($"Name={action.Title}"); StringBuilder stringBuilder7 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(17, 1, stringBuilder2);
handler.AppendLiteral("[Desktop Action ");
handler.AppendFormatted(action.Id);
handler.AppendLiteral("]");
stringBuilder7.AppendLine(ref handler);
stringBuilder2 = stringBuilder;
StringBuilder stringBuilder8 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(5, 1, stringBuilder2);
handler.AppendLiteral("Name=");
handler.AppendFormatted(action.Title);
stringBuilder8.AppendLine(ref handler);
if (!string.IsNullOrEmpty(action.Subtitle)) if (!string.IsNullOrEmpty(action.Subtitle))
{ {
content.AppendLine($"Comment={action.Subtitle}"); stringBuilder2 = stringBuilder;
StringBuilder stringBuilder9 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(8, 1, stringBuilder2);
handler.AppendLiteral("Comment=");
handler.AppendFormatted(action.Subtitle);
stringBuilder9.AppendLine(ref handler);
} }
stringBuilder2 = stringBuilder;
content.AppendLine($"Exec={execPath} --action={action.Id}"); StringBuilder stringBuilder10 = stringBuilder2;
handler = new StringBuilder.AppendInterpolatedStringHandler(15, 2, stringBuilder2);
{ handler.AppendLiteral("Exec=");
} handler.AppendFormatted(execPath);
handler.AppendLiteral(" --action=");
content.AppendLine(); handler.AppendFormatted(action.Id);
stringBuilder10.AppendLine(ref handler);
stringBuilder.AppendLine();
} }
} }
return stringBuilder.ToString();
return content.ToString();
} }
} }

147
Services/AppInfoService.cs Normal file
View File

@@ -0,0 +1,147 @@
using System;
using System.Diagnostics;
using System.Reflection;
using Microsoft.Maui.ApplicationModel;
namespace Microsoft.Maui.Platform.Linux.Services;
public class AppInfoService : IAppInfo
{
private static readonly Lazy<AppInfoService> _instance = new Lazy<AppInfoService>(() => new AppInfoService());
private readonly Assembly _entryAssembly;
private readonly string _packageName;
private readonly string _name;
private readonly string _versionString;
private readonly Version _version;
private readonly string _buildString;
public static AppInfoService Instance => _instance.Value;
public string PackageName => _packageName;
public string Name => _name;
public string VersionString => _versionString;
public Version Version => _version;
public string BuildString => _buildString;
public LayoutDirection RequestedLayoutDirection => (LayoutDirection)1;
public AppTheme RequestedTheme
{
get
{
//IL_0042: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Unknown result type (might be due to invalid IL or missing references)
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
try
{
string environmentVariable = Environment.GetEnvironmentVariable("GTK_THEME");
if (!string.IsNullOrEmpty(environmentVariable) && environmentVariable.Contains("dark", StringComparison.OrdinalIgnoreCase))
{
return (AppTheme)2;
}
if (GetGnomeColorScheme().Contains("dark", StringComparison.OrdinalIgnoreCase))
{
return (AppTheme)2;
}
return (AppTheme)1;
}
catch
{
return (AppTheme)1;
}
}
}
public AppPackagingModel PackagingModel
{
get
{
if (Environment.GetEnvironmentVariable("FLATPAK_ID") == null)
{
if (Environment.GetEnvironmentVariable("SNAP") == null)
{
if (Environment.GetEnvironmentVariable("APPIMAGE") == null)
{
return (AppPackagingModel)1;
}
return (AppPackagingModel)0;
}
return (AppPackagingModel)0;
}
return (AppPackagingModel)0;
}
}
public AppInfoService()
{
_entryAssembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
_packageName = _entryAssembly.GetName().Name ?? "Unknown";
_name = _entryAssembly.GetCustomAttribute<AssemblyTitleAttribute>()?.Title ?? _packageName;
_versionString = (_version = _entryAssembly.GetName().Version ?? new Version(1, 0)).ToString();
_buildString = _entryAssembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? _versionString;
}
private string GetGnomeColorScheme()
{
try
{
using Process process = Process.Start(new ProcessStartInfo
{
FileName = "gsettings",
Arguments = "get org.gnome.desktop.interface color-scheme",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
});
if (process != null)
{
string text = process.StandardOutput.ReadToEnd();
process.WaitForExit(1000);
return text.Trim().Trim('\'');
}
}
catch
{
}
return "";
}
public void ShowSettingsUI()
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "gnome-control-center",
UseShellExecute = true
});
}
catch
{
try
{
Process.Start(new ProcessStartInfo
{
FileName = "xdg-open",
Arguments = "x-settings:",
UseShellExecute = true
});
}
catch
{
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More