Fixes for GTK - still wrong

This commit is contained in:
2026-01-24 06:13:13 +00:00
parent 38c48fc99f
commit f1e3630d1b
11 changed files with 845 additions and 41 deletions

View File

@@ -154,6 +154,9 @@ public sealed class GtkWebViewPlatformView : IDisposable
}; };
GtkNative.gtk_window_set_title(gtkDialog, title); 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 // Make dialog modal to parent if we have a parent
if (parentWindow != IntPtr.Zero) if (parentWindow != IntPtr.Zero)
{ {
@@ -211,6 +214,9 @@ public sealed class GtkWebViewPlatformView : IDisposable
return false; return false;
} }
// Apply theme-aware CSS styling
ApplyDialogTheme(gtkDialog);
// Get the content area // Get the content area
IntPtr contentArea = GtkNative.gtk_dialog_get_content_area(gtkDialog); IntPtr contentArea = GtkNative.gtk_dialog_get_content_area(gtkDialog);
@@ -277,6 +283,134 @@ public sealed class GtkWebViewPlatformView : IDisposable
} }
} }
/// <summary>
/// Applies theme-aware CSS styling to a GTK dialog based on the app's current theme.
/// </summary>
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) private void OnLoadChanged(IntPtr webView, int loadEvent, IntPtr userData)
{ {
try try

View File

@@ -310,12 +310,19 @@ public class LinuxApplication : IDisposable
Console.WriteLine("[LinuxApplication] Set initial UserAppTheme to Light based on system theme"); 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 // Handle user-initiated theme changes
((BindableObject)mauiApplication).PropertyChanged += (s, e) => ((BindableObject)mauiApplication).PropertyChanged += (s, e) =>
{ {
if (e.PropertyName == "UserAppTheme") if (e.PropertyName == "UserAppTheme")
{ {
Console.WriteLine($"[LinuxApplication] User theme changed to: {mauiApplication.UserAppTheme}"); Console.WriteLine($"[LinuxApplication] User theme changed to: {mauiApplication.UserAppTheme}");
// Apply GTK CSS for dialogs, menus, and window decorations
GtkThemeService.ApplyTheme();
LinuxViewRenderer.CurrentSkiaShell?.RefreshTheme(); LinuxViewRenderer.CurrentSkiaShell?.RefreshTheme();
// Force re-render the entire page to pick up theme changes // Force re-render the entire page to pick up theme changes

View File

@@ -235,6 +235,31 @@ internal static class GtkNative
[DllImport("libgtk-3.so.0")] [DllImport("libgtk-3.so.0")]
public static extern void gtk_window_set_modal(IntPtr window, bool modal); 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) // Dialog with custom content (for prompt dialogs)
[DllImport("libgtk-3.so.0")] [DllImport("libgtk-3.so.0")]
public static extern IntPtr gtk_dialog_new_with_buttons( public static extern IntPtr gtk_dialog_new_with_buttons(

View File

@@ -55,6 +55,11 @@
<None Include="assets/icon.png" Pack="true" PackagePath="" /> <None Include="assets/icon.png" Pack="true" PackagePath="" />
</ItemGroup> </ItemGroup>
<!-- Include build targets in package - auto-imports when package is referenced -->
<ItemGroup>
<None Include="build\OpenMaui.Controls.Linux.targets" Pack="true" PackagePath="build\OpenMaui.Controls.Linux.targets" />
</ItemGroup>
<!-- Exclude old handler files, samples, templates, and VSIX --> <!-- Exclude old handler files, samples, templates, and VSIX -->
<ItemGroup> <ItemGroup>
<Compile Remove="Handlers/*.Linux.cs" /> <Compile Remove="Handlers/*.Linux.cs" />

View File

@@ -75,6 +75,9 @@ public static class GtkContextMenuService
GtkNative.gtk_widget_show(menuItem); GtkNative.gtk_widget_show(menuItem);
} }
// Apply theme-aware CSS styling
ApplyMenuTheme(menu);
GtkNative.gtk_widget_show(menu); GtkNative.gtk_widget_show(menu);
IntPtr currentEvent = GtkNative.gtk_get_current_event(); 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"); Console.WriteLine($"[GtkContextMenuService] Showed GTK menu with {items.Count} items");
} }
/// <summary>
/// Applies theme-aware CSS styling to a GTK menu based on the app's current theme.
/// </summary>
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}");
}
}
} }

544
Services/GtkThemeService.cs Normal file
View File

@@ -0,0 +1,544 @@
using System;
using Microsoft.Maui.Platform.Linux.Native;
namespace Microsoft.Maui.Platform.Linux.Services;
/// <summary>
/// Service for applying theme-aware CSS to GTK widgets.
/// </summary>
public static class GtkThemeService
{
private static IntPtr _currentCssProvider = IntPtr.Zero;
private static bool _initialized = false;
/// <summary>
/// Initializes the GTK theme service and applies initial CSS.
/// </summary>
public static void Initialize()
{
if (_initialized) return;
_initialized = true;
ApplyTheme();
}
/// <summary>
/// Applies GTK CSS based on the app's current theme.
/// Call this whenever the app theme changes.
/// </summary>
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;
}
";
}
}

