[Git][wxwidgets/wxwidgets][master] 26 commits: Fix drawing color emoji in the drawing sample with MSVS 2015

2 views
Skip to first unread message

Vadim Zeitlin (@_VZ_)

unread,
Jul 20, 2026, 7:19:59 PM (23 hours ago) Jul 20
to wx-commi...@googlegroups.com


Vadim Zeitlin pushed to branch master at wxWidgets / wxWidgets


Commits:
fc2bf0bc by PB at 2026-07-17T09:09:23+02:00
Fix drawing color emoji in the drawing sample with MSVS 2015

The old-but-still-supported MSVS 2015 has problem parsing Unicode
literals outside the BMP when using the '\U' escape. Use raw octet
syntax to work around this.

- - - - -
bb21ab03 by PB at 2026-07-17T09:12:17+02:00
Remove conflicting keyboard shortcut from the menu sample.

The sample used <Alt+F> for the Get menu info command. However,
this conflicted with the shortcut used to invoke the File menu.

Fix this by using a different shortcut for the Get menu info.

- - - - -
fcb83509 by Steve Cornett at 2026-07-18T06:59:15-07:00
Remove some disabled code

Remove _USE_VISATTR disabled code

- - - - -
763b682e by Steve Cornett at 2026-07-18T08:24:08-07:00
Fix wxGrid for system colour change

Update the text and background colours in wxGrid upon wxSysColourChangedEvent. Moved the colour setting code from Create() into a new function UpdateColours(), and call that from Create() and upon colour change event. To avoid overwriting custom colours, added flag members to track whether custom colours have been set.

Deleted the code that was disabled by #if _USE_VISATTR. That code has been inactive since 2004.

- - - - -
cb77a4e9 by Richard at 2026-07-20T20:06:14+02:00
Correct wxHTML IMG ALIGN=CENTER placement

Capture the current text metrics when parsing image cells and use them to
position ALIGN=CENTER images relative to the surrounding text center and
not to the total cell height.

Add a parser regression test for the image and adjacent word vertical
centers.

Fixes #2976.

Closes #26669.

- - - - -
fbc474f4 by Richard at 2026-07-20T20:12:44+02:00
Track active wxGrid editor while editing

Remember the editor instance used for the shown edit control and use it
for visibility checks, painting, hiding, and saving. This keeps the edit
control lifetime independent of later attribute changes, so replacing a
cell or column editor while editing does not make wxGrid hide or save
through the wrong editor.

Add a regression covering replacement of a column bool editor while its
cell is being edited.

Fixes #21986.

Closes #26670.

- - - - -
34191600 by Richard at 2026-07-20T20:17:31+02:00
Fix generic calendar day colour painting

Reset the default text foreground and background before drawing each
calendar day, using the control colours instead of the cached system
background. This prevents selected, surrounding, or attributed dates from
leaking colour state into neighbouring day cells on non-default colours.

Fixes #21885.

Closes #26671.

- - - - -
4e9b924e by Richard at 2026-07-20T20:23:34+02:00
HTML: Honor TABLE ALIGN as table placement

Apply TABLE ALIGN to the wrapper that positions the table instead of
using it as the default alignment for TD/TH content. Leave cell text
alignment controlled by row/cell alignment, with the existing TH default.

Add a parser layout regression test and sample markup for the centered
table case.

Fixes #21853.

Closes #26672.

- - - - -
e04e3260 by Richard at 2026-07-20T20:35:07+02:00
Handle context-help mouse capture loss

End wxContextHelp mode when mouse capture is lost and only release the
mouse if the help window still owns capture after the modal help loop.
Add coverage for the capture-loss path so context help exits without
using another input event.

Fixes #21534.

Closes #26674.

- - - - -
c250dfb7 by Richard at 2026-07-20T20:42:00+02:00
Fix HTML MIME fallback without registry content type

Let wxFileSystemHandler fall back to its built-in extension MIME map
when the MIME manager finds a file type but cannot return a MIME type.
This keeps .htm and .html files recognized as text/html on stripped
Windows systems missing the registry Content Type value.

Fixes #20967.

Closes #26675.

- - - - -
b03850e3 by Vadim Zeitlin at 2026-07-20T20:45:48+02:00
Reduce manual memory management in wxFileSystemHandler code

Use unique_ptr when this can be easily done instead of manually calling
new/delete.

No real changes.

- - - - -
272da6bc by Richard at 2026-07-20T20:53:16+02:00
Fix wxHTML BODY background colour in print rendering

Store BODY BGCOLOR in the wxHTML parser state even when there is no
window interface, then carry the parsed colour onto the renderer root
cell so wxHtmlDCRenderer fills printed pages with it instead of white.

Add a renderer regression test covering BODY BGCOLOR output.

Fixes #20652.

Closes #26677.

- - - - -
9857cab9 by Richard at 2026-07-20T21:01:39+02:00
Fix wxHTML line breaks around NBSP-only segments

Preserve non-breaking spaces in wxHtmlWinParser instead of converting them
to normal spaces, and prevent wxHtmlWordCell from allowing a line break
before NBSP-only text segments.

Add a parser regression test covering an &nbsp;-only segment between words.

Fixes #20399.

Closes #26678.

- - - - -
cb774bb3 by Bill Su at 2026-07-20T23:48:51+02:00
Ensure that wxAuiNotebook center pane uses expected dock values

Avoid the wxAuiNotebook center pane having non-zero dock_layer,
dock_row, or dock_pos values as it doesn't make sense for it.

Closes #26679.

- - - - -
651e2d7d by Vadim Zeitlin at 2026-07-20T23:54:34+02:00
Assert inside wxAuiPaneInfo::IsValid() itself

Instead of always calling this function inside an assert-like macro,
just assert in the function itself if the pane settings are
inconsistent.

This allows to give a more precise message, e.g. we can now explain what
is wrong with the center pane settings if any of the dock-related values
are non-zero.

See #26679.

- - - - -
aa522382 by Steve Cornett at 2026-07-21T00:02:00+02:00
Fix wxMSW wxSpinButton background in dark mode

The paint event handler clears the DC to the background colour before
drawing the bitmap to properly handle alpha transparency.

Use wxBufferedPaintDC to avoid flicker.

Closes #26682.

- - - - -
6de4ddb5 by Steve Cornett at 2026-07-21T00:04:49+02:00
Fix wxSpinButton dark mode switching under Windows 11 22H2

Add call to MSWSetDarkOrLightMode() upon control creation to get correct
dark mode drawing under Windows 11 22H2 and later and avoid inverting
colours if they are already drawn correctly for dark mode.

Closes #26683.

- - - - -
589b6228 by Steve Cornett at 2026-07-21T00:07:50+02:00
Remove not really used USE_NOTEBOOK_ANTIFLICKER from wxMSW code

Remove the USE_NOTEBOOK_ANTIFLICKER configuration option macro. It seems
this was only needed for Windows CE and it couldn't be set to 0 any more
as this started resulting in build errors since 16727d1714 (Start
implementing our own wxNotebook drawing in dark mode, 2022-12-21).

Closes #26684.

- - - - -
5ea5e65a by Steve Cornett at 2026-07-21T00:13:20+02:00
Improve code finding native spin control in wxNotebook

Simply use Win32 FindWindowEx() instead of enumerating child windows
with a loop.

Also extract this code into a reusable function.

No real changes yet.

See #26685.

- - - - -
4618ae1e by Steve Cornett at 2026-07-21T00:14:24+02:00
Fix wxNotebook dark mode spin control appearance upon AddPage()

Subclass the spin control from AddPage() as well as OnSize(), so that
the spin control is drawn properly in dark mode when it appears as a
result of adding a new page.

Closes #26685.

- - - - -
9415a545 by Steve Cornett at 2026-07-21T00:28:58+02:00
Fix wxNotebook border in wxMSW dark mode

On Windows in dark mode, draw a border around the wxNotebook body using
the same colour as the border for the tabs. This makes the dark mode
appearance more consistent with light mode.

See #25678.

Closes #26694.

- - - - -
abff4f07 by Steve Cornett at 2026-07-21T00:33:21+02:00
Fix wxNotebook background colour in wxMSW dark mode

In dark mode, make the wxNotebook regions affected by
SetbackgroundColour() match light mode. The default background colour is
wxSYS_COLOUR_WINDOW rather than wxSYS_COLOUR_BTNFACE, which looks too
light.

Closes #25678.

Closes #26695.

- - - - -
530a958e by Steve Cornett at 2026-07-21T00:56:34+02:00
Defer dark mode system colour change event

Defer dark mode system colour change event until after switch is
finished, when the new theme is already active.

Also call RefreshImmersiveColorPolicyState() to fix the initial dark
mode state which was always light before (but it was hidden by
processing WM_SETTINGCHANGE twice).

Closes #26692.

Closes #26698.

- - - - -
abaebefb by Vadim Zeitlin at 2026-07-21T01:04:55+02:00
Merge branch 'improve-samples' of github.com:PBfordev/wxWidgets

Tiny improvements to drawing and menu samples.

See #26702.

- - - - -
01b131c9 by Steve Cornett at 2026-07-21T01:09:36+02:00
Update generic wxSearchCtrl on system colour change

Update the background colour of the generic wxSearchCtrl upon
wxSysColourChangedEvent to notably fix its appearance after switching
between light and dark modes in wxMSW.

Closes #26704.

- - - - -
2650437c by Vadim Zeitlin at 2026-07-21T01:13:11+02:00
Merge branch 'wxGrid--system-color-change' of github.com:stevecor/wxWidgets

Fix wxGrid for system colour change.

See #26705.

- - - - -


30 changed files:

- docs/changes.txt
- include/wx/aui/framemanager.h
- include/wx/generic/grid.h
- include/wx/generic/srchctlg.h
- include/wx/msw/notebook.h
- samples/drawing/drawing.cpp
- samples/html/test/tables.htm
- samples/menu/menu.cpp
- src/aui/floatpane.cpp
- src/aui/framemanager.cpp
- src/common/cshelp.cpp
- src/common/filesys.cpp
- src/generic/calctrlg.cpp
- src/generic/grid.cpp
- src/generic/srchctlg.cpp
- src/html/htmlcell.cpp
- src/html/htmprint.cpp
- src/html/m_image.cpp
- src/html/m_layout.cpp
- src/html/m_tables.cpp
- src/html/winpars.cpp
- src/msw/darkmode.cpp
- src/msw/notebook.cpp
- src/msw/spinbutt.cpp
- src/msw/window.cpp
- tests/controls/gridtest.cpp
- tests/controls/windowtest.cpp
- tests/filesys/filesystest.cpp
- tests/html/htmlparser.cpp
- tests/html/htmprint.cpp


