Initial commit: .NET MAUI Linux Platform
Complete Linux platform implementation for .NET MAUI with:
- 35+ Skia-rendered controls (Button, Label, Entry, CarouselView, etc.)
- Platform services (Clipboard, FilePicker, Notifications, DragDrop, etc.)
- Accessibility support (AT-SPI2, High Contrast)
- HiDPI and Input Method support
- 216 unit tests
- CI/CD workflows
- Project templates
- Documentation
🤖 Generated with Claude Code
This commit is contained in:
258
Handlers/WindowHandler.cs
Normal file
258
Handlers/WindowHandler.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
// 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.Platform;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Microsoft.Maui.Platform.Linux.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// 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 =
|
||||
new PropertyMapper<IWindow, WindowHandler>(ElementHandler.ElementMapper)
|
||||
{
|
||||
[nameof(IWindow.Title)] = MapTitle,
|
||||
[nameof(IWindow.Content)] = MapContent,
|
||||
[nameof(IWindow.X)] = MapX,
|
||||
[nameof(IWindow.Y)] = MapY,
|
||||
[nameof(IWindow.Width)] = MapWidth,
|
||||
[nameof(IWindow.Height)] = MapHeight,
|
||||
[nameof(IWindow.MinimumWidth)] = MapMinimumWidth,
|
||||
[nameof(IWindow.MinimumHeight)] = MapMinimumHeight,
|
||||
[nameof(IWindow.MaximumWidth)] = MapMaximumWidth,
|
||||
[nameof(IWindow.MaximumHeight)] = MapMaximumHeight,
|
||||
};
|
||||
|
||||
public static CommandMapper<IWindow, WindowHandler> CommandMapper =
|
||||
new(ElementHandler.ElementCommandMapper)
|
||||
{
|
||||
};
|
||||
|
||||
public WindowHandler() : base(Mapper, CommandMapper)
|
||||
{
|
||||
}
|
||||
|
||||
public WindowHandler(IPropertyMapper? mapper, CommandMapper? commandMapper = null)
|
||||
: base(mapper ?? Mapper, commandMapper ?? CommandMapper)
|
||||
{
|
||||
}
|
||||
|
||||
protected override SkiaWindow CreatePlatformElement()
|
||||
{
|
||||
return new SkiaWindow();
|
||||
}
|
||||
|
||||
protected override void ConnectHandler(SkiaWindow platformView)
|
||||
{
|
||||
base.ConnectHandler(platformView);
|
||||
platformView.CloseRequested += OnCloseRequested;
|
||||
platformView.SizeChanged += OnSizeChanged;
|
||||
}
|
||||
|
||||
protected override void DisconnectHandler(SkiaWindow platformView)
|
||||
{
|
||||
platformView.CloseRequested -= OnCloseRequested;
|
||||
platformView.SizeChanged -= OnSizeChanged;
|
||||
base.DisconnectHandler(platformView);
|
||||
}
|
||||
|
||||
private void OnCloseRequested(object? sender, EventArgs e)
|
||||
{
|
||||
VirtualView?.Destroying();
|
||||
}
|
||||
|
||||
private void OnSizeChanged(object? sender, SizeChangedEventArgs e)
|
||||
{
|
||||
VirtualView?.FrameChanged(new Rect(0, 0, e.Width, e.Height));
|
||||
}
|
||||
|
||||
public static void MapTitle(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.Title = window.Title ?? "MAUI Application";
|
||||
}
|
||||
|
||||
public static void MapContent(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
|
||||
var content = window.Content;
|
||||
if (content?.Handler?.PlatformView is SkiaView skiaContent)
|
||||
{
|
||||
handler.PlatformView.Content = skiaContent;
|
||||
}
|
||||
}
|
||||
|
||||
public static void MapX(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.X = (int)window.X;
|
||||
}
|
||||
|
||||
public static void MapY(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.Y = (int)window.Y;
|
||||
}
|
||||
|
||||
public static void MapWidth(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.Width = (int)window.Width;
|
||||
}
|
||||
|
||||
public static void MapHeight(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.Height = (int)window.Height;
|
||||
}
|
||||
|
||||
public static void MapMinimumWidth(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.MinWidth = (int)window.MinimumWidth;
|
||||
}
|
||||
|
||||
public static void MapMinimumHeight(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.MinHeight = (int)window.MinimumHeight;
|
||||
}
|
||||
|
||||
public static void MapMaximumWidth(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.MaxWidth = (int)window.MaximumWidth;
|
||||
}
|
||||
|
||||
public static void MapMaximumHeight(WindowHandler handler, IWindow window)
|
||||
{
|
||||
if (handler.PlatformView is null) return;
|
||||
handler.PlatformView.MaxHeight = (int)window.MaximumHeight;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skia window wrapper for Linux display servers.
|
||||
/// </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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user