Add MSWShouldUseAutoDarkMode() and use it for wxSpinButton

This control doesn't seem to support dark mode natively, whichever theme
is used for it, so paint it in dark mode ourselves simply by inverting
its colours -- this is not great, but passable and better than leaving
it with a light background.

As this probably won't be the only control which will need this,
implement it at wxWindow level and allow the derived classes to opt-in
this "automatic dark mode support" by simply overriding the new
MSWShouldUseAutoDarkMode() function.
This commit is contained in:
Vadim Zeitlin 2022-12-07 15:49:42 +00:00
parent 9106a8306e
commit cb85871831
6 changed files with 90 additions and 1 deletions

View file

@ -35,6 +35,14 @@ 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.
// Otherwise just return false without doing anything.
//
// This can only be called from WM_PAINT handler for a native control and
// assumes that this control handles WPARAM argument of WM_PAINT as HDC to
// paint on.
bool PaintIfNecessary(wxWindow* w);
} // namespace wxMSWDarkMode
#endif // _WX_MSW_PRIVATE_DARKMODE_H_

View file

@ -61,6 +61,8 @@ public:
virtual void SetIncrement(int value) override;
virtual int GetIncrement() const override;
virtual bool MSWShouldUseAutoDarkMode() const override;
protected:
virtual wxSize DoGetBestSize() const override;

View file

@ -211,6 +211,13 @@ public:
void OnPaint(wxPaintEvent& event);
// Override this to return true to automatically invert the window colours
// in dark mode.
//
// This doesn't result in visually great results, but may still be better
// than using light background.
virtual bool MSWShouldUseAutoDarkMode() const { return false; }
public:
// Windows subclassing
void SubclassWin(WXHWND hWnd);

View file

@ -34,12 +34,16 @@
#ifndef WX_PRECOMP
#include "wx/app.h"
#include "wx/bitmap.h"
#include "wx/dcmemory.h"
#include "wx/image.h"
#include "wx/log.h"
#endif // WX_PRECOMP
#include "wx/dynlib.h"
#include "wx/module.h"
#include "wx/msw/dc.h"
#include "wx/msw/uxtheme.h"
static const char* TRACE_DARKMODE = "msw-darkmode";
@ -335,6 +339,57 @@ HBRUSH GetBackgroundBrush()
return brush ? GetHbrushOf(*brush) : 0;
}
bool PaintIfNecessary(wxWindow* w)
{
#if wxUSE_IMAGE
if ( !wxMSWImpl::ShouldUseDarkMode() )
return false;
const wxSize size = w->GetClientSize();
// Don't bother doing anything with the empty windows.
if ( size == wxSize() )
return false;
// Ask the control to paint itself on the given bitmap.
wxBitmap bmp(size);
{
wxMemoryDC mdc(bmp);
w->MSWDefWindowProc(WM_PAINT, (WPARAM)GetHdcOf(mdc), 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(GetHwndOf(w), &ps), size);
dc.DrawBitmap(wxBitmap(image), 0, 0);
::EndPaint(GetHwndOf(w), &ps);
return true;
#else // !wxUSE_IMAGE
wxUnusedVar(w);
return false;
#endif
}
} // namespace wxMSWDarkMode
#else // !wxUSE_DARK_MODE
@ -370,6 +425,11 @@ HBRUSH GetBackgroundBrush()
return 0;
}
bool PaintIfNecessary(wxWindow* WXUNUSED(w))
{
return false;
}
} // namespace wxMSWDarkMode
#endif // wxUSE_DARK_MODE/!wxUSE_DARK_MODE

View file

@ -139,6 +139,13 @@ wxSpinButton::~wxSpinButton()
{
}
bool wxSpinButton::MSWShouldUseAutoDarkMode() const
{
// Native control doesn't seem to have any support for dark theme, so
// invert it in dark mode -- this is not great, but better than nothing.
return true;
}
// ----------------------------------------------------------------------------
// size calculation
// ----------------------------------------------------------------------------

View file

@ -76,6 +76,7 @@
#endif
#include "wx/msw/private.h"
#include "wx/msw/private/darkmode.h"
#include "wx/msw/private/dpiaware.h"
#include "wx/msw/private/keyboard.h"
#include "wx/msw/private/paint.h"
@ -3132,7 +3133,11 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
}
else // no DC given
{
processed = HandlePaint();
if ( MSWShouldUseAutoDarkMode() &&
wxMSWDarkMode::PaintIfNecessary(this) )
processed = true;
else
processed = HandlePaint();
}
break;