Changes:

=====================================
docs/changes.txt
=====================================
@@ -145,6 +145,10 @@ Changes in behaviour not resulting in compilation errors
wxTextDataObject, but for the code working with the raw bytes in this format
you may need to change it to use wxConvUTF8 instead of wxConvLocal.

+- Creating central wxAUI pane window with non-zero layer, row or position
+ values was previously silently allowed but couldn't work correctly, so now
+ attempting to do it will assert. Don't set these values for the central pane.
+

Changes in behaviour which may result in build errors
-----------------------------------------------------


=====================================
include/wx/aui/framemanager.h
=====================================
@@ -188,10 +188,8 @@ public:
// unsafe bits of "dest"
source.window = window;
source.frame = frame;
- wxCHECK_RET(source.IsValid(),
- "window settings and pane settings are incompatible");
- // now assign
- *this = source;
+ if (source.IsValid())
+ *this = source;
}

bool IsOk() const { return window != nullptr; }
@@ -230,9 +228,8 @@ public:
{
wxAuiPaneInfo test(*this);
test.window = w;
- wxCHECK_MSG(test.IsValid(), *this,
- "window settings and pane settings are incompatible");
- *this = test;
+ if ( test.IsValid() )
+ *this = test;
return *this;
}
wxAuiPaneInfo& Name(const wxString& n) { name = n; return *this; }
@@ -243,9 +240,16 @@ public:
wxAuiPaneInfo& Right() { dock_direction = wxAUI_DOCK_RIGHT; return *this; }
wxAuiPaneInfo& Top() { dock_direction = wxAUI_DOCK_TOP; return *this; }
wxAuiPaneInfo& Bottom() { dock_direction = wxAUI_DOCK_BOTTOM; return *this; }
- wxAuiPaneInfo& Center() { dock_direction = wxAUI_DOCK_CENTER; return *this; }
- wxAuiPaneInfo& Centre() { dock_direction = wxAUI_DOCK_CENTRE; return *this; }
- wxAuiPaneInfo& Direction(int direction) { dock_direction = direction; return *this; }
+ wxAuiPaneInfo& Center() { dock_direction = wxAUI_DOCK_CENTER; return Layer(0).Row(0).Position(0); }
+ wxAuiPaneInfo& Centre() { dock_direction = wxAUI_DOCK_CENTRE; return Layer(0).Row(0).Position(0); }
+ wxAuiPaneInfo& Direction(int direction)
+ {
+ if (dock_direction == wxAUI_DOCK_CENTRE)
+ return Centre();
+
+ dock_direction = direction;
+ return *this;
+ }
wxAuiPaneInfo& Layer(int layer) { dock_layer = layer; return *this; }
wxAuiPaneInfo& Row(int row) { dock_row = row; return *this; }
wxAuiPaneInfo& Position(int pos) { dock_pos = pos; return *this; }
@@ -298,9 +302,8 @@ public:
optionLeftDockable | optionRightDockable |
optionFloatable | optionMovable | optionResizable |
optionCaption | optionPaneBorder | buttonClose;
- wxCHECK_MSG(test.IsValid(), *this,
- "window settings and pane settings are incompatible");
- *this = test;
+ if (test.IsValid())
+ *this = test;
return *this;
}

@@ -328,9 +331,8 @@ public:
test.state |= flag;
else
test.state &= ~flag;
- wxCHECK_MSG(test.IsValid(), *this,
- "window settings and pane settings are incompatible");
- *this = test;
+ if (test.IsValid())
+ *this = test;
return *this;
}

@@ -414,6 +416,8 @@ public:

wxRect rect; // current rectangle (populated by wxAUI)

+ // If the pane settings are internally consistent, return true, otherwise
+ // return false and trigger an assertion failure in debug builds.
bool IsValid() const;
};



=====================================
include/wx/generic/grid.h
=====================================
@@ -5,6 +5,7 @@
// Modified by: Santiago Palacios
// Created: 1/08/1999
// Copyright: (c) Michael Bedward
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -2827,6 +2828,9 @@ protected:
bool m_editable; // applies to whole grid
bool m_cellEditCtrlEnabled; // is in-place edit currently shown?

+ // Editor used by the currently active edit control.
+ wxGridCellEditorPtr m_activeCellEditor;
+
TabBehaviour m_tabBehaviour; // determines how the TAB key behaves

void Init(); // common part of all ctors
@@ -2915,6 +2919,9 @@ private:
// out the grid.
void OnDPIChanged(wxDPIChangedEvent& event);

+ void OnSysColourChanged(wxSysColourChangedEvent& event);
+ void UpdateColours();
+
// implement wxScrolledCanvas method to return m_gridWin size
virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size) override;

@@ -3170,6 +3177,15 @@ private:
);
}

+ // Return the editor actually being used by the current edit control.
+ wxGridCellEditorPtr GetActiveCellEditorPtr() const
+ {
+ if ( m_activeCellEditor )
+ return m_activeCellEditor;
+
+ return GetCurrentCellEditorPtr();
+ }
+
// Show/hide the cell editor for the current cell unconditionally.

// Return false if the editor was activated instead of being shown and also
@@ -3203,6 +3219,12 @@ private:
wxGridFixedIndicesSet *m_setFixedRows,
*m_setFixedCols;

+ // Whether background and text colours were set by the user.
+ bool m_hasUserCellBg = false;
+ bool m_hasUserCellFg = false;
+ bool m_hasUserLabelBg = false;
+ bool m_hasUserLabelFg = false;
+
wxDECLARE_DYNAMIC_CLASS(wxGrid);
wxDECLARE_EVENT_TABLE();
wxDECLARE_NO_COPY_CLASS(wxGrid);


=====================================
include/wx/generic/srchctlg.h
=====================================
@@ -188,6 +188,9 @@ private:

void RecalcBitmaps();

+ void OnSysColourChanged(wxSysColourChangedEvent& event);
+ void UpdateColours();
+
enum class BitmapType
{
Search,


=====================================
include/wx/msw/notebook.h
=====================================
@@ -202,6 +202,9 @@ private:
// It has the same semantics as HitTest().
int MSWHitTestLeftRight(const wxPoint& pt, long *flags) const;

+ // Subclass the spin button if it exists.
+ void MSWSubclassSpin();
+
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxNotebook);
wxDECLARE_EVENT_TABLE();
};


=====================================
samples/drawing/drawing.cpp
=====================================
@@ -1156,7 +1156,7 @@ void MyCanvas::DrawText(wxDC& dc)
#ifdef __WXMSW__
" (only when using Direct2D)"
#endif
- ": \U0001F60A"), dc.FromDIP(400), y);
+ ": \xF0\x9F\x98\x8A"), dc.FromDIP(400), y);
}

static const struct


=====================================
samples/html/test/tables.htm
=====================================
@@ -34,6 +34,15 @@ tests page...</H3>
</TR>
</TABLE></CENTER>

+<P>TABLE ALIGN moves the table, not the cell text:
+<BR>&nbsp;
+<TABLE BORDER ALIGN=CENTER WIDTH="40%">
+<TR>
+<TD>Left-aligned cell text</TD>
+<TD>More text</TD>
+</TR>
+</TABLE>
+
<P>Subsampling is shown there:
<BR>&nbsp;
<TABLE BORDER COLS=2 WIDTH="100%" NOSAVE >


=====================================
samples/menu/menu.cpp
=====================================
@@ -638,7 +638,7 @@ MyFrame::MyFrame()
menuMenu->AppendSeparator();
#endif

- menuMenu->Append(Menu_Menu_GetInfo, "Get menu item in&fo\tAlt-F",
+ menuMenu->Append(Menu_Menu_GetInfo, "Get menu item inf&o\tAlt-O",
"Show the state of the last menu item");
#if wxUSE_TEXTDLG
menuMenu->Append(Menu_Menu_SetLabel, "Set menu item label\tAlt-L",


=====================================
src/aui/floatpane.cpp
=====================================
@@ -85,8 +85,7 @@ void wxAuiFloatingFrame::SetPaneWindow(const wxAuiPaneInfo& pane)
wxAuiPaneInfo contained_pane = pane;
contained_pane.Dock().Center().Show().
CaptionVisible(false).
- PaneBorder(false).
- Layer(0).Row(0).Position(0);
+ PaneBorder(false);

// Carry over the minimum size
wxSize pane_min_size = pane.window->GetMinSize();


=====================================
src/aui/framemanager.cpp
=====================================
@@ -532,11 +532,26 @@ static int PaneSortFunc(wxAuiPaneInfo** p1, wxAuiPaneInfo** p2)

bool wxAuiPaneInfo::IsValid() const
{
+ if ( dock_direction == wxAUI_DOCK_CENTRE &&
+ !(dock_layer == 0 && dock_row == 0 && dock_pos == 0) )
+ {
+ wxFAIL_MSG("Center pane must have dock layer, row and pos set to 0");
+
+ return false;
+ }
+
// Should this RTTI and function call be rewritten as
// sending a new event type to allow other window types
// to check the pane settings?
wxAuiToolBar* toolbar = wxDynamicCast(window, wxAuiToolBar);
- return !toolbar || toolbar->IsPaneValid(*this);
+ if ( toolbar && !toolbar->IsPaneValid(*this) )
+ {
+ wxFAIL_MSG("toolbar style and pane docking flags are incompatible");
+
+ return false;
+ }
+
+ return true;
}

// -- wxAuiManager class implementation --
@@ -1031,8 +1046,8 @@ bool wxAuiManager::AddPane(wxWindow* window, const wxAuiPaneInfo& paneInfo)
{
// see whether non-default docking flags are valid
test.window = window;
- wxCHECK_MSG(test.IsValid(), false,
- "toolbar style and pane docking flags are incompatible");
+ if (!test.IsValid())
+ return false;
}
}



=====================================
src/common/cshelp.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Julian Smart, Vadim Zeitlin
// Created: 08/09/2000
// Copyright: (c) 2000 Julian Smart, Vadim Zeitlin
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -108,7 +109,8 @@ bool wxContextHelp::BeginContextHelp(wxWindow* win)

EventLoop();

- win->ReleaseMouse();
+ if ( win->HasCapture() )
+ win->ReleaseMouse();

win->PopEventHandler(true);

@@ -173,10 +175,11 @@ bool wxContextHelpEvtHandler::ProcessEvent(wxEvent& event)
return true;
}

