[Git][wxwidgets/wxwidgets][master] 5 commits: Add wxWindow::MSWGetBorderThickness()

1 view
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
12:25 PM (11 hours ago) 12:25 PM
to wx-commi...@googlegroups.com

Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets

Commits:

  • 3fc3a33f
    by Steve Cornett at 2026-06-10T17:22:09+02:00
    Add wxWindow::MSWGetBorderThickness()
    
    This function is more convenient to use in MSW-specific code than
    GetWindowBorderSize() as it returns the size of a single border and so
    doesn't require the caller to divide the return value by 2.
    
    No real changes.
    
    See #26571.
    
  • a36ff353
    by Vadim Zeitlin at 2026-06-10T17:28:48+02:00
    Take wxWindowMSW parameter in wxUxThemeHandle ctor
    
    We don't need wxUniv wxWindow deriving from wxWindowMSW in that build
    configuration here, just wxWindowMSW is enough and avoids the need to
    cast wxWindowMSW pointers to wxWindow ones in wxUniv/MSW build.
    
  • 8a573dd7
    by Steve Cornett at 2026-06-10T17:33:42+02: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.
    
  • fa884fc0
    by ryancog at 2026-06-10T17:35:20+02:00
    wxOSX: perform cleanup when applicationWillTerminate: is called
    
    Make wxOSX behaviour more similar to wxMSW and ensure that
    wxApp::OnExit() is called even when the application is being shut down
    unconditionally.
    
    Closes #26542.
    
  • de114413
    by Steve Cornett at 2026-06-10T18:01:02+02:00
    Fix wxChoice/wxComboBox popup border in MSW dark mode
    
    This was broken by 5bd892f7ac (Fix dark mode selection in list controls
    in latest Windows 11, 2026-05-31) which styled the entire list as scroll
    bar which was wrong.
    
    Use "DarkMode_DarkTheme" instead of "Explorer" to make the list appear
    correctly both under Windows 10 and 11.
    
    Closes #26573.
    
    Closes #26574.
    

7 changed files:

