From f1e3630d1bf0369a447847d096d65fb8cdba376d Mon Sep 17 00:00:00 2001 From: logikonline Date: Sat, 24 Jan 2026 06:13:13 +0000 Subject: [PATCH] Fixes for GTK - still wrong --- Handlers/GtkWebViewPlatformView.cs | 134 +++++++ LinuxApplication.cs | 7 + Native/GtkNative.cs | 25 ++ OpenMaui.Controls.Linux.csproj | 5 + Services/GtkContextMenuService.cs | 80 ++++ Services/GtkThemeService.cs | 544 ++++++++++++++++++++++++++ Views/LinuxDialogService.cs | 2 + Views/SkiaAlertDialog.cs | 25 +- Views/SkiaContextMenu.cs | 26 +- Views/SkiaTheme.cs | 17 +- build/OpenMaui.Controls.Linux.targets | 21 +- 11 files changed, 845 insertions(+), 41 deletions(-) create mode 100644 Services/GtkThemeService.cs diff --git a/Handlers/GtkWebViewPlatformView.cs b/Handlers/GtkWebViewPlatformView.cs index 9501222..a879b84 100644 --- a/Handlers/GtkWebViewPlatformView.cs +++ b/Handlers/GtkWebViewPlatformView.cs @@ -154,6 +154,9 @@ public sealed class GtkWebViewPlatformView : IDisposable }; GtkNative.gtk_window_set_title(gtkDialog, title); + // Apply theme-aware CSS styling based on app's current theme + ApplyDialogTheme(gtkDialog); + // Make dialog modal to parent if we have a parent if (parentWindow != IntPtr.Zero) { @@ -211,6 +214,9 @@ public sealed class GtkWebViewPlatformView : IDisposable return false; } + // Apply theme-aware CSS styling + ApplyDialogTheme(gtkDialog); + // Get the content area IntPtr contentArea = GtkNative.gtk_dialog_get_content_area(gtkDialog); @@ -277,6 +283,134 @@ public sealed class GtkWebViewPlatformView : IDisposable } } + /// + /// Applies theme-aware CSS styling to a GTK dialog based on the app's current theme. + /// + private void ApplyDialogTheme(IntPtr gtkDialog) + { + try + { + // Check the app's current theme (not the system theme) + bool isDark = Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + + // If UserAppTheme is Unspecified, fall back to RequestedTheme + if (Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Unspecified) + { + isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + } + + Console.WriteLine($"[GtkWebViewPlatformView] ApplyDialogTheme: isDark={isDark}, UserAppTheme={Microsoft.Maui.Controls.Application.Current?.UserAppTheme}"); + + // Create comprehensive CSS based on the theme - targeting all dialog elements + string css = isDark + ? @" + * { + background-color: #303030; + color: #E0E0E0; + } + window, dialog, messagedialog, .background { + background-color: #303030; + color: #E0E0E0; + } + headerbar, headerbar *, .titlebar, .titlebar * { + background-color: #252525; + background-image: none; + color: #E0E0E0; + border-color: #404040; + box-shadow: none; + } + headerbar button, .titlebar button { + background-color: #353535; + background-image: none; + color: #E0E0E0; + } + .dialog-action-area, .dialog-action-box, actionbar { + background-color: #303030; + } + label, .message-dialog-message, .message-dialog-secondary-message { + color: #E0E0E0; + } + button { + background-image: none; + background-color: #505050; + color: #E0E0E0; + border-color: #606060; + } + button:hover { + background-color: #606060; + } + entry { + background-color: #404040; + color: #E0E0E0; + } + " + : @" + * { + background-color: #FFFFFF; + color: #212121; + } + window, dialog, messagedialog, .background { + background-color: #FFFFFF; + color: #212121; + } + headerbar, headerbar *, .titlebar, .titlebar * { + background-color: #F5F5F5; + background-image: none; + color: #212121; + border-color: #E0E0E0; + box-shadow: none; + } + headerbar button, .titlebar button { + background-color: #EBEBEB; + background-image: none; + color: #212121; + } + .dialog-action-area, .dialog-action-box, actionbar { + background-color: #FFFFFF; + } + label, .message-dialog-message, .message-dialog-secondary-message { + color: #212121; + } + button { + background-image: none; + background-color: #F5F5F5; + color: #212121; + border-color: #E0E0E0; + } + button:hover { + background-color: #E0E0E0; + } + entry { + background-color: #FFFFFF; + color: #212121; + } + "; + + // Create CSS provider and apply to the screen so all child widgets inherit it + IntPtr cssProvider = GtkNative.gtk_css_provider_new(); + if (cssProvider != IntPtr.Zero) + { + GtkNative.gtk_css_provider_load_from_data(cssProvider, css, -1, IntPtr.Zero); + + // Get the screen from the dialog and apply CSS to entire screen for this dialog + IntPtr screen = GtkNative.gtk_widget_get_screen(gtkDialog); + if (screen == IntPtr.Zero) + { + screen = GtkNative.gdk_screen_get_default(); + } + + if (screen != IntPtr.Zero) + { + GtkNative.gtk_style_context_add_provider_for_screen(screen, cssProvider, GtkNative.GTK_STYLE_PROVIDER_PRIORITY_USER); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"[GtkWebViewPlatformView] Error applying dialog theme: {ex.Message}"); + } + } + private void OnLoadChanged(IntPtr webView, int loadEvent, IntPtr userData) { try diff --git a/LinuxApplication.cs b/LinuxApplication.cs index ef0c50e..6c8b4c9 100644 --- a/LinuxApplication.cs +++ b/LinuxApplication.cs @@ -310,12 +310,19 @@ public class LinuxApplication : IDisposable Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Light based on system theme"); } + // Initialize GTK theme service and apply initial CSS + GtkThemeService.ApplyTheme(); + // Handle user-initiated theme changes ((BindableObject)mauiApplication).PropertyChanged += (s, e) => { if (e.PropertyName == "UserAppTheme") { Console.WriteLine($"[LinuxApplication] User theme changed to: {mauiApplication.UserAppTheme}"); + + // Apply GTK CSS for dialogs, menus, and window decorations + GtkThemeService.ApplyTheme(); + LinuxViewRenderer.CurrentSkiaShell?.RefreshTheme(); // Force re-render the entire page to pick up theme changes diff --git a/Native/GtkNative.cs b/Native/GtkNative.cs index 04067fe..3e94d08 100644 --- a/Native/GtkNative.cs +++ b/Native/GtkNative.cs @@ -235,6 +235,31 @@ internal static class GtkNative [DllImport("libgtk-3.so.0")] public static extern void gtk_window_set_modal(IntPtr window, bool modal); + // CSS styling for dialogs + [DllImport("libgtk-3.so.0")] + public static extern IntPtr gtk_css_provider_new(); + + [DllImport("libgtk-3.so.0")] + public static extern bool gtk_css_provider_load_from_data(IntPtr provider, string data, int length, IntPtr error); + + [DllImport("libgtk-3.so.0")] + public static extern IntPtr gtk_widget_get_style_context(IntPtr widget); + + [DllImport("libgtk-3.so.0")] + public static extern void gtk_style_context_add_provider(IntPtr context, IntPtr provider, uint priority); + + [DllImport("libgtk-3.so.0")] + public static extern void gtk_style_context_add_provider_for_screen(IntPtr screen, IntPtr provider, uint priority); + + [DllImport("libgtk-3.so.0")] + public static extern IntPtr gtk_widget_get_screen(IntPtr widget); + + [DllImport("libgdk-3.so.0")] + public static extern IntPtr gdk_screen_get_default(); + + public const uint GTK_STYLE_PROVIDER_PRIORITY_APPLICATION = 600; + public const uint GTK_STYLE_PROVIDER_PRIORITY_USER = 800; + // Dialog with custom content (for prompt dialogs) [DllImport("libgtk-3.so.0")] public static extern IntPtr gtk_dialog_new_with_buttons( diff --git a/OpenMaui.Controls.Linux.csproj b/OpenMaui.Controls.Linux.csproj index 7ac086c..66db52d 100644 --- a/OpenMaui.Controls.Linux.csproj +++ b/OpenMaui.Controls.Linux.csproj @@ -55,6 +55,11 @@ + + + + + diff --git a/Services/GtkContextMenuService.cs b/Services/GtkContextMenuService.cs index ed576eb..a04fa46 100644 --- a/Services/GtkContextMenuService.cs +++ b/Services/GtkContextMenuService.cs @@ -75,6 +75,9 @@ public static class GtkContextMenuService GtkNative.gtk_widget_show(menuItem); } + // Apply theme-aware CSS styling + ApplyMenuTheme(menu); + GtkNative.gtk_widget_show(menu); IntPtr currentEvent = GtkNative.gtk_get_current_event(); @@ -87,4 +90,81 @@ public static class GtkContextMenuService Console.WriteLine($"[GtkContextMenuService] Showed GTK menu with {items.Count} items"); } + + /// + /// Applies theme-aware CSS styling to a GTK menu based on the app's current theme. + /// + private static void ApplyMenuTheme(IntPtr menu) + { + try + { + // Check the app's current theme (not the system theme) + bool isDark = Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + + // If UserAppTheme is Unspecified, fall back to RequestedTheme + if (Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Unspecified) + { + isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + } + + Console.WriteLine($"[GtkContextMenuService] ApplyMenuTheme: isDark={isDark}"); + + // Create comprehensive CSS based on the theme + string css = isDark + ? @" + * { + background-color: #303030; + color: #E0E0E0; + } + menu, menuitem, .menu, .menuitem { + background-color: #303030; + color: #E0E0E0; + } + menuitem:hover, .menuitem:hover { + background-color: #505050; + } + separator { + background-color: #505050; + } + " + : @" + * { + background-color: #FFFFFF; + color: #212121; + } + menu, menuitem, .menu, .menuitem { + background-color: #FFFFFF; + color: #212121; + } + menuitem:hover, .menuitem:hover { + background-color: #E0E0E0; + } + separator { + background-color: #E0E0E0; + } + "; + + // Create CSS provider and apply to the screen + IntPtr cssProvider = GtkNative.gtk_css_provider_new(); + if (cssProvider != IntPtr.Zero) + { + GtkNative.gtk_css_provider_load_from_data(cssProvider, css, -1, IntPtr.Zero); + + IntPtr screen = GtkNative.gtk_widget_get_screen(menu); + if (screen == IntPtr.Zero) + { + screen = GtkNative.gdk_screen_get_default(); + } + + if (screen != IntPtr.Zero) + { + GtkNative.gtk_style_context_add_provider_for_screen(screen, cssProvider, GtkNative.GTK_STYLE_PROVIDER_PRIORITY_USER); + } + } + } + catch (Exception ex) + { + Console.WriteLine($"[GtkContextMenuService] Error applying menu theme: {ex.Message}"); + } + } } diff --git a/Services/GtkThemeService.cs b/Services/GtkThemeService.cs new file mode 100644 index 0000000..d4220b4 --- /dev/null +++ b/Services/GtkThemeService.cs @@ -0,0 +1,544 @@ +using System; +using Microsoft.Maui.Platform.Linux.Native; + +namespace Microsoft.Maui.Platform.Linux.Services; + +/// +/// Service for applying theme-aware CSS to GTK widgets. +/// +public static class GtkThemeService +{ + private static IntPtr _currentCssProvider = IntPtr.Zero; + private static bool _initialized = false; + + /// + /// Initializes the GTK theme service and applies initial CSS. + /// + public static void Initialize() + { + if (_initialized) return; + _initialized = true; + ApplyTheme(); + } + + /// + /// Applies GTK CSS based on the app's current theme. + /// Call this whenever the app theme changes. + /// + public static void ApplyTheme() + { + try + { + // Check the app's current theme + bool isDark = Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + + // If UserAppTheme is Unspecified, fall back to RequestedTheme + if (Microsoft.Maui.Controls.Application.Current?.UserAppTheme == Microsoft.Maui.ApplicationModel.AppTheme.Unspecified) + { + isDark = Microsoft.Maui.Controls.Application.Current?.RequestedTheme == Microsoft.Maui.ApplicationModel.AppTheme.Dark; + } + + Console.WriteLine($"[GtkThemeService] ApplyTheme: isDark={isDark}"); + + // Create comprehensive CSS based on the theme + string css = isDark ? GetDarkCss() : GetLightCss(); + + // Get the default screen + IntPtr screen = GtkNative.gdk_screen_get_default(); + if (screen == IntPtr.Zero) + { + Console.WriteLine("[GtkThemeService] Failed to get default screen"); + return; + } + + // Create new CSS provider + IntPtr newProvider = GtkNative.gtk_css_provider_new(); + if (newProvider == IntPtr.Zero) + { + Console.WriteLine("[GtkThemeService] Failed to create CSS provider"); + return; + } + + // Load CSS data + if (!GtkNative.gtk_css_provider_load_from_data(newProvider, css, -1, IntPtr.Zero)) + { + Console.WriteLine("[GtkThemeService] Failed to load CSS data"); + return; + } + + // Apply to screen (this affects all GTK widgets) + GtkNative.gtk_style_context_add_provider_for_screen(screen, newProvider, GtkNative.GTK_STYLE_PROVIDER_PRIORITY_USER); + + // Store reference to current provider + _currentCssProvider = newProvider; + + Console.WriteLine("[GtkThemeService] CSS applied successfully"); + } + catch (Exception ex) + { + Console.WriteLine($"[GtkThemeService] Error applying theme: {ex.Message}"); + } + } + + private static string GetDarkCss() + { + return @" + /* Dark theme - base */ + * { + background-color: #303030; + color: #E0E0E0; + } + + /* Windows and dialogs */ + window, dialog, .background { + background-color: #303030; + color: #E0E0E0; + } + + /* Message dialogs - all parts same color */ + messagedialog, + messagedialog.background, + messagedialog .background, + messagedialog box, + messagedialog grid, + messagedialog .dialog-vbox, + messagedialog .message-area, + messagedialog .dialog-action-area, + messagedialog .dialog-action-box, + messagedialog actionbar, + messagedialog buttonbox, + messagedialog box.vertical, + messagedialog box.horizontal, + messagedialog .linked, + messagedialog .linked button, + dialog box, + dialog .dialog-vbox, + dialog .dialog-action-area { + background-color: #303030; + background-image: none; + border: none; + border-style: none; + box-shadow: none; + } + + messagedialog separator, + dialog separator { + background-color: transparent; + background-image: none; + min-height: 0; + min-width: 0; + border: none; + } + + messagedialog .dialog-action-area, + dialog .dialog-action-area { + background-color: #303030; + background-image: none; + border: none; + border-top-style: none; + margin: 0; + padding: 8px; + } + + messagedialog .dialog-action-area button, + dialog .dialog-action-area button { + background-color: #505050; + background-image: none; + } + + /* Header bars and title bars */ + headerbar, .titlebar { + background-color: #252525; + background-image: none; + color: #E0E0E0; + border: none; + box-shadow: none; + } + + headerbar *, .titlebar * { + background-color: transparent; + background-image: none; + } + + headerbar button, .titlebar button { + background-color: #353535; + background-image: none; + color: #E0E0E0; + border-color: #353535; + border-radius: 6px; + } + + headerbar button:hover, .titlebar button:hover { + background-color: #454545; + border-color: #454545; + } + + /* Window control buttons */ + windowcontrols button, + button.titlebutton, + .titlebutton { + background-color: #252525; + background-image: none; + border-color: #252525; + border-radius: 50%; + min-width: 24px; + min-height: 24px; + padding: 4px; + } + + windowcontrols button *, + button.titlebutton *, + .titlebutton * { + background-color: transparent; + background-image: none; + } + + windowcontrols button:hover, + button.titlebutton:hover, + .titlebutton:hover { + background-color: #404040; + border-color: #404040; + } + + /* Dialog action areas */ + .dialog-action-area, + .dialog-action-box, + actionbar { + background-color: #303030; + background-image: none; + border: none; + } + + /* Labels */ + label { + background-color: transparent; + color: #E0E0E0; + } + + /* Buttons */ + button { + background-image: none; + background-color: #505050; + color: #E0E0E0; + border-color: #505050; + border-radius: 6px; + } + + button * { + background-color: transparent; + background-image: none; + } + + button:hover { + background-color: #606060; + border-color: #606060; + } + + button:hover * { + background-color: transparent; + } + + button:active { + background-color: #404040; + border-color: #404040; + } + + /* Entries and text inputs */ + entry, textview, text { + background-color: #404040; + color: #E0E0E0; + border-color: #505050; + border-radius: 4px; + } + + /* Menus and context menus */ + menu, .menu, .popup { + background-color: #303030; + color: #E0E0E0; + border: none; + border-radius: 8px; + padding: 4px; + } + + menuitem { + background-color: transparent; + color: #E0E0E0; + border: none; + border-radius: 6px; + padding: 6px 12px; + margin: 2px 4px; + } + + menuitem * { + background-color: transparent; + background-image: none; + } + + menuitem:hover { + background-color: #505050; + } + + menuitem:hover * { + background-color: transparent; + } + + /* Popover */ + popover, popover.background { + background-color: #303030; + border: none; + border-radius: 8px; + } + + /* Separators */ + separator { + background-color: #505050; + } + + /* Scrollbars */ + scrollbar { + background-color: #252525; + } + + scrollbar slider { + background-color: #505050; + border-radius: 4px; + } + + /* Focus styling */ + *:focus { + outline-color: #606060; + } + "; + } + + private static string GetLightCss() + { + return @" + /* Light theme - base */ + * { + background-color: #FFFFFF; + color: #212121; + } + + /* Windows and dialogs */ + window, dialog, .background { + background-color: #FFFFFF; + color: #212121; + } + + /* Message dialogs - all parts same color */ + messagedialog, + messagedialog.background, + messagedialog .background, + messagedialog box, + messagedialog grid, + messagedialog .dialog-vbox, + messagedialog .message-area, + messagedialog .dialog-action-area, + messagedialog .dialog-action-box, + messagedialog actionbar, + messagedialog buttonbox, + messagedialog box.vertical, + messagedialog box.horizontal, + messagedialog .linked, + messagedialog .linked button, + dialog box, + dialog .dialog-vbox, + dialog .dialog-action-area { + background-color: #FFFFFF; + background-image: none; + border: none; + border-style: none; + box-shadow: none; + } + + messagedialog separator, + dialog separator { + background-color: transparent; + background-image: none; + min-height: 0; + min-width: 0; + border: none; + } + + messagedialog .dialog-action-area, + dialog .dialog-action-area { + background-color: #FFFFFF; + background-image: none; + border: none; + border-top-style: none; + margin: 0; + padding: 8px; + } + + messagedialog .dialog-action-area button, + dialog .dialog-action-area button { + background-color: #F5F5F5; + background-image: none; + } + + /* Header bars and title bars */ + headerbar, .titlebar { + background-color: #F5F5F5; + background-image: none; + color: #212121; + border: none; + box-shadow: none; + } + + headerbar *, .titlebar * { + background-color: transparent; + background-image: none; + } + + headerbar button, .titlebar button { + background-color: #EBEBEB; + background-image: none; + color: #212121; + border-color: #EBEBEB; + border-radius: 6px; + } + + headerbar button:hover, .titlebar button:hover { + background-color: #DDDDDD; + border-color: #DDDDDD; + } + + /* Window control buttons */ + windowcontrols button, + button.titlebutton, + .titlebutton { + background-color: #F5F5F5; + background-image: none; + border-color: #F5F5F5; + border-radius: 50%; + min-width: 24px; + min-height: 24px; + padding: 4px; + } + + windowcontrols button *, + button.titlebutton *, + .titlebutton * { + background-color: transparent; + background-image: none; + } + + windowcontrols button:hover, + button.titlebutton:hover, + .titlebutton:hover { + background-color: #E0E0E0; + border-color: #E0E0E0; + } + + /* Dialog action areas */ + .dialog-action-area, + .dialog-action-box, + actionbar { + background-color: #FFFFFF; + background-image: none; + border: none; + } + + /* Labels */ + label { + background-color: transparent; + color: #212121; + } + + /* Buttons */ + button { + background-image: none; + background-color: #F5F5F5; + color: #212121; + border-color: #E0E0E0; + border-radius: 6px; + } + + button * { + background-color: transparent; + background-image: none; + } + + button:hover { + background-color: #E0E0E0; + border-color: #D0D0D0; + } + + button:hover * { + background-color: transparent; + } + + button:active { + background-color: #D0D0D0; + border-color: #C0C0C0; + } + + /* Entries and text inputs */ + entry, textview, text { + background-color: #FFFFFF; + color: #212121; + border-color: #E0E0E0; + border-radius: 4px; + } + + /* Menus and context menus */ + menu, .menu, .popup { + background-color: #FFFFFF; + color: #212121; + border: none; + border-radius: 8px; + padding: 4px; + } + + menuitem { + background-color: transparent; + color: #212121; + border: none; + border-radius: 6px; + padding: 6px 12px; + margin: 2px 4px; + } + + menuitem * { + background-color: transparent; + background-image: none; + } + + menuitem:hover { + background-color: #E8E8E8; + } + + menuitem:hover * { + background-color: transparent; + } + + /* Popover */ + popover, popover.background { + background-color: #FFFFFF; + border: none; + border-radius: 8px; + } + + /* Separators */ + separator { + background-color: #E0E0E0; + } + + /* Scrollbars */ + scrollbar { + background-color: #F5F5F5; + } + + scrollbar slider { + background-color: #C0C0C0; + border-radius: 4px; + } + + /* Focus styling */ + *:focus { + outline-color: #C0C0C0; + } + "; + } +} diff --git a/Views/LinuxDialogService.cs b/Views/LinuxDialogService.cs index 9ac4287..b72294c 100644 --- a/Views/LinuxDialogService.cs +++ b/Views/LinuxDialogService.cs @@ -63,8 +63,10 @@ public static class LinuxDialogService public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds) { + Console.WriteLine($"[LinuxDialogService] DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}"); foreach (var dialog in _activeDialogs) { + Console.WriteLine($"[LinuxDialogService] Drawing dialog: IsVisible={dialog.IsVisible}, Opacity={dialog.Opacity}"); dialog.Measure(new Size(bounds.Width, bounds.Height)); dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height)); dialog.Draw(canvas); diff --git a/Views/SkiaAlertDialog.cs b/Views/SkiaAlertDialog.cs index e8a8ebd..1afea84 100644 --- a/Views/SkiaAlertDialog.cs +++ b/Views/SkiaAlertDialog.cs @@ -22,17 +22,17 @@ public class SkiaAlertDialog : SkiaView private bool _cancelHovered; private bool _acceptHovered; - // Dialog styling - using SkiaTheme for MAUI-compliant theming - private static readonly SKColor OverlayColor = SkiaTheme.Overlay50SK; - private static readonly SKColor DialogBackground = SkiaTheme.BackgroundWhiteSK; - private static readonly SKColor TitleColor = SkiaTheme.TextPrimarySK; - private static readonly SKColor MessageColor = SkiaTheme.TextSecondarySK; - private static readonly SKColor ButtonColor = SkiaTheme.PrimarySK; - private static readonly SKColor ButtonHoverColor = SkiaTheme.PrimaryDarkSK; - private static readonly SKColor ButtonTextColor = SkiaTheme.BackgroundWhiteSK; - private static readonly SKColor CancelButtonColor = SkiaTheme.ButtonCancelSK; - private static readonly SKColor CancelButtonHoverColor = SkiaTheme.ButtonCancelHoverSK; - private static readonly SKColor BorderColor = SkiaTheme.BorderLightSK; + // Dialog styling - theme-aware colors (evaluated at draw time) + private static SKColor OverlayColor => SkiaTheme.Overlay50SK; + private static SKColor DialogBackground => SkiaTheme.CurrentSurfaceSK; + private static SKColor TitleColor => SkiaTheme.CurrentTextSK; + private static SKColor MessageColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray400SK : SkiaTheme.TextSecondarySK; + private static SKColor ButtonColor => SkiaTheme.PrimarySK; + private static SKColor ButtonHoverColor => SkiaTheme.PrimaryDarkSK; + private static SKColor ButtonTextColor => SKColors.White; + private static SKColor CancelButtonColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray600SK : SkiaTheme.ButtonCancelSK; + private static SKColor CancelButtonHoverColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray700SK : SkiaTheme.ButtonCancelHoverSK; + private static SKColor BorderColor => SkiaTheme.CurrentBorderSK; private const float DialogWidth = 400; private const float DialogPadding = 24; @@ -61,6 +61,9 @@ public class SkiaAlertDialog : SkiaView protected override void OnDraw(SKCanvas canvas, SKRect bounds) { + var app = Application.Current; + Console.WriteLine($"[SkiaAlertDialog] OnDraw: app={app != null}, UserAppTheme={app?.UserAppTheme}, RequestedTheme={app?.RequestedTheme}, IsDarkMode={SkiaTheme.IsDarkMode}, DialogBg={DialogBackground}"); + // Draw semi-transparent overlay covering entire screen using var overlayPaint = new SKPaint { diff --git a/Views/SkiaContextMenu.cs b/Views/SkiaContextMenu.cs index dd94ee9..fec03ef 100644 --- a/Views/SkiaContextMenu.cs +++ b/Views/SkiaContextMenu.cs @@ -12,15 +12,13 @@ public class SkiaContextMenu : SkiaView private int _hoveredIndex = -1; private SKRect[] _itemBounds = Array.Empty(); - private static readonly SKColor MenuBackground = SkiaTheme.BackgroundWhiteSK; - private static readonly SKColor MenuBackgroundDark = SkiaTheme.DarkBackgroundSK; - private static readonly SKColor ItemHoverBackground = SkiaTheme.PrimarySelectionSK; - private static readonly SKColor ItemHoverBackgroundDark = SkiaTheme.DarkHoverSK; - private static readonly SKColor ItemTextColor = SkiaTheme.TextPrimarySK; - private static readonly SKColor ItemTextColorDark = SkiaTheme.DarkTextSK; - private static readonly SKColor DisabledTextColor = SkiaTheme.TextDisabledSK; - private static readonly SKColor SeparatorColor = SkiaTheme.Gray300SK; - private static readonly SKColor ShadowColor = SkiaTheme.Shadow25SK; + // Theme-aware colors (evaluated at draw time) + private static SKColor MenuBackground => SkiaTheme.CurrentSurfaceSK; + private static SKColor ItemHoverBackground => SkiaTheme.IsDarkMode ? SkiaTheme.DarkHoverSK : SkiaTheme.PrimarySelectionSK; + private static SKColor ItemTextColor => SkiaTheme.CurrentTextSK; + private static SKColor DisabledTextColor => SkiaTheme.TextDisabledSK; + private static SKColor SeparatorColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray600SK : SkiaTheme.Gray300SK; + private static SKColor ShadowColor => SkiaTheme.Shadow25SK; private const float MenuPadding = 4f; private const float ItemHeight = 32f; @@ -29,14 +27,12 @@ public class SkiaContextMenu : SkiaView private const float CornerRadius = 4f; private const float MinWidth = 120f; - private bool _isDarkTheme; - public SkiaContextMenu(float x, float y, List items, bool isDarkTheme = false) { _x = x; _y = y; _items = items; - _isDarkTheme = isDarkTheme; + // isDarkTheme parameter kept for API compatibility but ignored - we use SkiaTheme.IsDarkMode instead IsFocusable = true; } @@ -73,7 +69,7 @@ public class SkiaContextMenu : SkiaView // Draw background using (var bgPaint = new SKPaint { - Color = _isDarkTheme ? MenuBackgroundDark : MenuBackground, + Color = MenuBackground, IsAntialias = true }) { @@ -120,7 +116,7 @@ public class SkiaContextMenu : SkiaView { using (var hoverPaint = new SKPaint { - Color = _isDarkTheme ? ItemHoverBackgroundDark : ItemHoverBackground, + Color = ItemHoverBackground, IsAntialias = true }) { @@ -131,7 +127,7 @@ public class SkiaContextMenu : SkiaView // Draw text using (var textPaint = new SKPaint { - Color = !item.IsEnabled ? DisabledTextColor : (_isDarkTheme ? ItemTextColorDark : ItemTextColor), + Color = !item.IsEnabled ? DisabledTextColor : ItemTextColor, TextSize = 14f, IsAntialias = true, Typeface = SKTypeface.Default diff --git a/Views/SkiaTheme.cs b/Views/SkiaTheme.cs index 6bec6b8..46645a3 100644 --- a/Views/SkiaTheme.cs +++ b/Views/SkiaTheme.cs @@ -322,8 +322,23 @@ public static class SkiaTheme /// /// Returns true if dark mode is currently active. + /// Checks UserAppTheme first, falls back to system RequestedTheme. /// - public static bool IsDarkMode => Application.Current?.UserAppTheme == AppTheme.Dark; + public static bool IsDarkMode + { + get + { + var app = Application.Current; + if (app == null) return false; + + // If user explicitly set a theme, use that + if (app.UserAppTheme != AppTheme.Unspecified) + return app.UserAppTheme == AppTheme.Dark; + + // Otherwise use system theme + return app.RequestedTheme == AppTheme.Dark; + } + } /// /// Gets the appropriate button background color for the current theme. diff --git a/build/OpenMaui.Controls.Linux.targets b/build/OpenMaui.Controls.Linux.targets index 02c70b8..8ac24ae 100644 --- a/build/OpenMaui.Controls.Linux.targets +++ b/build/OpenMaui.Controls.Linux.targets @@ -1,28 +1,21 @@ - - + - - - - - + - - - + + + +