- if ((event.GetEventType() == wxEVT_CHAR) ||
- (event.GetEventType() == wxEVT_KEY_DOWN) ||
- (event.GetEventType() == wxEVT_ACTIVATE) ||
- (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED))
+ if ( (event.GetEventType() == wxEVT_CHAR) ||
+ (event.GetEventType() == wxEVT_KEY_DOWN) ||
+ (event.GetEventType() == wxEVT_ACTIVATE) ||
+ (event.GetEventType() == wxEVT_MOUSE_CAPTURE_CHANGED) ||
+ (event.GetEventType() == wxEVT_MOUSE_CAPTURE_LOST) )
{
// May have already been set to true by a left-click
//m_contextHelp->SetStatus(false);


=====================================
src/common/filesys.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxFileSystem class - interface for opening files
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -25,6 +26,7 @@
#include "wx/filename.h"
#include "wx/tokenzr.h"
#include "wx/private/fileback.h"
+#include "wx/private/make_unique.h"
#include "wx/utils.h"

// ----------------------------------------------------------------------------
@@ -49,6 +51,25 @@ const wxString& wxFSFile::GetMimeType() const
wxIMPLEMENT_ABSTRACT_CLASS(wxFileSystemHandler, wxObject);


+static wxString GetMimeTypeFromExtFallback(const wxString& ext)
+{
+ if ( ext.IsSameAs("htm", false) ||
+ ext.IsSameAs("html", false) )
+ return "text/html";
+ if ( ext.IsSameAs("jpg", false) ||
+ ext.IsSameAs("jpeg", false) )
+ return "image/jpeg";
+ if ( ext.IsSameAs("gif", false) )
+ return "image/gif";
+ if ( ext.IsSameAs("png", false) )
+ return "image/png";
+ if ( ext.IsSameAs("bmp", false) )
+ return "image/bmp";
+
+ return wxString();
+}
+
+
/* static */
wxString wxFileSystemHandler::GetMimeTypeFromExt(const wxString& location)
{
@@ -117,31 +138,16 @@ wxString wxFileSystemHandler::GetMimeTypeFromExt(const wxString& location)
s_MinimalMimeEnsured = true;
}

- wxFileType *ft = wxTheMimeTypesManager->GetFileTypeFromExtension(ext);
- if ( !ft || !ft -> GetMimeType(&mime) )
- {
- mime.clear();
- }
-
- delete ft;
-
- return mime;
+ std::unique_ptr<wxFileType>
+ ft(wxTheMimeTypesManager->GetFileTypeFromExtension(ext));
+ if ( ft && ft->GetMimeType(&mime) && !mime.empty() )
+ return mime;
}
#endif // wxUSE_MIMETYPE

- // Without wxUSE_MIMETYPE, recognize just a few hardcoded special cases.
- if ( ext.IsSameAs(wxT("htm"), false) || ext.IsSameAs(wxT("html"), false) )
- return wxT("text/html");
- if ( ext.IsSameAs(wxT("jpg"), false) || ext.IsSameAs(wxT("jpeg"), false) )
- return wxT("image/jpeg");
- if ( ext.IsSameAs(wxT("gif"), false) )
- return wxT("image/gif");
- if ( ext.IsSameAs(wxT("png"), false) )
- return wxT("image/png");
- if ( ext.IsSameAs(wxT("bmp"), false) )
- return wxT("image/bmp");
-
- return wxString();
+ // Recognize just a few hardcoded special cases if wxUSE_MIMETYPE is
+ // disabled or if the MIME manager did not know the MIME type.
+ return GetMimeTypeFromExtFallback(ext);
}


@@ -280,19 +286,16 @@ wxFSFile* wxLocalFSHandler::OpenFile(wxFileSystem& WXUNUSED(fs), const wxString&
// we need to check whether we can really read from this file, otherwise
// wxFSFile is not going to work
#if wxUSE_FFILE
- wxFFileInputStream *is = new wxFFileInputStream(fullpath);
+ auto is = std::make_unique<wxFFileInputStream>(fullpath);
#elif wxUSE_FILE
- wxFileInputStream *is = new wxFileInputStream(fullpath);
+ auto is = std::make_unique<wxFileInputStream>(fullpath);
#else
#error One of wxUSE_FILE or wxUSE_FFILE must be set to 1 for wxFSHandler to work
#endif
if ( !is->IsOk() )
- {
- delete is;
return nullptr;
- }

- return new wxFSFile(is,
+ return new wxFSFile(is.get(),
location,
wxEmptyString,
GetAnchor(location)
@@ -592,10 +595,9 @@ bool wxFileSystem::FindFileInPath(wxString *pStr,
strFile += wxFILE_SEP_PATH;
strFile += name;

- wxFSFile *file = OpenFile(strFile);
+ std::unique_ptr<wxFSFile> file(OpenFile(strFile));
if ( file )
{
- delete file;
*pStr = strFile;
return true;
}


=====================================
src/generic/calctrlg.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Vadim Zeitlin
// Created: 29.12.99
// Copyright: (c) 1999 Vadim Zeitlin <zei...@dptmaths.ens-cachan.fr>
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////

@@ -928,7 +929,10 @@ void wxGenericCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
}

// then the calendar itself
- dc.SetTextForeground(GetForegroundColour());
+ const wxColour colDefaultFg = GetForegroundColour();
+ const wxColour colDefaultBg = GetBackgroundColour();
+
+ dc.SetTextForeground(colDefaultFg);

wxDateTime date = GetStartDate();

@@ -954,7 +958,9 @@ void wxGenericCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))

for ( int wd = 0; wd < 7; wd++ )
{
- dc.SetTextBackground(m_colBackground);
+ dc.SetTextForeground(colDefaultFg);
+ dc.SetTextBackground(colDefaultBg);
+
if ( IsDateShown(date) )
{
// don't use wxDate::Format() which prepends 0s
@@ -1034,7 +1040,7 @@ void wxGenericCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
}
else
{
- colBorder = GetForegroundColour();
+ colBorder = colDefaultFg;
}

wxPen pen(colBorder, 1, wxPENSTYLE_SOLID);
@@ -1061,8 +1067,8 @@ void wxGenericCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))

if ( changedColours )
{
- dc.SetTextForeground(GetForegroundColour());
- dc.SetTextBackground(GetBackgroundColour());
+ dc.SetTextForeground(colDefaultFg);
+ dc.SetTextBackground(colDefaultBg);
}

if ( changedFont )


=====================================
src/generic/grid.cpp
=====================================
@@ -5,6 +5,7 @@
// Modified by: Robin Dunn, Vadim Zeitlin, Santiago Palacios
// Created: 1/08/1999
// Copyright: (c) Michael Bedward (mbed...@ozemail.com.au)
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -2865,6 +2866,7 @@ wxBEGIN_EVENT_TABLE( wxGrid, wxScrolledCanvas )
EVT_DPI_CHANGED( wxGrid::OnDPIChanged )
EVT_KEY_DOWN( wxGrid::OnKeyDown )
EVT_CHAR ( wxGrid::OnChar )
+ EVT_SYS_COLOUR_CHANGED(wxGrid::OnSysColourChanged)
wxEND_EVENT_TABLE()

