[Git][wxwidgets/wxwidgets][master] 6 commits: Fix wxEventLoopManual implementation check for wxGTK under macOS

1 view
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
Jan 10, 2026, 10:36:43 AMJan 10
to wx-commi...@googlegroups.com

Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets

Commits:

  • d07273a0
    by Vadim Zeitlin at 2026-01-10T15:31:59+01:00
    Fix wxEventLoopManual implementation check for wxGTK under macOS
    
    This was broken by the recent 894a04f914 (Fix using wxConsoleEventLoop
    in non-GUI applications under macOS, 2026-01-07) which resulted in the
    test around this class declaration in the header and its implementation
    in the source file getting out of sync.
    
    Fix this by introducing wxHAS_EVENTLOOP_MANUAL symbol, defining it in
    the header if this class is used and checking for it in the source file
    to make sure this doesn't happen again.
    
    Note that wxHAS_EVENTLOOP_MANUAL is intentionally not documented as it's
    not clear if there is ever any legitimate need to use wxEventLoopManual
    directly in the application code, so it's better to not encourage it.
    
    Closes #26089.
    
  • b4678dd1
    by Bill Su at 2026-01-10T15:50:54+01:00
    Fix typo in wxPopupWindow::Ref comment
    
    Fix typo in 96eca6412e (Provide safe API for detecting closing
    wxTipWindow, 2026-01-02).
    
    No real changes.
    
  • 89754f24
    by Bill Su at 2026-01-10T15:55:59+01:00
    Avoid destroying wxPopupTransientWindow twice in wxMSW
    
    Explicitly check that the window is not being already destroyed before
    scheduling its dismissal.
    
    Closes #26083.
    
  • d9c1bd7e
    by Robert Roebling at 2026-01-10T16:03:53+01:00
    Disallow wxSplitterWindow to grab the focus
    
      - There is no keyboard handling in wxSplitterWindow
      - Currently, the child windows handled by wxSplitterWindow
        lose focus when resizing and e.g. change the colour of
        selected items. That is unexpected and other apps keep
        the child windows (or one of them) in focus.
      - Tested on macOS so far
    
    Closes #26085.
    
  • a8db1675
    by Vadim Zeitlin at 2026-01-10T16:05:49+01:00
    Fix flushing white background for check/radio buttons in dark mode
    
    Use correct background brush for erasing wxCheckBox and wxRadioButton
    background in dark mode even if no custom background brush is specified
    as we can't rely on the default behaviour, which uses white background
    brush, when using it.
    
    This fixes incorrect flushes of white background when these controls
    were focused after the containing TLW got focus and before it got
    repainted again using a different code path which erased background
    correctly.
    
    See #26087.
    
    Closes #23380.
    
  • c73b1b12
    by moi15moi at 2026-01-10T16:27:37+01:00
    Handle font names longer than 31 characters in wxMSW
    
    Get the full font name using ::GetOutlineTextMetrics() if the name is
    exactly 31 characters long, as it may indicate that it was truncated to
    fit into LOGFONT::lfFaceName buffer which has fixed size of 32.
    
    This commit is best viewed with Git --color-moved option.
    
    Closes #25333.
    
    Closes #26078.
    

9 changed files:

