From 5f02608a91ffb60947eac85b05a977da35fb0068 Mon Sep 17 00:00:00 2001 From: Istvan Matejcsok <119620946+matejcsok-ee@users.noreply.github.com> Date: Wed, 17 Jun 2026 14:26:03 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20wxWidgets=20text=20ellips?= =?UTF-8?q?is?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/apps/Makefile.wasm | 10 +++ .../stattext-ellipsize_test.cpp | 85 +++++++++++++++++++ tests/e2e/dom-port-bugs.spec.ts | 45 ++++++++++ wxwidgets | 2 +- 4 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 tests/apps/standalone/stattext-ellipsize/stattext-ellipsize_test.cpp diff --git a/tests/apps/Makefile.wasm b/tests/apps/Makefile.wasm index b807331..ba1f06f 100644 --- a/tests/apps/Makefile.wasm +++ b/tests/apps/Makefile.wasm @@ -186,6 +186,7 @@ all: minimal_test.html \ $(S)/coroutine-pthread/nested_repro_ex.html \ $(S)/coroutine-pthread/vcall_repro.html \ $(S)/textctrl-reentry/textctrl-reentry_test.html \ + $(S)/stattext-ellipsize/stattext-ellipsize_test.html \ $(S)/tooltip-lifetime/tooltip-lifetime_test.html \ $(S)/warp-pointer/warp-pointer_test.html @@ -208,6 +209,15 @@ $(S)/textctrl-reentry/textctrl-reentry_test.html: $(S)/textctrl-reentry/textctrl textctrl-reentry: $(S)/textctrl-reentry/textctrl-reentry_test.html +# wxStaticText ellipsize-on-resize repro: re-ellipsize the label on wxEVT_SIZE. +$(S)/stattext-ellipsize/stattext-ellipsize_test.o: $(S)/stattext-ellipsize/stattext-ellipsize_test.cpp + $(CXX) -c $(CXXFLAGS) $< -o $@ + +$(S)/stattext-ellipsize/stattext-ellipsize_test.html: $(S)/stattext-ellipsize/stattext-ellipsize_test.o $(WX_CORE_LIB) $(JS_FILES) + $(CXX) $< $(LDFLAGS_NOGL) --pre-js $(JS) --shell-file $(HTML) -o $@ + +stattext-ellipsize: $(S)/stattext-ellipsize/stattext-ellipsize_test.html + # wxToolTip hover-window lifetime repro (relies on wxWasmTooltipDebugHoverWindow). $(S)/tooltip-lifetime/tooltip-lifetime_test.o: $(S)/tooltip-lifetime/tooltip-lifetime_test.cpp $(CXX) -c $(CXXFLAGS) $< -o $@ diff --git a/tests/apps/standalone/stattext-ellipsize/stattext-ellipsize_test.cpp b/tests/apps/standalone/stattext-ellipsize/stattext-ellipsize_test.cpp new file mode 100644 index 0000000..0f6c41a --- /dev/null +++ b/tests/apps/standalone/stattext-ellipsize/stattext-ellipsize_test.cpp @@ -0,0 +1,85 @@ +// wxStaticText ellipsize-on-resize regression (DOM port). +// +// Bug (src/wasm/stattext.cpp): the label was ellipsized only inside SetLabel(), +// using the control's client size AT THAT MOMENT. A wxStaticText placed in a +// growable sizer cell (e.g. KiCad's gerbview Layers Manager, which uses +// WX_ELLIPSIZED_STATIC_TEXT) is constructed narrow and only widened later by +// layout. The DOM-port wxStaticText had no wxEVT_SIZE handler, so the visible +// label stayed truncated to the early (tiny) width — rendering "1..." instead of +// "1 tinytapeout-demo-User_2.gbr (Other, User)". Native ports re-ellipsize on +// resize via wxStaticTextBase::UpdateLabel(); the fix binds wxEVT_SIZE to do the +// same. +// +// This repro drives the exact sequence without a sizer (so the width changes are +// unambiguous): the label is set while the control is narrow (ellipsized away), +// then a button widens the control. wxST_NO_AUTORESIZE keeps our explicit width +// so the only thing that can lengthen the visible text is the resize handler. +// +// RED (bug present): after widening, the visible text is still the narrow +// ellipsization — the full name never appears. +// GREEN (fixed): widening re-ellipsizes; the full label becomes visible. + +#include "wx/wxprec.h" +#ifndef WX_PRECOMP + #include "wx/wx.h" +#endif + +#ifdef __EMSCRIPTEN__ +#include +#endif + +// Long enough that it must be ellipsized at 60px but fits fully at >600px. +static const char* const LONG_LABEL = + "1 tinytapeout-demo-User_2.gbr (Other, User)"; + +enum { ID_GROW = wxID_HIGHEST + 1 }; + +class EllipsizeFrame : public wxFrame +{ +public: + EllipsizeFrame() + : wxFrame(nullptr, wxID_ANY, "stattext ellipsize repro", + wxDefaultPosition, wxSize(700, 200)) + { + wxButton* grow = new wxButton(this, ID_GROW, "Grow", + wxPoint(10, 10), wxSize(120, 30)); + grow->Bind(wxEVT_BUTTON, &EllipsizeFrame::OnGrow, this); + + m_text = new wxStaticText(this, wxID_ANY, wxEmptyString, + wxPoint(10, 60), wxSize(60, 24), + wxST_ELLIPSIZE_MIDDLE | wxST_NO_AUTORESIZE); + // Establish a valid (but small) client width, THEN set the label, so the + // first ellipsization happens at the narrow width. + m_text->SetSize(60, 24); + m_text->SetLabel(LONG_LABEL); + +#ifdef __EMSCRIPTEN__ + CallAfter([] { EM_ASM({ console.log('[REPRO] stattext ready'); }); }); +#endif + } + +private: + void OnGrow(wxCommandEvent&) + { + // Widen past the label's natural width: the fix re-ellipsizes here (the + // full label now fits); the bug leaves the old truncation in place. + m_text->SetSize(640, 24); + } + + wxStaticText* m_text = nullptr; +}; + +class EllipsizeApp : public wxApp +{ +public: + bool OnInit() override + { + if (!wxApp::OnInit()) + return false; + + (new EllipsizeFrame())->Show(true); + return true; + } +}; + +wxIMPLEMENT_APP(EllipsizeApp); diff --git a/tests/e2e/dom-port-bugs.spec.ts b/tests/e2e/dom-port-bugs.spec.ts index d293eb7..f82e5e2 100644 --- a/tests/e2e/dom-port-bugs.spec.ts +++ b/tests/e2e/dom-port-bugs.spec.ts @@ -70,4 +70,49 @@ test.describe('wx DOM-port bug reproductions', () => { const line = reproLine(testLogger.consoleLogs, name)!; expect(line, `repro line was: ${line}`).toContain(`[REPRO] ${name}: PASS`); }); + + // stattext.cpp ellipsized the label only inside SetLabel(), at the control's + // client size at that moment. A wxStaticText in a growable sizer cell (e.g. + // gerbview's Layers Manager) is constructed narrow and widened later by layout, + // but the DOM-port wxStaticText had no wxEVT_SIZE handler — so the label stayed + // truncated to the early tiny width ("1..." instead of the layer name). The fix + // re-ellipsizes on resize via UpdateLabel(). The app sets the long label while + // narrow, then a button widens the control. + test('wxStaticText: an ellipsized label must re-expand when the control is widened', async ({ + page, + testLogger, + }) => { + await page.goto('/standalone/stattext-ellipsize/stattext-ellipsize_test.html'); + expect(await tryLoadApp(page, 30000), 'repro app should load').toBe(true); + + await expect + .poll(() => testLogger.consoleLogs.some((l) => l.includes('[REPRO] stattext ready')), { + timeout: 30000, + message: 'repro app should finish setup', + }) + .toBe(true); + + // The middle of the label only renders when the control is wide enough; this + // exact substring is ellipsized away at the narrow width. + const fullNameVisible = () => + page.evaluate(() => + Array.from(document.querySelectorAll('span, div')).some((el) => + (el.textContent || '').includes('tinytapeout-demo-User_2'), + ), + ); + + // Narrow: the long name is ellipsized away. + expect(await fullNameVisible(), 'label starts ellipsized at the narrow width').toBe(false); + + await page.getByRole('button', { name: 'Grow' }).click(); + + // Widening must re-ellipsize so the full label shows. RED before the wasm fix: + // nothing re-ran ellipsization on resize, so the label stayed truncated. + await expect + .poll(fullNameVisible, { + timeout: 10000, + message: 'widening the control must re-ellipsize so the full label becomes visible', + }) + .toBe(true); + }); }); diff --git a/wxwidgets b/wxwidgets index d8d3b49..c299d71 160000 --- a/wxwidgets +++ b/wxwidgets @@ -1 +1 @@ -Subproject commit d8d3b49d5730ae3488480dd38613b29d3b8eacfc +Subproject commit c299d716aac59de6f86c6711213d4ced3338b4c8