bool wxGrid::Create(wxWindow *parent, wxWindowID id,
@@ -2891,6 +2893,7 @@ wxGrid::~wxGrid()
// otherwise we crash later when the editor tries to do something with the
// half destroyed grid
HideCellEditControl();
+ m_activeCellEditor.reset(nullptr);

// Must do this or ~wxScrollHelper will pop the wrong event handler
SetTargetWindow(this);
@@ -2928,10 +2931,6 @@ wxGrid::~wxGrid()
// ----- internal init and update functions
//

-// NOTE: If using the default visual attributes works everywhere then this can
-// be removed as well as the #else cases below.
-#define _USE_VISATTR 0
-
void wxGrid::Create()
{
// create the type registry
@@ -2950,20 +2949,6 @@ void wxGrid::Create()
m_defaultCellAttr->SetEditor(new wxGridCellTextEditor);
m_defaultCellAttr->SetFitMode(wxGridFitMode::Overflow());

-#if _USE_VISATTR
- wxVisualAttributes gva = wxListBox::GetClassDefaultAttributes();
- wxVisualAttributes lva = wxPanel::GetClassDefaultAttributes();
-
- m_defaultCellAttr->SetTextColour(gva.colFg);
- m_defaultCellAttr->SetBackgroundColour(gva.colBg);
-
-#else
- m_defaultCellAttr->SetTextColour(
- wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
- m_defaultCellAttr->SetBackgroundColour(
- wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
-#endif
-
m_numRows = 0;
m_numCols = 0;
m_numFrozenRows = 0;
@@ -2978,30 +2963,7 @@ void wxGrid::Create()

SetTargetWindow( m_gridWin );

-#if _USE_VISATTR
- wxColour gfg = gva.colFg;
- wxColour gbg = gva.colBg;
- wxColour lfg = lva.colFg;
- wxColour lbg = lva.colBg;
-#else
- wxColour gfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
- wxColour gbg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
- wxColour lfg = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
- wxColour lbg = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
-#endif
-
- m_cornerLabelWin->SetOwnForegroundColour(lfg);
- m_cornerLabelWin->SetOwnBackgroundColour(lbg);
- m_rowLabelWin->SetOwnForegroundColour(lfg);
- m_rowLabelWin->SetOwnBackgroundColour(lbg);
- m_colLabelWin->SetOwnForegroundColour(lfg);
- m_colLabelWin->SetOwnBackgroundColour(lbg);
-
- m_gridWin->SetOwnForegroundColour(gfg);
- m_gridWin->SetOwnBackgroundColour(gbg);
-
- m_labelBackgroundColour = m_rowLabelWin->GetBackgroundColour();
- m_labelTextColour = m_rowLabelWin->GetForegroundColour();
+ UpdateColours();

InitPixelFields();

@@ -3397,7 +3359,7 @@ void wxGrid::CalcDimensions()
// take into account editor if shown
if ( IsCellEditControlShown() )
{
- const wxRect rect = GetCurrentCellEditorPtr()->GetWindow()->GetRect();
+ const wxRect rect = GetActiveCellEditorPtr()->GetWindow()->GetRect();
if ( rect.GetRight() > w )
w = rect.GetRight();
if ( rect.GetBottom() > h )
@@ -5150,7 +5112,7 @@ wxGrid::DoGridCellLeftUp(wxMouseEvent& event,
ClearSelection();

if ( DoEnableCellEditControl(wxGridActivationSource::From(event)) )
- GetCurrentCellEditorPtr()->StartingClick();
+ GetActiveCellEditorPtr()->StartingClick();

m_waitForSlowClick = false;
}
@@ -6279,6 +6241,47 @@ void wxGrid::OnDPIChanged(wxDPIChangedEvent& event)
event.Skip();
}

+void wxGrid::OnSysColourChanged(wxSysColourChangedEvent& event)
+{
+ UpdateColours();
+ event.Skip();
+}
+
+void wxGrid::UpdateColours()
+{
+ auto windowText = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
+
+ if ( !m_hasUserCellBg )
+ {
+ auto window = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+ m_defaultCellAttr->SetBackgroundColour(window);
+ m_gridWin->SetBackgroundColour(window);
+ }
+
+ if ( !m_hasUserCellFg )
+ {
+ m_defaultCellAttr->SetTextColour(windowText);
+ m_gridWin->SetForegroundColour(windowText);
+ }
+
+ if ( !m_hasUserLabelBg )
+ {
+ auto btnFace = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
+ m_labelBackgroundColour = btnFace;
+ m_cornerLabelWin->SetBackgroundColour(btnFace);
+ m_rowLabelWin->SetBackgroundColour(btnFace);
+ m_colLabelWin->SetBackgroundColour(btnFace);
+ }
+
+ if ( !m_hasUserLabelFg )
+ {
+ m_labelTextColour = windowText;
+ m_cornerLabelWin->SetForegroundColour(windowText);
+ m_rowLabelWin->SetForegroundColour(windowText);
+ m_colLabelWin->SetForegroundColour(windowText);
+ }
+}
+
void wxGrid::OnKeyDown( wxKeyEvent& event )
{
// propagate the event up and see if it gets processed
@@ -6594,7 +6597,7 @@ void wxGrid::OnChar( wxKeyEvent& event )

if ( DoEnableCellEditControl(wxGridActivationSource::From(event))
&& !specialEditKey )
- editor->StartingKey(event);
+ GetActiveCellEditorPtr()->StartingKey(event);
}
else
{
@@ -6893,7 +6896,7 @@ void wxGrid::DrawCell( wxDC& dc, const wxGridCellCoords& coords )
// Note: However, only if it is really _shown_, i.e. not hidden!
if ( isCurrent && IsCellEditControlShown() )
{
- attr->GetEditorPtr(this, row, col)->PaintBackground(dc, rect, *attr);
+ GetActiveCellEditorPtr()->PaintBackground(dc, rect, *attr);
}
else
{
@@ -8003,7 +8006,7 @@ bool wxGrid::IsCellEditControlShown() const

if ( m_cellEditCtrlEnabled )
{
- if ( wxGridCellEditorPtr editor = GetCurrentCellEditorPtr() )
+ if ( wxGridCellEditorPtr editor = GetActiveCellEditorPtr() )
{
if ( editor->IsCreated() )
{
@@ -8022,6 +8025,7 @@ void wxGrid::ShowCellEditControl()
if ( !IsVisible( m_currentCellCoords, false ) )
{
m_cellEditCtrlEnabled = false;
+ m_activeCellEditor.reset(nullptr);
return;
}

@@ -8082,6 +8086,7 @@ bool wxGrid::DoShowCellEditControl(const wxGridActivationSource& actSource)
// before generating any events in case their user-defined handlers decide
// to call EnableCellEditControl() to avoid reentrancy problems.
m_cellEditCtrlEnabled = true;
+ m_activeCellEditor = editor;

wxGridWindow *gridWindow = CellToGridWindow(row, col);

@@ -8216,7 +8221,7 @@ void wxGrid::HideCellEditControl()

void wxGrid::DoHideCellEditControl()
{
- wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
+ wxGridCellEditorPtr editor = GetActiveCellEditorPtr();
const bool editorHadFocus = editor->GetWindow()->IsDescendant(FindFocus());

if ( editor->GetWindow()->GetParent() != m_gridWin )
@@ -8279,6 +8284,7 @@ void wxGrid::DoAcceptCellEditControl()
DoHideCellEditControl();

DoSaveEditControlValue();
+ m_activeCellEditor.reset(nullptr);
}

void wxGrid::SaveEditControlValue()
@@ -8296,7 +8302,7 @@ void wxGrid::DoSaveEditControlValue()

wxString oldval = GetCellValue(m_currentCellCoords);

- wxGridCellEditorPtr editor = GetCurrentCellEditorPtr();
+ wxGridCellEditorPtr editor = GetActiveCellEditorPtr();

wxString newval;
if ( !editor->EndEdit(row, col, this, oldval, &newval) )
@@ -9372,6 +9378,7 @@ void wxGrid::SetLabelBackgroundColour( const wxColour& colour )
RefreshArea(wxGA_Labels);
}
}
+ m_hasUserLabelBg = true;
}

void wxGrid::SetLabelTextColour( const wxColour& colour )
@@ -9388,6 +9395,7 @@ void wxGrid::SetLabelTextColour( const wxColour& colour )
RefreshArea(wxGA_Labels);
}
}
+ m_hasUserLabelFg = true;
}

void wxGrid::SetLabelFont( const wxFont& font )
@@ -9747,11 +9755,13 @@ void wxGrid::SetDefaultCellBackgroundColour( const wxColour& col )
#if defined(__WXGTK__) || defined(__WXQT__)
m_gridWin->SetBackgroundColour(col);
#endif
+ m_hasUserCellBg = true;
}

void wxGrid::SetDefaultCellTextColour( const wxColour& col )
{
m_defaultCellAttr->SetTextColour(col);
+ m_hasUserCellFg = true;
}

void wxGrid::SetDefaultCellAlignment( int horiz, int vert )


=====================================
src/generic/srchctlg.cpp
=====================================
@@ -153,7 +153,6 @@ public:
m_eventType(eventType),
m_bmp(bmp)
{
- SetBackgroundColour(search->GetBackgroundColour());
}

void SetBitmapLabel(const wxBitmap& label)
@@ -233,6 +232,7 @@ wxBEGIN_EVENT_TABLE(wxSearchCtrl, wxSearchCtrlBase)
EVT_SEARCH_CANCEL(wxID_ANY, wxSearchCtrl::OnCancelButton)
EVT_SIZE(wxSearchCtrl::OnSize)
EVT_DPI_CHANGED(wxSearchCtrl::OnDPIChanged)
+ EVT_SYS_COLOUR_CHANGED(wxSearchCtrl::OnSysColourChanged)
wxEND_EVENT_TABLE()

wxIMPLEMENT_DYNAMIC_CLASS(wxSearchCtrl, wxSearchCtrlBase);
@@ -302,10 +302,7 @@ bool wxSearchCtrl::Create(wxWindow *parent, wxWindowID id,
wxEVT_SEARCH,
m_searchBitmap);

- m_text->SetBackgroundColour(wxColour());
- SetBackgroundColour( m_text->GetBackgroundColour() );
-
- RecalcBitmaps();
+ UpdateColours();

SetInitialSize(size);
Move(pos);
@@ -406,7 +403,7 @@ void wxSearchCtrl::ShowCancelButton( bool show )
m_cancelButton = new wxSearchButton(this,
wxEVT_SEARCH_CANCEL,
m_cancelBitmap);
- RecalcBitmaps();
+ UpdateColours();
}

m_cancelButton->Show(show);
@@ -1061,16 +1058,10 @@ void wxSearchCtrl::RecalcBitmaps()

if ( !m_searchBitmapUser )
{
- if (
- !m_searchBitmap.IsOk() ||
- m_searchBitmap.GetSize() != bitmapSize
- )
+ m_searchBitmap = RenderBitmap(bitmapSize, BitmapType::Search);
+ if ( !HasMenu() )
{
- m_searchBitmap = RenderBitmap(bitmapSize, BitmapType::Search);
- if ( !HasMenu() )
- {
- m_searchButton->SetBitmapLabel(m_searchBitmap);
- }
+ m_searchButton->SetBitmapLabel(m_searchBitmap);
}
// else this bitmap was set by user, don't alter
}
@@ -1078,16 +1069,10 @@ void wxSearchCtrl::RecalcBitmaps()
#if wxUSE_MENUS
if ( !m_searchMenuBitmapUser )
{
- if (
- !m_searchMenuBitmap.IsOk() ||
- m_searchMenuBitmap.GetSize() != bitmapSize
- )
+ m_searchMenuBitmap = RenderBitmap(bitmapSize, BitmapType::Menu);
+ if ( m_menu )
{
- m_searchMenuBitmap = RenderBitmap(bitmapSize, BitmapType::Menu);
- if ( m_menu )
- {
- m_searchButton->SetBitmapLabel(m_searchMenuBitmap);
- }
+ m_searchButton->SetBitmapLabel(m_searchMenuBitmap);
}
// else this bitmap was set by user, don't alter
}
@@ -1108,18 +1093,32 @@ void wxSearchCtrl::RecalcBitmaps()

if ( m_cancelButton && !m_cancelBitmapUser )
{
- if (
- !m_cancelBitmap.IsOk() ||
- m_cancelBitmap.GetSize() != bitmapSize
- )
- {
- m_cancelBitmap = RenderBitmap(bitmapSize, BitmapType::Cancel);
- m_cancelButton->SetBitmapLabel(m_cancelBitmap);
- }
+ m_cancelBitmap = RenderBitmap(bitmapSize, BitmapType::Cancel);
+ m_cancelButton->SetBitmapLabel(m_cancelBitmap);
// else this bitmap was set by user, don't alter
}
}

