Major production merge: GTK support, context menus, and dispatcher fixes
Core Infrastructure: - Add Dispatching folder with LinuxDispatcher, LinuxDispatcherProvider, LinuxDispatcherTimer - Add Native folder with P/Invoke wrappers (GTK, GLib, GDK, Cairo, WebKit) - Add GTK host window system with GtkHostWindow and GtkSkiaSurfaceWidget - Update LinuxApplication with GTK mode, theme handling, and icon support - Fix duplicate LinuxDispatcher in LinuxMauiContext Handlers: - Add GtkWebViewManager and GtkWebViewPlatformView for GTK WebView - Add FlexLayoutHandler and GestureManager - Update multiple handlers with ToViewHandler fix and missing mappers - Add MauiHandlerExtensions with ToViewHandler extension method Views: - Add SkiaContextMenu with hover, keyboard, and dark theme support - Add LinuxDialogService with context menu management - Add SkiaFlexLayout for flex container support - Update SkiaShell with RefreshTheme, MauiShell, ContentRenderer - Update SkiaWebView with SetMainWindow, ProcessGtkEvents - Update SkiaImage with LoadFromBitmap method Services: - Add AppInfoService, ConnectivityService, DeviceDisplayService, DeviceInfoService - Add GtkHostService, GtkContextMenuService, MauiIconGenerator Window: - Add CursorType enum and GtkHostWindow - Update X11Window with SetIcon, SetCursor methods Build: SUCCESS (0 errors) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,13 @@ public class X11Window : IDisposable
|
||||
private int _width;
|
||||
private int _height;
|
||||
|
||||
// Cursor handles
|
||||
private IntPtr _arrowCursor;
|
||||
private IntPtr _handCursor;
|
||||
private IntPtr _textCursor;
|
||||
private IntPtr _currentCursor;
|
||||
private CursorType _currentCursorType = CursorType.Arrow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the native display handle.
|
||||
/// </summary>
|
||||
@@ -155,7 +162,97 @@ public class X11Window : IDisposable
|
||||
// Set up WM_DELETE_WINDOW protocol for proper close handling
|
||||
_wmDeleteMessage = X11.XInternAtom(_display, "WM_DELETE_WINDOW", false);
|
||||
|
||||
// Would need XSetWMProtocols here, simplified for now
|
||||
// Initialize cursors
|
||||
_arrowCursor = X11.XCreateFontCursor(_display, 68); // XC_left_ptr
|
||||
_handCursor = X11.XCreateFontCursor(_display, 60); // XC_hand2
|
||||
_textCursor = X11.XCreateFontCursor(_display, 152); // XC_xterm
|
||||
_currentCursor = _arrowCursor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cursor type for this window.
|
||||
/// </summary>
|
||||
public void SetCursor(CursorType cursorType)
|
||||
{
|
||||
if (_currentCursorType != cursorType)
|
||||
{
|
||||
_currentCursorType = cursorType;
|
||||
IntPtr cursor = cursorType switch
|
||||
{
|
||||
CursorType.Hand => _handCursor,
|
||||
CursorType.Text => _textCursor,
|
||||
_ => _arrowCursor,
|
||||
};
|
||||
if (cursor != _currentCursor)
|
||||
{
|
||||
_currentCursor = cursor;
|
||||
X11.XDefineCursor(_display, _window, _currentCursor);
|
||||
X11.XFlush(_display);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the window icon from a file.
|
||||
/// </summary>
|
||||
public unsafe void SetIcon(string iconPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(iconPath) || !System.IO.File.Exists(iconPath))
|
||||
{
|
||||
Console.WriteLine("[X11Window] Icon file not found: " + iconPath);
|
||||
return;
|
||||
}
|
||||
Console.WriteLine("[X11Window] SetIcon called: " + iconPath);
|
||||
try
|
||||
{
|
||||
SkiaSharp.SKBitmap? bitmap = SkiaSharp.SKBitmap.Decode(iconPath);
|
||||
if (bitmap == null)
|
||||
{
|
||||
Console.WriteLine("[X11Window] Failed to load icon: " + iconPath);
|
||||
return;
|
||||
}
|
||||
Console.WriteLine($"[X11Window] Loaded bitmap: {bitmap.Width}x{bitmap.Height}");
|
||||
|
||||
// Scale to 64x64 if needed
|
||||
int targetSize = 64;
|
||||
if (bitmap.Width != targetSize || bitmap.Height != targetSize)
|
||||
{
|
||||
var scaled = new SkiaSharp.SKBitmap(targetSize, targetSize, false);
|
||||
bitmap.ScalePixels(scaled, SkiaSharp.SKFilterQuality.High);
|
||||
bitmap.Dispose();
|
||||
bitmap = scaled;
|
||||
}
|
||||
|
||||
int width = bitmap.Width;
|
||||
int height = bitmap.Height;
|
||||
int dataSize = 2 + width * height;
|
||||
uint[] iconData = new uint[dataSize];
|
||||
iconData[0] = (uint)width;
|
||||
iconData[1] = (uint)height;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
var pixel = bitmap.GetPixel(x, y);
|
||||
iconData[2 + y * width + x] = (uint)((pixel.Alpha << 24) | (pixel.Red << 16) | (pixel.Green << 8) | pixel.Blue);
|
||||
}
|
||||
}
|
||||
bitmap.Dispose();
|
||||
|
||||
IntPtr property = X11.XInternAtom(_display, "_NET_WM_ICON", false);
|
||||
IntPtr type = X11.XInternAtom(_display, "CARDINAL", false);
|
||||
fixed (uint* data = iconData)
|
||||
{
|
||||
X11.XChangeProperty(_display, _window, property, type, 32, 0, (nint)data, dataSize);
|
||||
}
|
||||
X11.XFlush(_display);
|
||||
Console.WriteLine($"[X11Window] Set window icon: {width}x{height}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("[X11Window] Failed to set icon: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user