Improve MSW menu dark-mode rendering and Windows 11 menu appearance (PR #26053)

53 views
Skip to first unread message

Mohmed abdel-fattah

unread,
Dec 24, 2025, 3:16:11 AM12/24/25
to wx-...@googlegroups.com, Subscribed

Description

This PR fixes several MSW menu rendering issues observed in dark mode and on Windows 11.
1- It includes changes to make menu breaks render correctly in dark mode (owner-drawn).
2- applies dark-mode-aware menu background colors, adjusts owner-drawn menu item drawing (text color fallbacks).
3- applies Windows 11 DWM window attributes to popup menu windows to get rounded corners and proper border color.

High-level items implemented or touched by this PR:

  • Force owner-draw for menu-break items when dark mode is active so they render correctly.
  • Use theme-aware colors for popup menu background and menu item text; add fallbacks when dark-theme values are unavailable.
  • change popup menu background .
  • Update theme handle usage (supply dark-mode theme identifiers where applicable).
  • Set DWM attributes (rounded corners and border color) for menu windows on Windows 11+ dynamically.

Changes Proposed

  • src/msw/menu.cpp
    • Include darkmode and uxtheme private headers.
    • When m_doBreak is set and dark mode is active, mark the item owner-drawn.
    • On menu creation, when wxMSWDarkMode is active and owner-drawn is used, query theme popup background color.
  • src/msw/menuitem.cpp
    • Include darkmode header.
    • Use dark-mode-aware theme string when creating wxUxThemeHandle).
    • Obtain TMT_TEXTCOLOR from theme in dark mode, with fallback to system menu text color if not available.
  • src/msw/window.cpp
    • Dynamically call DwmSetWindowAttribute to set DWMWA_WINDOW_CORNER_PREFERENCE (rounded corners) and DWMWA_BORDER_COLOR.
    • Load dwmapi.dll and DwmSetWindowAttribute with LoadLibrary/GetProcAddress to avoid link dependency.

Motivation and Context

  • Dark-mode menus and menu breaks displayed incorrectly with default rendering; forcing owner-draw and using theme colors addresses incorrect backgrounds and text colors in dark themes.
  • Windows 11 introduced different menu visuals (rounded corners, border colors). Applying DWM attributes to popup menu windows improves visual consistency for native-looking menus.
  • The changes aim to preserve visual correctness across themes and OS versions while avoiding hard dependencies on newer APIs at link time.

How Has This Been Tested?

  • Built and ran on Windows 11 (verify rounded corners and border color on popup menus).
  • Built and ran on Windows 10 (verify no regressions; attributes not applied).
  • Tested owner-drawn menu breaks in dark mode and light mode (verify layout and separators).
  • Verified submenu arrows render for owner-drawn popup items at multiple DPIs.
  • Verified no GDI resource leaks (monitor GDI handles while repeatedly opening/closing menus).

Screenshots

before
Screenshot.2025-12-24.101158.png (view on web)

after
Screenshot.2025-12-24.100600.png (view on web)

Developer notes and required follow-ups

Address the following soon after merging this PR:

  • HBRUSH ownership and GDI leaks

    • The PR creates HBRUSH via CreateSolidBrush and passes it to SetMenuInfo. Confirm whether the system copies the brush or expects the app to keep it alive. If the app owns it, ensure it is deleted at the appropriate lifecycle point (menu destruction) to avoid GDI leaks. Consider caching brushes keyed by color and cleaning them up at process/owner cleanup.
  • Initialization placement

    • The menu background setting is done in Create(); evaluate whether it should be moved to wxMenu::Init() or a more central
  • Visual correctness

Related issues

Fixes #25577
Fixes #22518

Suggested reviewers

@vadz


You can view, comment on, or merge this pull request online at:

  https://github.com/wxWidgets/wxWidgets/pull/26053

Commit Summary

  • e2dc406 Fix menu rounded corners in Windows 11 and higher
  • a357f52 Set dark mode menu background color and if menu has break Set it OwnerDrawn at darkMode
  • 713363d Add DarkMode Theme to OwnerDrawn Menu Items

File Changes

(3 files)

Patch Links:


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053@github.com>

Mohmed abdel-fattah

unread,
Dec 24, 2025, 4:08:01 AM12/24/25
to wx-...@googlegroups.com, Push

@memoarfaa pushed 1 commit.

  • e35d792 fix trailing whitespace build errors


