Merge branch 'dark-mode-fixes'

Various cosmetic -- but rather important -- fixes for MSW dark mode.

See #23570.
This commit is contained in:
Vadim Zeitlin 2023-05-27 21:46:34 +02:00
commit fff31e01b7
5 changed files with 127 additions and 32 deletions

View file

@ -47,6 +47,11 @@ protected:
int sizeFlags = wxSIZE_AUTO) override;
virtual wxSize DoGetBestClientSize() const override;
virtual bool MSWHandleMessage(WXLRESULT *result,
WXUINT message,
WXWPARAM wParam,
WXLPARAM lParam) override;
virtual wxString WXGetVisibleLabel() const override;
virtual void WXSetVisibleLabel(const wxString& str) override;

View file

@ -2603,8 +2603,9 @@ void wxDataViewMainWindow::OnPaint( wxPaintEvent &WXUNUSED(event) )
const wxColour bgColour = m_owner->GetBackgroundColour();
// Depending on the background, alternate row color
// will be 3% more dark or 50% brighter.
int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 150;
// will be 3% more dark or 10% brighter -- because 3% brighter
// would be unnoticeable.
int alpha = bgColour.GetRGB() > 0x808080 ? 97 : 110;
altRowColour = bgColour.ChangeLightness(alpha);
}

View file

@ -340,13 +340,13 @@ wxColour wxDarkModeSettings::GetMenuColour(wxMenuColour which)
return wxColour(0xffffff);
case wxMenuColour::StandardBg:
return wxColour(0x6d6d6d);
return GetColour(wxSYS_COLOUR_MENU);
case wxMenuColour::DisabledFg:
return wxColour(0x414141);
return wxColour(0x6d6d6d);
case wxMenuColour::HotBg:
return wxColour(0x2b2b2b);
return wxColour(0x414141);
}
wxFAIL_MSG( "unreachable" );

View file

