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:
before
Screenshot.2025-12-24.101158.png (view on web)
after
Screenshot.2025-12-24.100600.png (view on web)
Address the following soon after merging this PR:
HBRUSH ownership and GDI leaks
Initialization placement
Visual correctness
https://github.com/wxWidgets/wxWidgets/pull/26053
(3 files)
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@memoarfaa pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@memoarfaa pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@memoarfaa pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@memoarfaa pushed 1 commit.
—
View it on GitHub or unsubscribe.
You are receiving this because you are subscribed to this thread.![]()
@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?
> @@ -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)?
> @@ -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:
> + // 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.
> {
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.![]()
@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"
> @@ -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"
> @@ -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
> @@ -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.
> @@ -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.![]()
@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.![]()
@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.![]()