From 88679dfae8ba3a468bc12ecaadf9b9c53ba89648 Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 17 Jan 2026 15:19:03 +0000 Subject: [PATCH] context menu fixes --- LinuxApplication.cs | 2 +- Rendering/GpuRenderingEngine.cs | 4 +- Rendering/SkiaRenderingEngine.cs | 4 +- Views/SkiaEditor.cs | 79 +++++++++++++++++++++++--------- Views/SkiaEntry.cs | 77 ++++++++++++++++++++++--------- 5 files changed, 119 insertions(+), 47 deletions(-) diff --git a/LinuxApplication.cs b/LinuxApplication.cs index 23d7672..d155db9 100644 --- a/LinuxApplication.cs +++ b/LinuxApplication.cs @@ -900,7 +900,7 @@ public class LinuxApplication : IDisposable private void OnPointerPressed(object? sender, PointerEventArgs e) { - Console.WriteLine($"[LinuxApplication] OnPointerPressed at ({e.X}, {e.Y})"); + Console.WriteLine($"[LinuxApplication] OnPointerPressed at ({e.X}, {e.Y}), Button={e.Button}"); // Route to context menu if one is active if (LinuxDialogService.HasContextMenu) diff --git a/Rendering/GpuRenderingEngine.cs b/Rendering/GpuRenderingEngine.cs index b253cbb..5af64a1 100644 --- a/Rendering/GpuRenderingEngine.cs +++ b/Rendering/GpuRenderingEngine.cs @@ -253,8 +253,8 @@ public class GpuRenderingEngine : IDisposable // Draw popup overlays SkiaView.DrawPopupOverlays(_canvas); - // Draw modal dialogs - if (LinuxDialogService.HasActiveDialog) + // Draw modal dialogs and context menus + if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu) { LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); } diff --git a/Rendering/SkiaRenderingEngine.cs b/Rendering/SkiaRenderingEngine.cs index 0cf6ee8..ee4c3bd 100644 --- a/Rendering/SkiaRenderingEngine.cs +++ b/Rendering/SkiaRenderingEngine.cs @@ -205,8 +205,8 @@ public class SkiaRenderingEngine : IDisposable // Draw popup overlays (always on top, full redraw) SkiaView.DrawPopupOverlays(_canvas); - // Draw modal dialogs on top of everything - if (LinuxDialogService.HasActiveDialog) + // Draw modal dialogs and context menus on top of everything + if (LinuxDialogService.HasActiveDialog || LinuxDialogService.HasContextMenu) { LinuxDialogService.DrawDialogs(_canvas, new SKRect(0, 0, Width, Height)); } diff --git a/Views/SkiaEditor.cs b/Views/SkiaEditor.cs index ef33e61..f904849 100644 --- a/Views/SkiaEditor.cs +++ b/Views/SkiaEditor.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux; using Microsoft.Maui.Platform.Linux.Rendering; using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; @@ -1086,11 +1087,13 @@ public class SkiaEditor : SkiaView, IInputContext public override void OnPointerPressed(PointerEventArgs e) { + Console.WriteLine($"[SkiaEditor] OnPointerPressed: Button={e.Button}, IsEnabled={IsEnabled}"); if (!IsEnabled) return; // Handle right-click context menu if (e.Button == PointerButton.Right) { + Console.WriteLine("[SkiaEditor] Right-click detected, showing context menu"); ShowContextMenu(e.X, e.Y); return; } @@ -1503,35 +1506,69 @@ public class SkiaEditor : SkiaView, IInputContext private void ShowContextMenu(float x, float y) { - Console.WriteLine($"[SkiaEditor] ShowContextMenu at ({x}, {y})"); + Console.WriteLine($"[SkiaEditor] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); bool hasSelection = _selectionLength != 0; bool hasText = !string.IsNullOrEmpty(Text); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); bool isEditable = !IsReadOnly; - GtkContextMenuService.ShowContextMenu(new List + if (LinuxApplication.IsGtkMode) { - new GtkMenuItem("Cut", () => + // Use GTK context menu when running in GTK mode (e.g., with WebView) + GtkContextMenuService.ShowContextMenu(new List { - CutToClipboard(); - Invalidate(); - }, hasSelection && isEditable), - new GtkMenuItem("Copy", () => + new GtkMenuItem("Cut", () => + { + CutToClipboard(); + Invalidate(); + }, hasSelection && isEditable), + new GtkMenuItem("Copy", () => + { + CopyToClipboard(); + }, hasSelection), + new GtkMenuItem("Paste", () => + { + PasteFromClipboard(); + Invalidate(); + }, hasClipboard && isEditable), + GtkMenuItem.Separator, + new GtkMenuItem("Select All", () => + { + SelectAll(); + Invalidate(); + }, hasText) + }); + } + else + { + // Use Skia-rendered context menu for pure Skia mode (Wayland/X11) + bool isDarkTheme = Application.Current?.RequestedTheme == AppTheme.Dark; + var items = new List { - CopyToClipboard(); - }, hasSelection), - new GtkMenuItem("Paste", () => - { - PasteFromClipboard(); - Invalidate(); - }, hasClipboard && isEditable), - GtkMenuItem.Separator, - new GtkMenuItem("Select All", () => - { - SelectAll(); - Invalidate(); - }, hasText) - }); + new ContextMenuItem("Cut", () => + { + CutToClipboard(); + Invalidate(); + }, hasSelection && isEditable), + new ContextMenuItem("Copy", () => + { + CopyToClipboard(); + }, hasSelection), + new ContextMenuItem("Paste", () => + { + PasteFromClipboard(); + Invalidate(); + }, hasClipboard && isEditable), + ContextMenuItem.Separator, + new ContextMenuItem("Select All", () => + { + SelectAll(); + Invalidate(); + }, hasText) + }; + var menu = new SkiaContextMenu(x, y, items, isDarkTheme); + LinuxDialogService.ShowContextMenu(menu); + } } #endregion diff --git a/Views/SkiaEntry.cs b/Views/SkiaEntry.cs index c96aad9..479a4ee 100644 --- a/Views/SkiaEntry.cs +++ b/Views/SkiaEntry.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using Microsoft.Maui.Controls; using Microsoft.Maui.Graphics; +using Microsoft.Maui.Platform.Linux; using Microsoft.Maui.Platform.Linux.Rendering; using Microsoft.Maui.Platform.Linux.Services; using SkiaSharp; @@ -1643,34 +1644,68 @@ public class SkiaEntry : SkiaView, IInputContext private void ShowContextMenu(float x, float y) { - Console.WriteLine($"[SkiaEntry] ShowContextMenu at ({x}, {y})"); + Console.WriteLine($"[SkiaEntry] ShowContextMenu at ({x}, {y}), IsGtkMode={LinuxApplication.IsGtkMode}"); bool hasSelection = _selectionLength != 0; bool hasText = !string.IsNullOrEmpty(Text); bool hasClipboard = !string.IsNullOrEmpty(SystemClipboard.GetText()); - GtkContextMenuService.ShowContextMenu(new List + if (LinuxApplication.IsGtkMode) { - new GtkMenuItem("Cut", () => + // Use GTK context menu when running in GTK mode (e.g., with WebView) + GtkContextMenuService.ShowContextMenu(new List { - CutToClipboard(); - Invalidate(); - }, hasSelection), - new GtkMenuItem("Copy", () => + new GtkMenuItem("Cut", () => + { + CutToClipboard(); + Invalidate(); + }, hasSelection), + new GtkMenuItem("Copy", () => + { + CopyToClipboard(); + }, hasSelection), + new GtkMenuItem("Paste", () => + { + PasteFromClipboard(); + Invalidate(); + }, hasClipboard), + GtkMenuItem.Separator, + new GtkMenuItem("Select All", () => + { + SelectAll(); + Invalidate(); + }, hasText) + }); + } + else + { + // Use Skia-rendered context menu for pure Skia mode (Wayland/X11) + bool isDarkTheme = Application.Current?.RequestedTheme == AppTheme.Dark; + var items = new List { - CopyToClipboard(); - }, hasSelection), - new GtkMenuItem("Paste", () => - { - PasteFromClipboard(); - Invalidate(); - }, hasClipboard), - GtkMenuItem.Separator, - new GtkMenuItem("Select All", () => - { - SelectAll(); - Invalidate(); - }, hasText) - }); + new ContextMenuItem("Cut", () => + { + CutToClipboard(); + Invalidate(); + }, hasSelection), + new ContextMenuItem("Copy", () => + { + CopyToClipboard(); + }, hasSelection), + new ContextMenuItem("Paste", () => + { + PasteFromClipboard(); + Invalidate(); + }, hasClipboard), + ContextMenuItem.Separator, + new ContextMenuItem("Select All", () => + { + SelectAll(); + Invalidate(); + }, hasText) + }; + var menu = new SkiaContextMenu(x, y, items, isDarkTheme); + LinuxDialogService.ShowContextMenu(menu); + } } public override void OnFocusGained()