+void wxSearchCtrl::OnSysColourChanged(wxSysColourChangedEvent& event)
+{
+ UpdateColours();
+ event.Skip();
+}
+
+void wxSearchCtrl::UpdateColours()
+{
+ if ( !m_hasBgCol )
+ {
+ m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
+ m_text->SetBackgroundColour(m_backgroundColour);
+ m_searchButton->SetBackgroundColour(m_backgroundColour);
+ if ( m_cancelButton )
+ m_cancelButton->SetBackgroundColour(m_backgroundColour);
+ }
+
+ RecalcBitmaps();
+}
+
void wxSearchCtrl::OnCancelButton( wxCommandEvent& event )
{
m_text->Clear();


=====================================
src/html/htmlcell.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxHtmlCell - basic element of HTML output
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -306,6 +307,18 @@ wxString wxHtmlCell::Dump(int indent) const

wxIMPLEMENT_ABSTRACT_CLASS(wxHtmlWordCell, wxHtmlCell);

+static constexpr unsigned NBSP_VALUE = 0x00a0;
+
+static bool wxHtmlIsNBSP(wxUniChar c)
+{
+ return c == wxUniChar(NBSP_VALUE);
+}
+
+static bool wxHtmlIsLineBreakingSpace(wxUniChar c)
+{
+ return !wxHtmlIsNBSP(c) && wxIsspace(c);
+}
+
wxHtmlWordCell::wxHtmlWordCell(const wxString& word, const wxDC& dc) : wxHtmlCell()
, m_Word(word)
{
@@ -316,12 +329,15 @@ wxHtmlWordCell::wxHtmlWordCell(const wxString& word, const wxDC& dc) : wxHtmlCel
m_Descent = d;
SetCanLiveOnPagebreak(false);
m_allowLinebreak = true;
+ if ( !m_Word.empty() && wxHtmlIsNBSP(m_Word[0u]) )
+ m_allowLinebreak = false;
}

void wxHtmlWordCell::SetPreviousWord(wxHtmlWordCell *cell)
{
if ( cell && m_Parent == cell->m_Parent &&
- !wxIsspace(cell->m_Word.Last()) && !wxIsspace(m_Word[0u]) )
+ !wxHtmlIsLineBreakingSpace(cell->m_Word.Last()) &&
+ !wxHtmlIsLineBreakingSpace(m_Word[0u]) )
{
m_allowLinebreak = false;
}


=====================================
src/html/htmprint.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Vaclav Slavik
// Created: 25/09/99
// Copyright: (c) Vaclav Slavik, 1999
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -112,6 +113,10 @@ void wxHtmlDCRenderer::SetHtmlText(const wxString& html, const wxString& basepat
wxHtmlContainerCell* const cell = (wxHtmlContainerCell*) m_Parser.Parse(html);
wxCHECK_RET( cell, "Failed to parse HTML" );

+ const wxColour bg = m_Parser.GetActualBackgroundColor();
+ if ( bg.IsOk() )
+ cell->SetBackgroundColour(bg);
+
DoSetHtmlCell(cell);

m_ownsCells = true;
@@ -187,8 +192,15 @@ void wxHtmlDCRenderer::Render(int x, int y, int from, int to)
wxHtmlRenderingInfo rinfo;
wxDefaultHtmlRenderingStyle rstyle;
rinfo.SetStyle(&rstyle);
- m_DC->SetBrush(*wxWHITE_BRUSH);
wxDCClipper clip(*m_DC, x, y, m_Width, hght);
+ {
+ wxColour bg = m_Cells->GetBackgroundColour();
+ if ( !bg.IsOk() )
+ bg = *wxWHITE;
+ wxDCBrushChanger brushChanger(*m_DC, wxBrush(bg));
+ wxDCPenChanger penChanger(*m_DC, *wxTRANSPARENT_PEN);
+ m_DC->DrawRectangle(x, y, m_Width, hght);
+ }
m_Cells->Draw(*m_DC,
x, (y - from),
y, y + hght,


=====================================
src/html/m_image.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxHtml module for displaying images
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik, Joel Lucsy
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -291,6 +292,7 @@ public:
int w = wxDefaultCoord, bool wpercent = false,
int h = wxDefaultCoord, bool hpresent = false,
double scale = 1.0, int align = wxHTML_ALIGN_BOTTOM,
+ int textHeight = 0, int textDescent = 0,
const wxString& mapname = wxEmptyString);
virtual ~wxHtmlImageCell();
void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2,
@@ -318,6 +320,7 @@ public:
private:
wxBitmap *m_bitmap;
int m_align;
+ int m_textHeight, m_textDescent;
int m_bmpW, m_bmpH;
bool m_bmpWpercent:1;
bool m_bmpHpresent:1;
@@ -364,6 +367,7 @@ wxHtmlImageCell::wxHtmlImageCell(const wxHtmlTag& tag,
wxHtmlWindowInterface *windowIface,
wxFSFile *input, double scaleHDPI,
int w, bool wpercent, int h, bool hpresent, double scale, int align,
+ int textHeight, int textDescent,
const wxString& mapname) : wxHtmlCell(tag)
, m_mapName(mapname)
{
@@ -374,6 +378,8 @@ wxHtmlImageCell::wxHtmlImageCell(const wxHtmlTag& tag,
m_bmpW = w;
m_bmpH = h;
m_align = align;
+ m_textHeight = textHeight;
+ m_textDescent = textDescent;
m_bmpWpercent = wpercent;
m_bmpHpresent = hpresent;
m_imageMap = nullptr;
@@ -564,7 +570,9 @@ void wxHtmlImageCell::Layout(int w)
m_Descent = m_Height;
break;
case wxHTML_ALIGN_CENTER :
- m_Descent = m_Height / 2;
+ // Center the image vertically with respect to the text and not the
+ // total cell height.
+ m_Descent = (m_Height - m_textHeight) / 2 + m_textDescent;
break;
case wxHTML_ALIGN_BOTTOM :
default :
@@ -748,12 +756,15 @@ TAG_HANDLER_BEGIN(IMG, "IMG,MAP,AREA")
mn = mn.Mid( 1 );
}
}
+
+ m_WParser->CreateCurrentFont();
+ const wxFontMetrics fm = m_WParser->GetDC()->GetFontMetrics();
wxHtmlImageCell *cel = new wxHtmlImageCell(
tag,
m_WParser->GetWindowInterface(),
str, scaleHDPI, w, wpercent, h, hpresent,
m_WParser->GetPixelScale(),
- al, mn);
+ al, fm.height, fm.descent, mn);
m_WParser->ApplyStateToCell(cel);
m_WParser->StopCollapsingSpaces();
cel->SetAlt(tag.GetParam(wxT("alt")));


=====================================
src/html/m_layout.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxHtml module for basic paragraphs/layout handling
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -12,6 +13,7 @@
#if wxUSE_HTML && wxUSE_STREAMS

#ifndef WX_PRECOMP
+ #include "wx/brush.h"
#include "wx/image.h"
#endif

@@ -313,36 +315,41 @@ TAG_HANDLER_BEGIN(BODY, "BODY")
if (tag.GetParamAsColour(wxT("LINK"), &clr))
m_WParser->SetLinkColor(clr);

- wxHtmlWindowInterface *winIface = m_WParser->GetWindowInterface();
- // the rest of this function requires a window:
- if ( !winIface )
- return false;
+ const bool hasBgColour = tag.GetParamAsColour("BGCOLOR", &clr);
+ if ( hasBgColour )
+ {
+ m_WParser->SetActualBackgroundColor(clr);
+ m_WParser->SetActualBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
+ m_WParser->GetContainer()->InsertCell(
+ new wxHtmlColourCell(clr, wxHTML_CLR_TRANSPARENT_BACKGROUND));
+ }

- wxString bg;
- if (tag.GetParamAsString(wxT("BACKGROUND"), &bg))
+ wxHtmlWindowInterface *winIface = m_WParser->GetWindowInterface();
+ if ( winIface )
{
- wxFSFile *fileBgImage = m_WParser->OpenURL(wxHTML_URL_IMAGE, bg);
- if ( fileBgImage )
+ if ( hasBgColour )
+ winIface->SetHTMLBackgroundColour(clr);
+
+ wxString bg;
+ if (tag.GetParamAsString("BACKGROUND", &bg))
{
- wxInputStream *is = fileBgImage->GetStream();
- if ( is )
+ wxFSFile *fileBgImage =
+ m_WParser->OpenURL(wxHTML_URL_IMAGE, bg);
+ if ( fileBgImage )
{
- wxImage image(*is);
- if ( image.IsOk() )
- winIface->SetHTMLBackgroundImage(image);
+ wxInputStream *is = fileBgImage->GetStream();
+ if ( is )
+ {
+ wxImage image(*is);
+ if ( image.IsOk() )
+ winIface->SetHTMLBackgroundImage(image);
+ }
+
+ delete fileBgImage;
}
-
- delete fileBgImage;
}
}

- if (tag.GetParamAsColour(wxT("BGCOLOR"), &clr))
- {
- m_WParser->GetContainer()->InsertCell(
- new wxHtmlColourCell(clr, wxHTML_CLR_TRANSPARENT_BACKGROUND));
- winIface->SetHTMLBackgroundColour(clr);
- }
-
return false;
}



=====================================
src/html/m_tables.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxHtml module for tables
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -683,9 +684,25 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")

TAG_HANDLER_VARS
wxHtmlTableCell* m_Table;
- wxString m_tAlign, m_rAlign;
+ wxString m_rAlign;
wxHtmlContainerCell *m_enclosingContainer;

+ void SetTableAlignment(wxHtmlContainerCell *c, const wxHtmlTag& tag)
+ {
+ wxString align;
+ if ( !tag.GetParamAsString("ALIGN", &align) )
+ return;
+
+ align.MakeUpper();
+
+ if ( align == "RIGHT" )
+ c->SetAlignHor(wxHTML_ALIGN_RIGHT);
+ else if ( align == "LEFT" )
+ c->SetAlignHor(wxHTML_ALIGN_LEFT);
+ else if ( align == "CENTER" )
+ c->SetAlignHor(wxHTML_ALIGN_CENTER);
+ }
+
// Call ParseInner() preserving background colour and mode after any
// changes done by it.
void CallParseInnerWithBg(const wxHtmlTag& tag, const wxColour& colBg)
@@ -721,7 +738,6 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
{
m_Table = nullptr;
m_enclosingContainer = nullptr;
- m_tAlign.clear();
m_rAlign.clear();
}

@@ -734,9 +750,11 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
if (tag.GetName() == wxT("TABLE"))
{
wxHtmlTableCell *oldt = m_Table;
+ const wxString oldRowAlign = m_rAlign;

wxHtmlContainerCell *oldEnclosing = m_enclosingContainer;
m_enclosingContainer = c = m_WParser->OpenContainer();
+ SetTableAlignment(c, tag);

m_Table = new wxHtmlTableCell(c, tag, m_WParser->GetPixelScale());

@@ -759,8 +777,6 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
m_Table->SetWidthFloat(0, wxHTML_UNITS_PIXELS);
}
int oldAlign = m_WParser->GetAlign();
- if (!tag.GetParamAsString(wxT("ALIGN"), &m_tAlign))
- m_tAlign.clear();

CallParseInnerWithBg(tag, m_Table->GetBackgroundColour());

@@ -769,6 +785,7 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
m_WParser->CloseContainer();

m_Table = oldt;
+ m_rAlign = oldRowAlign;
m_enclosingContainer = oldEnclosing;

return true; // ParseInner() called
@@ -781,8 +798,8 @@ TAG_HANDLER_BEGIN(TABLE, "TABLE,TR,TD,TH")
if (tag.GetName() == wxT("TR"))
{
m_Table->AddRow(tag);
- if (!tag.GetParamAsString(wxT("ALIGN"), &m_rAlign))
- m_rAlign = m_tAlign;
+ if ( !tag.GetParamAsString("ALIGN", &m_rAlign) )
+ m_rAlign.clear();
}

// new cell


=====================================
src/html/winpars.cpp
=====================================
@@ -3,6 +3,7 @@
// Purpose: wxHtmlParser class (generic parser)
// Author: Vaclav Slavik
// Copyright: (c) 1999 Vaclav Slavik
+// (c) 2026 wxWidgets development team
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////

@@ -316,8 +317,6 @@ wxFSFile *wxHtmlWinParser::OpenURL(wxHtmlURLType type,
return GetFS()->OpenFile(myurl, flags);
}

-static constexpr wxChar CUR_NBSP_VALUE = L'\xA0';
-
void wxHtmlWinParser::AddText(const wxString& txt)
{
if ( m_whitespaceMode == Whitespace_Normal )
@@ -362,7 +361,6 @@ void wxHtmlWinParser::AddText(const wxString& txt)
if (x)
{
m_tmpStrBuf.append(' ');
- m_tmpStrBuf.Replace(CUR_NBSP_VALUE, ' ');
AddWord(m_tmpStrBuf);
m_tmpStrBuf.clear();

@@ -394,18 +392,7 @@ void wxHtmlWinParser::AddText(const wxString& txt)
}
else // m_whitespaceMode == Whitespace_Pre
{
- if ( txt.find(CUR_NBSP_VALUE) != wxString::npos )
- {
- // we need to substitute spaces for &nbsp; here just like we
- // did in the Whitespace_Normal branch above
- wxString txt2(txt);
- txt2.Replace(CUR_NBSP_VALUE, ' ');
- AddPreBlock(txt2);
- }
- else
- {
- AddPreBlock(txt);
- }
+ AddPreBlock(txt);

// don't eat any whitespace in <pre> block
m_tmpLastWasSpace = false;


=====================================
src/msw/darkmode.cpp
=====================================
@@ -184,6 +184,7 @@ namespace wxMSWImpl
// don't appear in the SDK headers at all.
//
// Note that, not being public, they use C++ bool type and not Win32 BOOL.
+void (WINAPI *gs_RefreshImmersiveColorPolicyState)() = nullptr;
bool (WINAPI *gs_ShouldAppsUseDarkMode)() = nullptr;
bool (WINAPI *gs_AllowDarkModeForWindow)(HWND hwnd, bool allow) = nullptr;
PreferredAppMode (WINAPI *gs_SetPreferredAppMode)(PreferredAppMode appMode) = nullptr;
@@ -191,6 +192,13 @@ PreferredAppMode (WINAPI *gs_SetPreferredAppMode)(PreferredAppMode appMode) = nu
// Wrappers for the undocumented functions, to make sure we never dereference
// a null function pointer.

+void RefreshImmersiveColorPolicyState()
+{
+ if ( gs_RefreshImmersiveColorPolicyState == nullptr )
+ return;
+ gs_RefreshImmersiveColorPolicyState();
+}
+
bool ShouldAppsUseDarkMode()
{
if (gs_ShouldAppsUseDarkMode == nullptr)
@@ -229,7 +237,8 @@ bool InitDarkMode()

// These functions are not only undocumented but are not even exported by
// name, and have to be resolved using their ordinals.
- return TryLoadByOrd(gs_ShouldAppsUseDarkMode, dllUxTheme, 132) &&
+ return TryLoadByOrd(gs_RefreshImmersiveColorPolicyState, dllUxTheme, 104) &&
+ TryLoadByOrd(gs_ShouldAppsUseDarkMode, dllUxTheme, 132) &&
TryLoadByOrd(gs_AllowDarkModeForWindow, dllUxTheme, 133) &&
TryLoadByOrd(gs_SetPreferredAppMode, dllUxTheme, 135);
}
@@ -284,6 +293,11 @@ bool wxApp::MSWEnableDarkMode(int flags, wxDarkModeSettings* settings)
mode, rc);
}

+ // Update the active state so that ShouldAppsUseDarkMode() called by
+ // wxMSWDarkMode::IsActive() returns the correct value even if no windows
+ // had been created yet.
+ wxMSWImpl::RefreshImmersiveColorPolicyState();
+
gs_appMode = mode;
gs_wasActiveOnStartup = wxMSWDarkMode::IsActive();



=====================================
src/msw/notebook.cpp
=====================================
@@ -48,11 +48,6 @@
// check that the page index is valid
#define IS_VALID_PAGE(nPage) ((nPage) < GetPageCount())

-// you can set USE_NOTEBOOK_ANTIFLICKER to 0 for desktop Windows versions too
-// to disable code which results in flicker-less notebook redrawing at the
-// expense of some extra GDI resource consumption
-#define USE_NOTEBOOK_ANTIFLICKER 1
-
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
@@ -74,8 +69,6 @@
// global variables
// ----------------------------------------------------------------------------

-#if USE_NOTEBOOK_ANTIFLICKER
-
// the pointer to standard spin button wnd proc
static WXWNDPROC gs_wndprocNotebookSpinBtn = nullptr;

@@ -85,8 +78,6 @@ static WXWNDPROC gs_wndprocNotebook = nullptr;
LRESULT APIENTRY
wxNotebookWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

-#endif // USE_NOTEBOOK_ANTIFLICKER
-
// ----------------------------------------------------------------------------
// global functions
// ----------------------------------------------------------------------------
@@ -122,10 +113,7 @@ wxEND_EVENT_TABLE()
void wxNotebook::Init()
{
m_hbrBackground = nullptr;
-
-#if USE_NOTEBOOK_ANTIFLICKER
m_hasSubclassedUpdown = false;
-#endif // USE_NOTEBOOK_ANTIFLICKER
}

// default for dynamic class
@@ -162,7 +150,6 @@ bool wxNotebook::Create(wxWindow *parent,

LPCTSTR className = WC_TABCONTROL;

-#if USE_NOTEBOOK_ANTIFLICKER
// SysTabCtl32 class has natively CS_HREDRAW and CS_VREDRAW enabled and it
// causes horrible flicker when resizing notebook, so get rid of it by
// using a class without these styles (but otherwise identical to it)
@@ -198,7 +185,6 @@ bool wxNotebook::Create(wxWindow *parent,
className = s_clsNotebook.GetName().c_str();
}
}
-#endif // USE_NOTEBOOK_ANTIFLICKER

if ( !CreateControl(parent, id, pos, size, style | wxTAB_TRAVERSAL,
wxDefaultValidator, name) )
@@ -730,6 +716,7 @@ bool wxNotebook::InsertPage(size_t nPage,
DoSetSelectionAfterInsertion(nPage, bSelect);

InvalidateBestSize();
+ MSWSubclassSpin();

return true;
}
@@ -975,8 +962,6 @@ int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
// flicker-less notebook redraw
// ----------------------------------------------------------------------------

-#if USE_NOTEBOOK_ANTIFLICKER
-
// wnd proc for the spin button
LRESULT APIENTRY
wxNotebookSpinBtnWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
@@ -996,6 +981,24 @@ wxNotebookSpinBtnWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
hwnd, message, wParam, lParam);
}

+void wxNotebook::MSWSubclassSpin()
+{
+ if ( !m_hasSubclassedUpdown )
+ {
+ // Find the spin button.
+ HWND hwndSpin = FindWindowExA(m_hWnd, nullptr, UPDOWN_CLASSA, nullptr);
+ if ( hwndSpin )
+ {
+ // Subclass the spin button.
+ if ( !gs_wndprocNotebookSpinBtn )
+ gs_wndprocNotebookSpinBtn = wxGetWindowProc(hwndSpin);
+
+ wxSetWindowProc(hwndSpin, wxNotebookSpinBtnWndProc);
+ m_hasSubclassedUpdown = true;
+ }
+ }
+}
+
LRESULT APIENTRY
wxNotebookWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
@@ -1063,8 +1066,7 @@ ExpandSelectedTab(wxRect& rectTab, wxDirection tabOrient)
// Note that this function relies on the appropriate pen being selected into
// the DC and uses the current pen for drawing the tab borders.
void
-DrawNotebookTab(wxWindow* win,
- wxDC& dc,
+DrawNotebookTab(wxDC& dc,
const wxRect& rectOrig,
const wxString& label,
const wxBitmap& image,
@@ -1077,7 +1079,7 @@ DrawNotebookTab(wxWindow* win,
{
dc.SetClippingRegion(ExpandSelectedTab(rectTab, tabOrient));

- colTab = win->GetBackgroundColour();
+ colTab = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
}
else // not the selected tab
{
@@ -1256,7 +1258,7 @@ void wxNotebook::MSWNotebookPaint()
if ( n == 0 )
flags |= wxCONTROL_SPECIAL;

- DrawNotebookTab(this, dc, rect,
+ DrawNotebookTab(dc, rect,
GetPageText(n),
GetImageBitmapFor(this, GetPageImage(n)),
tabOrient,
@@ -1273,40 +1275,41 @@ void wxNotebook::MSWNotebookPaint()
rectTabArea.SetRight(sizeWindow.x);
else
rectTabArea.SetBottom(sizeWindow.y);
- dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
+ dc.SetBrush(GetBackgroundColour());
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(rectTabArea);

- // Set colour for tab borders (it's not really the same as menu bar colour,
+ // Set colour for borders (it's not really the same as menu bar colour,
// but this seems to look acceptable, so use it for now).
dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR));
+ dc.SetBrush(*wxTRANSPARENT_BRUSH);

- // Draw the separating line of the tab area.
- wxPoint ptStart = rectTabArea.GetTopLeft();
- wxPoint ptEnd = rectTabArea.GetBottomRight();
+ // Draw the body border.
+ wxRect rectBody = GetClientRect();
switch ( tabOrient )
{
case wxTOP:
- ptStart.y = ptEnd.y;
+ rectBody.y = rectTabArea.height - 1;
+ rectBody.height -= rectBody.y;
break;

case wxBOTTOM:
- ptEnd.y = ptStart.y;
+ rectBody.height = rectTabArea.y + 1;
break;

case wxLEFT:
- ptStart.x = ptEnd.x;
+ rectBody.x = rectTabArea.width - 1;
+ rectBody.width -= rectBody.x;
break;

case wxRIGHT:
- ptEnd.x = ptStart.x;
+ rectBody.width = rectTabArea.x + 1;
break;

default:
wxFAIL_MSG("unreachable");
}
-
- dc.DrawLine(ptStart, ptEnd);
+ dc.DrawRectangle(rectBody);

// Then draw all the individual tabs.
for ( size_t n = 0; n < pages; ++n )
@@ -1444,8 +1447,6 @@ void wxNotebook::OnPaint(wxPaintEvent& event)
dc.Blit(0, 0, rc.right, rc.bottom, &memdc, 0, 0);
}

-#endif // USE_NOTEBOOK_ANTIFLICKER
-
// ----------------------------------------------------------------------------
// wxNotebook callbacks
// ----------------------------------------------------------------------------
@@ -1546,33 +1547,7 @@ void wxNotebook::OnSize(wxSizeEvent& event)
false);
}

-#if USE_NOTEBOOK_ANTIFLICKER
- // subclass the spin control used by the notebook to scroll pages to
- // prevent it from flickering on resize
- if ( !m_hasSubclassedUpdown )
- {
- // iterate over all child windows to find spin button
- for ( HWND child = ::GetWindow(GetHwnd(), GW_CHILD);
- child;
- child = ::GetWindow(child, GW_HWNDNEXT) )
- {
- wxWindow *childWindow = wxFindWinFromHandle((WXHWND)child);
-
- // see if it exists, if no wxWindow found then assume it's the spin
- // btn
- if ( !childWindow )
- {
- // subclass the spin button to override WM_ERASEBKGND
- if ( !gs_wndprocNotebookSpinBtn )
- gs_wndprocNotebookSpinBtn = wxGetWindowProc(child);
-
- wxSetWindowProc(child, wxNotebookSpinBtnWndProc);
- m_hasSubclassedUpdown = true;
- break;
- }
- }
- }
-#endif // USE_NOTEBOOK_ANTIFLICKER
+ MSWSubclassSpin();

event.Skip();
}
@@ -1683,13 +1658,11 @@ bool wxNotebook::SetBackgroundColour(const wxColour& colour)

UpdateBgBrush();

-#if USE_NOTEBOOK_ANTIFLICKER
Unbind(wxEVT_ERASE_BACKGROUND, &wxNotebook::OnEraseBackground, this);
if ( m_hasBgCol || !wxUxThemeIsActive() )
{
Bind(wxEVT_ERASE_BACKGROUND, &wxNotebook::OnEraseBackground, this);
}
-#endif // USE_NOTEBOOK_ANTIFLICKER

return true;
}


=====================================
src/msw/spinbutt.cpp
=====================================
@@ -30,6 +30,7 @@

#include "wx/spinbutt.h"

+#include "wx/dcbuffer.h"
#include "wx/msw/dc.h"
#include "wx/msw/private.h"
#include "wx/msw/private/darkmode.h"
@@ -143,6 +144,9 @@ bool wxSpinButton::Create(wxWindow *parent,

SetInitialSize(size);

+ if ( wxMSWDarkMode::IsActive() )
+ MSWSetDarkOrLightMode(SetMode::Initial);
+
return true;
}

@@ -177,8 +181,9 @@ void wxSpinButton::OnPaint(wxPaintEvent& event)
{
if ( wxMSWDarkMode::IsActive() )
{
- // Unfortunately PaintIfNecessary() can't be used here as we need to
- // handle the extra border below, so duplicate what it does here.
+ // Have the updown control draw itself, then modify the result with a
+ // border and possibly inverted colours.
+
const RECT rc = wxGetClientRect(GetHwnd());
const wxSize size{rc.right - rc.left, rc.bottom - rc.top};

@@ -212,6 +217,10 @@ void wxSpinButton::OnPaint(wxPaintEvent& event)

wxImage image = bmp.ConvertToImage();

+ // Prior to Windows 11 22H2 (build 22621), the updown control always
+ // draws in light mode, so invert the colours to make it dark.
+ const bool shouldInvert = !wxCheckOsVersion(10, 0, 22621);
+
const int width = image.GetWidth();
const int height = image.GetHeight();
unsigned char *data = image.GetData();
@@ -229,7 +238,7 @@ void wxSpinButton::OnPaint(wxPaintEvent& event)
if ( alpha )
*alpha = wxALPHA_OPAQUE;
}
- else
+ else if ( shouldInvert )
{
// This uses a slightly different formula than the one in
// InvertBitmapPixel() because the one there results in the
@@ -252,7 +261,10 @@ void wxSpinButton::OnPaint(wxPaintEvent& event)
bmp = wxBitmap(image);
#endif // wxUSE_IMAGE

- wxPaintDC dc(this);
+ wxBufferedPaintDC dc(this);
+ // Clear the background, otherwise alpha transparency in the bitmap
+ // appears white.
+ dc.Clear();
dc.DrawBitmap(bmp, 0, 0);
}
else


=====================================
src/msw/window.cpp
=====================================
@@ -3556,7 +3556,16 @@ wxWindowMSW::MSWHandleMessage(WXLRESULT *result,
// Check for the special case of the message which notifies about
// the colours change.
if ( wxIsSystemColourChange(lParam) )
- processed = HandleSysColorChange();
+ {
+ // When switching between light and dark modes, Windows normally
+ // sends two WM_SETTINGCHANGE messages with "ImmersiveColorSet",
+ // one before and one after. Sometimes the second message does not
+ // appear. To guarantee we process at least one system colour
+ // change event after the switch, defer processing this message
+ // until after the switch.
+ ::PostMessage(m_hWnd, WM_SYSCOLORCHANGE, 0, 0);
+ processed = true;
+ }
else
processed = HandleSettingChange(wParam, lParam);
break;


=====================================
tests/controls/gridtest.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Steven Lamerton
// Created: 2010-06-25
// Copyright: (c) 2010 Steven Lamerton
+// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////

#include "testprec.h"
@@ -1541,6 +1542,29 @@ TEST_CASE_METHOD(GridTestCase, "Grid::ReadOnly", "[grid]")
#endif
}

+TEST_CASE_METHOD(GridTestCase, "Grid::ChangeEditorWhileEditing", "[grid]")
+{
+ wxGridCellAttr* attr = new wxGridCellAttr();
+ attr->SetEditor(new wxGridCellBoolEditor);
+ attr->SetRenderer(new wxGridCellBoolRenderer);
+ m_grid->SetColAttr(0, attr);
+
+ m_grid->SetCellValue(0, 0, "1");
+ m_grid->SetGridCursor(0, 0);
+ m_grid->EnableCellEditControl();
+
+ REQUIRE(m_grid->IsCellEditControlShown());
+
+ attr = new wxGridCellAttr();
+ attr->SetEditor(new wxGridCellBoolEditor);
+ attr->SetRenderer(new wxGridCellBoolRenderer);
+ m_grid->SetColAttr(0, attr);
+
+ m_grid->DisableCellEditControl();
+
+ CHECK(!m_grid->IsCellEditControlShown());
+}
+
TEST_CASE_METHOD(GridTestCase, "Grid::WindowAsEditorControl", "[grid]")
{
#if wxUSE_UIACTIONSIMULATOR


=====================================
tests/controls/windowtest.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Steven Lamerton
// Created: 2010-07-10
// Copyright: (c) 2010 Steven Lamerton
+// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////

#include "testprec.h"
@@ -25,6 +26,7 @@
#include "wx/caret.h"
#include "wx/cshelp.h"
#include "wx/dcclient.h"
+#include "wx/timer.h"
#include "wx/tooltip.h"
#include "wx/wupdlock.h"

@@ -55,6 +57,78 @@ protected:
wxDECLARE_NO_COPY_CLASS(WindowTestCase);
};

+#if wxUSE_HELP
+class ContextHelpCaptureLostTester : public wxWindow
+{
+public:
+ ContextHelpCaptureLostTester(wxWindow* parent)
+ : wxWindow(parent, wxID_ANY)
+ {
+ }
+
+ void SimulateCaptureLost()
+ {
+ DoReleaseMouse();
+ NotifyCaptureLost();
+ }
+};
+
+class ContextHelpCaptureLostState : public wxEvtHandler
+{
+public:
+ explicit ContextHelpCaptureLostState(ContextHelpCaptureLostTester* win)
+ : m_win(win),
+ m_captureLostTimer(this),
+ m_fallbackTimer(this)
+ {
+ Bind(wxEVT_TIMER, &ContextHelpCaptureLostState::OnTimer, this);
+ }
+
+ void Start()
+ {
+ m_captureLostTimer.StartOnce(1);
+ }
+
+ void Done()
+ {
+ m_done = true;
+ m_captureLostTimer.Stop();
+ m_fallbackTimer.Stop();
+ }
+
+ bool WasCaptureLostSent() const { return m_captureLostSent; }
+ bool WasFallbackUsed() const { return m_fallbackUsed; }
+
+private:
+ void OnTimer(wxTimerEvent& event)
+ {
+ if ( m_done )
+ return;
+
+ if ( &event.GetTimer() == &m_captureLostTimer )
+ {
+ m_captureLostSent = true;
+ m_win->SimulateCaptureLost();
+ m_fallbackTimer.StartOnce(100);
+ return;
+ }
+
+ m_fallbackUsed = true;
+
+ wxKeyEvent eventKey(wxEVT_KEY_DOWN);
+ eventKey.SetEventObject(m_win);
+ m_win->GetEventHandler()->ProcessEvent(eventKey);
+ }
+
+ ContextHelpCaptureLostTester* const m_win;
+ wxTimer m_captureLostTimer;
+ wxTimer m_fallbackTimer;
+ bool m_done = false;
+ bool m_captureLostSent = false;
+ bool m_fallbackUsed = false;
+};
+#endif // wxUSE_HELP
+
static void DoTestShowHideEvent(wxWindow* window)
{
EventCounter show(window, wxEVT_SHOW);
@@ -179,6 +253,26 @@ TEST_CASE_METHOD(WindowTestCase, "Window::Mouse", "[window]")
CHECK(!m_window->HasCapture());
}

+TEST_CASE_METHOD(WindowTestCase, "Window::ContextHelpCaptureLost",
+ "[window][help]")
+{
+#if wxUSE_HELP
+ ContextHelpCaptureLostTester* const win =
+ new ContextHelpCaptureLostTester(wxTheApp->GetTopWindow());
+ ContextHelpCaptureLostState state(win);
+ state.Start();
+
+ wxContextHelp contextHelp(win, false);
+
+ CHECK(contextHelp.BeginContextHelp(win));
+
+ state.Done();
+ CHECK(state.WasCaptureLostSent());
+ CHECK(!state.WasFallbackUsed());
+ CHECK(!win->HasCapture());
+#endif // wxUSE_HELP
+}
+
TEST_CASE_METHOD(WindowTestCase, "Window::Properties", "[window]")
{
m_window->SetLabel("label");


=====================================
tests/filesys/filesystest.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Vaclav Slavik, Vyacheslav Lisovski
// Created: 2004-03-28
// Copyright: (c) 2004 Vaclav Slavik
+// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////

// ----------------------------------------------------------------------------
@@ -18,6 +19,7 @@
#endif // WX_PRECOMP

#include "wx/filesys.h"
+#include "wx/sysopt.h"

#if wxUSE_FILESYSTEM

@@ -50,6 +52,32 @@ public:
};


+#if wxUSE_SYSTEM_OPTIONS
+class TempSystemOption
+{
+public:
+ TempSystemOption(const wxString& name, int value)
+ : m_name(name),
+ m_value(wxSystemOptions::GetOption(name)),
+ m_hadValue(wxSystemOptions::HasOption(name))
+ {
+ wxSystemOptions::SetOption(name, value);
+ }
+
+ ~TempSystemOption()
+ {
+ wxSystemOptions::SetOption(m_name,
+ m_hadValue ? m_value : wxString());
+ }
+
+private:
+ const wxString m_name;
+ const wxString m_value;
+ const bool m_hadValue;
+};
+#endif // wxUSE_SYSTEM_OPTIONS
+
+
// ----------------------------------------------------------------------------
// tests themselves
// ----------------------------------------------------------------------------
@@ -137,6 +165,20 @@ TEST_CASE("wxFileSystem::URLParsing", "[filesys][url][parse]")
}
}

+TEST_CASE("wxFileSystem::HTMLMimeFallback", "[filesys][mime]")
+{
+#if wxUSE_MIMETYPE && wxUSE_SYSTEM_OPTIONS
+ TempSystemOption noMimeManager("filesys.no-mimetypesmanager", 1);
+#endif
+
+ CHECK(wxFileSystemHandler::GetMimeTypeFromExt("index.htm") ==
+ "text/html");
+ CHECK(wxFileSystemHandler::GetMimeTypeFromExt("index.html") ==
+ "text/html");
+ CHECK(wxFileSystemHandler::GetMimeTypeFromExt("INDEX.HTML") ==
+ "text/html");
+}
+
TEST_CASE("wxFileSystem::FileNameToUrlConversion", "[filesys][url][filename]")
{
const static struct Data {


=====================================
tests/html/htmlparser.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Vadim Zeitlin
// Created: 2011-01-13
// Copyright: (c) 2011 Vadim Zeitlin <va...@wxwidgets.org>
+// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////

// ----------------------------------------------------------------------------
@@ -16,12 +17,51 @@


#ifndef WX_PRECOMP
+ #include "wx/bitmap.h"
#include "wx/dcmemory.h"
+ #include "wx/log.h"
#endif // WX_PRECOMP

#include "wx/html/winpars.h"

#include <memory>
+#include <vector>
+
+namespace
+{
+
+void AddTextCells(const wxHtmlCell *cell,
+ std::vector<const wxHtmlCell *>& cells)
+{
+ if ( !cell )
+ return;
+
+ const wxString text = cell->ConvertToText(nullptr);
+ if ( !text.empty() )
+ cells.push_back(cell);
+
+ for ( const wxHtmlCell *child = cell->GetFirstChild(); child;
+ child = child->GetNext() )
+ {
+ AddTextCells(child, cells);
+ }
+}
+
+} // anonymous namespace
+
+namespace
+{
+
+const wxHtmlCell *GetFirstNonFormattingChild(const wxHtmlContainerCell *cell)
+{
+ const wxHtmlCell *child = cell->GetFirstChild();
+ while ( child && child->IsFormattingCell() )
+ child = child->GetNext();
+
+ return child;
+}
+
+} // anonymous namespace

// Test that parsing invalid HTML simply fails but doesn't crash for example.
TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
@@ -42,6 +82,62 @@ TEST_CASE("wxHtmlParser::ParseInvalid", "[html][parser][error]")
delete p.Parse("<!---");
}

+TEST_CASE("wxHtmlParser::ImageAlignCenter", "[html][parser]")
+{
+ wxBitmap bmp(200, 200);
+ wxMemoryDC dc(bmp);
+
+ wxHtmlWinParser p;
+ p.SetDC(&dc);
+
+ wxLogNull noLog;
+ std::unique_ptr<wxHtmlContainerCell> const cells(
+ static_cast<wxHtmlContainerCell*>(p.Parse(
+ R"(<img id="img" width="100" height="100" align="center" src="dummy"/>Text)"
+ ))
+ );
+
+ REQUIRE( cells );
+ cells->Layout(200);
+
+ const wxString id("img");
+ const wxHtmlCell* const image = cells->Find(wxHTML_COND_ISANCHOR, &id);
+ REQUIRE( image );
+
+ const wxHtmlCell* const word = image->GetNext();
+ REQUIRE( word );
+ REQUIRE_FALSE( word->IsFormattingCell() );
+
+ CHECK( word->GetPosY() + word->GetHeight() / 2 ==
+ image->GetPosY() + image->GetHeight() / 2 );
+}
+
+TEST_CASE("wxHtmlParser::NBSPLineBreak", "[html][parser]")
+{
+ wxBitmap bmp(100, 100);
+ wxMemoryDC dc(bmp);
+ wxHtmlWinParser p;
+ p.SetDC(&dc);
+
+ const wxString html = "aaa <font color=\"#ff0000\">"
+ "&nbsp;&nbsp;</font>bbb";
+ std::unique_ptr<wxHtmlContainerCell> const top(
+ static_cast<wxHtmlContainerCell *>(p.Parse(html)));
+ REQUIRE(top);
+
+ top->Layout(1);
+
+ std::vector<const wxHtmlCell *> cells;
+ AddTextCells(top.get(), cells);
+
+ REQUIRE(cells.size() >= 3);
+ CHECK(cells[0]->ConvertToText(nullptr) == "aaa ");
+ CHECK(cells[1]->ConvertToText(nullptr) == wxString(L'\xa0', 2));
+ CHECK(cells[2]->ConvertToText(nullptr) == "bbb");
+ CHECK(cells[0]->GetAbsPos().y == cells[1]->GetAbsPos().y);
+ CHECK(cells[1]->GetAbsPos().y == cells[2]->GetAbsPos().y);
+}
+
TEST_CASE("wxHtmlCell::Detach", "[html][cell]")
{
wxMemoryDC dc;
@@ -102,4 +198,58 @@ TEST_CASE("wxHtmlCell::Detach", "[html][cell]")
}
}

+TEST_CASE("wxHtmlParser::TableAlign", "[html][parser][table]")
+{
+ wxBitmap bmp(400, 400);
+ wxMemoryDC dc(bmp);
+
+ wxHtmlWinParser parser;
+ parser.SetDC(&dc);
+
+ std::unique_ptr<wxHtmlContainerCell> const root(
+ static_cast<wxHtmlContainerCell*>(
+ parser.Parse(
+ "<table align=\"center\"><tr><td>test</td><td>ok</td></tr></table>"
+ )
+ )
+ );
+ REQUIRE( root );
+
+ root->Layout(200);
+
+ const wxHtmlCell *cell = GetFirstNonFormattingChild(root.get());
+ REQUIRE( cell );
+
+ const wxHtmlContainerCell *body =
+ static_cast<const wxHtmlContainerCell*>(cell);
+
+ cell = GetFirstNonFormattingChild(body);
+ REQUIRE( cell );
+
+ const wxHtmlContainerCell *tableWrapper =
+ static_cast<const wxHtmlContainerCell*>(cell);
+
+ cell = GetFirstNonFormattingChild(tableWrapper);
+ REQUIRE( cell );
+
+ const wxHtmlContainerCell *table =
+ static_cast<const wxHtmlContainerCell*>(cell);
+
+ CHECK( table->GetPosX() > 0 );
+
+ cell = GetFirstNonFormattingChild(table);
+ REQUIRE( cell );
+
+ const wxHtmlContainerCell *td =
+ static_cast<const wxHtmlContainerCell*>(cell);
+
+ cell = GetFirstNonFormattingChild(td);
+ REQUIRE( cell );
+
+ const wxHtmlContainerCell *tdContent =
+ static_cast<const wxHtmlContainerCell*>(cell);
+
+ CHECK( tdContent->GetAlignHor() == wxHTML_ALIGN_LEFT );
+}
+
#endif //wxUSE_HTML


=====================================
tests/html/htmprint.cpp
=====================================
@@ -4,6 +4,7 @@
// Author: Vadim Zeitlin
// Created: 2018-05-22
// Copyright: (c) 2018 Vadim Zeitlin <va...@wxwidgets.org>
+// (c) 2026 wxWidgets development team
///////////////////////////////////////////////////////////////////////////////

// ----------------------------------------------------------------------------
@@ -16,7 +17,10 @@
#if wxUSE_PRINTING_ARCHITECTURE

#ifndef WX_PRECOMP
+ #include "wx/bitmap.h"
+ #include "wx/brush.h"
#include "wx/dcmemory.h"
+ #include "wx/image.h"
#endif // WX_PRECOMP

#include "wx/html/htmprint.h"
@@ -44,6 +48,31 @@ int CountPages(wxHtmlPrintout& pr)

} // anonymous namespace

+TEST_CASE("wxHtmlDCRenderer::BodyBgColour", "[html][print]")
+{
+ const wxColour bg(0x12, 0x34, 0x56);
+ wxBitmap bmp(100, 100);
+ {
+ wxMemoryDC dc(bmp);
+ dc.SetBackground(*wxWHITE_BRUSH);
+ dc.Clear();
+
+ wxHtmlDCRenderer renderer;
+ renderer.SetDC(&dc);
+ renderer.SetSize(bmp.GetWidth(), bmp.GetHeight());
+ renderer.SetHtmlText("<body bgcolor=\"#123456\">"
+ "<p>Hello world!</p>"
+ "</body>");
+ renderer.Render(0, 0);
+ }
+
+ const wxImage image = bmp.ConvertToImage();
+ const wxColour actual(image.GetRed(90, 90),
+ image.GetGreen(90, 90),
+ image.GetBlue(90, 90));
+ CHECK(actual == bg);
+}
+
TEST_CASE("wxHtmlPrintout::Pagination", "[html][print]")
{
wxHtmlPrintout pr;



View it on GitLab: https://gitlab.com/wxwidgets/wxwidgets/-/compare/6c85b395869bbd052d9b867738265e1480ca1c60...2650437c7699bf7858b0d97e58249760b46d941a

--
View it on GitLab: https://gitlab.com/wxwidgets/wxwidgets/-/compare/6c85b395869bbd052d9b867738265e1480ca1c60...2650437c7699bf7858b0d97e58249760b46d941a
You're receiving this email because of your account on gitlab.com. Manage all notifications: https://gitlab.com/-/profile/notifications | Help: https://gitlab.com/help


Reply all
Reply to author
Forward
0 new messages