From d3ab43ae02a1383dbb3fda042d1e9d3838a56b76 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 19 Feb 2023 17:18:17 +0000 Subject: [PATCH 1/5] Add a possibility to customize dark mode colours in wxMSW Add MSW-specific wxDarkModeSettings class which can be used to customize the colours used in the dark mode (and will be also used for other customizations in the future). This commit is best viewed using Git --color-moved option. --- Makefile.in | 1 + build/bakefiles/files.bkl | 1 + build/cmake/files.cmake | 1 + build/files | 1 + build/msw/wx_core.vcxproj | 1 + build/msw/wx_core.vcxproj.filters | 3 + include/wx/msw/app.h | 7 +- include/wx/msw/darkmode.h | 32 +++++++ interface/wx/app.h | 5 +- interface/wx/msw/darkmode.h | 62 +++++++++++++ src/msw/darkmode.cpp | 141 +++++++++++++++++++----------- 11 files changed, 203 insertions(+), 52 deletions(-) create mode 100644 include/wx/msw/darkmode.h create mode 100644 interface/wx/msw/darkmode.h diff --git a/Makefile.in b/Makefile.in index 2359e53008..d9a5ffa452 100644 --- a/Makefile.in +++ b/Makefile.in @@ -3150,6 +3150,7 @@ COND_TOOLKIT_MSW_GUI_HDR = \ wx/msw/datectrl.h \ wx/msw/calctrl.h \ wx/generic/activityindicator.h \ + wx/msw/darkmode.h \ wx/msw/checklst.h \ wx/msw/fdrepdlg.h \ wx/msw/fontdlg.h \ diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index 18a596c268..aad00cee62 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -1953,6 +1953,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! wx/msw/datectrl.h wx/msw/calctrl.h wx/generic/activityindicator.h + wx/msw/darkmode.h diff --git a/build/cmake/files.cmake b/build/cmake/files.cmake index 514016d530..cb52dd35c7 100644 --- a/build/cmake/files.cmake +++ b/build/cmake/files.cmake @@ -1843,6 +1843,7 @@ set(MSW_HDR wx/msw/datetimectrl.h wx/msw/timectrl.h wx/generic/activityindicator.h + wx/msw/darkmode.h ) set(MSW_RSC diff --git a/build/files b/build/files index 6ad7a8fc82..34eb2756f0 100644 --- a/build/files +++ b/build/files @@ -1760,6 +1760,7 @@ MSW_HDR = wx/msw/ctrlsub.h wx/msw/cursor.h wx/msw/custombgwin.h + wx/msw/darkmode.h wx/msw/datectrl.h wx/msw/datetimectrl.h wx/msw/dc.h diff --git a/build/msw/wx_core.vcxproj b/build/msw/wx_core.vcxproj index 98ded50540..6629a45445 100644 --- a/build/msw/wx_core.vcxproj +++ b/build/msw/wx_core.vcxproj @@ -1517,6 +1517,7 @@ + diff --git a/build/msw/wx_core.vcxproj.filters b/build/msw/wx_core.vcxproj.filters index dfe24b3836..1fc9c41d52 100644 --- a/build/msw/wx_core.vcxproj.filters +++ b/build/msw/wx_core.vcxproj.filters @@ -1738,6 +1738,9 @@ MSW Headers + + MSW Headers + MSW Headers diff --git a/include/wx/msw/app.h b/include/wx/msw/app.h index 6b50e59386..56a77065f9 100644 --- a/include/wx/msw/app.h +++ b/include/wx/msw/app.h @@ -17,6 +17,7 @@ class WXDLLIMPEXP_FWD_CORE wxFrame; class WXDLLIMPEXP_FWD_CORE wxWindow; class WXDLLIMPEXP_FWD_CORE wxApp; +class WXDLLIMPEXP_FWD_CORE wxDarkModeSettings; class WXDLLIMPEXP_FWD_CORE wxKeyEvent; class WXDLLIMPEXP_FWD_BASE wxLog; @@ -38,12 +39,16 @@ public: virtual int GetPrintMode() const { return m_printMode; } // MSW-specific function to enable experimental dark mode support. + // + // If settings are specified, the function takes ownership of the pointer, + // otherwise the defaults are used. enum { DarkMode_Auto = 0, // Use dark mode if the system is using it. DarkMode_Always = 1 // Force using dark mode. }; - bool MSWEnableDarkMode(int flags = 0); + bool + MSWEnableDarkMode(int flags = 0, wxDarkModeSettings* settings = nullptr); // implementation only void OnIdle(wxIdleEvent& event); diff --git a/include/wx/msw/darkmode.h b/include/wx/msw/darkmode.h new file mode 100644 index 0000000000..4cce68d7c9 --- /dev/null +++ b/include/wx/msw/darkmode.h @@ -0,0 +1,32 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/darkmode.h +// Purpose: MSW-specific header with dark mode related declarations. +// Author: Vadim Zeitlin +// Created: 2023-02-19 +// Copyright: (c) 2023 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_MSW_DARKMODE_H_ +#define _WX_MSW_DARKMODE_H_ + +#include "wx/settings.h" + +// ---------------------------------------------------------------------------- +// wxDarkModeSettings: allows to customize some of dark mode settings +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_CORE wxDarkModeSettings +{ +public: + wxDarkModeSettings() = default; + virtual ~wxDarkModeSettings(); + + // Get the colour to use for the given system colour when dark mode is on. + virtual wxColour GetColour(wxSystemColour index); + +private: + wxDECLARE_NO_COPY_CLASS(wxDarkModeSettings); +}; + +#endif // _WX_MSW_DARKMODE_H_ diff --git a/interface/wx/app.h b/interface/wx/app.h index c4aea965db..7d1bb20651 100644 --- a/interface/wx/app.h +++ b/interface/wx/app.h @@ -1212,6 +1212,8 @@ public: dark mode for the application, even if the system doesn't use the dark mode by default. Otherwise dark mode is only used if it is the default mode for the applications on the current system. + @param settings If specified, allows to customize dark mode appearance. + Please see wxDarkModeSettings documentation for more information. @return @true if dark mode support was enabled, @false if it couldn't be done, most likely because the system doesn't support dark mode. @@ -1220,7 +1222,8 @@ public: @since 3.3.0 */ - bool MSWEnableDarkMode(int flags = 0); + bool + MSWEnableDarkMode(int flags = 0, wxDarkModeSettings* settings = nullptr); //@} }; diff --git a/interface/wx/msw/darkmode.h b/interface/wx/msw/darkmode.h new file mode 100644 index 0000000000..1a9fd41c4c --- /dev/null +++ b/interface/wx/msw/darkmode.h @@ -0,0 +1,62 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/msw/darkmode.h +// Purpose: Documentation for MSW-specific dark mode functionality. +// Author: Vadim Zeitlin +// Created: 2023-02-19 +// Copyright: (c) 2023 Vadim Zeitlin +// Licence: wxWindows Licence +///////////////////////////////////////////////////////////////////////////// + +/** + Allows to customize some of the settings used in MSW dark mode. + + An object of this class may be passed to wxApp::MSWEnableDarkMode() to + customize some aspects of the dark mode when it is used under MSW systems. + + For example, to customize the background colour to use a reddish black + instead of normal black used by default, you could do the following: + @code + class MySettings : public wxDarkModeSettings + { + public: + wxColour GetColour(wxSystemColour index) override + { + switch ( index ) + { + case wxSYS_COLOUR_ACTIVECAPTION: + case wxSYS_COLOUR_APPWORKSPACE: + case wxSYS_COLOUR_INFOBK: + case wxSYS_COLOUR_LISTBOX: + case wxSYS_COLOUR_WINDOW: + case wxSYS_COLOUR_BTNFACE: + // Default colour used here is 0x202020. + return wxColour(0x402020); + } + + return wxDarkModeSettings::GetColour(index); + } + }; + + wxTheApp->MSWEnableDarkMode(wxApp::DarkMode_Always, new MySettings()); + @endcode + + @since 3.3.0 + */ +class wxDarkModeSettings +{ +public: + /** + Default constructor does nothing. + */ + wxDarkModeSettings() = default; + + /** + Get the colour to use for the given system colour when dark mode is on. + + The base class version of this function returns the colours commonly + used in dark mode. As the rest of dark mode support, their exact values + are not documented and are subject to change in the future Windows or + wxWidgets versions. + */ + virtual wxColour GetColour(wxSystemColour index); +}; diff --git a/src/msw/darkmode.cpp b/src/msw/darkmode.cpp index e7a0de778a..71b2aeb151 100644 --- a/src/msw/darkmode.cpp +++ b/src/msw/darkmode.cpp @@ -44,11 +44,14 @@ #include "wx/dynlib.h" #include "wx/module.h" +#include "wx/msw/darkmode.h" #include "wx/msw/dc.h" #include "wx/msw/uxtheme.h" #include "wx/msw/private/darkmode.h" +#include + static const char* TRACE_DARKMODE = "msw-darkmode"; #define DWMWA_USE_IMMERSIVE_DARK_MODE 20 @@ -168,10 +171,25 @@ public: virtual bool OnInit() override { return true; } virtual void OnExit() override { + ms_settings.reset(); + ms_pfnDwmSetWindowAttribute = (DwmSetWindowAttribute_t)-1; ms_dllDWM.Unload(); } + // Takes ownership of the provided pointer. + static void SetSettings(wxDarkModeSettings* settings) + { + ms_settings.reset(settings); + } + + // Returns the currently used settings: may only be called when the dark + // mode is on. + static wxDarkModeSettings& GetSettings() + { + return *ms_settings; + } + static DwmSetWindowAttribute_t GetDwmSetWindowAttribute() { if ( ms_pfnDwmSetWindowAttribute == (DwmSetWindowAttribute_t)-1 ) @@ -187,12 +205,15 @@ private: static wxDynamicLibrary ms_dllDWM; static DwmSetWindowAttribute_t ms_pfnDwmSetWindowAttribute; + static std::unique_ptr ms_settings; + wxDECLARE_DYNAMIC_CLASS(wxDarkModeModule); }; wxIMPLEMENT_DYNAMIC_CLASS(wxDarkModeModule, wxModule); wxDynamicLibrary wxDarkModeModule::ms_dllDWM; +std::unique_ptr wxDarkModeModule::ms_settings; DwmSetWindowAttribute_t wxDarkModeModule::ms_pfnDwmSetWindowAttribute = (DwmSetWindowAttribute_t)-1; @@ -201,7 +222,7 @@ wxDarkModeModule::ms_pfnDwmSetWindowAttribute = (DwmSetWindowAttribute_t)-1; // Public API // ---------------------------------------------------------------------------- -bool wxApp::MSWEnableDarkMode(int flags) +bool wxApp::MSWEnableDarkMode(int flags, wxDarkModeSettings* settings) { if ( !wxMSWImpl::InitDarkMode() ) return false; @@ -220,61 +241,23 @@ bool wxApp::MSWEnableDarkMode(int flags) gs_appMode = mode; + // Set up the settings to use, allocating a default one if none specified. + if ( !settings ) + settings = new wxDarkModeSettings(); + + wxDarkModeModule::SetSettings(settings); + return true; } // ---------------------------------------------------------------------------- -// Supporting functions for the rest of wxMSW code +// Default wxDarkModeSettings implementation // ---------------------------------------------------------------------------- -namespace wxMSWDarkMode -{ +// Implemented here to ensure that it's generated inside the DLL. +wxDarkModeSettings::~wxDarkModeSettings() = default; -bool IsActive() -{ - return wxMSWImpl::ShouldUseDarkMode(); -} - -void EnableForTLW(HWND hwnd) -{ - // Nothing to do, dark mode support not enabled or dark mode is not used. - if ( !wxMSWImpl::ShouldUseDarkMode() ) - return; - - BOOL useDarkMode = TRUE; - HRESULT hr = wxDarkModeModule::GetDwmSetWindowAttribute() - ( - hwnd, - DWMWA_USE_IMMERSIVE_DARK_MODE, - &useDarkMode, - sizeof(useDarkMode) - ); - if ( FAILED(hr) ) - wxLogApiError("DwmSetWindowAttribute(USE_IMMERSIVE_DARK_MODE)", hr); - - wxMSWImpl::AllowDarkModeForWindow(hwnd, true); -} - -void AllowForWindow(HWND hwnd, const wchar_t* themeName, const wchar_t* themeId) -{ - if ( !wxMSWImpl::ShouldUseDarkMode() ) - return; - - if ( wxMSWImpl::AllowDarkModeForWindow(hwnd, true) ) - wxLogTrace(TRACE_DARKMODE, "Allow dark mode for %p failed", hwnd); - - if ( themeName || themeId ) - { - HRESULT hr = ::SetWindowTheme(hwnd, themeName, themeId); - if ( FAILED(hr) ) - { - wxLogApiError(wxString::Format("SetWindowTheme(%p, %s, %s)", - hwnd, themeName, themeId), hr); - } - } -} - -wxColour GetColour(wxSystemColour index) +wxColour wxDarkModeSettings::GetColour(wxSystemColour index) { // This is not great at all, but better than using light mode colours that // are not appropriate for the dark mode. @@ -347,6 +330,62 @@ wxColour GetColour(wxSystemColour index) return wxColour(); } +// ---------------------------------------------------------------------------- +// Supporting functions for the rest of wxMSW code +// ---------------------------------------------------------------------------- + +namespace wxMSWDarkMode +{ + +bool IsActive() +{ + return wxMSWImpl::ShouldUseDarkMode(); +} + +void EnableForTLW(HWND hwnd) +{ + // Nothing to do, dark mode support not enabled or dark mode is not used. + if ( !wxMSWImpl::ShouldUseDarkMode() ) + return; + + BOOL useDarkMode = TRUE; + HRESULT hr = wxDarkModeModule::GetDwmSetWindowAttribute() + ( + hwnd, + DWMWA_USE_IMMERSIVE_DARK_MODE, + &useDarkMode, + sizeof(useDarkMode) + ); + if ( FAILED(hr) ) + wxLogApiError("DwmSetWindowAttribute(USE_IMMERSIVE_DARK_MODE)", hr); + + wxMSWImpl::AllowDarkModeForWindow(hwnd, true); +} + +void AllowForWindow(HWND hwnd, const wchar_t* themeName, const wchar_t* themeId) +{ + if ( !wxMSWImpl::ShouldUseDarkMode() ) + return; + + if ( wxMSWImpl::AllowDarkModeForWindow(hwnd, true) ) + wxLogTrace(TRACE_DARKMODE, "Allow dark mode for %p failed", hwnd); + + if ( themeName || themeId ) + { + HRESULT hr = ::SetWindowTheme(hwnd, themeName, themeId); + if ( FAILED(hr) ) + { + wxLogApiError(wxString::Format("SetWindowTheme(%p, %s, %s)", + hwnd, themeName, themeId), hr); + } + } +} + +wxColour GetColour(wxSystemColour index) +{ + return wxDarkModeModule::GetSettings().GetColour(index); +} + HBRUSH GetBackgroundBrush() { wxBrush* const brush = @@ -630,7 +669,9 @@ HandleMenuMessage(WXLRESULT* result, #else // !wxUSE_DARK_MODE -bool wxApp::MSWEnableDarkMode(int WXUNUSED(flags)) +bool +wxApp::MSWEnableDarkMode(int WXUNUSED(flags), + wxDarkModeSettings* WXUNUSED(settings)) { return false; } From f21bf04a3cfb568f784376467064428e200b4f66 Mon Sep 17 00:00:00 2001 From: Eran Ifrah Date: Sun, 19 Feb 2023 18:36:17 +0000 Subject: [PATCH 2/5] Draw a grey border around wxStaticBox in dark mode Avoid drawing a too bright/high contrast white border when using dark mode by drawing the border ourselves in grey colour instead. Closes #23107. --- src/msw/statbox.cpp | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/msw/statbox.cpp b/src/msw/statbox.cpp index 71225a2586..11ef160709 100644 --- a/src/msw/statbox.cpp +++ b/src/msw/statbox.cpp @@ -42,6 +42,7 @@ #include "wx/msw/private.h" #include "wx/msw/missing.h" #include "wx/msw/dc.h" +#include "wx/msw/private/darkmode.h" #include "wx/msw/private/winstyle.h" namespace @@ -538,7 +539,42 @@ void wxStaticBox::PaintBackground(wxDC& dc, const RECT& rc) void wxStaticBox::PaintForeground(wxDC& dc, const RECT&) { wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); - MSWDefWindowProc(WM_PAINT, (WPARAM)GetHdcOf(*impl), 0); + if ( wxMSWDarkMode::IsActive() ) + { + // draw grey border which has less contrast in dark mode than the default + // white box which is "too shiny" + const wxRect clientRect = GetClientRect(); + wxRect rect = clientRect; + wxDCBrushChanger brushChanger(dc, *wxTRANSPARENT_BRUSH); + wxDCPenChanger penChanger(dc, *wxGREY_PEN); + if ( !m_labelWin && !GetLabel().empty() ) + { + // if the control has a font, use it + wxDCFontChanger fontChanger(dc); + if ( GetFont().IsOk() ) + { + dc.SetFont(GetFont()); + } + + // Make sure that the label is vertically aligned with the border + wxCoord width, height; + + // Use "Tp" as our sampling text to get the + // maximum height from the current font + dc.GetTextExtent(L"Tp", &width, &height); + + // adjust the border height & Y coordinate + int offsetFromTop = (height / 2) + LABEL_VERT_BORDER; + rect.SetTop(offsetFromTop); + rect.SetHeight(rect.GetHeight() - offsetFromTop); + } + + dc.DrawRectangle(rect); + } + else + { + MSWDefWindowProc(WM_PAINT, (WPARAM)GetHdcOf(*impl), 0); + } #if wxUSE_UXTHEME // when using XP themes, neither setting the text colour nor transparent From d051ff0215fe836524879044652d9852a6f22aa3 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 19 Feb 2023 18:50:41 +0000 Subject: [PATCH 3/5] Allow customizing wxStaticBox border colour in dark mode This is useful because there is no really standard value for this colour and the choice of grey (0x808080) is completely subjective, but also because it may be useful to disable custom drawing of the border entirely if it results in any problems with the future Windows versions. See #23107. --- include/wx/msw/darkmode.h | 7 +++++++ include/wx/msw/private/darkmode.h | 3 +++ interface/wx/msw/darkmode.h | 12 ++++++++++++ src/msw/darkmode.cpp | 19 +++++++++++++++++++ src/msw/statbox.cpp | 13 ++++++++++--- 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/include/wx/msw/darkmode.h b/include/wx/msw/darkmode.h index 4cce68d7c9..3f95953ce7 100644 --- a/include/wx/msw/darkmode.h +++ b/include/wx/msw/darkmode.h @@ -25,6 +25,13 @@ public: // Get the colour to use for the given system colour when dark mode is on. virtual wxColour GetColour(wxSystemColour index); + // Get the pen to use for drawing wxStaticBox border in dark mode. + // + // Returning an invalid pen indicates that the default border drawn by the + // system should be used, which doesn't look very well in dark mode but + // shouldn't result in any problems worse than cosmetic ones. + virtual wxPen GetBorderPen(); + private: wxDECLARE_NO_COPY_CLASS(wxDarkModeSettings); }; diff --git a/include/wx/msw/private/darkmode.h b/include/wx/msw/private/darkmode.h index 3423e593c3..99dcd5bb79 100644 --- a/include/wx/msw/private/darkmode.h +++ b/include/wx/msw/private/darkmode.h @@ -36,6 +36,9 @@ void AllowForWindow(HWND hwnd, // colour if it isn't. wxColour GetColour(wxSystemColour index); +// Get the pen to use for drawing the border, see wxDarkModeSettings. +wxPen GetBorderPen(); + // Return the background brush to be used by default in dark mode. HBRUSH GetBackgroundBrush(); diff --git a/interface/wx/msw/darkmode.h b/interface/wx/msw/darkmode.h index 1a9fd41c4c..387fffdba4 100644 --- a/interface/wx/msw/darkmode.h +++ b/interface/wx/msw/darkmode.h @@ -59,4 +59,16 @@ public: wxWidgets versions. */ virtual wxColour GetColour(wxSystemColour index); + + /** + Get the pen to use for drawing wxStaticBox border in dark mode. + + Returning an invalid pen indicates that the default border drawn by the + system should be used, which doesn't look very well in dark mode but + shouldn't result in any problems worse than cosmetic ones. + + The base class version returns a grey pen, which looks better than the + default white one. + */ + virtual wxPen GetBorderPen(); }; diff --git a/src/msw/darkmode.cpp b/src/msw/darkmode.cpp index 71b2aeb151..f1f41d341b 100644 --- a/src/msw/darkmode.cpp +++ b/src/msw/darkmode.cpp @@ -330,6 +330,15 @@ wxColour wxDarkModeSettings::GetColour(wxSystemColour index) return wxColour(); } +wxPen wxDarkModeSettings::GetBorderPen() +{ + // Use a darker pen than the default white one by default. There doesn't + // seem to be any standard colour to use for it, Windows itself uses both + // 0x666666 and 0x797979 for the borders in the "Colours" control panel + // window, so it doesn't seem like anybody cares about consistency here. + return *wxGREY_PEN; +} + // ---------------------------------------------------------------------------- // Supporting functions for the rest of wxMSW code // ---------------------------------------------------------------------------- @@ -386,6 +395,11 @@ wxColour GetColour(wxSystemColour index) return wxDarkModeModule::GetSettings().GetColour(index); } +wxPen GetBorderPen() +{ + return wxDarkModeModule::GetSettings().GetBorderPen(); +} + HBRUSH GetBackgroundBrush() { wxBrush* const brush = @@ -697,6 +711,11 @@ wxColour GetColour(wxSystemColour WXUNUSED(index)) return wxColour(); } +wxPen GetBorderPen() +{ + return wxPen{}; +} + HBRUSH GetBackgroundBrush() { return 0; diff --git a/src/msw/statbox.cpp b/src/msw/statbox.cpp index 11ef160709..7ba22d8e50 100644 --- a/src/msw/statbox.cpp +++ b/src/msw/statbox.cpp @@ -539,14 +539,21 @@ void wxStaticBox::PaintBackground(wxDC& dc, const RECT& rc) void wxStaticBox::PaintForeground(wxDC& dc, const RECT&) { wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl(); + + // Optionally use this pen to draw a border which has less contrast in dark + // mode than the default white box which is "too shiny" + wxPen penBorder; if ( wxMSWDarkMode::IsActive() ) { - // draw grey border which has less contrast in dark mode than the default - // white box which is "too shiny" + penBorder = wxMSWDarkMode::GetBorderPen(); + } + + if ( penBorder.IsOk() ) + { const wxRect clientRect = GetClientRect(); wxRect rect = clientRect; wxDCBrushChanger brushChanger(dc, *wxTRANSPARENT_BRUSH); - wxDCPenChanger penChanger(dc, *wxGREY_PEN); + wxDCPenChanger penChanger(dc, penBorder); if ( !m_labelWin && !GetLabel().empty() ) { // if the control has a font, use it From 76fffafcd0a789796ca74ace19dcfe4683ee6626 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 19 Feb 2023 23:20:19 +0000 Subject: [PATCH 4/5] Allow customizing menu bar menus colours in MSW dark mode This allows to customize the hard-coded colours used for drawing the menu bar too. --- include/wx/msw/darkmode.h | 13 +++++++++++ interface/wx/msw/darkmode.h | 37 +++++++++++++++++++++++++++++++ src/msw/darkmode.cpp | 44 +++++++++++++++++++++++++++---------- 3 files changed, 82 insertions(+), 12 deletions(-) diff --git a/include/wx/msw/darkmode.h b/include/wx/msw/darkmode.h index 3f95953ce7..985919b827 100644 --- a/include/wx/msw/darkmode.h +++ b/include/wx/msw/darkmode.h @@ -12,6 +12,15 @@ #include "wx/settings.h" +// Constants used with wxDarkModeSettings::GetMenuColour(). +enum class wxMenuColour +{ + StandardFg, + StandardBg, + DisabledFg, + HotBg +}; + // ---------------------------------------------------------------------------- // wxDarkModeSettings: allows to customize some of dark mode settings // ---------------------------------------------------------------------------- @@ -25,6 +34,10 @@ public: // Get the colour to use for the given system colour when dark mode is on. virtual wxColour GetColour(wxSystemColour index); + // Menu items don't use any of the standard colours, but are defined by + // this function. + virtual wxColour GetMenuColour(wxMenuColour which); + // Get the pen to use for drawing wxStaticBox border in dark mode. // // Returning an invalid pen indicates that the default border drawn by the diff --git a/interface/wx/msw/darkmode.h b/interface/wx/msw/darkmode.h index 387fffdba4..9166af00be 100644 --- a/interface/wx/msw/darkmode.h +++ b/interface/wx/msw/darkmode.h @@ -7,6 +7,26 @@ // Licence: wxWindows Licence ///////////////////////////////////////////////////////////////////////////// +/** + Constants used with wxDarkModeSettings::GetMenuColour(). + + @since 3.3.0 + */ +enum class wxMenuColour +{ + /// Text colour used for the normal items in dark mode. + StandardFg, + + /// Standard menu background colour. + StandardBg, + + /// Foreground colour for the disabled items. + DisabledFg, + + /// Background colour used for the item over which mouse is hovering. + HotBg +}; + /** Allows to customize some of the settings used in MSW dark mode. @@ -57,9 +77,26 @@ public: used in dark mode. As the rest of dark mode support, their exact values are not documented and are subject to change in the future Windows or wxWidgets versions. + + @see GetMenuColour() */ virtual wxColour GetColour(wxSystemColour index); + /** + Get the colour to use for the menu bar in the given state. + + Currently the colours used by the menus in the menu bar in dark mode + don't correspond to any of wxSystemColour values and this separate + function is used for customizing them instead of GetColour(). + + Note that the colours returned by this function only affect the top + level menus, the colours of the menu items inside them can be + customized in the usual way using wxOwnerDrawn::SetTextColour(). + + The returned colour must be valid. + */ + virtual wxColour GetMenuColour(wxMenuColour which); + /** Get the pen to use for drawing wxStaticBox border in dark mode. diff --git a/src/msw/darkmode.cpp b/src/msw/darkmode.cpp index f1f41d341b..4efafba960 100644 --- a/src/msw/darkmode.cpp +++ b/src/msw/darkmode.cpp @@ -330,6 +330,27 @@ wxColour wxDarkModeSettings::GetColour(wxSystemColour index) return wxColour(); } +wxColour wxDarkModeSettings::GetMenuColour(wxMenuColour which) +{ + switch ( which ) + { + case wxMenuColour::StandardFg: + return wxColour(0xffffff); + + case wxMenuColour::StandardBg: + return wxColour(0x6d6d6d); + + case wxMenuColour::DisabledFg: + return wxColour(0x414141); + + case wxMenuColour::HotBg: + return wxColour(0x2b2b2b); + } + + wxFAIL_MSG( "unreachable" ); + return wxColour(); +} + wxPen wxDarkModeSettings::GetBorderPen() { // Use a darker pen than the default white one by default. There doesn't @@ -510,14 +531,15 @@ struct MenuBarDrawMenuItem MenuBarMenuItem mbmi; }; -constexpr COLORREF COL_STANDARD = 0xffffff; -constexpr COLORREF COL_DISABLED = 0x6d6d6d; -constexpr COLORREF COL_MENU_HOT = 0x414141; +wxColour GetMenuColour(wxMenuColour which) +{ + return wxDarkModeModule::GetSettings().GetMenuColour(which); +} -HBRUSH GetMenuBrush() +HBRUSH GetMenuBrush(wxMenuColour which = wxMenuColour::StandardBg) { wxBrush* const brush = - wxTheBrushList->FindOrCreateBrush(GetColour(wxSYS_COLOUR_MENU)); + wxTheBrushList->FindOrCreateBrush(GetMenuColour(which)); return brush ? GetHbrushOf(*brush) : 0; } @@ -621,9 +643,11 @@ HandleMenuMessage(WXLRESULT* result, HBRUSH hbr = 0; int partState = 0; + wxMenuColour colText = wxMenuColour::StandardFg; if ( itemState & ODS_INACTIVE ) { partState = MBI_DISABLED; + colText = wxMenuColour::DisabledFg; } else if ( (itemState & ODS_GRAYED) && (itemState & ODS_HOTLIGHT) ) { @@ -632,15 +656,13 @@ HandleMenuMessage(WXLRESULT* result, else if ( itemState & ODS_GRAYED ) { partState = MBI_DISABLED; + colText = wxMenuColour::DisabledFg; } else if ( itemState & (ODS_HOTLIGHT | ODS_SELECTED) ) { partState = MBI_HOT; - auto* const - brush = wxTheBrushList->FindOrCreateBrush(COL_MENU_HOT); - if ( brush ) - hbr = GetHbrushOf(*brush); + hbr = GetMenuBrush(wxMenuColour::HotBg); } else { @@ -660,9 +682,7 @@ HandleMenuMessage(WXLRESULT* result, DTTOPTS textOpts; textOpts.dwSize = sizeof(textOpts); textOpts.dwFlags = DTT_TEXTCOLOR; - textOpts.crText = itemState & (ODS_INACTIVE | ODS_GRAYED) - ? COL_DISABLED - : COL_STANDARD; + textOpts.crText = wxColourToRGB(GetMenuColour(colText)); DWORD drawTextFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER; if ( itemState & ODS_NOACCEL) From 24e8297bddceb83cc9209f8a98ea28054043bd59 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 12 Mar 2023 13:41:08 +0100 Subject: [PATCH 5/5] Change example in wxDarkModeSettings docs to avoid -Wswitch As written, the code snippet in the docs would result in a warning from gcc, so change it to avoid this. See #23028. --- interface/wx/msw/darkmode.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/wx/msw/darkmode.h b/interface/wx/msw/darkmode.h index 9166af00be..0f9fd5e821 100644 --- a/interface/wx/msw/darkmode.h +++ b/interface/wx/msw/darkmode.h @@ -51,9 +51,10 @@ enum class wxMenuColour case wxSYS_COLOUR_BTNFACE: // Default colour used here is 0x202020. return wxColour(0x402020); - } - return wxDarkModeSettings::GetColour(index); + default: + return wxDarkModeSettings::GetColour(index); + } } };