Changes:

  • include/wx/evtloop.h
    ... ... @@ -229,6 +229,8 @@ private:
    229 229
     
    
    230 230
     #if defined(__WINDOWS__) || defined(__WXDFB__) || (defined(__UNIX__) && !defined(__DARWIN__))
    
    231 231
     
    
    232
    +#define wxHAS_EVENTLOOP_MANUAL
    
    233
    +
    
    232 234
     // this class can be used to implement a standard event loop logic using
    
    233 235
     // Pending() and Dispatch()
    
    234 236
     //
    

  • include/wx/generic/splitter.h
    ... ... @@ -186,6 +186,11 @@ public:
    186 186
         // minimum pane size is zero.
    
    187 187
         virtual void OnDoubleClickSash(int x, int y);
    
    188 188
     
    
    189
    +    // Do not allow wxSplitterWindow to get the focus as there is
    
    190
    +    // no keyboard handling anyways and the child windows should
    
    191
    +    // keep the focus
    
    192
    +    bool AcceptsFocus() const override { return false; }
    
    193
    +
    
    189 194
     ////////////////////////////////////////////////////////////////////////////
    
    190 195
     // Implementation
    
    191 196
     
    

  • include/wx/msw/private.h
    ... ... @@ -1041,6 +1041,16 @@ WXDLLIMPEXP_CORE wxFont wxCreateFontFromLogFont(const LOGFONT *logFont);
    1041 1041
     WXDLLIMPEXP_CORE void wxGetCharSize(WXHWND wnd, int *x, int *y, const wxFont& the_font);
    
    1042 1042
     WXDLLIMPEXP_CORE wxFontEncoding wxGetFontEncFromCharSet(int charset);
    
    1043 1043
     
    
    1044
    +// Helper function to check if the facename might be truncated: if it is,
    
    1045
    +// wxGetMSWFaceNameFromHFONT() should be used to get the full name.
    
    1046
    +inline bool wxIsFaceNamePossiblyTruncated(const wxString& facename)
    
    1047
    +{
    
    1048
    +    return facename.size() == LF_FACESIZE - 1;
    
    1049
    +}
    
    1050
    +
    
    1051
    +// Get full face name (i.e. possibly longer than LF_FACESIZE) from an HFONT.
    
    1052
    +wxString wxGetMSWFaceNameFromHFONT(HFONT hFont);
    
    1053
    +
    
    1044 1054
     inline void wxSetWindowFont(HWND hwnd, const wxFont& font)
    
    1045 1055
     {
    
    1046 1056
         ::SendMessage(hwnd, WM_SETFONT,
    

  • include/wx/tipwin.h
    ... ... @@ -34,7 +34,7 @@ public:
    34 34
         //
    
    35 35
         // Note that this is not a wxWeakRef<> because this is set to nullptr when
    
    36 36
         // wxTipWindow is closed, which may be "long" before wxTipWindow is
    
    37
    -    // destroyed, bug wxWeakRef<> is set to nullptr on object destruction
    
    37
    +    // destroyed, but wxWeakRef<> is set to nullptr on object destruction
    
    38 38
         class WXDLLIMPEXP_CORE Ref
    
    39 39
         {
    
    40 40
         public:
    

  • src/common/evtloopcmn.cpp
    ... ... @@ -212,8 +212,8 @@ wxEventLoopBase::AddSourceForFD(int fd,
    212 212
     }
    
    213 213
     
    
    214 214
     #endif // wxUSE_EVENTLOOP_SOURCE
    
    215
    -// wxEventLoopManual is unused in the other ports
    
    216
    -#if defined(__WINDOWS__) || defined(__WXDFB__) || ( ( defined(__UNIX__) && !defined(__WXOSX__) ) && wxUSE_BASE)
    
    215
    +
    
    216
    +#ifdef wxHAS_EVENTLOOP_MANUAL
    
    217 217
     
    
    218 218
     // ============================================================================
    
    219 219
     // wxEventLoopManual implementation
    
    ... ... @@ -400,5 +400,4 @@ void wxEventLoopManual::DoStop(int rc)
    400 400
         WakeUp();
    
    401 401
     }
    
    402 402
     
    
    403
    -#endif // __WINDOWS__ || __WXMAC__ || __WXDFB__
    
    404
    -
    403
    +#endif // wxHAS_EVENTLOOP_MANUAL

  • src/msw/control.cpp
    ... ... @@ -578,7 +578,17 @@ bool wxMSWOwnerDrawnButtonBase::MSWDrawButton(WXDRAWITEMSTRUCT *item)
    578 578
         }
    
    579 579
     
    
    580 580
         // Erase the background.
    
    581
    -    ::FillRect(hdc, &rect, m_win->MSWGetBgBrush(hdc));
    
    581
    +    HBRUSH hbr = m_win->MSWGetBgBrush(hdc);
    
    582
    +    if ( !hbr && wxMSWDarkMode::IsActive() )
    
    583
    +    {
    
    584
    +        // We always need to use custom background in dark mode, default
    
    585
    +        // behaviour is never correct.
    
    586
    +        auto const colBg = m_win->GetBackgroundColour();
    
    587
    +        wxBrush* brush = wxTheBrushList->FindOrCreateBrush(colBg);
    
    588
    +        hbr = (WXHBRUSH)brush->GetResourceHandle();
    
    589
    +    }
    
    590
    +
    
    591
    +    ::FillRect(hdc, &rect, hbr);
    
    582 592
     
    
    583 593
         // draw the button itself
    
    584 594
         wxDCTemp dc(hdc);
    

  • src/msw/font.cpp
    ... ... @@ -45,6 +45,43 @@
    45 45
     // the mask used to extract the pitch from LOGFONT::lfPitchAndFamily field
    
    46 46
     static const int PITCH_MASK = FIXED_PITCH | VARIABLE_PITCH;
    
    47 47
     
    
    48
    +// ----------------------------------------------------------------------------
    
    49
    +// global functions implementation
    
    50
    +// ----------------------------------------------------------------------------
    
    51
    +
    
    52
    +wxString wxGetMSWFaceNameFromHFONT(HFONT hFont)
    
    53
    +{
    
    54
    +    ScreenHDC hdc;
    
    55
    +    SelectInHDC selectFont(hdc, hFont);
    
    56
    +
    
    57
    +    UINT otmSize = GetOutlineTextMetrics(hdc, 0, nullptr);
    
    58
    +    if ( !otmSize )
    
    59
    +    {
    
    60
    +        wxLogLastError("GetOutlineTextMetrics(nullptr)");
    
    61
    +        return wxString();
    
    62
    +    }
    
    63
    +
    
    64
    +    OUTLINETEXTMETRIC * const
    
    65
    +        otm = static_cast<OUTLINETEXTMETRIC *>(malloc(otmSize));
    
    66
    +    wxON_BLOCK_EXIT1( free, otm );
    
    67
    +
    
    68
    +    otm->otmSize = otmSize;
    
    69
    +    if ( !GetOutlineTextMetrics(hdc, otmSize, otm) )
    
    70
    +    {
    
    71
    +        wxLogLastError("GetOutlineTextMetrics()");
    
    72
    +        return wxString();
    
    73
    +    }
    
    74
    +
    
    75
    +    // in spite of its type, the otmpFamilyName field of OUTLINETEXTMETRIC
    
    76
    +    // gives an offset in _bytes_ of the face (not family!) name from the
    
    77
    +    // struct start while the name itself is an array of TCHARs
    
    78
    +    //
    
    79
    +    // FWIW otmpFaceName contains the same thing as otmpFamilyName followed
    
    80
    +    // by a possible " Italic" or " Bold" or something else suffix
    
    81
    +    return reinterpret_cast<wxChar *>(otm) +
    
    82
    +                wxPtrToUInt(otm->otmpFamilyName)/sizeof(wxChar);
    
    83
    +}
    
    84
    +
    
    48 85
     // ----------------------------------------------------------------------------
    
    49 86
     // wxFontRefData - the internal description of the font
    
    50 87
     // ----------------------------------------------------------------------------
    
    ... ... @@ -115,9 +152,9 @@ public:
    115 152
         wxString GetFaceName() const
    
    116 153
         {
    
    117 154
             wxString facename = m_nativeFontInfo.GetFaceName();
    
    118
    -        if ( facename.empty() )
    
    155
    +        if ( facename.empty() || wxIsFaceNamePossiblyTruncated(facename) )
    
    119 156
             {
    
    120
    -            facename = GetMSWFaceName();
    
    157
    +            facename = wxGetMSWFaceNameFromHFONT((HFONT)GetHFONT());
    
    121 158
                 if ( !facename.empty() )
    
    122 159
                 {
    
    123 160
                     // cache the face name, it shouldn't change unless the family
    
    ... ... @@ -266,41 +303,6 @@ protected:
    266 303
                 const_cast<wxFontRefData *>(this)->Alloc();
    
    267 304
         }
    
    268 305
     
    
    269
    -    // retrieve the face name really being used by the font: this is used to
    
    270
    -    // get the face name selected by the system when we don't specify it (but
    
    271
    -    // use just the family for example)
    
    272
    -    wxString GetMSWFaceName() const
    
    273
    -    {
    
    274
    -        ScreenHDC hdc;
    
    275
    -        SelectInHDC selectFont(hdc, (HFONT)GetHFONT());
    
    276
    -
    
    277
    -        UINT otmSize = GetOutlineTextMetrics(hdc, 0, nullptr);
    
    278
    -        if ( !otmSize )
    
    279
    -        {
    
    280
    -            wxLogLastError("GetOutlineTextMetrics(nullptr)");
    
    281
    -            return wxString();
    
    282
    -        }
    
    283
    -
    
    284
    -        OUTLINETEXTMETRIC * const
    
    285
    -            otm = static_cast<OUTLINETEXTMETRIC *>(malloc(otmSize));
    
    286
    -        wxON_BLOCK_EXIT1( free, otm );
    
    287
    -
    
    288
    -        otm->otmSize = otmSize;
    
    289
    -        if ( !GetOutlineTextMetrics(hdc, otmSize, otm) )
    
    290
    -        {
    
    291
    -            wxLogLastError("GetOutlineTextMetrics()");
    
    292
    -            return wxString();
    
    293
    -        }
    
    294
    -
    
    295
    -        // in spite of its type, the otmpFamilyName field of OUTLINETEXTMETRIC
    
    296
    -        // gives an offset in _bytes_ of the face (not family!) name from the
    
    297
    -        // struct start while the name itself is an array of TCHARs
    
    298
    -        //
    
    299
    -        // FWIW otmpFaceName contains the same thing as otmpFamilyName followed
    
    300
    -        // by a possible " Italic" or " Bold" or something else suffix
    
    301
    -        return reinterpret_cast<wxChar *>(otm) +
    
    302
    -                    wxPtrToUInt(otm->otmpFamilyName)/sizeof(wxChar);
    
    303
    -    }
    
    304 306
     
    
    305 307
         // are we using m_nativeFontInfo.lf.lfHeight for point size or pixel size?
    
    306 308
         bool             m_sizeUsingPixels;
    

  • src/msw/fontenum.cpp
    ... ... @@ -155,6 +155,24 @@ void wxFontEnumeratorHelper::DoEnumerate()
    155 155
     bool wxFontEnumeratorHelper::OnFont(const LPLOGFONT lf,
    
    156 156
                                         const LPTEXTMETRIC tm) const
    
    157 157
     {
    
    158
    +    wxString facename = lf->lfFaceName;
    
    159
    +    if ( facename.empty() || wxIsFaceNamePossiblyTruncated(facename) )
    
    160
    +    {
    
    161
    +        AutoHFONT hFont(*lf);
    
    162
    +        if ( !hFont )
    
    163
    +        {
    
    164
    +            wxLogLastError("CreateFontIndirect");
    
    165
    +        }
    
    166
    +        else
    
    167
    +        {
    
    168
    +            const wxString fullname = wxGetMSWFaceNameFromHFONT(hFont);
    
    169
    +            if ( !fullname.empty() )
    
    170
    +            {
    
    171
    +                facename = fullname;
    
    172
    +            }
    
    173
    +        }
    
    174
    +    }
    
    175
    +
    
    158 176
         if ( m_enumEncodings )
    
    159 177
         {
    
    160 178
             // is this a new charset?
    
    ... ... @@ -165,13 +183,13 @@ bool wxFontEnumeratorHelper::OnFont(const LPLOGFONT lf,
    165 183
     
    
    166 184
     #if wxUSE_FONTMAP
    
    167 185
                 wxFontEncoding enc = wxGetFontEncFromCharSet(cs);
    
    168
    -            return m_fontEnum->OnFontEncoding(lf->lfFaceName,
    
    186
    +            return m_fontEnum->OnFontEncoding(facename,
    
    169 187
                                                   wxFontMapper::GetEncodingName(enc));
    
    170 188
     #else // !wxUSE_FONTMAP
    
    171 189
                 // Just use some unique and, hopefully, understandable, name.
    
    172 190
                 return m_fontEnum->OnFontEncoding
    
    173 191
                                    (
    
    174
    -                                lf->lfFaceName,
    
    192
    +                                facename,
    
    175 193
                                     wxString::Format(wxS("Code page %d"), cs)
    
    176 194
                                    );
    
    177 195
     #endif // wxUSE_FONTMAP/!wxUSE_FONTMAP
    
    ... ... @@ -207,17 +225,17 @@ bool wxFontEnumeratorHelper::OnFont(const LPLOGFONT lf,
    207 225
             // we can get the same facename twice or more in this case because it
    
    208 226
             // may exist in several charsets but we only want to return one copy of
    
    209 227
             // it (note that this can't happen for m_charset != DEFAULT_CHARSET)
    
    210
    -        if ( m_facenames.Index(lf->lfFaceName) != wxNOT_FOUND )
    
    228
    +        if ( m_facenames.Index(facename) != wxNOT_FOUND )
    
    211 229
             {
    
    212 230
                 // continue enumeration
    
    213 231
                 return true;
    
    214 232
             }
    
    215 233
     
    
    216 234
             wxConstCast(this, wxFontEnumeratorHelper)->
    
    217
    -            m_facenames.Add(lf->lfFaceName);
    
    235
    +            m_facenames.Add(facename);
    
    218 236
         }
    
    219 237
     
    
    220
    -    return m_fontEnum->OnFacename(lf->lfFaceName);
    
    238
    +    return m_fontEnum->OnFacename(facename);
    
    221 239
     }
    
    222 240
     
    
    223 241
     // ----------------------------------------------------------------------------
    

  • src/msw/popupwin.cpp
    ... ... @@ -22,6 +22,7 @@
    22 22
     #if wxUSE_POPUPWIN
    
    23 23
     
    
    24 24
     #ifndef WX_PRECOMP
    
    25
    +    #include "wx/app.h"
    
    25 26
     #endif //WX_PRECOMP
    
    26 27
     
    
    27 28
     #include "wx/popupwin.h"
    
    ... ... @@ -238,6 +239,14 @@ wxPopupTransientWindow::MSWHandleMessage(WXLRESULT *result,
    238 239
             case WM_ACTIVATE:
    
    239 240
                 if ( wParam == WA_INACTIVE )
    
    240 241
                 {
    
    242
    +                if ( wxTheApp->IsScheduledForDestruction(this) )
    
    243
    +                {
    
    244
    +                    // It is possible that we get this message again after
    
    245
    +                    // already getting it once, avoid scheduling the window for
    
    246
    +                    // destruction again in this case.
    
    247
    +                    break;
    
    248
    +                }
    
    249
    +
    
    241 250
                     // We need to dismiss this window, however doing it directly
    
    242 251
                     // from here seems to confuse ::ShowWindow(), which ends up
    
    243 252
                     // calling this handler, and may result in losing activation
    

Reply all
Reply to author
Forward
0 new messages