@@ -87,6 +87,7 @@ export class MenuBar extends Disposable {
8787
8888 private numMenusShown : number = 0 ;
8989 private overflowLayoutScheduled : IDisposable | undefined = undefined ;
90+ private isCleaningUp : boolean = false ;
9091
9192 private readonly menuDisposables = this . _register ( new DisposableStore ( ) ) ;
9293
@@ -998,64 +999,89 @@ export class MenuBar extends Disposable {
998999 }
9991000
10001001 private cleanupCustomMenu ( ) : void {
1001- if ( this . focusedMenu ) {
1002- // Remove focus from the menus first
1003- if ( this . focusedMenu . index === MenuBar . OVERFLOW_INDEX ) {
1004- this . overflowMenu . buttonElement . focus ( ) ;
1005- } else {
1006- this . menus [ this . focusedMenu . index ] . buttonElement ?. focus ( ) ;
1007- }
1008-
1009- if ( this . focusedMenu . holder ) {
1010- // Remove 'open' class from button element (not from document.body on Windows)
1011- const actualMenuIndex = this . focusedMenu . index >= this . numMenusShown ? MenuBar . OVERFLOW_INDEX : this . focusedMenu . index ;
1012- const customMenu = actualMenuIndex === MenuBar . OVERFLOW_INDEX ? this . overflowMenu : this . menus [ actualMenuIndex ] ;
1013- customMenu . buttonElement ?. classList . remove ( 'open' ) ;
1002+ // Prevent re-entrancy - if we're already cleaning up, don't do it again
1003+ if ( this . isCleaningUp ) {
1004+ return ;
1005+ }
10141006
1015- this . focusedMenu . holder . remove ( ) ;
1016- }
1007+ this . isCleaningUp = true ;
1008+ try {
1009+ const focusedMenu = this . focusedMenu ;
1010+ if ( focusedMenu ) {
1011+ // Defensive check: ensure holder exists before accessing it
1012+ // This prevents errors when cleanupCustomMenu is called in edge cases
1013+ if ( focusedMenu . holder ) {
1014+ // Remove 'open' class from button element (not from document.body on Windows)
1015+ const actualMenuIndex = focusedMenu . index >= this . numMenusShown ? MenuBar . OVERFLOW_INDEX : focusedMenu . index ;
1016+ const customMenu = actualMenuIndex === MenuBar . OVERFLOW_INDEX ? this . overflowMenu : this . menus [ actualMenuIndex ] ;
1017+ customMenu . buttonElement ?. classList . remove ( 'open' ) ;
1018+
1019+ focusedMenu . holder . remove ( ) ;
1020+ }
10171021
1018- this . focusedMenu . widget ?. dispose ( ) ;
1022+ focusedMenu . widget ?. dispose ( ) ;
10191023
1020- this . focusedMenu = { index : this . focusedMenu . index } ;
1024+ // Only update focusedMenu if it hasn't been cleared by another operation
1025+ if ( this . focusedMenu === focusedMenu ) {
1026+ this . focusedMenu = { index : focusedMenu . index } ;
1027+ }
1028+ }
1029+ this . menuDisposables . clear ( ) ;
1030+ } finally {
1031+ this . isCleaningUp = false ;
10211032 }
1022- this . menuDisposables . clear ( ) ;
10231033 }
10241034
10251035 private showCustomMenu ( menuIndex : number , selectFirst = true ) : void {
10261036 const actualMenuIndex = menuIndex >= this . numMenusShown ? MenuBar . OVERFLOW_INDEX : menuIndex ;
10271037 const customMenu = actualMenuIndex === MenuBar . OVERFLOW_INDEX ? this . overflowMenu : this . menus [ actualMenuIndex ] ;
10281038
1029- if ( ! customMenu . actions || ! customMenu . buttonElement || ! customMenu . titleElement ) {
1039+ if ( ! customMenu . actions || customMenu . actions . length === 0 || ! customMenu . buttonElement || ! customMenu . titleElement ) {
10301040 return ;
10311041 }
10321042
10331043 const menuHolder = $ ( 'div.menubar-menu-items-holder' , { 'title' : '' } ) ;
10341044
10351045 customMenu . buttonElement . classList . add ( 'open' ) ;
10361046
1047+ // Ensure the title element is visible and laid out before getting bounding rect
1048+ // This is critical on Windows where elements might not be laid out immediately
10371049 const titleBoundingRect = customMenu . titleElement . getBoundingClientRect ( ) ;
10381050 const titleBoundingRectZoom = DOM . getDomNodeZoomLevel ( customMenu . titleElement ) ;
10391051
1052+ // Validate bounding rect - if invalid, use button element as fallback
1053+ let validBoundingRect = titleBoundingRect ;
1054+ if ( titleBoundingRect . width === 0 && titleBoundingRect . height === 0 ) {
1055+ validBoundingRect = customMenu . buttonElement . getBoundingClientRect ( ) ;
1056+ }
1057+
10401058 if ( this . options . compactMode ?. horizontal === HorizontalDirection . Right ) {
1041- menuHolder . style . left = `${ titleBoundingRect . left + this . container . clientWidth } px` ;
1059+ menuHolder . style . left = `${ validBoundingRect . left + this . container . clientWidth } px` ;
10421060 } else if ( this . options . compactMode ?. horizontal === HorizontalDirection . Left ) {
10431061 const windowWidth = DOM . getWindow ( this . container ) . innerWidth ;
1044- menuHolder . style . right = `${ windowWidth - titleBoundingRect . left } px` ;
1062+ menuHolder . style . right = `${ windowWidth - validBoundingRect . left } px` ;
10451063 menuHolder . style . left = 'auto' ;
10461064 } else {
1047- menuHolder . style . left = `${ titleBoundingRect . left * titleBoundingRectZoom } px` ;
1065+ menuHolder . style . left = `${ validBoundingRect . left * titleBoundingRectZoom } px` ;
10481066 }
10491067
10501068 if ( this . options . compactMode ?. vertical === VerticalDirection . Above ) {
10511069 // TODO@benibenj Do not hardcode the height of the menu holder
1052- menuHolder . style . top = `${ titleBoundingRect . top - this . menus . length * 30 + this . container . clientHeight } px` ;
1070+ menuHolder . style . top = `${ validBoundingRect . top - this . menus . length * 30 + this . container . clientHeight } px` ;
10531071 } else if ( this . options . compactMode ?. vertical === VerticalDirection . Below ) {
1054- menuHolder . style . top = `${ titleBoundingRect . top } px` ;
1072+ menuHolder . style . top = `${ validBoundingRect . top } px` ;
10551073 } else {
1056- menuHolder . style . top = `${ titleBoundingRect . bottom * titleBoundingRectZoom } px` ;
1074+ menuHolder . style . top = `${ validBoundingRect . bottom * titleBoundingRectZoom } px` ;
10571075 }
10581076
1077+ // Ensure menu holder is visible with explicit styles
1078+ menuHolder . style . position = 'fixed' ;
1079+ menuHolder . style . zIndex = '10000' ;
1080+ menuHolder . style . visibility = 'visible' ;
1081+ menuHolder . style . display = 'block' ;
1082+ menuHolder . style . opacity = '1' ;
1083+ menuHolder . style . pointerEvents = 'auto' ;
1084+
10591085 // On Windows, append dropdown to document.body to avoid stacking context issues
10601086 // where the dropdown renders behind the workbench content
10611087 if ( isWindows ) {
0 commit comments