Changes:

  • include/wx/msw/uxtheme.h
    ... ... @@ -207,7 +207,7 @@ public:
    207 207
         // wxWindow pointer here must be valid and its DPI is always used.
    
    208 208
         // If classesDark is non-nullptr and the dark mode is active, it's used
    
    209 209
         // instead of classes.
    
    210
    -    wxUxThemeHandle(const wxWindow* win,
    
    210
    +    wxUxThemeHandle(const wxWindowMSW* win,
    
    211 211
                         const wchar_t* classes,
    
    212 212
                         const wchar_t* classesDark = nullptr);
    
    213 213
     
    

  • include/wx/msw/window.h
    ... ... @@ -794,6 +794,8 @@ private:
    794 794
         bool MSWSafeIsDialogMessage(WXMSG* msg);
    
    795 795
     #endif // __WXUNIVERSAL__
    
    796 796
     
    
    797
    +    int MSWGetBorderThickness() const;
    
    798
    +
    
    797 799
         static inline bool MSWIsPositionDirectlySupported(int x, int y)
    
    798 800
         {
    
    799 801
             // The supported coordinate intervals for various functions are:
    

  • src/msw/choice.cpp
    ... ... @@ -225,7 +225,7 @@ void wxChoice::MSWSetDarkOrLightMode(SetMode setmode)
    225 225
         WinStruct<COMBOBOXINFO> info;
    
    226 226
         if ( ::GetComboBoxInfo(GetHwnd(), &info) && info.hwndList )
    
    227 227
         {
    
    228
    -        wxMSWDarkMode::AllowForWindow(info.hwndList, L"Explorer", L"ScrollBar");
    
    228
    +        wxMSWDarkMode::AllowForWindow(info.hwndList, L"DarkMode_DarkTheme");
    
    229 229
         }
    
    230 230
     }
    
    231 231
     
    

  • src/msw/textctrl.cpp
    ... ... @@ -2992,33 +2992,6 @@ void wxTextCtrl::MSWSetDarkOrLightMode(SetMode setmode)
    2992 2992
                 ::SendMessage(m_hWnd, EM_SETBKGNDCOLOR, 0, wxColourToRGB(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)));
    
    2993 2993
         }
    
    2994 2994
     #endif
    
    2995
    -
    
    2996
    -    // The text control automatically adds WS_EX_CLIENTEDGE to its style for
    
    2997
    -    // some reason and while this isn't very noticeable in light mode, it
    
    2998
    -    // looks really bad in dark mode, so forcibly remove it unless it was
    
    2999
    -    // explicitly requested.
    
    3000
    -    const auto border = GetBorder();
    
    3001
    -    if ( border != wxBORDER_SUNKEN )
    
    3002
    -    {
    
    3003
    -        const auto exStyle = ::GetWindowLongPtr(m_hWnd, GWL_EXSTYLE);
    
    3004
    -        ::SetWindowLongPtr(m_hWnd, GWL_EXSTYLE, exStyle & ~WS_EX_CLIENTEDGE);
    
    3005
    -    }
    
    3006
    -
    
    3007
    -    // When created in dark mode, the text control has a gray border.
    
    3008
    -    // But when switched from light to dark, that border is missing.
    
    3009
    -    // Explicitly enable it by toggling WS_BORDER, unless that was already
    
    3010
    -    // explicitly requested.
    
    3011
    -    if ( wxMSWDarkMode::HasChanged() && border != wxBORDER_SIMPLE )
    
    3012
    -    {
    
    3013
    -        auto style = GetWindowLongPtr(m_hWnd, GWL_STYLE);
    
    3014
    -        if (wxMSWDarkMode::IsActive())
    
    3015
    -            style |= WS_BORDER;
    
    3016
    -        else
    
    3017
    -            style &= ~WS_BORDER;
    
    3018
    -        SetWindowLongPtr(m_hWnd, GWL_STYLE, style);
    
    3019
    -        SetWindowPos(m_hWnd, nullptr, 0, 0, 0, 0,
    
    3020
    -            SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
    
    3021
    -    }
    
    3022 2995
     }
    
    3023 2996
     
    
    3024 2997
     void wxTextCtrl::MSWUpdateFontOnDPIChange(const wxSize& newDPI)
    

  • src/msw/uxtheme.cpp
    ... ... @@ -43,7 +43,7 @@ bool wxUxThemeIsActive()
    43 43
         return s_isActive != 0;
    
    44 44
     }
    
    45 45
     
    
    46
    -wxUxThemeHandle::wxUxThemeHandle(const wxWindow* win,
    
    46
    +wxUxThemeHandle::wxUxThemeHandle(const wxWindowMSW* win,
    
    47 47
                                      const wchar_t* classes,
    
    48 48
                                      const wchar_t* classesDark)
    
    49 49
         : m_hTheme{DoOpenThemeData(GetHwndOf(win),
    

  • src/msw/window.cpp
    ... ... @@ -1466,14 +1466,6 @@ wxBorder wxWindowMSW::DoTranslateBorder(wxBorder border) const
    1466 1466
     {
    
    1467 1467
         if (border == wxBORDER_THEME)
    
    1468 1468
         {
    
    1469
    -        // In dark mode the standard sunken border is too bright, so prefer
    
    1470
    -        // using a simple(r) and darker border instead.
    
    1471
    -        //
    
    1472
    -        // And themed borders don't look good either in dark mode, so don't
    
    1473
    -        // use them in it.
    
    1474
    -        if ( wxMSWDarkMode::IsActive() )
    
    1475
    -            return wxBORDER_SIMPLE;
    
    1476
    -
    
    1477 1469
             if (CanApplyThemeBorder())
    
    1478 1470
             {
    
    1479 1471
                 if ( wxUxThemeIsActive() )
    
    ... ... @@ -2274,23 +2266,20 @@ void wxWindowMSW::DoSetClientSize(int width, int height)
    2274 2266
         }
    
    2275 2267
     }
    
    2276 2268
     
    
    2277
    -wxSize wxWindowMSW::GetWindowBorderSize() const
    
    2269
    +int wxWindowMSW::MSWGetBorderThickness() const
    
    2278 2270
     {
    
    2279
    -    wxCoord border;
    
    2280 2271
         switch ( GetBorder() )
    
    2281 2272
         {
    
    2282 2273
             case wxBORDER_STATIC:
    
    2283 2274
             case wxBORDER_SIMPLE:
    
    2284
    -            border = 1;
    
    2285
    -            break;
    
    2275
    +            return 1;
    
    2286 2276
     
    
    2287 2277
             case wxBORDER_SUNKEN:
    
    2288 2278
             case wxBORDER_THEME:
    
    2289
    -            border = 2;
    
    2290
    -            break;
    
    2279
    +            return 2;
    
    2291 2280
     
    
    2292 2281
             case wxBORDER_RAISED:
    
    2293
    -            border = 3;
    
    2282
    +            return 3;
    
    2294 2283
                 break;
    
    2295 2284
     
    
    2296 2285
             default:
    
    ... ... @@ -2298,9 +2287,13 @@ wxSize wxWindowMSW::GetWindowBorderSize() const
    2298 2287
                 wxFALLTHROUGH;
    
    2299 2288
     
    
    2300 2289
             case wxBORDER_NONE:
    
    2301
    -            border = 0;
    
    2290
    +            return 0;
    
    2302 2291
         }
    
    2292
    +}
    
    2303 2293
     
    
    2294
    +wxSize wxWindowMSW::GetWindowBorderSize() const
    
    2295
    +{
    
    2296
    +    const auto border = MSWGetBorderThickness();
    
    2304 2297
         return 2*wxSize(border, border);
    
    2305 2298
     }
    
    2306 2299
     
    
    ... ... @@ -3820,6 +3813,8 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
    3820 3813
             // If we want the default themed border then we need to draw it ourselves
    
    3821 3814
             case WM_NCCALCSIZE:
    
    3822 3815
                 {
    
    3816
    +                // The default handling for this message is proper for all
    
    3817
    +                // border styles except wxBORDER_THEME.
    
    3823 3818
                     if (DoTranslateBorder(GetBorder()) == wxBORDER_THEME)
    
    3824 3819
                     {
    
    3825 3820
                         // first ask the widget to calculate the border size
    
    ... ... @@ -3839,93 +3834,74 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
    3839 3834
                         {
    
    3840 3835
                             rect = (RECT *)lParam;
    
    3841 3836
                         }
    
    3842
    -
    
    3843
    -                    wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
    
    3844
    -
    
    3845
    -                    // There is no need to initialize rcClient: either it will
    
    3846
    -                    // be done by GetThemeBackgroundContentRect() or we'll do
    
    3847
    -                    // it below if it fails.
    
    3848
    -                    RECT rcClient;
    
    3849
    -
    
    3850
    -                    ClientHDC hdc(GetHwnd());
    
    3851
    -
    
    3852
    -                    if ( ::GetThemeBackgroundContentRect
    
    3853
    -                                (
    
    3854
    -                                 hTheme,
    
    3855
    -                                 hdc,
    
    3856
    -                                 EP_EDITTEXT,
    
    3857
    -                                 IsEnabled() ? ETS_NORMAL : ETS_DISABLED,
    
    3858
    -                                 rect,
    
    3859
    -                                 &rcClient) != S_OK )
    
    3860
    -                    {
    
    3861
    -                        // If GetThemeBackgroundContentRect() failed, as can
    
    3862
    -                        // happen with at least some custom themes, just use
    
    3863
    -                        // the original client rectangle.
    
    3864
    -                        rcClient = *rect;
    
    3865
    -                    }
    
    3866
    -
    
    3867
    -                    InflateRect(&rcClient, -1, -1);
    
    3868
    -                    if (wParam)
    
    3869
    -                        csparam->rgrc[0] = rcClient;
    
    3870
    -                    else
    
    3871
    -                        *((RECT*)lParam) = rcClient;
    
    3872
    -
    
    3873
    -                    // WVR_REDRAW triggers a bug whereby child windows are moved up and left,
    
    3874
    -                    // so don't use.
    
    3875
    -                    // rc.result = WVR_REDRAW;
    
    3837
    +                    const auto thickness = MSWGetBorderThickness();
    
    3838
    +                    InflateRect(rect, -thickness, -thickness);
    
    3876 3839
                     }
    
    3877 3840
                 }
    
    3878 3841
                 break;
    
    3879 3842
     
    
    3880 3843
             case WM_NCPAINT:
    
    3881 3844
                 {
    
    3882
    -                if (DoTranslateBorder(GetBorder()) == wxBORDER_THEME)
    
    3845
    +                // Determine whether we should draw a border.
    
    3846
    +                bool drawBorder = false;
    
    3847
    +                switch ( DoTranslateBorder(GetBorder()) )
    
    3848
    +                {
    
    3849
    +                    case wxBORDER_THEME:
    
    3850
    +                        drawBorder = true;
    
    3851
    +                        break;
    
    3852
    +
    
    3853
    +                    case wxBORDER_STATIC:
    
    3854
    +                    case wxBORDER_RAISED:
    
    3855
    +                    case wxBORDER_SUNKEN:
    
    3856
    +                        // In dark mode, explicitly draw these border styles because
    
    3857
    +                        // the default drawing uses light mode colours.
    
    3858
    +                        drawBorder = wxMSWDarkMode::IsActive();
    
    3859
    +                        break;
    
    3860
    +
    
    3861
    +                    case wxBORDER_NONE:
    
    3862
    +                    case wxBORDER_SIMPLE:
    
    3863
    +                    default:
    
    3864
    +                        break;
    
    3865
    +                }
    
    3866
    +
    
    3867
    +                if ( drawBorder )
    
    3883 3868
                     {
    
    3884 3869
                         // first ask the widget to paint its non-client area, such as scrollbars, etc.
    
    3885 3870
                         rc.result = MSWDefWindowProc(message, wParam, lParam);
    
    3886 3871
                         processed = true;
    
    3887 3872
     
    
    3888
    -                    wxUxThemeHandle hTheme((const wxWindow *)this, L"EDIT");
    
    3889 3873
                         wxWindowDC dc((wxWindow *)this);
    
    3890 3874
                         wxMSWDCImpl *impl = (wxMSWDCImpl*) dc.GetImpl();
    
    3891
    -
    
    3892
    -                    // Clip the DC so that you only draw on the non-client area
    
    3893 3875
                         RECT rcBorder;
    
    3894 3876
                         wxCopyRectToRECT(GetSize(), rcBorder);
    
    3895 3877
     
    
    3896
    -                    RECT rcClient;
    
    3878
    +                    // Exclude the client area and any scroll bars.
    
    3879
    +                    RECT rcClient = rcBorder;
    
    3880
    +                    const auto thickness = MSWGetBorderThickness();
    
    3881
    +                    InflateRect(&rcClient, -thickness, -thickness);
    
    3882
    +                    ::ExcludeClipRect(GetHdcOf(*impl), rcClient.left, rcClient.top,
    
    3883
    +                                      rcClient.right, rcClient.bottom);
    
    3897 3884
     
    
    3898
    -                    const int nState = IsEnabled() ? ETS_NORMAL : ETS_DISABLED;
    
    3885
    +                    // Draw the theme border and background.
    
    3899 3886
     
    
    3900
    -                    if ( ::GetThemeBackgroundContentRect
    
    3901
    -                                (
    
    3902
    -                                 hTheme,
    
    3903
    -                                 GetHdcOf(*impl),
    
    3904
    -                                 EP_EDITTEXT,
    
    3905
    -                                 nState,
    
    3906
    -                                 &rcBorder,
    
    3907
    -                                 &rcClient
    
    3908
    -                                ) != S_OK )
    
    3909
    -                    {
    
    3910
    -                        // As above in WM_NCCALCSIZE, fall back on something
    
    3911
    -                        // reasonable for themes which don't implement this
    
    3912
    -                        // function.
    
    3913
    -                        rcClient = rcBorder;
    
    3914
    -                    }
    
    3915
    -
    
    3916
    -                    InflateRect(&rcClient, -1, -1);
    
    3887
    +                    // The EDIT theme gives a good general purpose border in light mode.
    
    3888
    +                    // There does not seem to be a dark mode EDIT theme that looks good.
    
    3889
    +                    // The ListView theme below looks good in dark mode.
    
    3890
    +                    wxUxThemeHandle hTheme(this, L"EDIT", L"DarkMode_DarkTheme::ListView");
    
    3917 3891
     
    
    3918
    -                    ::ExcludeClipRect(GetHdcOf(*impl), rcClient.left, rcClient.top,
    
    3919
    -                                      rcClient.right, rcClient.bottom);
    
    3892
    +                    // Ensure that the part and state we use have the same
    
    3893
    +                    // values for both EDIT and ListView.
    
    3894
    +                    static_assert((int)EP_EDITTEXT == (int)LVP_LISTITEM, "parts differ?");
    
    3895
    +                    static_assert((int)ETS_NORMAL == (int)LISS_NORMAL, "states differ?");
    
    3920 3896
     
    
    3921 3897
                         // Make sure the background is in a proper state
    
    3922
    -                    if (::IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, nState))
    
    3898
    +                    if (::IsThemeBackgroundPartiallyTransparent(hTheme, EP_EDITTEXT, ETS_NORMAL))
    
    3923 3899
                         {
    
    3924 3900
                             ::DrawThemeParentBackground(GetHwnd(), GetHdcOf(*impl), &rcBorder);
    
    3925 3901
                         }
    
    3926 3902
     
    
    3927 3903
                         // Draw the border
    
    3928
    -                    hTheme.DrawBackground(GetHdcOf(*impl), rcBorder, EP_EDITTEXT, nState);
    
    3904
    +                    hTheme.DrawBackground(GetHdcOf(*impl), rcBorder, EP_EDITTEXT, ETS_NORMAL);
    
    3929 3905
                     }
    
    3930 3906
                 }
    
    3931 3907
                 break;
    

  • src/osx/cocoa/utils.mm
    ... ... @@ -238,6 +238,17 @@ void wxBell()
    238 238
     - (void)applicationWillTerminate:(NSNotification *)application {
    
    239 239
         wxUnusedVar(application);
    
    240 240
         wxTheApp->OSXOnWillTerminate();
    
    241
    +
    
    242
    +    // Cocoa will `exit(0)` soon after returning from
    
    243
    +    // `applicationWillTerminate:`, without another opportunity for cleanup, so
    
    244
    +    // do it here.
    
    245
    +
    
    246
    +    // Ignore the return code. It's probably better to let Cocoa do the rest of
    
    247
    +    // its cleanup like normal rather than calling `exit()` here, and the
    
    248
    +    // return code doesn't mean much, if anything, for macOS usually.
    
    249
    +    (void)wxTheApp->CallOnExit();
    
    250
    +
    
    251
    +    wxEntryCleanup();
    
    241 252
     }
    
    242 253
     
    
    243 254
     - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
    

Reply all
Reply to author
Forward
0 new messages