From 37118da5f9b538a2d441565912422ac91e2f2eae Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 18:42:47 +0100 Subject: [PATCH 1/8] Don't give spin button used in wxSpinCtrl any border This doesn't actually seem to change its appearance, but using border for it doesn't make sense, so don't do it -- and explain it, to avoid confusion. --- src/msw/spinctrl.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index 22e7ee286b..25b25daf47 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -281,6 +281,7 @@ bool wxSpinCtrl::Create(wxWindow *parent, // set style for the base class style |= wxSP_VERTICAL; + // the border is only used for the text control part if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT ) style |= wxBORDER_SUNKEN; @@ -327,8 +328,10 @@ bool wxSpinCtrl::Create(wxWindow *parent, } - // create the spin button - if ( !wxSpinButton::Create(parent, id, pos, wxSize(0, 0), style, name) ) + // create the spin button without any border as it doesn't make sense for + // it (even if it doesn't seem to be actually taken into account anyhow) + if ( !wxSpinButton::Create(parent, id, pos, wxSize(0, 0), + (style & ~wxBORDER_MASK) | wxBORDER_NONE, name) ) { return false; } From dc7f934fb3a739433bad9b12adef231204d12d3b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 19:52:07 +0100 Subject: [PATCH 2/8] Don't use wxBORDER_SUNKEN for wxSpinCtrl in dark mode This applies the change done in f5dd5eaf19 (Use darker border in wxMSW dark mode by default, 2023-06-16) for all the other controls to wxSpinCtrl too, as it still explicitly used wxBORDER_SUNKEN. The border around the text part of the control is now correct, but it looks even uglier in the dark mode due to a completely wrong border being used around the spin button -- but this will be addressed by the upcoming commits. --- src/msw/spinctrl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/msw/spinctrl.cpp b/src/msw/spinctrl.cpp index 25b25daf47..8179e92c68 100644 --- a/src/msw/spinctrl.cpp +++ b/src/msw/spinctrl.cpp @@ -283,7 +283,7 @@ bool wxSpinCtrl::Create(wxWindow *parent, // the border is only used for the text control part if ( (style & wxBORDER_MASK) == wxBORDER_DEFAULT ) - style |= wxBORDER_SUNKEN; + style |= DoTranslateBorder(wxBORDER_THEME); SetWindowStyle(style); From 83f5b6f3c6bce261c4e696426d495f190459030b Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 20:13:44 +0100 Subject: [PATCH 3/8] Remove unnecessary wxSpinButton::MSWCommand() This function did nothing, which was already exactly what the base class version did too. --- include/wx/msw/spinbutt.h | 1 - src/msw/spinbutt.cpp | 6 ------ 2 files changed, 7 deletions(-) diff --git a/include/wx/msw/spinbutt.h b/include/wx/msw/spinbutt.h index 4ae6bf0946..270254bdb5 100644 --- a/include/wx/msw/spinbutt.h +++ b/include/wx/msw/spinbutt.h @@ -48,7 +48,6 @@ public: virtual void SetRange(int minVal, int maxVal) override; // implementation - virtual bool MSWCommand(WXUINT param, WXWORD id) override; virtual bool MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result) override; virtual bool MSWOnScroll(int orientation, WXWORD wParam, WXWORD pos, WXHWND control) override; diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index ffc7864098..9146cb7a8d 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -289,12 +289,6 @@ bool wxSpinButton::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM * return processed; } -bool wxSpinButton::MSWCommand(WXUINT WXUNUSED(cmd), WXWORD WXUNUSED(id)) -{ - // No command messages - return false; -} - void wxSpinButton::SetIncrement(int value) { UDACCEL accel; From 0130a066dfd9f58b734bbc8347a4f4f5e006667c Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 20:15:38 +0100 Subject: [PATCH 4/8] Make section comments in wxSpinButton more accurate No changes whatsoever, just improve the comments to correspond to the actual functions under them. --- src/msw/spinbutt.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index 9146cb7a8d..306c577fa9 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -166,7 +166,7 @@ wxSize wxSpinButton::DoGetBestSize() const } // ---------------------------------------------------------------------------- -// Attributes +// value and range // ---------------------------------------------------------------------------- int wxSpinButton::GetValue() const @@ -231,6 +231,10 @@ void wxSpinButton::SetRange(int minVal, int maxVal) } } +// ---------------------------------------------------------------------------- +// event generation +// ---------------------------------------------------------------------------- + bool wxSpinButton::MSWOnScroll(int WXUNUSED(orientation), WXWORD wParam, WXWORD WXUNUSED(pos), WXHWND control) { @@ -289,6 +293,10 @@ bool wxSpinButton::MSWOnNotify(int WXUNUSED(idCtrl), WXLPARAM lParam, WXLPARAM * return processed; } +// ---------------------------------------------------------------------------- +// increment +// ---------------------------------------------------------------------------- + void wxSpinButton::SetIncrement(int value) { UDACCEL accel; From 79567c83f4263df2860b0c4da38d91da834c1aab Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 20:24:54 +0100 Subject: [PATCH 5/8] Fix wxSpinButton redrawing when using WS_EX_COMPOSITED Explicitly redraw it ourselves instead of relying on DefWndProc() to do it, as with 15e4f5b (Work around wxListCtrl repainting problems with WS_EX_COMPOSITED, 2023-06-05) which did the same thing for wxListCtrl, doing it seems to be enough to make the control draw correctly. Closes #23656. --- include/wx/msw/spinbutt.h | 2 ++ src/msw/spinbutt.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/wx/msw/spinbutt.h b/include/wx/msw/spinbutt.h index 270254bdb5..cd359092b4 100644 --- a/include/wx/msw/spinbutt.h +++ b/include/wx/msw/spinbutt.h @@ -69,6 +69,8 @@ protected: virtual void NormalizeValue(); private: + void OnPaint(wxPaintEvent& event); + wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxSpinButton); }; diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index 306c577fa9..ba2ab18109 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -23,6 +23,7 @@ #ifndef WX_PRECOMP #include "wx/msw/wrapcctl.h" // include "properly" #include "wx/app.h" + #include "wx/dcclient.h" #endif #if wxUSE_SPINBTN @@ -130,6 +131,8 @@ bool wxSpinButton::Create(wxWindow *parent, SubclassWin(m_hWnd); + Bind(wxEVT_PAINT, &wxSpinButton::OnPaint, this); + SetInitialSize(size); return true; @@ -165,6 +168,21 @@ wxSize wxSpinButton::DoGetBestSize() const return bestSize; } +// ---------------------------------------------------------------------------- +// painting +// ---------------------------------------------------------------------------- + +void wxSpinButton::OnPaint(wxPaintEvent& event) +{ + // We need to always paint this control explicitly instead of letting + // DefWndProc() do it, as this avoids whichever optimization the latter + // function does when WS_EX_COMPOSITED is on that result in not drawing + // parts of the control at all (see #23656). + wxPaintDC dc(this); + + wxSpinButtonBase::OnPaint(event); +} + // ---------------------------------------------------------------------------- // value and range // ---------------------------------------------------------------------------- From 68666d929d326e38b8432eb924751a6dfd1fa333 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 20:29:59 +0100 Subject: [PATCH 6/8] Invert wxSpinButton in dark mode in its own OnPaint() This doesn't change anything yet, as the same thing was done by wxWindow by default thanks to the overridden MSWShouldUseAutoDarkMode(), but this will be easier to customize in the upcoming commits. This commit is best viewed ignoring whitespace-only changes. --- src/msw/spinbutt.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index ba2ab18109..74c76e4821 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -31,6 +31,7 @@ #include "wx/spinbutt.h" #include "wx/msw/private.h" +#include "wx/msw/private/darkmode.h" #ifndef UDM_SETRANGE32 #define UDM_SETRANGE32 (WM_USER+111) @@ -174,13 +175,16 @@ wxSize wxSpinButton::DoGetBestSize() const void wxSpinButton::OnPaint(wxPaintEvent& event) { - // We need to always paint this control explicitly instead of letting - // DefWndProc() do it, as this avoids whichever optimization the latter - // function does when WS_EX_COMPOSITED is on that result in not drawing - // parts of the control at all (see #23656). - wxPaintDC dc(this); + if ( !wxMSWDarkMode::PaintIfNecessary(GetHwnd(), m_oldWndProc) ) + { + // We need to always paint this control explicitly instead of letting + // DefWndProc() do it, as this avoids whichever optimization the latter + // function does when WS_EX_COMPOSITED is on that result in not drawing + // parts of the control at all (see #23656). + wxPaintDC dc(this); - wxSpinButtonBase::OnPaint(event); + wxSpinButtonBase::OnPaint(event); + } } // ---------------------------------------------------------------------------- From 595e2f93260bd45bed16a4784f90d2321088685a Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 22:24:56 +0100 Subject: [PATCH 7/8] Tweak wxSpinCtrl appearance in dark mode Previously wxSpinButton using a buddy control had borders which were much too bright in the dark mode. Now the entire border is perhaps a bit too dark, but it's still better than before. --- src/msw/spinbutt.cpp | 86 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index 74c76e4821..be489b4085 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -24,12 +24,14 @@ #include "wx/msw/wrapcctl.h" // include "properly" #include "wx/app.h" #include "wx/dcclient.h" + #include "wx/dcmemory.h" #endif #if wxUSE_SPINBTN #include "wx/spinbutt.h" +#include "wx/msw/dc.h" #include "wx/msw/private.h" #include "wx/msw/private/darkmode.h" @@ -175,7 +177,89 @@ wxSize wxSpinButton::DoGetBestSize() const void wxSpinButton::OnPaint(wxPaintEvent& event) { - if ( !wxMSWDarkMode::PaintIfNecessary(GetHwnd(), m_oldWndProc) ) + if ( wxMSWDarkMode::IsActive() ) + { + // Unfortunately PaintIfNecessary() can't be used here as we need to + // handle the extra border below, so duplicate what it does here. + const RECT rc = wxGetClientRect(GetHwnd()); + const wxSize size{rc.right - rc.left, rc.bottom - rc.top}; + + if ( size == wxSize() ) + return; + + wxBitmap bmp(size); + { + wxMemoryDC mdc(bmp); + + ::CallWindowProc(m_oldWndProc, + GetHwnd(), WM_PAINT, (WPARAM)GetHdcOf(mdc), 0); + } + +#if wxUSE_IMAGE + // When using a buddy control, the spin button tries to mimic being a + // part of it by adding an extra border, not used for standalone + // controls. This doesn't work very well even in light mode in modern + // Windows (it was apparently done for the classic 3D appearance and + // never updated since then), but looks completely horrible in dark + // mode, so we must get rid of this border by overdrawing it. + const bool drawBorder = ::SendMessage(GetHwnd(), UDM_GETBUDDY, 0, 0); + wxImage::RGBValue border; + if ( drawBorder ) + { + const auto col = wxMSWDarkMode::GetBorderPen().GetColour(); + border.red = col.GetRed(); + border.green = col.GetGreen(); + border.blue = col.GetBlue(); + } + + wxImage image = bmp.ConvertToImage(); + + const int width = image.GetWidth(); + const int height = image.GetHeight(); + unsigned char *data = image.GetData(); + unsigned char *alpha = image.GetAlpha(); + for ( int y = 0; y < height; ++y ) + { + for ( int x = 0; x < width; ++x ) + { + wxImage::RGBValue rgb(data[0], data[1], data[2]); + + if ( drawBorder && + (y == 0 || y == height - 1 || x == width - 1) ) + { + rgb = border; + if ( alpha ) + *alpha = wxALPHA_OPAQUE; + } + else + { + // This uses a slightly different formula than the one in + // InvertBitmapPixel() because the one there results in the + // lines being too bright. + auto hsv = wxImage::RGBtoHSV(rgb); + hsv.value = 1.0 - hsv.value; + rgb = wxImage::HSVtoRGB(hsv); + } + + data[0] = rgb.red; + data[1] = rgb.green; + data[2] = rgb.blue; + data += 3; + + if ( alpha ) + alpha++; + } + } + + bmp = wxBitmap(image); +#endif // wxUSE_IMAGE + + PAINTSTRUCT ps; + wxDCTemp dc(::BeginPaint(GetHwnd(), &ps), size); + dc.DrawBitmap(bmp, 0, 0); + ::EndPaint(GetHwnd(), &ps); + } + else { // We need to always paint this control explicitly instead of letting // DefWndProc() do it, as this avoids whichever optimization the latter From 1dafca9e1dcce770c498cc019ef288c0fe61b917 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sat, 8 Jul 2023 22:26:14 +0100 Subject: [PATCH 8/8] Remove "automatic" dark mode support This commit reverts cb85871831 (Add MSWShouldUseAutoDarkMode() and use it for wxSpinButton, 2022-12-07) as, contrary to the expectations of this commit, other controls didn't use this function and even wxSpinButton itself doesn't need it any more after the changes of the parent commit, which were required because automatic support was not flexible enough. --- include/wx/msw/spinbutt.h | 2 -- include/wx/msw/window.h | 7 ------- src/msw/spinbutt.cpp | 7 ------- src/msw/window.cpp | 6 +----- 4 files changed, 1 insertion(+), 21 deletions(-) diff --git a/include/wx/msw/spinbutt.h b/include/wx/msw/spinbutt.h index cd359092b4..743260e094 100644 --- a/include/wx/msw/spinbutt.h +++ b/include/wx/msw/spinbutt.h @@ -60,8 +60,6 @@ public: virtual void SetIncrement(int value) override; virtual int GetIncrement() const override; - virtual bool MSWShouldUseAutoDarkMode() const override; - protected: virtual wxSize DoGetBestSize() const override; diff --git a/include/wx/msw/window.h b/include/wx/msw/window.h index 9d2677284a..af9f673cdd 100644 --- a/include/wx/msw/window.h +++ b/include/wx/msw/window.h @@ -213,13 +213,6 @@ 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); diff --git a/src/msw/spinbutt.cpp b/src/msw/spinbutt.cpp index be489b4085..45995ee9a2 100644 --- a/src/msw/spinbutt.cpp +++ b/src/msw/spinbutt.cpp @@ -145,13 +145,6 @@ 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 // ---------------------------------------------------------------------------- diff --git a/src/msw/window.cpp b/src/msw/window.cpp index c911d5de18..444634f9b6 100644 --- a/src/msw/window.cpp +++ b/src/msw/window.cpp @@ -3197,11 +3197,7 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result, } else // no DC given { - if ( MSWShouldUseAutoDarkMode() && - wxMSWDarkMode::PaintIfNecessary(GetHwnd(), m_oldWndProc) ) - processed = true; - else - processed = HandlePaint(); + processed = HandlePaint(); } break;