@ -621,14 +621,20 @@ void wxListCtrl::SetWindowStyleFlag(long flag)
bool wxListCtrl::MSWGetDarkModeSupport(MSWDarkModeSupport& support) const
{
// There doesn't seem to be any theme that works well here:
// There doesn't seem to be any theme that works well out of the box:
//
// - "Explorer" draws bluish hover highlight rectangle which is not at
// all like the greyish one used by the actual Explorer in dark mode.
// It also draws vertical separator lines, unlike the Explorer itself,
// which wouldn't be too bad if they were not misaligned with the
// separators drawn in the header, when it is used, which looks ugly.
// - "DarkMode_Explorer" uses the same selection colours as the light mode
// and doesn't draw hover rectangle at all.
// - "ItemsView", which we use currently, draws the selection and hover as
// expected, but uses light mode scrollbars.
support.themeName = L"ItemsView";
// - "ItemsView" draws the selection and hover as expected, but uses light
// mode scrollbars and also misaligned vertical separators.
//
// We currently use DarkMode_Explorer and override selection drawing.
support.themeName = L"DarkMode_Explorer";
return true;
}
@ -3016,7 +3022,10 @@ bool wxListCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
// custom draw stuff
// ----------------------------------------------------------------------------
static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
namespace
{
RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
{
RECT rc;
wxGetListCtrlItemRect(nmcd.hdr.hwndFrom, nmcd.dwItemSpec, LVIR_BOUNDS, rc);
@ -3031,7 +3040,6 @@ static RECT GetCustomDrawnItemRect(const NMCUSTOMDRAW& nmcd)
return rc;
}
static
bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
{
NMCUSTOMDRAW& nmcd = pLVCD->nmcd;
@ -3055,13 +3063,13 @@ bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
RECT rc2;
wxGetListCtrlSubItemRect(hwndList, item, 1, LVIR_BOUNDS, rc2);
rc.right = rc2.left;
rc.left += 4;
}
else // not first subitem
{
rc.left += 6;
}
// This mysterious offset is necessary for the owner drawn items to align
// with the non-owner-drawn ones. Note that it's intentionally *not* scaled
// by DPI factor because it doesn't seem to depend on the resolution.
rc.left += 6;
// get the image and text to draw
wxChar text[512];
LV_ITEM it;
@ -3131,19 +3139,35 @@ bool HandleSubItemPrepaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int colCount)
return true;
}
static void HandleItemPostpaint(NMCUSTOMDRAW nmcd)
void HandleItemPostpaint(NMCUSTOMDRAW nmcd)
{
// The control seems to always draw its own focus rectangle when not using
// dark mode, so don't draw another one.
if ( !wxMSWDarkMode::IsActive() )
return;
if ( nmcd.uItemState & CDIS_FOCUS )
{
RECT rc = GetCustomDrawnItemRect(nmcd);
// don't use the provided HDC, it's in some strange state by now
::DrawFocusRect(ClientHDC(nmcd.hdr.hwndFrom), &rc);
::DrawFocusRect(nmcd.hdc, &rc);
}
}
// Flags for HandleItemPaint()
enum
{
Paint_Default = 0,
Paint_OnlySelected = 1
};
// This function is normally called only if we use custom colours, but it's
// also called when using dark mode to draw the item if it's selected. In this
// case, Paint_OnlySelected is used and we do nothing and return false if the
// item is not selected.
//
// pLVCD->clrText and clrTextBk should contain the colours to use
static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
bool HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont, int flags = 0)
{
NMCUSTOMDRAW& nmcd = pLVCD->nmcd; // just a shortcut
@ -3184,25 +3208,31 @@ static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
if ( nmcd.uItemState & CDIS_SELECTED )
{
int syscolFg, syscolBg;
wxSystemColour syscolFg, syscolBg;
if ( ::GetFocus() == hwndList )
{
syscolFg = COLOR_HIGHLIGHTTEXT;
syscolBg = COLOR_HIGHLIGHT;
syscolFg = wxSYS_COLOUR_HIGHLIGHTTEXT;
syscolBg = wxSYS_COLOUR_HIGHLIGHT;
}
else // selected but unfocused
{
syscolFg = COLOR_WINDOWTEXT;
syscolBg = COLOR_BTNFACE;
syscolFg = wxSYS_COLOUR_WINDOWTEXT;
syscolBg = wxSYS_COLOUR_BTNFACE;
// don't grey out the icon in this case either
nmcd.uItemState &= ~CDIS_SELECTED;
}
pLVCD->clrText = ::GetSysColor(syscolFg);
pLVCD->clrTextBk = ::GetSysColor(syscolBg);
pLVCD->clrText = wxColourToRGB(wxSystemSettings::GetColour(syscolFg));
pLVCD->clrTextBk = wxColourToRGB(wxSystemSettings::GetColour(syscolBg));
}
else // not selected
{
if ( flags & Paint_OnlySelected )
return false;
// Continue and use normal colours from pLVCD
}
//else: not selected, use normal colours from pLVCD
HDC hdc = nmcd.hdc;
RECT rc = GetCustomDrawnItemRect(nmcd);
@ -3220,14 +3250,25 @@ static void HandleItemPaint(LPNMLVCUSTOMDRAW pLVCD, HFONT hfont)
}
HandleItemPostpaint(nmcd);
return true;
}
static WXLPARAM HandleItemPrepaint(wxListCtrl *listctrl,
LPNMLVCUSTOMDRAW pLVCD,
wxItemAttr *attr)
WXLPARAM HandleItemPrepaint(wxListCtrl *listctrl,
LPNMLVCUSTOMDRAW pLVCD,
wxItemAttr *attr)
{
if ( !attr )
{
if ( wxMSWDarkMode::IsActive() )
{
// We need to always paint selected items ourselves as they come
// out completely wrong in DarkMode_Explorer theme, see the comment
// before MSWGetDarkModeSupport().
if ( HandleItemPaint(pLVCD, nullptr, Paint_OnlySelected) )
return CDRF_SKIPDEFAULT;
}
// nothing to do for this item
return CDRF_DODEFAULT;
}
@ -3280,6 +3321,8 @@ static WXLPARAM HandleItemPrepaint(wxListCtrl *listctrl,
return CDRF_DODEFAULT;
}
} // anonymous namespace
WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
{
LPNMLVCUSTOMDRAW pLVCD = (LPNMLVCUSTOMDRAW)lParam;
@ -3292,7 +3335,7 @@ WXLPARAM wxListCtrl::OnCustomDraw(WXLPARAM lParam)
//
// for virtual controls, always suppose that we have attributes as
// there is no way to check for this
if ( IsVirtual() || m_hasAnyAttr )
if ( IsVirtual() || m_hasAnyAttr || wxMSWDarkMode::IsActive() )
return CDRF_NOTIFYITEMDRAW;
break;

View file

@ -23,7 +23,9 @@
#include "wx/settings.h"
#endif
#include "wx/msw/darkmode.h"
#include "wx/msw/private.h"
#include "wx/msw/private/darkmode.h"
#include "wx/msw/private/winstyle.h"
bool wxStaticText::Create(wxWindow *parent,
@ -145,6 +147,50 @@ void wxStaticText::DoSetSize(int x, int y, int w, int h, int sizeFlags)
Refresh();
}
bool
wxStaticText::MSWHandleMessage(WXLRESULT *result,
WXUINT message,
WXWPARAM wParam,
WXLPARAM lParam)
{
if ( wxStaticTextBase::MSWHandleMessage(result, message, wParam, lParam) )
return true;
switch ( message )
{
case WM_PAINT:
// We only customize drawing of disabled labels in dark mode.
if ( IsEnabled() || !wxMSWDarkMode::IsActive() )
break;
// For them, the default "greying out" of the text for the disabled
// controls looks ugly and unreadable in dark mode, so we draw it
// as normal text but use a different colour for it.
//
// We could alternatively make the control owner-drawn, which would
// be slightly cleaner, but would require more effort.
wxMSWWinStyleUpdater updateStyle(GetHwnd());
updateStyle.TurnOff(WS_DISABLED).Apply();
// Don't use Get/SetForegroundColour() here as they do more than we
// need, we just want to change m_foregroundColour temporarily
// without any side effects.
const auto colFgOrig = m_foregroundColour;
wxDarkModeSettings darkModeSettings;
m_foregroundColour = darkModeSettings.GetMenuColour(wxMenuColour::DisabledFg);
*result = MSWDefWindowProc(WM_PAINT, wParam, lParam);
updateStyle.TurnOn(WS_DISABLED).Apply();
if ( m_hasFgCol )
m_foregroundColour = colFgOrig;
return true;
}
return false;
}
void wxStaticText::SetLabel(const wxString& label)
{
// If the label doesn't really change, avoid flicker by not doing anything.