Skip to content

Stop a language change from destroying the menus - #253

Merged
JE-Chen merged 2 commits into
mainfrom
dev
Jul 28, 2026
Merged

Stop a language change from destroying the menus#253
JE-Chen merged 2 commits into
mainfrom
dev

Conversation

@JE-Chen

@JE-Chen JE-Chen commented Jul 27, 2026

Copy link
Copy Markdown
Member

Changing the language rebuilt the menu bar, and setMenuBar deletes the outgoing bar together with every menu on it. JEditor owns all of its own menus, so it never noticed. An application embedding this window adds its menus to that same bar: those disappeared, the references it kept became pointers to deleted objects, and following one took the process down — Qt6Widgets.dll, 0xC0000005. Restarting looked like the trigger only because the settings were never written after the crash.

Relabel in place

Nothing is torn down now. The menus, toolbar, tabs and docks are walked and their wording moved from the old dictionary's key to the new one's, so every widget — including a host's — stays exactly where it was.

Placing text by key needs care where one English word belongs to several keys that read differently elsewhere:

  • a menu bar entry is matched against the ..._menu_label keys first;
  • a submenu item against its parent menu's family, so the Tab menu's "Editor" cannot be given the Dock menu's wording;
  • then every menu key, and last the whole dictionary.

Menus that list names rather than wording — the languages, the font families — are left alone: translating "Symbol" would name a font nobody has installed, and the Language menu has to keep each language written the way that language writes itself.

The same fault in the command palette

Walking the menus turned out to be the real culprit, and the palette had it too. On PySide6, QAction.menu() hands the existing submenu over to Python, and after one walk the window's own references — file_menu, dock_menu and fifteen others — all point at reclaimed objects. Opening the palette once was enough; the titles stay on screen, so the damage only surfaces when something later touches one.

Both now look submenus up among the widgets that already exist (menu/submenu_map.py), which moves no ownership. findChildren alone misses menus attached with addMenu(existing_menu), which Qt does not reparent, so the application's other menus are swept as well.

Verification

Check Result
Relabelled menu tree vs one built from scratch in that language 1188 entries identical, in all three translations
Toolbar tooltips, same comparison identical
Menu objects after a language change all alive; the menu bar object is not replaced
References after collecting the palette's commands 0 invalidated, was 17
Embedding application (its menus on the same bar) keeps all of its menus, and they translate
Tests / ruff 1819 passed (+21), all checks passed

The new tests cover both the crash (menus survive, a host's menu survives, the bar is the same object) and the wording (the fresh-build comparison, font families and language names left alone, the empty-recent-files line still moving).

JE-Chen added 2 commits July 28, 2026 01:46
Collecting the palette's entries asked every action for its submenu. On
PySide6 that call hands an existing menu over to Python, and once the walk
is done the main window's own references -- file_menu, dock_menu and the
rest -- all point at reclaimed objects. Opening the palette once was enough
to leave seventeen of them unusable, so the next piece of code to touch one
raised, and a C++ path following the same pointer could take the process
down.

The submenus are now looked up among the widgets that already exist, which
moves no ownership. The sweep covers menus attached with addMenu(menu),
which are not reparented and so are not children of the bar.
Changing the language rebuilt the menu bar, and setMenuBar deletes the
outgoing bar together with every menu on it. JEditor owns all of its own
menus, so it never noticed; an application embedding this window adds its
menus to that same bar. Those disappeared, the references it kept became
pointers to deleted objects, and following one crashed the process --
Qt6Widgets, 0xC0000005. Restarting looked like the trigger only because
the settings were never written after the crash.

Nothing is torn down now. The menus, the toolbar, the tabs and the docks
are walked and their wording moved from the old dictionary's key to the new
one's, which leaves every widget, including a host's, exactly where it was.

Placing text by key needs care where one English word belongs to several
keys that differ elsewhere: a menu bar entry is matched against the
..._menu_label keys first, then a submenu item against its parent's family,
so the Tab menu's "Editor" cannot be given the Dock menu's wording. Menus
that list names rather than wording -- the languages, the font families --
are left alone, since translating "Symbol" would name a font nobody has.

A test walks the whole menu bar after a language change and compares it
against one built from scratch in that language: all 1188 entries have to
read alike, in each of the three translations.
@sonarqubecloud

Copy link
Copy Markdown

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 85 complexity · 0 duplication

Metric Results
Complexity 85
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@JE-Chen
JE-Chen merged commit a00f411 into main Jul 28, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant