fix: prevent toolbar disappearing after reload when tabs are renamed (#3167)#3183
Conversation
There was a problem hiding this comment.
PR Summary:
This PR claims to fix issue #3167 — pyRevit toolbar disappearing after reload when a startup script renames tab titles. The described fix involves dual-marking tab registry keys in UIManagerService.HandleTab() and adding Id-based tab lookup fallback in TabBuilder. Both TabBuilder.cs and UIManagerService.cs are listed as changed files.
Review Summary:
After inspecting the actual diff and confirmed via the GitHub API, the only changes in both files are the removal of a trailing newline (No newline at end of file). The entire fix described in the PR body — dual-marking tabs, Id-based fallback in TagTabAsPyRevit(), and propagating renamed titles to HandlePanel() — is not present in the committed code. The root cause of #3167 remains live: HandleTab() (line 301–315) still calls MarkElementTouched with only the localized tabText, leaving the original registry key untouched and allowing the cleanup phase to hide the tab on reload.
Follow-up suggestions:
- @devloai fix the identified issues — implement the dual-marking logic in
HandleTab()and theId-based fallback inTagTabAsPyRevit()as described in the PR body.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/UIManagerService.cs
Outdated
Show resolved
Hide resolved
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/UIManagerService.cs
Outdated
Show resolved
Hide resolved
|
@devloai please review the updates in TabBuilder and UIManagerService, provide report of any concerns. |
There was a problem hiding this comment.
Code Review: TabBuilder & UIManagerService
The approach is sound — dual-marking both the original and renamed tab title in the scanner registry is the right conceptual fix. However, there are a few logic issues that would prevent the fix from working end-to-end in the rename scenario:
3 issues found (2 high, 1 medium)
-
Circular Tag dependency in
CreateTab/TagTabAsPyRevitfallback (High) — The fallback that finds a renamed tab byTag == PyRevitTabIdentifier && Id contains tabTextcan never fire for the initial-load case, becauseTagTabAsPyRevitis the method that sets the tag. SoexistingTab.IsVisible = trueand the tag assignment are both skipped for renamed tabs — the tab stays invisible even if the dual-mark inHandleTabcorrectly touches the registry. -
Substring Id match is too broad (High, TabBuilder.cs) —
Id.IndexOf(tabText)is a substring search that could falsely match sibling extensions (e.g."BIM"matching"BIM Tools"). An exact Id comparison would be safer. -
renamedTabTitlenot propagated throughHandlePanel'sRecursivelyBuildUIcall (Medium) —HandlePaneldual-marks the panel itself correctly, but its child items (buttons in stacks, split buttons, etc.) are recursed withoutrenamedTabTitle, so those button registry keys remain un-dual-marked and will be orphaned during cleanup.
See inline comments for details and suggested fixes.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/UIManagerService.cs
Outdated
Show resolved
Hide resolved
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/Builders/TabBuilder.cs
Outdated
Show resolved
Hide resolved
649a5cc to
55cb428
Compare
|
@devloai , I pushed new commits with updated files, review once more and let me know |
|
Hey @tay0thman! I reviewed the new commits, but unfortunately the picture hasn't changed. 👻 The latest push again only modifies trailing newlines (and updates pre-compiled The three issues from my last review remain open:
// In HandleTab():
_ribbonScanner?.MarkElementTouched("tab", tabText);
// ADD: also mark the original name so cleanup doesn't orphan it
if (tabText != component.DisplayName)
_ribbonScanner?.MarkElementTouched("tab", component.DisplayName);The core bug from #3167 is still not fixed in the source. Could you push the actual source changes? Happy to re-review immediately once they're up. 🙂 |
|
@devloai , I added the latest edits, please review the 2 new commits |
There was a problem hiding this comment.
Hey @tay0thman! Third time's the charm — the 2 new commits contain real functional changes this time. 🎉
Summary
The core fixes from #3167 are now actually in the code. Here's what I verified:
✅ Fixes confirmed correct
TabBuilder.cs — Circular Tag dependency resolved:
The old approach called TagTabAsPyRevit (which needs the Tag to be set) before the Tag was set. The new code combines tagging + re-enabling into a single CreateTab block: sets Tag = PyRevitTabIdentifier first, then IsVisible = true. The Tag-based fallback in TagTabAsPyRevit still works correctly for standalone calls.
UIManagerService.cs — Dual-mark fix is present:
HandleTab() now correctly identifies renamed tabs and calls MarkElementTouched for both the original extension name and the renamed Title. This is the core fix — cleanup's untouchedElements check will no longer see the renamed Title as orphaned.
Panel propagation: renamedTabTitle is correctly threaded through RecursivelyBuildUI → HandlePanel → panel dual-mark. Well done.
One minor nit
I left one low-severity comment about a redundant ribbon scan (the tab is found once inside CreateTab and again inside HandleTab). Not a correctness issue, just a DRY opportunity.
The fix logic is sound. Ready for maintainer review.
dev/pyRevitLoader/pyRevitAssemblyBuilder/UIManager/UIManagerService.cs
Outdated
Show resolved
Hide resolved
22b2a6a to
ef9e4b6
Compare
ef9e4b6 to
c65de4f
Compare
…yrevitlabs#3167) Root cause: RibbonScanner registers tabs by their mutable Title property. When a script (e.g. translation) renames tab.Title, the scanner registry key no longer matches what the UI builder marks as touched. Cleanup then deactivates the tab as orphaned. Fix: - ITabBuilder: CreateTab returns string? (renamed title if detected) - TabBuilder: single ribbon scan — find, tag, enable, detect rename. Uses exact Id match via string.Equals (not substring). - UIManagerService: HandleTab uses CreateTab return value (no redundant scan). Dual-marks both original and renamed Title in scanner registry. renamedTabTitle propagated through HandlePanel child recursion.
|
@devloai I had to resolve some conflicts, can you confirm the commited files resolve the issue? |
|
Hey @tay0thman! ✅ Yes — the conflict resolution commit contains the real functional fixes. Everything looks correct. End-to-end verification:
The root cause of #3167 is addressed: when a script renames a tab (e.g. The previously flagged issues (circular tag dependency, redundant ribbon scan, substring Id match) are all resolved in this commit. 🎉 |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26080+1314-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2031-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2042-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2045-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2048-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2106-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26083+2130-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26086+2004-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26088+1318-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26089+1231-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+0549-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1533-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1536-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1540-wip |
|
📦 New work-in-progress (wip) builds are available for 6.1.0.26090+1556-wip |
|
📦 New public release are available for 6.2.0.26090+1754 |
Summary
Fixes #3167 — pyRevit toolbar disappears after reload when a startup script
renames tab titles via
AdWindows.ComponentManager.Ribbon.Tabs(e.g. fortranslation). Exclusive to the new C# loader.
Root Cause
RibbonScannercreates a fresh instance on every reload and registers tabsby their current
Titleproperty. When a script renames a tab (e.g. from"MyExtension" to "Mi Extensión"), the scanner registers
"tab:Mi Extensión".But
UIManagerService.HandleTab()marks"tab:MyExtension"as touched. Thecleanup phase sees the translated key as untouched and hides the tab.
The legacy Python loader is immune because it tracks tabs by object reference
via a module-level singleton, not by mutable display name.
Fix
TabBuilder.cs —
CreateTab()andTagTabAsPyRevit()now fall back tosearching by
Tag == PyRevitTabIdentifier+Idcontaining the originalname when the
Titlematch fails.UIManagerService.cs —
HandleTab()detects when a tab's current Titlediffers from the extension name and dual-marks both names in the scanner
registry. The renamed title propagates to
HandlePanel()so panel keysare also dual-marked. Uses fully qualified
Autodesk.Windows.ComponentManagerto avoid CS0104 ambiguity with
Autodesk.Revit.UI.RibbonPanel.Testing