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; 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)