View File

@@ -63,8 +63,10 @@ public static class LinuxDialogService
public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds) public static void DrawDialogsOnly(SKCanvas canvas, SKRect bounds)
{ {
Console.WriteLine($"[LinuxDialogService] DrawDialogsOnly: {_activeDialogs.Count} dialogs, IsDarkMode={SkiaTheme.IsDarkMode}");
foreach (var dialog in _activeDialogs) 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.Measure(new Size(bounds.Width, bounds.Height));
dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height)); dialog.Arrange(new Rect(bounds.Left, bounds.Top, bounds.Width, bounds.Height));
dialog.Draw(canvas); dialog.Draw(canvas);

View File

@@ -22,17 +22,17 @@ public class SkiaAlertDialog : SkiaView
private bool _cancelHovered; private bool _cancelHovered;
private bool _acceptHovered; private bool _acceptHovered;
// Dialog styling - using SkiaTheme for MAUI-compliant theming // Dialog styling - theme-aware colors (evaluated at draw time)
private static readonly SKColor OverlayColor = SkiaTheme.Overlay50SK; private static SKColor OverlayColor => SkiaTheme.Overlay50SK;
private static readonly SKColor DialogBackground = SkiaTheme.BackgroundWhiteSK; private static SKColor DialogBackground => SkiaTheme.CurrentSurfaceSK;
private static readonly SKColor TitleColor = SkiaTheme.TextPrimarySK; private static SKColor TitleColor => SkiaTheme.CurrentTextSK;
private static readonly SKColor MessageColor = SkiaTheme.TextSecondarySK; private static SKColor MessageColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray400SK : SkiaTheme.TextSecondarySK;
private static readonly SKColor ButtonColor = SkiaTheme.PrimarySK; private static SKColor ButtonColor => SkiaTheme.PrimarySK;
private static readonly SKColor ButtonHoverColor = SkiaTheme.PrimaryDarkSK; private static SKColor ButtonHoverColor => SkiaTheme.PrimaryDarkSK;
private static readonly SKColor ButtonTextColor = SkiaTheme.BackgroundWhiteSK; private static SKColor ButtonTextColor => SKColors.White;
private static readonly SKColor CancelButtonColor = SkiaTheme.ButtonCancelSK; private static SKColor CancelButtonColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray600SK : SkiaTheme.ButtonCancelSK;
private static readonly SKColor CancelButtonHoverColor = SkiaTheme.ButtonCancelHoverSK; private static SKColor CancelButtonHoverColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray700SK : SkiaTheme.ButtonCancelHoverSK;
private static readonly SKColor BorderColor = SkiaTheme.BorderLightSK; private static SKColor BorderColor => SkiaTheme.CurrentBorderSK;
private const float DialogWidth = 400; private const float DialogWidth = 400;
private const float DialogPadding = 24; private const float DialogPadding = 24;
@@ -61,6 +61,9 @@ public class SkiaAlertDialog : SkiaView
protected override void OnDraw(SKCanvas canvas, SKRect bounds) 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 // Draw semi-transparent overlay covering entire screen
using var overlayPaint = new SKPaint using var overlayPaint = new SKPaint
{ {

View File

@@ -12,15 +12,13 @@ public class SkiaContextMenu : SkiaView
private int _hoveredIndex = -1; private int _hoveredIndex = -1;
private SKRect[] _itemBounds = Array.Empty<SKRect>(); private SKRect[] _itemBounds = Array.Empty<SKRect>();
private static readonly SKColor MenuBackground = SkiaTheme.BackgroundWhiteSK; // Theme-aware colors (evaluated at draw time)
private static readonly SKColor MenuBackgroundDark = SkiaTheme.DarkBackgroundSK; private static SKColor MenuBackground => SkiaTheme.CurrentSurfaceSK;
private static readonly SKColor ItemHoverBackground = SkiaTheme.PrimarySelectionSK; private static SKColor ItemHoverBackground => SkiaTheme.IsDarkMode ? SkiaTheme.DarkHoverSK : SkiaTheme.PrimarySelectionSK;
private static readonly SKColor ItemHoverBackgroundDark = SkiaTheme.DarkHoverSK; private static SKColor ItemTextColor => SkiaTheme.CurrentTextSK;
private static readonly SKColor ItemTextColor = SkiaTheme.TextPrimarySK; private static SKColor DisabledTextColor => SkiaTheme.TextDisabledSK;
private static readonly SKColor ItemTextColorDark = SkiaTheme.DarkTextSK; private static SKColor SeparatorColor => SkiaTheme.IsDarkMode ? SkiaTheme.Gray600SK : SkiaTheme.Gray300SK;
private static readonly SKColor DisabledTextColor = SkiaTheme.TextDisabledSK; private static SKColor ShadowColor => SkiaTheme.Shadow25SK;
private static readonly SKColor SeparatorColor = SkiaTheme.Gray300SK;
private static readonly SKColor ShadowColor = SkiaTheme.Shadow25SK;
private const float MenuPadding = 4f; private const float MenuPadding = 4f;
private const float ItemHeight = 32f; private const float ItemHeight = 32f;
@@ -29,14 +27,12 @@ public class SkiaContextMenu : SkiaView
private const float CornerRadius = 4f; private const float CornerRadius = 4f;
private const float MinWidth = 120f; private const float MinWidth = 120f;
private bool _isDarkTheme;
public SkiaContextMenu(float x, float y, List<ContextMenuItem> items, bool isDarkTheme = false) public SkiaContextMenu(float x, float y, List<ContextMenuItem> items, bool isDarkTheme = false)
{ {
_x = x; _x = x;
_y = y; _y = y;
_items = items; _items = items;
_isDarkTheme = isDarkTheme; // isDarkTheme parameter kept for API compatibility but ignored - we use SkiaTheme.IsDarkMode instead
IsFocusable = true; IsFocusable = true;
} }
@@ -73,7 +69,7 @@ public class SkiaContextMenu : SkiaView
// Draw background // Draw background
using (var bgPaint = new SKPaint using (var bgPaint = new SKPaint
{ {
Color = _isDarkTheme ? MenuBackgroundDark : MenuBackground, Color = MenuBackground,
IsAntialias = true IsAntialias = true
}) })
{ {
@@ -120,7 +116,7 @@ public class SkiaContextMenu : SkiaView
{ {
using (var hoverPaint = new SKPaint using (var hoverPaint = new SKPaint
{ {
Color = _isDarkTheme ? ItemHoverBackgroundDark : ItemHoverBackground, Color = ItemHoverBackground,
IsAntialias = true IsAntialias = true
}) })
{ {
@@ -131,7 +127,7 @@ public class SkiaContextMenu : SkiaView
// Draw text // Draw text
using (var textPaint = new SKPaint using (var textPaint = new SKPaint
{ {
Color = !item.IsEnabled ? DisabledTextColor : (_isDarkTheme ? ItemTextColorDark : ItemTextColor), Color = !item.IsEnabled ? DisabledTextColor : ItemTextColor,
TextSize = 14f, TextSize = 14f,
IsAntialias = true, IsAntialias = true,
Typeface = SKTypeface.Default Typeface = SKTypeface.Default

View File

@@ -322,8 +322,23 @@ public static class SkiaTheme
/// <summary> /// <summary>
/// Returns true if dark mode is currently active. /// Returns true if dark mode is currently active.
/// Checks UserAppTheme first, falls back to system RequestedTheme.
/// </summary> /// </summary>
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;
}
}
/// <summary> /// <summary>
/// Gets the appropriate button background color for the current theme. /// Gets the appropriate button background color for the current theme.

View File

@@ -1,28 +1,21 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- <!--
OpenMaui Linux Build Targets OpenMaui Linux Build Targets
Handles MauiImage items for Linux platform since we don't use the full MAUI SDK targets. Handles MauiImage items for Linux platform since we don't use the full MAUI SDK targets.
--> -->
<!-- Copy MauiImage items to output directory --> <!-- Copy MauiImage items to output directory after Build -->
<Target Name="CopyMauiImagesToOutput" AfterTargets="Build" Condition="@(MauiImage) != ''"> <Target Name="CopyMauiImagesToOutput" AfterTargets="Build" Condition="@(MauiImage) != ''">
<Message Importance="normal" Text="OpenMaui: Copying MauiImage items to output..." /> <Message Importance="normal" Text="OpenMaui: Copying MauiImage items to output..." />
<Copy SourceFiles="@(MauiImage)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(MauiImage)"
DestinationFolder="$(OutputPath)"
SkipUnchangedFiles="true" />
<!-- Also copy to Resources/Images subfolder for alternate lookup -->
<Copy SourceFiles="@(MauiImage)"
DestinationFolder="$(OutputPath)Resources/Images"
SkipUnchangedFiles="true" />
</Target> </Target>
<!-- Ensure the Resources/Images directory exists --> <!-- Copy MauiImage items to publish directory after Publish -->
<Target Name="EnsureResourcesImagesDirectory" BeforeTargets="CopyMauiImagesToOutput"> <Target Name="CopyMauiImagesToPublish" AfterTargets="Publish" Condition="@(MauiImage) != ''">
<MakeDir Directories="$(OutputPath)Resources/Images" Condition="!Exists('$(OutputPath)Resources/Images')" /> <Message Importance="normal" Text="OpenMaui: Copying MauiImage items to publish folder..." />
<Copy SourceFiles="@(MauiImage)" DestinationFolder="$(PublishDir)" SkipUnchangedFiles="true" />
</Target> </Target>
</Project> </Project>