[Git][wxwidgets/wxwidgets][master] 5 commits: Add support for switching to dark mode to wxTreeCtrl

2 views
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
Jun 16, 2026, 8:48:30 PM (9 days ago) Jun 16
to wx-commi...@googlegroups.com

Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets

Commits:

  • 86f6a8d0
    by Steve Cornett at 2026-06-16T18:44:45+02:00
    Add support for switching to dark mode to wxTreeCtrl
    
    Update wxTreeCtrl to respond to system colour changes, including
    switching into and out of dark mode.
    
    Closes #26601.
    
  • 1281d510
    by Steve Cornett at 2026-06-17T01:49:13+02:00
    Fix wxNotebook background colour in dark mode
    
    After the changes of f8363719d4 (Preserve wxNotebook custom colours upon
    dark mode switch, 2026-06-15), we need to erase background using the
    proper brush.
    
    Closes #26605.
    
    Closes #26606.
    
  • 11f8ff7b
    by dxbjavid at 2026-06-17T02:03:13+02:00
    Treat truncated reads as errors in wxDataInputStream
    
    Reading from a truncated stream returned values built from partially
    or completely uninitialised buffers, e.g. ReadString() decoded the
    unread tail of its temporary buffer and the fixed size readers returned
    whatever happened to be on the stack. Check that all the requested bytes
    were actually read and, if not, mark the input stream as being in error
    and return an empty/zero value instead of silently wrong data.
    
    Closes #26600.
    
  • 32d9ea2b
    by Vadim Zeitlin at 2026-06-17T02:06:28+02:00
    Use std::vector<> in wxDataInputStream code
    
    Prefer using the standard container instead of manual memory management.
    
    No real changes.
    
  • 7148a49c
    by Stefan Csomor at 2026-06-17T02:14:01+02:00
    Map wxDF_TEXT to text in UTF-8 encoding in wxOSX
    
    Don't use legacy encoding for it, this was unexpected and resulted in
    e.g. wxClipboard::IsSupported(wxDF_TEXT) returning false when there was
    text on the clipboard and IsSupported(wxDF_UNICODETEXT) returned true.
    
    On current systems text and Unicode text are the same thing, so make
    behaviour of wxDF_TEXT consistent with wxDF_UNICODETEXT.
    
    See #26552.
    
    Closes #26565.
    

10 changed files:

Changes:

  • docs/changes.txt
    ... ... @@ -140,6 +140,11 @@ Changes in behaviour not resulting in compilation errors
    140 140
       already didn't do it in wxMSW. Call Show() explicitly before Raise() if
    
    141 141
       necessary.
    
    142 142
     
    
    143
    +- wxDF_TEXT on macOS now corresponds to the text in UTF-8 encoding instead of
    
    144
    +  previously used MacRoman. No changes are necessary for the programs using
    
    145
    +  wxTextDataObject, but for the code working with the raw bytes in this format
    
    146
    +  you may need to change it to use wxConvUTF8 instead of wxConvLocal.
    
    147
    +
    
    143 148
     
    
    144 149
     Changes in behaviour which may result in build errors
    
    145 150
     -----------------------------------------------------
    

  • include/wx/datstrm.h
    ... ... @@ -112,6 +112,14 @@ public:
    112 112
     protected:
    
    113 113
         wxInputStream *m_input;
    
    114 114
     
    
    115
    +private:
    
    116
    +    // Try to read exactly the given number of bytes into the provided buffer.
    
    117
    +    //
    
    118
    +    // Return true if all of them could be read or mark the input stream as
    
    119
    +    // being in error and return false if fewer bytes than requested are
    
    120
    +    // available, as this means the data is truncated and can't be used.
    
    121
    +    bool ReadBytes(void *buffer, size_t size);
    
    122
    +
    
    115 123
         wxDECLARE_NO_COPY_CLASS(wxDataInputStream);
    
    116 124
     };
    
    117 125
     
    

  • include/wx/msw/treectrl.h
    ... ... @@ -310,7 +310,11 @@ private:
    310 310
         bool MSWDeleteItem(const wxTreeItemId& item);
    
    311 311
     
    
    312 312
         void OnDPIChanged(wxDPIChangedEvent& event);
    
    313
    +    void OnSysColourChanged(wxSysColourChangedEvent& event);
    
    313 314
     
    
    315
    +    // Set the native control's text and background colours to the
    
    316
    +    // current foreground and background colours.
    
    317
    +    void UpdateNativeColours();
    
    314 318
     
    
    315 319
         // Return guaranteed non-null non-owning pointer to the attribute for the
    
    316 320
         // given item.
    

  • interface/wx/dataobj.h
    ... ... @@ -751,8 +751,8 @@ public:
    751 751
     
    
    752 752
         /**
    
    753 753
             Returns 2 under wxMac and wxGTK, where text data coming from the
    
    754
    -        clipboard may be provided as ANSI (@c wxDF_TEXT) or as Unicode text
    
    755
    -        (@c wxDF_UNICODETEXT).
    
    754
    +        clipboard may be provided as (@c wxDF_TEXT), in ANSI (wxGTK) or UTF8 (wxMac),
    
    755
    +        or as Unicode text (@c wxDF_UNICODETEXT).
    
    756 756
     
    
    757 757
             Returns 1 under other platforms (e.g. wxMSW).
    
    758 758
         */
    

  • src/common/datstrm.cpp
    ... ... @@ -20,6 +20,8 @@
    20 20
         #include "wx/math.h"
    
    21 21
     #endif //WX_PRECOMP
    
    22 22
     
    
    23
    +#include <vector>
    
    24
    +
    
    23 25
     namespace
    
    24 26
     {
    
    25 27
     
    
    ... ... @@ -78,9 +80,21 @@ wxDataInputStream::wxDataInputStream(wxInputStream& s, const wxMBConv& conv)
    78 80
     {
    
    79 81
     }
    
    80 82
     
    
    83
    +bool wxDataInputStream::ReadBytes(void *buffer, size_t size)
    
    84
    +{
    
    85
    +    if ( m_input->Read(buffer, size).LastRead() == size )
    
    86
    +        return true;
    
    87
    +
    
    88
    +    // We didn't get as many bytes as requested, so the stream is truncated and
    
    89
    +    // we can't return any meaningful data: mark it as being in error to let
    
    90
    +    // the caller know about it instead of silently returning wrong values.
    
    91
    +    m_input->Reset(wxSTREAM_READ_ERROR);
    
    92
    +    return false;
    
    93
    +}
    
    94
    +
    
    81 95
     wxUint64 wxDataInputStream::Read64()
    
    82 96
     {
    
    83
    -  wxUint64 tmp;
    
    97
    +  wxUint64 tmp = 0;
    
    84 98
       Read64(&tmp, 1);
    
    85 99
       return tmp;
    
    86 100
     }
    
    ... ... @@ -89,7 +103,8 @@ wxUint32 wxDataInputStream::Read32()
    89 103
     {
    
    90 104
       wxUint32 i32;
    
    91 105
     
    
    92
    -  m_input->Read(&i32, 4);
    
    106
    +  if ( !ReadBytes(&i32, 4) )
    
    107
    +    return 0;
    
    93 108
     
    
    94 109
       if (m_be_order)
    
    95 110
         return wxUINT32_SWAP_ON_LE(i32);
    
    ... ... @@ -101,7 +116,8 @@ wxUint16 wxDataInputStream::Read16()
    101 116
     {
    
    102 117
       wxUint16 i16;
    
    103 118
     
    
    104
    -  m_input->Read(&i16, 2);
    
    119
    +  if ( !ReadBytes(&i16, 2) )
    
    120
    +    return 0;
    
    105 121
     
    
    106 122
       if (m_be_order)
    
    107 123
         return wxUINT16_SWAP_ON_LE(i16);
    
    ... ... @@ -113,7 +129,9 @@ wxUint8 wxDataInputStream::Read8()
    113 129
     {
    
    114 130
       wxUint8 buf;
    
    115 131
     
    
    116
    -  m_input->Read(&buf, 1);
    
    132
    +  if ( !ReadBytes(&buf, 1) )
    
    133
    +    return 0;
    
    134
    +
    
    117 135
       return (wxUint8)buf;
    
    118 136
     }
    
    119 137
     
    
    ... ... @@ -124,7 +142,9 @@ double wxDataInputStream::ReadDouble()
    124 142
         {
    
    125 143
             char buf[10];
    
    126 144
     
    
    127
    -        m_input->Read(buf, 10);
    
    145
    +        if ( !ReadBytes(buf, 10) )
    
    146
    +            return 0.0;
    
    147
    +
    
    128 148
             return wxConvertFromIeeeExtended((const wxInt8 *)buf);
    
    129 149
         }
    
    130 150
         else
    
    ... ... @@ -174,8 +194,11 @@ wxString wxDataInputStream::ReadString()
    174 194
             wxCharBuffer tmp(len);
    
    175 195
             if ( tmp )
    
    176 196
             {
    
    177
    -            m_input->Read(tmp.data(), len);
    
    178
    -            ret = m_conv->cMB2WC(tmp.data(), len, nullptr);
    
    197
    +            // Only decode the string if we could read all of its bytes: a
    
    198
    +            // shorter read means the stream is truncated and the rest of the
    
    199
    +            // buffer is uninitialised, so don't let it leak into the result.
    
    200
    +            if ( ReadBytes(tmp.data(), len) )
    
    201
    +                ret = m_conv->cMB2WC(tmp.data(), len, nullptr);
    
    179 202
             }
    
    180 203
         }
    
    181 204
     
    
    ... ... @@ -187,9 +210,14 @@ static
    187 210
     void DoReadLL(T *buffer, size_t size, wxInputStream *input, bool be_order)
    
    188 211
     {
    
    189 212
         typedef T DataType;
    
    190
    -    unsigned char *pchBuffer = new unsigned char[size * 8];
    
    213
    +    std::vector<unsigned char> pchBuffer(size * 8);
    
    191 214
         // TODO: Check for overflow when size is of type uint and is > than 512m
    
    192
    -    input->Read(pchBuffer, size * 8);
    
    215
    +    if ( input->Read(pchBuffer.data(), size * 8).LastRead() != size * 8 )
    
    216
    +    {
    
    217
    +        // Stream is truncated, don't use the partially read data.
    
    218
    +        input->Reset(wxSTREAM_READ_ERROR);
    
    219
    +        return;
    
    220
    +    }
    
    193 221
         size_t idx_base = 0;
    
    194 222
         if ( be_order )
    
    195 223
         {
    
    ... ... @@ -219,14 +247,13 @@ void DoReadLL(T *buffer, size_t size, wxInputStream *input, bool be_order)
    219 247
                 idx_base += 8;
    
    220 248
             }
    
    221 249
         }
    
    222
    -    delete[] pchBuffer;
    
    223 250
     }
    
    224 251
     
    
    225 252
     template <class T>
    
    226 253
     static void DoWriteLL(const T *buffer, size_t size, wxOutputStream *output, bool be_order)
    
    227 254
     {
    
    228 255
         typedef T DataType;
    
    229
    -    unsigned char *pchBuffer = new unsigned char[size * 8];
    
    256
    +    std::vector<unsigned char> pchBuffer(size * 8);
    
    230 257
         size_t idx_base = 0;
    
    231 258
         if ( be_order )
    
    232 259
         {
    
    ... ... @@ -260,8 +287,7 @@ static void DoWriteLL(const T *buffer, size_t size, wxOutputStream *output, bool
    260 287
         }
    
    261 288
     
    
    262 289
         // TODO: Check for overflow when size is of type uint and is > than 512m
    
    263
    -    output->Write(pchBuffer, size * 8);
    
    264
    -    delete[] pchBuffer;
    
    290
    +    output->Write(pchBuffer.data(), size * 8);
    
    265 291
     }
    
    266 292
     
    
    267 293
     template <class T>
    
    ... ... @@ -271,7 +297,12 @@ void DoReadI64(T *buffer, size_t size, wxInputStream *input, bool be_order)
    271 297
         typedef T DataType;
    
    272 298
         unsigned char *pchBuffer = (unsigned char*) buffer;
    
    273 299
         // TODO: Check for overflow when size is of type uint and is > than 512m
    
    274
    -    input->Read(pchBuffer, size * 8);
    
    300
    +    if ( input->Read(pchBuffer, size * 8).LastRead() != size * 8 )
    
    301
    +    {
    
    302
    +        // Stream is truncated, don't use the partially read data.
    
    303
    +        input->Reset(wxSTREAM_READ_ERROR);
    
    304
    +        return;
    
    305
    +    }
    
    275 306
         if ( be_order )
    
    276 307
         {
    
    277 308
             for ( wxUint32 i = 0; i < size; i++ )
    
    ... ... @@ -348,14 +379,15 @@ void wxDataInputStream::ReadLL(wxLongLong *buffer, size_t size)
    348 379
     
    
    349 380
     wxLongLong wxDataInputStream::ReadLL(void)
    
    350 381
     {
    
    351
    -    wxLongLong ll;
    
    382
    +    wxLongLong ll = 0;
    
    352 383
         DoReadLL(&ll, (size_t)1, m_input, m_be_order);
    
    353 384
         return ll;
    
    354 385
     }
    
    355 386
     
    
    356 387
     void wxDataInputStream::Read32(wxUint32 *buffer, size_t size)
    
    357 388
     {
    
    358
    -    m_input->Read(buffer, size * 4);
    
    389
    +    if ( !ReadBytes(buffer, size * 4) )
    
    390
    +        return;
    
    359 391
     
    
    360 392
         if (m_be_order)
    
    361 393
         {
    
    ... ... @@ -377,7 +409,8 @@ void wxDataInputStream::Read32(wxUint32 *buffer, size_t size)
    377 409
     
    
    378 410
     void wxDataInputStream::Read16(wxUint16 *buffer, size_t size)
    
    379 411
     {
    
    380
    -  m_input->Read(buffer, size * 2);
    
    412
    +  if ( !ReadBytes(buffer, size * 2) )
    
    413
    +    return;
    
    381 414
     
    
    382 415
       if (m_be_order)
    
    383 416
       {
    
    ... ... @@ -399,7 +432,7 @@ void wxDataInputStream::Read16(wxUint16 *buffer, size_t size)
    399 432
     
    
    400 433
     void wxDataInputStream::Read8(wxUint8 *buffer, size_t size)
    
    401 434
     {
    
    402
    -  m_input->Read(buffer, size);
    
    435
    +  ReadBytes(buffer, size);
    
    403 436
     }
    
    404 437
     
    
    405 438
     void wxDataInputStream::ReadDouble(double *buffer, size_t size)
    

  • src/common/dobjcmn.cpp
    ... ... @@ -345,7 +345,11 @@ inline wxMBConv& GetConv(const wxDataFormat& format)
    345 345
         static wxMBConvUTF16 s_UTF16Converter;
    
    346 346
     
    
    347 347
         return format == wxDF_UNICODETEXT ? static_cast<wxMBConv&>(s_UTF16Converter)
    
    348
    +#ifdef __WXOSX__
    
    349
    +                                      : static_cast<wxMBConv&>(wxConvUTF8);
    
    350
    +#else
    
    348 351
                                           : static_cast<wxMBConv&>(wxConvLocal);
    
    352
    +#endif
    
    349 353
     }
    
    350 354
     
    
    351 355
     } // anonymous namespace
    

  • src/msw/notebook.cpp
    ... ... @@ -274,6 +274,8 @@ void wxNotebook::MSWSetDarkOrLightMode(SetMode setmode)
    274 274
         // Background must always be set, unless there is a custom colour.
    
    275 275
         if ( !m_hasBgCol )
    
    276 276
             m_backgroundColour = GetDefaultAttributes().colBg;
    
    277
    +
    
    278
    +    UpdateBgBrush();
    
    277 279
     }
    
    278 280
     
    
    279 281
     int wxNotebook::MSWGetToolTipMessage() const
    
    ... ... @@ -1699,7 +1701,7 @@ WXHBRUSH wxNotebook::QueryBgBitmap()
    1699 1701
         if ( ::IsRectEmpty(&rc) )
    
    1700 1702
             return 0;
    
    1701 1703
     
    
    1702
    -    wxUxThemeHandle theme(this, L"TAB");
    
    1704
    +    wxUxThemeHandle theme(this, L"TAB", L"DarkMode::ItemsView");
    
    1703 1705
         if ( !theme )
    
    1704 1706
             return 0;
    
    1705 1707
     
    
    ... ... @@ -1778,7 +1780,7 @@ bool wxNotebook::MSWPrintChild(WXHDC hDC, wxWindow *child)
    1778 1780
         }
    
    1779 1781
         else // No solid background colour, try to use themed background.
    
    1780 1782
         {
    
    1781
    -        wxUxThemeHandle theme(child, L"TAB");
    
    1783
    +        wxUxThemeHandle theme(child, L"TAB", L"DarkMode::ItemsView");
    
    1782 1784
             if ( theme )
    
    1783 1785
             {
    
    1784 1786
                 // we have the content area (page size), but we need to draw all of the
    

  • src/msw/treectrl.cpp
    ... ... @@ -807,8 +807,7 @@ bool wxTreeCtrl::Create(wxWindow *parent,
    807 807
         if ( !MSWCreateControl(WC_TREEVIEW, wxString{}, pos, size) )
    
    808 808
             return false;
    
    809 809
     
    
    810
    -    SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    
    811
    -    SetForegroundColour(wxWindow::GetParent()->GetForegroundColour());
    
    810
    +    UpdateNativeColours();
    
    812 811
     
    
    813 812
         wxSetCCUnicodeFormat(GetHwnd());
    
    814 813
     
    
    ... ... @@ -825,6 +824,8 @@ bool wxTreeCtrl::Create(wxWindow *parent,
    825 824
         // And ensure we adjust it again if the DPI changes in the future.
    
    826 825
         Bind(wxEVT_DPI_CHANGED, &wxTreeCtrl::OnDPIChanged, this);
    
    827 826
     
    
    827
    +    Bind(wxEVT_SYS_COLOUR_CHANGED, &wxTreeCtrl::OnSysColourChanged, this);
    
    828
    +
    
    828 829
         return true;
    
    829 830
     }
    
    830 831
     
    
    ... ... @@ -1035,6 +1036,12 @@ bool wxTreeCtrl::SetForegroundColour(const wxColour &colour)
    1035 1036
         return true;
    
    1036 1037
     }
    
    1037 1038
     
    
    1039
    +void wxTreeCtrl::UpdateNativeColours()
    
    1040
    +{
    
    1041
    +    ::SendMessage(m_hWnd, TVM_SETBKCOLOR, 0, GetBackgroundColour().GetPixel());
    
    1042
    +    ::SendMessage(m_hWnd, TVM_SETTEXTCOLOR, 0, GetForegroundColour().GetPixel());
    
    1043
    +}
    
    1044
    +
    
    1038 1045
     // ----------------------------------------------------------------------------
    
    1039 1046
     // Item access
    
    1040 1047
     // ----------------------------------------------------------------------------
    
    ... ... @@ -2346,6 +2353,12 @@ void wxTreeCtrl::OnDPIChanged(wxDPIChangedEvent& event)
    2346 2353
         event.Skip();
    
    2347 2354
     }
    
    2348 2355
     
    
    2356
    +void wxTreeCtrl::OnSysColourChanged(wxSysColourChangedEvent& event)
    
    2357
    +{
    
    2358
    +    UpdateNativeColours();
    
    2359
    +    event.Skip();
    
    2360
    +}
    
    2361
    +
    
    2349 2362
     bool wxTreeCtrl::MSWIsOnItem(unsigned flags) const
    
    2350 2363
     {
    
    2351 2364
         unsigned mask = TVHT_ONITEM;
    

  • src/osx/carbon/dataobj.cpp
    ... ... @@ -33,8 +33,6 @@
    33 33
     
    
    34 34
     #include <memory>
    
    35 35
     
    
    36
    -static CFStringRef kUTTypeTraditionalMacText = CFSTR("com.apple.traditional-mac-plain-text");
    
    37
    -
    
    38 36
     static wxString privateUTIPrefix = "org.wxwidgets.private.";
    
    39 37
     
    
    40 38
     // ----------------------------------------------------------------------------
    
    ... ... @@ -94,7 +92,7 @@ wxDataFormat::NativeFormat wxDataFormat::GetFormatForType(wxDataFormatId type)
    94 92
         switch (type)
    
    95 93
         {
    
    96 94
             case wxDF_TEXT:
    
    97
    -            f = kUTTypeTraditionalMacText;
    
    95
    +            f = kUTTypeUTF8PlainText;
    
    98 96
                 break;
    
    99 97
     
    
    100 98
             case wxDF_UNICODETEXT:
    

  • tests/streams/datastreamtest.cpp
    ... ... @@ -21,6 +21,7 @@
    21 21
     
    
    22 22
     #include "wx/datstrm.h"
    
    23 23
     #include "wx/wfstream.h"
    
    24
    +#include "wx/mstream.h"
    
    24 25
     #include "wx/math.h"
    
    25 26
     
    
    26 27
     #include "testfile.h"
    
    ... ... @@ -39,6 +40,8 @@ private:
    39 40
             CPPUNIT_TEST( FloatRW );
    
    40 41
             CPPUNIT_TEST( DoubleRW );
    
    41 42
             CPPUNIT_TEST( StringRW );
    
    43
    +        CPPUNIT_TEST( ReadTruncatedString );
    
    44
    +        CPPUNIT_TEST( ReadTruncatedValue );
    
    42 45
             CPPUNIT_TEST( LongLongRW );
    
    43 46
             CPPUNIT_TEST( Int64RW );
    
    44 47
             CPPUNIT_TEST( NaNRW );
    
    ... ... @@ -63,6 +66,8 @@ private:
    63 66
         void FloatRW();
    
    64 67
         void DoubleRW();
    
    65 68
         void StringRW();
    
    69
    +    void ReadTruncatedString();
    
    70
    +    void ReadTruncatedValue();
    
    66 71
         void LongLongRW();
    
    67 72
         void Int64RW();
    
    68 73
         void NaNRW();
    
    ... ... @@ -248,6 +253,39 @@ void DataStreamTestCase::StringRW()
    248 253
         CPPUNIT_ASSERT_EQUAL( TestRW(s), s );
    
    249 254
     }
    
    250 255
     
    
    256
    +void DataStreamTestCase::ReadTruncatedString()
    
    257
    +{
    
    258
    +    // A string is stored as a 32 bit length followed by that many bytes. If the
    
    259
    +    // length is larger than the number of bytes actually present (a corrupt or
    
    260
    +    // malicious stream), ReadString() must not decode the uninitialised tail of
    
    261
    +    // its temporary buffer but return an empty string and put the stream into
    
    262
    +    // an error state.
    
    263
    +    const unsigned char data[] =
    
    264
    +    {
    
    265
    +        0x04, 0x00, 0x00, 0x00,     // little endian length: claims 4 bytes
    
    266
    +        'H', 'i'                    // but only 2 bytes follow
    
    267
    +    };
    
    268
    +
    
    269
    +    wxMemoryInputStream input(data, sizeof(data));
    
    270
    +    wxDataInputStream dis(input);
    
    271
    +
    
    272
    +    CPPUNIT_ASSERT_EQUAL( wxString(), dis.ReadString() );
    
    273
    +    CPPUNIT_ASSERT( !dis.IsOk() );
    
    274
    +}
    
    275
    +
    
    276
    +void DataStreamTestCase::ReadTruncatedValue()
    
    277
    +{
    
    278
    +    // Reading a fixed size value from a truncated stream must also fail instead
    
    279
    +    // of returning a value built from uninitialised memory.
    
    280
    +    const unsigned char data[] = { 0x12, 0x34 };  // only 2 of the 4 bytes
    
    281
    +
    
    282
    +    wxMemoryInputStream input(data, sizeof(data));
    
    283
    +    wxDataInputStream dis(input);
    
    284
    +
    
    285
    +    CPPUNIT_ASSERT_EQUAL( 0u, dis.Read32() );
    
    286
    +    CPPUNIT_ASSERT( !dis.IsOk() );
    
    287
    +}
    
    288
    +
    
    251 289
     void DataStreamTestCase::LongLongRW()
    
    252 290
     {
    
    253 291
         TestMultiRW<wxLongLong>::ValueArray ValuesLL;
    

Reply all
Reply to author
Forward
0 new messages