Fixes for GTK - still wrong
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -55,6 +55,11 @@
|
||||
<None Include="assets/icon.png" Pack="true" PackagePath="" />
|
||||
</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 -->
|
||||
<ItemGroup>
|
||||
<Compile Remove="Handlers/*.Linux.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");
|
||||
}
|
||||
|
||||
/// <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
544
Services/GtkThemeService.cs
Normal 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;
|
||||
}
|
||||
";
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -12,15 +12,13 @@ public class SkiaContextMenu : SkiaView
|
||||
private int _hoveredIndex = -1;
|
||||
private SKRect[] _itemBounds = Array.Empty<SKRect>();
|
||||
|
||||
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<ContextMenuItem> 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
|
||||
|
||||
@@ -322,8 +322,23 @@ public static class SkiaTheme
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if dark mode is currently active.
|
||||
/// Checks UserAppTheme first, falls back to system RequestedTheme.
|
||||
/// </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>
|
||||
/// Gets the appropriate button background color for the current theme.
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
|
||||
<!--
|
||||
<!--
|
||||
OpenMaui Linux Build 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) != ''">
|
||||
<Message Importance="normal" Text="OpenMaui: Copying MauiImage items to output..." />
|
||||
|
||||
<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" />
|
||||
<Copy SourceFiles="@(MauiImage)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
<!-- Ensure the Resources/Images directory exists -->
|
||||
<Target Name="EnsureResourcesImagesDirectory" BeforeTargets="CopyMauiImagesToOutput">
|
||||
<MakeDir Directories="$(OutputPath)Resources/Images" Condition="!Exists('$(OutputPath)Resources/Images')" />
|
||||
<!-- Copy MauiImage items to publish directory after Publish -->
|
||||
<Target Name="CopyMauiImagesToPublish" AfterTargets="Publish" Condition="@(MauiImage) != ''">
|
||||
<Message Importance="normal" Text="OpenMaui: Copying MauiImage items to publish folder..." />
|
||||
<Copy SourceFiles="@(MauiImage)" DestinationFolder="$(PublishDir)" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user