View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/before/713363d9a8927fbb0aa94a8f2030280990b752a5/after/e35d79222ea969aaca4716250f4fac59ece184ad@github.com>

Mohmed abdel-fattah

unread,
Dec 25, 2025, 11:09:48 AM12/25/25
to wx-...@googlegroups.com, Push

@memoarfaa pushed 1 commit.

  • 2862100 add submenu arrows render for owner-drawn popup items at dark mode.


View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/before/e35d79222ea969aaca4716250f4fac59ece184ad/after/28621004f21b1728365709b0206015b7f19f42c7@github.com>

Mohmed abdel-fattah

unread,
Dec 25, 2025, 11:18:33 AM12/25/25
to wx-...@googlegroups.com, Push

@memoarfaa pushed 1 commit.


View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/before/28621004f21b1728365709b0206015b7f19f42c7/after/7ac12112401e4a1ed77c753711ab3683e708101c@github.com>

Mohmed abdel-fattah

unread,
Dec 25, 2025, 12:13:49 PM12/25/25
to wx-...@googlegroups.com, Push

@memoarfaa pushed 1 commit.

  • b586e98 Fix IsActive missing "()" build error


View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/before/7ac12112401e4a1ed77c753711ab3683e708101c/after/b586e98fb3e76b8e0f65a84d3da75ed0a517c9c6@github.com>

PB

unread,
Dec 27, 2025, 9:23:24 AM12/27/25
to wx-...@googlegroups.com, Subscribed

@PBfordev commented on this pull request.

Thanks for the work.

I am not really qualified to review this, so my comments are just nitpicks and questions.


In src/msw/menu.cpp:

