mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-08-17 00:47:34 +08:00
Improve handling of the borders in MSW dark mode
For Windows dark mode, draw themed borders instead of translating wxBORDER_THEME to wxBORDER_SIMPLE. Borders have the same thickness between light mode and dark mode for consistent alignment and positioning. See #26529. In dark mode, the border styles wxBORDER_STATIC, wxBORDER_RAISED and wxBORDER_SUNKEN are sometimes drawn by the system using light mode colours. So draw these borders similarly to themed borders, but using the proper width. There is no attempt to mimic a raised or sunken look. The WM_NCCALCSIZE and WM_NCPAINT message handling is simplified and corrected for calculating the border width. Closes #26571.
This commit is contained in:
committed by
Vadim Zeitlin
parent
a36ff353aa
commit
8a573dd7e7
@@ -2992,33 +2992,6 @@ void wxTextCtrl::MSWSetDarkOrLightMode(SetMode setmode)
|
||||
::SendMessage(m_hWnd, EM_SETBKGNDCOLOR, 0, wxColourToRGB(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
|
||||
}
|
||||
#endif
|
||||
|
||||
// The text control automatically adds WS_EX_CLIENTEDGE to its style for
|
||||
// some reason and while this isn't very noticeable in light mode, it
|
||||
// looks really bad in dark mode, so forcibly remove it unless it was
|
||||
// explicitly requested.
|
||||
const auto border = GetBorder();
|
||||
if ( border != wxBORDER_SUNKEN )
|
||||
{
|
||||
const auto exStyle = ::GetWindowLongPtr(m_hWnd, GWL_EXSTYLE);
|
||||
::SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_CLIENTEDGE);
|
||||
}
|
||||
|
||||
// When created in dark mode, the text control has a gray border.
|
||||
// But when switched from light to dark, that border is missing.
|
||||
// Explicitly enable it by toggling WS_BORDER, unless that was already
|
||||
// explicitly requested.
|
||||
if ( wxMSWDarkMode::HasChanged() && border != wxBORDER_SIMPLE )
|
||||
{
|
||||
auto style = GetWindowLongPtr(m_hWnd, GWL_STYLE);
|
||||
if (wxMSWDarkMode::IsActive())
|
||||
style |= WS_BORDER;
|
||||
else
|
||||
style &= ~WS_BORDER;
|
||||
SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
|
||||
SetWindowPos(m_hWnd, nullptr, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
|
||||
}
|
||||
}
|
||||
|
||||
void wxTextCtrl::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
|
||||
|
||||
+45
-70
@@ -1466,14 +1466,6 @@ wxBorder wxWindowMSW::DoTranslateBorder(wxBorder border) const
|
||||
{
|
||||
if (border == wxBORDER_THEME)
|
||||
{
|
||||
// In dark mode the standard sunken border is too bright, so prefer
|
||||
// using a simple(r) and darker border instead.
|
||||
//
|
||||
// And themed borders don't look good either in dark mode, so don't
|
||||
// use them in it.
|
||||
if ( wxMSWDarkMode::IsActive() )
|
||||
return wxBORDER_SIMPLE;
|
||||
|
||||
if (CanApplyThemeBorder())
|
||||
{
|
||||
if ( wxUxThemeIsActive() )
|
||||
@@ -3821,6 +3813,8 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
|
||||
// If we want the default themed border then we need to draw it ourselves
|
||||
case WM_NCCALCSIZE:
|
||||
{
|
||||
// The default handling for this message is proper for all
|
||||
// border styles except wxBORDER_THEME.
|
||||
if (DoTranslateBorder(GetBorder()) == wxBORDER_THEME)
|
||||
{
|
||||
// first ask the widget to calculate the border size
|
||||
@@ -3840,93 +3834,74 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
|
||||
{
|
||||
rect = (RECT *)lParam;
|
||||
}
|
||||
|
||||
wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
|
||||
|
||||
// There is no need to initialize rcClient: either it will
|
||||
// be done by GetThemeBackgroundContentRect() or we'll do
|
||||
// it below if it fails.
|
||||
RECT rcClient;
|
||||
|
||||
ClientHDC hdc(GetHwnd());
|
||||
|
||||
if ( ::GetThemeBackgroundContentRect
|
||||
(
|
||||
hTheme,
|
||||
hdc,
|
||||
EP_EDITTEXT,
|
||||
IsEnabled() ? ETS_NORMAL : ETS_DISABLED,
|
||||
rect,
|
||||
&rcClient) != S_OK )
|
||||
{
|
||||
// If GetThemeBackgroundContentRect() failed, as can
|
||||
// happen with at least some custom themes, just use
|
||||
// the original client rectangle.
|
||||
rcClient = *rect;
|
||||
}
|
||||
|
||||
InflateRect(&rcClient, -1, -1);
|
||||
if (wParam)
|
||||
csparam->rgrc[0] = rcClient;
|
||||
else
|
||||
*((RECT*)lParam) = rcClient;
|
||||
|
||||
// WVR_REDRAW triggers a bug whereby child windows are moved up and left,
|
||||
// so don't use.
|
||||
// rc.result = WVR_REDRAW;
|
||||
const auto thickness = MSWGetBorderThickness();
|
||||
InflateRect(rect, -thickness, -thickness);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_NCPAINT:
|
||||
{
|
||||
if (DoTranslateBorder(GetBorder()) == wxBORDER_THEME)
|
||||
// Determine whether we should draw a border.
|
||||
bool drawBorder = false;
|
||||
switch ( DoTranslateBorder(GetBorder()) )
|
||||
{
|
||||
case wxBORDER_THEME:
|
||||
drawBorder = true;
|
||||
break;
|
||||
|
||||
case wxBORDER_STATIC:
|
||||
case wxBORDER_RAISED:
|
||||
case wxBORDER_SUNKEN:
|
||||
// In dark mode, explicitly draw these border styles because
|
||||
// the default drawing uses light mode colours.
|
||||
drawBorder = wxMSWDarkMode::IsActive();
|
||||
break;
|
||||
|
||||
case wxBORDER_NONE:
|
||||
case wxBORDER_SIMPLE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if ( drawBorder )
|
||||
{
|
||||
// first ask the widget to paint its non-client area, such as scrollbars, etc.
|
||||
rc.result = MSWDefWindowProc(message, wParam, lParam);
|
||||
processed = true;
|
||||
|
||||
wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
|
||||
wxWindowDC dc((wxWindow *)this);
|
||||
wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
|
||||
|
||||
// Clip the DC so that you only draw on the non-client area
|
||||
RECT rcBorder;
|
||||
wxCopyRectToRECT(GetSize(), rcBorder);
|
||||
|
||||
RECT rcClient;
|
||||
|
||||
const int nState = IsEnabled() ? ETS_NORMAL : ETS_DISABLED;
|
||||
|
||||
if ( ::GetThemeBackgroundContentRect
|
||||
(
|
||||
hTheme,
|
||||
GetHdcOf(*impl),
|
||||
EP_EDITTEXT,
|
||||
nState,
|
||||
&rcBorder,
|
||||
&rcClient
|
||||
) != S_OK )
|
||||
{
|
||||
// As above in WM_NCCALCSIZE, fall back on something
|
||||
// reasonable for themes which don't implement this
|
||||
// function.
|
||||
rcClient = rcBorder;
|
||||
}
|
||||
|
||||
InflateRect(&rcClient, -1, -1);
|
||||
|
||||
// Exclude the client area and any scroll bars.
|
||||
RECT rcClient = rcBorder;
|
||||
const auto thickness = MSWGetBorderThickness();
|
||||
InflateRect(&rcClient, -thickness, -thickness);
|
||||
::ExcludeClipRect(GetHdcOf(*impl), rcClient.left, rcClient.top,
|
||||
rcClient.right, rcClient.bottom);
|
||||
|
||||
// Draw the theme border and background.
|
||||
|
||||
// The EDIT theme gives a good general purpose border in light mode.
|
||||
// There does not seem to be a dark mode EDIT theme that looks good.
|
||||
// The ListView theme below looks good in dark mode.
|
||||
wxUxThemeHandle hTheme(this, L"EDIT", L"DarkMode_DarkTheme::ListView");
|
||||
|
||||
// Ensure that the part and state we use have the same
|
||||
// values for both EDIT and ListView.
|
||||
static_assert((int)EP_EDITTEXT == (int)LVP_LISTITEM, "parts differ?");
|
||||
static_assert((int)ETS_NORMAL == (int)LISS_NORMAL, "states differ?");
|
||||
|
||||
// Make sure the background is in a proper state
|
||||
if (::IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, nState))
|
||||
if (::IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, ETS_NORMAL))
|
||||
{
|
||||
::DrawThemeParentBackground(GetHwnd(), GetHdcOf(*impl), &rcBorder);
|
||||
}
|
||||
|
||||
// Draw the border
|
||||
hTheme.DrawBackground(GetHdcOf(*impl), rcBorder, EP_EDITTEXT, nState);
|
||||
hTheme.DrawBackground(GetHdcOf(*impl), rcBorder, EP_EDITTEXT, ETS_NORMAL);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user