From 52750c2ee964b072485c1d0ee815f074f73e365f Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 29 Dec 2022 00:37:25 +0000 Subject: [PATCH 1/2] Factor our wxMSWDarkMode::InvertBitmap() function Extract it from PaintIfNecessary() to allow reusing it elsewhere. No real changes yet. --- include/wx/msw/private/darkmode.h | 7 +++- src/msw/darkmode.cpp | 55 ++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/include/wx/msw/private/darkmode.h b/include/wx/msw/private/darkmode.h index db3897d44b..3423e593c3 100644 --- a/include/wx/msw/private/darkmode.h +++ b/include/wx/msw/private/darkmode.h @@ -39,7 +39,12 @@ wxColour GetColour(wxSystemColour index); // Return the background brush to be used by default in dark mode. HBRUSH GetBackgroundBrush(); -// If dark mode is active, paint the given window using inverted colours. +// Invert the colours of the given bitmap trying to keep it readable. +wxBitmap InvertBitmap(const wxBitmap& bmp); + +// If dark mode is active, paint the given window using inverted colours by +// drawing it normally and then applying InvertBitmap() to it. +// // Otherwise just return false without doing anything. // // This can only be called from WM_PAINT handler for a native control and diff --git a/src/msw/darkmode.cpp b/src/msw/darkmode.cpp index e9a27e4d3a..15874781bc 100644 --- a/src/msw/darkmode.cpp +++ b/src/msw/darkmode.cpp @@ -353,6 +353,35 @@ HBRUSH GetBackgroundBrush() return brush ? GetHbrushOf(*brush) : 0; } +wxBitmap InvertBitmap(const wxBitmap& bmp) +{ +#if wxUSE_IMAGE + wxImage image = bmp.ConvertToImage(); + + unsigned char *data = image.GetData(); + const int len = image.GetWidth()*image.GetHeight(); + for ( int i = 0; i < len; ++i, data += 3 ) + { + wxImage::RGBValue rgb(data[0], data[1], data[2]); + wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); + + // There is no really good way to convert normal colours to dark mode, + // but try to do a bit better than just inverting the value because + // this results in colours which are much too dark. + hsv.value = sqrt(1.0 - hsv.value*hsv.value); + + rgb = wxImage::HSVtoRGB(hsv); + data[0] = rgb.red; + data[1] = rgb.green; + data[2] = rgb.blue; + } + + return wxBitmap(image); +#else // !wxUSE_IMAGE + return wxBitmap(); +#endif // wxUSE_IMAGE/!wxUSE_IMAGE +} + bool PaintIfNecessary(HWND hwnd, WXWNDPROC defWndProc) { #if wxUSE_IMAGE @@ -378,28 +407,9 @@ bool PaintIfNecessary(HWND hwnd, WXWNDPROC defWndProc) ::DefWindowProc(hwnd, WM_PAINT, wparam, 0); } - wxImage image = bmp.ConvertToImage(); - - unsigned char *data = image.GetData(); - for ( int i = 0; i < size.x*size.y; ++i, data += 3 ) - { - wxImage::RGBValue rgb(data[0], data[1], data[2]); - wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb); - - // There is no really good way to convert normal colours to dark mode, - // but try to do a bit better than just inverting the value because - // this results in colours which are much too dark. - hsv.value = sqrt(1.0 - hsv.value*hsv.value); - - rgb = wxImage::HSVtoRGB(hsv); - data[0] = rgb.red; - data[1] = rgb.green; - data[2] = rgb.blue; - } - PAINTSTRUCT ps; wxDCTemp dc(::BeginPaint(hwnd, &ps), size); - dc.DrawBitmap(wxBitmap(image), 0, 0); + dc.DrawBitmap(InvertBitmap(bmp), 0, 0); ::EndPaint(hwnd, &ps); return true; @@ -649,6 +659,11 @@ HBRUSH GetBackgroundBrush() return 0; } +wxBitmap InvertBitmap(const wxBitmap& WXUNUSED(bmp)) +{ + return wxBitmap(); +} + bool PaintIfNecessary(HWND WXUNUSED(hwnd), WXWNDPROC WXUNUSED(defWndProc)) { return false; From 64d75a55ed25309ff85d589b047b4bf6443cd3f9 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Thu, 29 Dec 2022 00:37:50 +0000 Subject: [PATCH 2/2] Invert the themed collapsible pane button in dark mode This looks better than using the light background version and more native than using the generic version that would be the only other alternative as there doesn't seem to be any way to draw a dark version of this button using uxtheme API. Closes #23096. --- src/msw/renderer.cpp | 60 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/src/msw/renderer.cpp b/src/msw/renderer.cpp index 40d9df8be7..7a95ca16cd 100644 --- a/src/msw/renderer.cpp +++ b/src/msw/renderer.cpp @@ -25,6 +25,7 @@ #include "wx/window.h" #include "wx/control.h" // for wxControl::Ellipsize() #include "wx/dc.h" + #include "wx/dcmemory.h" #include "wx/settings.h" #endif //WX_PRECOMP @@ -38,6 +39,8 @@ #include "wx/msw/wrapcctl.h" #include "wx/dynlib.h" +#include "wx/msw/private/darkmode.h" + // ---------------------------------------------------------------------------- // methods common to wxRendererMSW and wxRendererXP // ---------------------------------------------------------------------------- @@ -927,11 +930,8 @@ wxSize wxRendererXP::GetExpanderSize(wxWindow* win) return m_rendererNative.GetExpanderSize(win); } -void -wxRendererXP::DrawCollapseButton(wxWindow *win, - wxDC& dc, - const wxRect& rect, - int flags) +static bool +DoDrawCollapseButton(wxWindow* win, HDC hdc, RECT r, int flags) { wxUxThemeHandle hTheme(win, L"TASKDIALOG"); @@ -948,20 +948,62 @@ wxRendererXP::DrawCollapseButton(wxWindow *win, if ( flags & wxCONTROL_EXPANDED ) state += 3; - RECT r = ConvertToRECT(dc, rect); - ::DrawThemeBackground ( hTheme, - GetHdcOf(dc.GetTempHDC()), + hdc, TDLG_EXPANDOBUTTON, state, &r, nullptr ); + return true; + } + + return false; +} + +void +wxRendererXP::DrawCollapseButton(wxWindow *win, + wxDC& dc, + const wxRect& rect, + int flags) +{ + RECT r = ConvertToRECT(dc, rect); + + // Default theme draws the button on light background which looks very out + // of place when using dark mode, so invert it if necessary and fall back + // on the generic version if this fails. + // + // Ideal would be to find the theme drawing the version appropriate for the + // dark mode, but it's unknown if there is one providing this. + if ( wxMSWDarkMode::IsActive() ) + { + wxBitmap bmp(rect.GetSize()); + + bool ok; + { + wxMemoryDC mdc(bmp); + ok = DoDrawCollapseButton(win, GetHdcOf(mdc), r, flags); + } + + if ( ok ) + { + wxBitmap bmpInv = wxMSWDarkMode::InvertBitmap(bmp); + if ( bmpInv.IsOk() ) + { + dc.DrawBitmap(bmpInv, rect.GetPosition()); + return; + } + } } else - m_rendererNative.DrawCollapseButton(win, dc, rect, flags); + { + if ( DoDrawCollapseButton(win, GetHdcOf(dc.GetTempHDC()), r, flags) ) + return; + } + + m_rendererNative.DrawCollapseButton(win, dc, rect, flags); } wxSize wxRendererXP::GetCollapseButtonSize(wxWindow *win, wxDC& dc)