> @@ -916,30 +922,47 @@ void wxMenuBar::Refresh()
 
 WXHMENU wxMenuBar::Create()
 {
-    if ( m_hMenu != 0 )
+    if (m_hMenu != 0)

There is a bunch of whitespace changes going against the code style (perhaps done automatically by a tool). These should be reverted (and the new code formatting should match the expected style).


In src/msw/menu.cpp:

>  
+#if wxUSE_OWNER_DRAWN
+        if (wxMSWDarkMode::IsActive())
+        {
+            // Set dark mode menu background color
+            // May be this should be done in wxMenu::Init() instead?
+            wxUxThemeHandle hTheme(GetFrame()->AsWindow(), L"MENU", L"DarkMode_ImmersiveStart::Menu");
+            COLORREF crMenu = wxColourToRGB(hTheme.GetColour(MENU_POPUPBACKGROUND, TMT_FILLCOLOR));
+            WinStruct<MENUINFO> mi;
+            mi.fMask = MIM_BACKGROUND | MIM_APPLYTOSUBMENUS;
+            // Maybe we should clean up this brush to avoid GDI leaks?

Perhaps we could use wxTheBrushList->FindOrCreateBrush() here?


In src/msw/menu.cpp:

>  
+#if wxUSE_OWNER_DRAWN
+        if (wxMSWDarkMode::IsActive())
+        {
+            // Set dark mode menu background color
+            // May be this should be done in wxMenu::Init() instead?
+            wxUxThemeHandle hTheme(GetFrame()->AsWindow(), L"MENU", L"DarkMode_ImmersiveStart::Menu");
+            COLORREF crMenu = wxColourToRGB(hTheme.GetColour(MENU_POPUPBACKGROUND, TMT_FILLCOLOR));

I am not sure about getting the color from the theme (here and elsewhere): How does wxDarkModeSettings::GetColour() fits into Windows dark mode colors?


In src/msw/menuitem.cpp:

> @@ -1019,7 +1033,7 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
         int x = rcText.left;
         int y = rcText.top + (rcText.bottom - rcText.top - textSize.cy) / 2;
 
-        ::DrawState(hdc, nullptr, nullptr, wxMSW_CONV_LPARAM(text),
+        ::DrawStateW(hdc, nullptr, nullptr, wxMSW_CONV_LPARAM(text),

Why the change here: wxWidgets master supports non-wide char build too (UTF-8)?


In src/msw/window.cpp:

> @@ -3598,7 +3599,63 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
         case WM_ENDSESSION:
             processed = HandleEndSession(wParam != 0, lParam);
             break;
+        case WM_ENTERIDLE:

I have no idea if doing this here is where it should be done.

But I wonder if we should do this:

  1. For every idle message?
  2. When not in dark mode?
  3. If not Windows 11?

In src/msw/window.cpp:

>  
+                                // Apply border color to menu window

I have noticed that now (compared to the master), all the menus (including system window menu) have very light border. No such border is used by menus in other applications shipped with Windows.


In src/msw/menuitem.cpp:

>          {
             wxRGBToColour(colText, ::GetThemeSysColor(hTheme, COLOR_GRAYTEXT));
         }
         else
         {
             colText = GetTextColour();
-            if ( !colText.IsOk() )
-                wxRGBToColour(colText, ::GetThemeSysColor(hTheme, COLOR_MENUTEXT));
+            if (!colText.IsOk())
+            {
+                if (wxMSWDarkMode::IsActive())
+                {
+                    colText = hTheme.GetColour(MENU_POPUPITEM, TMT_TEXTCOLOR, 1);

Would it not be better to use the constant instead of literal 1 here?


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/review/3614329468@github.com>

VZ

unread,
Dec 31, 2025, 9:11:00 AM12/31/25
to wx-...@googlegroups.com, Subscribed

@vadz commented on this pull request.

Thanks a lot, this is definitely very useful and should be merged. It would be great if you could please address the comments, but if not I'll try doing it myself later.


In src/msw/menu.cpp:

> +#include <wx/msw/private/darkmode.h>
+#include <wx/msw/uxtheme.h>
⬇️ Suggested change
-#include <wx/msw/private/darkmode.h>
-#include <wx/msw/uxtheme.h>
+#include "wx/msw/private/darkmode.h"
+#include "wx/msw/uxtheme.h"

In src/msw/menuitem.cpp:

> @@ -134,6 +134,7 @@ class HDCBgModeChanger : HDCHandler
 #include "wx/msw/private/metrics.h"
 
 #endif // wxUSE_OWNER_DRAWN
+#include <wx/msw/private/darkmode.h>
⬇️ Suggested change
-#include <wx/msw/private/darkmode.h>
+#include "wx/msw/private/darkmode.h"

In src/msw/menuitem.cpp:

> @@ -972,6 +973,19 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
             }
 
             hTheme.DrawBackground(hdc, rcSelection, MENU_POPUPITEM, state);
+            // we need also to draw menu arrow if the menu item at popup menu and has subMenu for dark mode.

If I understand the meaning of this comment correctly, it should say

⬇️ Suggested change
-            // we need also to draw menu arrow if the menu item at popup menu and has subMenu for dark mode.
+            // we need also to draw menu arrow of sub-menus in dark mode

In src/msw/menuitem.cpp:

> @@ -954,15 +955,15 @@ bool wxMenuItem::OnDrawItem(wxDC& dc, const wxRect& rc,
                 state = MPI_NORMAL;
             }
 
-            wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU");
+            wxUxThemeHandle hTheme(GetMenu()->GetWindow(), L"MENU",L"DARKMODE::MENU");

I am not sure if classes names are case-sensitive, but we use L"DarkMode::MENU" in src/msw/renderer.cpp so I'd prefer to use the same case here (and below) too — or change the existing file to use DARKMODE if really necessary — for consistency.


In src/msw/window.cpp:

> @@ -3598,7 +3599,63 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
         case WM_ENDSESSION:
             processed = HandleEndSession(wParam != 0, lParam);
             break;
+        case WM_ENTERIDLE:

I agree with the points (2) and (3), i.e. we need to restrict this to dark mode under Windows 11 (and later). Point (1) is not relevant, I think, as we don't do this on every idle message, but only when entering a local message loop.

OTOH I'd like to add another point: it would be nice to move this into a separate function.


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/review/3620948296@github.com>

VZ

unread,
Jan 11, 2026, 9:07:45 AM (5 days ago) Jan 11
to wx-...@googlegroups.com, Subscribed
vadz left a comment (wxWidgets/wxWidgets#26053)

@memoarfaa Do you plan to make more changes here or should I take it over?


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/c3734601381@github.com>

Mohmed abdel-fattah

unread,
Jan 11, 2026, 12:55:10 PM (5 days ago) Jan 11
to wx-...@googlegroups.com, Subscribed
memoarfaa left a comment (wxWidgets/wxWidgets#26053)

@memoarfaa Do you plan to make more changes here or should I take it over?

Yes give me some time


Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you are subscribed to this thread.Message ID: <wxWidgets/wxWidgets/pull/26053/c3735188951@github.com>

Reply all
Reply to author
Forward
0 new messages