From a8461cf10fae793af5f99188752be292e2317140 Mon Sep 17 00:00:00 2001 From: SteVen Batten Date: Wed, 16 Oct 2019 15:13:59 -0700 Subject: [PATCH] strictPropertyInit refs #78168 --- src/vs/base/browser/ui/menu/menu.ts | 102 ++++++++++++------ src/vs/base/browser/ui/menu/menubar.ts | 22 ++-- .../browser/parts/titlebar/menubarControl.ts | 52 ++++++--- .../browser/parts/titlebar/titlebarPart.ts | 48 +++++---- 4 files changed, 144 insertions(+), 80 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 346605c3c72..83483ff912a 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -113,7 +113,7 @@ export class Menu extends ActionBar { const actions = this.mnemonics.get(key)!; if (actions.length === 1) { - if (actions[0] instanceof SubmenuMenuActionViewItem) { + if (actions[0] instanceof SubmenuMenuActionViewItem && actions[0].container) { this.focusItemByElement(actions[0].container); } @@ -122,7 +122,7 @@ export class Menu extends ActionBar { if (actions.length > 1) { const action = actions.shift(); - if (action) { + if (action && action.container) { this.focusItemByElement(action.container); actions.push(action); } @@ -356,17 +356,17 @@ interface IMenuItemOptions extends IActionViewItemOptions { class BaseMenuActionViewItem extends BaseActionViewItem { - public container: HTMLElement; + public container: HTMLElement | undefined; protected options: IMenuItemOptions; - protected item: HTMLElement; + protected item: HTMLElement | undefined; private runOnceToEnableMouseUp: RunOnceScheduler; - private label: HTMLElement; - private check: HTMLElement; - private mnemonic: string; + private label: HTMLElement | undefined; + private check: HTMLElement | undefined; + private mnemonic: string | undefined; private cssClass: string; - protected menuStyle: IMenuStyles; + protected menuStyle: IMenuStyles | undefined; constructor(ctx: any, action: IAction, options: IMenuItemOptions = {}) { options.isMenu = true; @@ -449,13 +449,19 @@ class BaseMenuActionViewItem extends BaseActionViewItem { focus(): void { super.focus(); - this.item.focus(); + + if (this.item) { + this.item.focus(); + } + this.applyStyle(); } updatePositionInSet(pos: number, setSize: number): void { - this.item.setAttribute('aria-posinset', `${pos}`); - this.item.setAttribute('aria-setsize', `${setSize}`); + if (this.item) { + this.item.setAttribute('aria-posinset', `${pos}`); + this.item.setAttribute('aria-setsize', `${setSize}`); + } } updateLabel(): void { @@ -467,7 +473,9 @@ class BaseMenuActionViewItem extends BaseActionViewItem { label = cleanLabel; } - this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&')); + if (this.label) { + this.label.setAttribute('aria-label', cleanLabel.replace(/&&/g, '&')); + } const matches = MENU_MNEMONIC_REGEX.exec(label); @@ -488,13 +496,17 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } label = label.replace(/&&/g, '&'); - this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); + if (this.item) { + this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); + } } else { label = label.replace(/&&/g, '&'); } } - this.label.innerHTML = label.trim(); + if (this.label) { + this.label.innerHTML = label.trim(); + } } } @@ -512,23 +524,23 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - if (title) { + if (title && this.item) { this.item.title = title; } } updateClass(): void { - if (this.cssClass) { + if (this.cssClass && this.item) { removeClasses(this.item, this.cssClass); } - if (this.options.icon) { + if (this.options.icon && this.label) { this.cssClass = this.getAction().class || ''; addClass(this.label, 'icon'); if (this.cssClass) { addClasses(this.label, this.cssClass); } this.updateEnabled(); - } else { + } else if (this.label) { removeClass(this.label, 'icon'); } } @@ -539,19 +551,27 @@ class BaseMenuActionViewItem extends BaseActionViewItem { removeClass(this.element, 'disabled'); } - removeClass(this.item, 'disabled'); - this.item.tabIndex = 0; + if (this.item) { + removeClass(this.item, 'disabled'); + this.item.tabIndex = 0; + } } else { if (this.element) { addClass(this.element, 'disabled'); } - addClass(this.item, 'disabled'); - removeTabIndexAndUpdateFocus(this.item); + if (this.item) { + addClass(this.item, 'disabled'); + removeTabIndexAndUpdateFocus(this.item); + } } } updateChecked(): void { + if (!this.item) { + return; + } + if (this.getAction().checked) { addClass(this.item, 'checked'); this.item.setAttribute('role', 'menuitemcheckbox'); @@ -563,7 +583,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } } - getMnemonic(): string { + getMnemonic(): string | undefined { return this.mnemonic; } @@ -577,10 +597,18 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; - this.item.style.color = fgColor ? `${fgColor}` : null; - this.check.style.backgroundColor = fgColor ? `${fgColor}` : ''; - this.item.style.backgroundColor = bgColor ? `${bgColor}` : ''; - this.container.style.border = border; + if (this.item) { + this.item.style.color = fgColor ? `${fgColor}` : null; + this.item.style.backgroundColor = bgColor ? `${bgColor}` : ''; + } + + if (this.check) { + this.check.style.backgroundColor = fgColor ? `${fgColor}` : ''; + } + + if (this.container) { + this.container.style.border = border; + } } style(style: IMenuStyles): void { @@ -590,11 +618,11 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { - private mysubmenu: Menu | null; + private mysubmenu: Menu | null = null; private submenuContainer: HTMLElement | undefined; - private submenuIndicator: HTMLElement; + private submenuIndicator: HTMLElement | undefined; private readonly submenuDisposables = this._register(new DisposableStore()); - private mouseOver: boolean; + private mouseOver: boolean = false; private showScheduler: RunOnceScheduler; private hideScheduler: RunOnceScheduler; private expandDirection: Direction; @@ -631,11 +659,13 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { return; } - addClass(this.item, 'monaco-submenu-item'); - this.item.setAttribute('aria-haspopup', 'true'); + if (this.item) { + addClass(this.item, 'monaco-submenu-item'); + this.item.setAttribute('aria-haspopup', 'true'); - this.submenuIndicator = append(this.item, $('span.submenu-indicator')); - this.submenuIndicator.setAttribute('aria-hidden', 'true'); + this.submenuIndicator = append(this.item, $('span.submenu-indicator')); + this.submenuIndicator.setAttribute('aria-hidden', 'true'); + } this._register(addDisposableListener(this.element, EventType.KEY_UP, e => { let event = new StandardKeyboardEvent(e); @@ -793,7 +823,9 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; - this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : ''; + if (this.submenuIndicator) { + this.submenuIndicator.style.backgroundColor = fgColor ? `${fgColor}` : ''; + } if (this.parentData.submenu) { this.parentData.submenu.style(this.menuStyle); diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 9c688e0f933..4ba4fff01bb 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -56,7 +56,7 @@ export class MenuBar extends Disposable { actions?: ReadonlyArray; }[]; - private overflowMenu: { + private overflowMenu!: { buttonElement: HTMLElement; titleElement: HTMLElement; label: string; @@ -73,22 +73,22 @@ export class MenuBar extends Disposable { private menuUpdater: RunOnceScheduler; // Input-related - private _mnemonicsInUse: boolean; - private openedViaKeyboard: boolean; - private awaitingAltRelease: boolean; - private ignoreNextMouseUp: boolean; + private _mnemonicsInUse: boolean = false; + private openedViaKeyboard: boolean = false; + private awaitingAltRelease: boolean = false; + private ignoreNextMouseUp: boolean = false; private mnemonics: Map; - private updatePending: boolean; + private updatePending: boolean = false; private _focusState: MenubarState; private actionRunner: IActionRunner; private readonly _onVisibilityChange: Emitter; private readonly _onFocusStateChange: Emitter; - private numMenusShown: number; - private menuStyle: IMenuStyles; - private overflowLayoutScheduled: IDisposable | null; + private numMenusShown: number = 0; + private menuStyle: IMenuStyles | undefined; + private overflowLayoutScheduled: IDisposable | null = null; constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) { super(); @@ -930,7 +930,9 @@ export class MenuBar extends Disposable { }; let menuWidget = this._register(new Menu(menuHolder, customMenu.actions, menuOptions)); - menuWidget.style(this.menuStyle); + if (this.menuStyle) { + menuWidget.style(this.menuStyle); + } this._register(menuWidget.onDidCancel(() => { this.focusState = MenubarState.FOCUSED; diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 095b1c53211..22c95eed774 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -77,7 +77,7 @@ export abstract class MenubarControl extends Disposable { 'Help': nls.localize({ key: 'mHelp', comment: ['&& denotes a mnemonic'] }, "&&Help") }; - protected recentlyOpened: IRecentlyOpened; + protected recentlyOpened: IRecentlyOpened = { files: [], workspaces: [] }; protected menuUpdater: RunOnceScheduler; @@ -259,10 +259,10 @@ export abstract class MenubarControl extends Disposable { } export class CustomMenubarControl extends MenubarControl { - private menubar: MenuBar; - private container: HTMLElement; - private alwaysOnMnemonics: boolean; - private focusInsideMenubar: boolean; + private menubar: MenuBar | undefined; + private container: HTMLElement | undefined; + private alwaysOnMnemonics: boolean = false; + private focusInsideMenubar: boolean = false; private readonly _onVisibilityChange: Emitter; private readonly _onFocusStateChange: Emitter; @@ -518,6 +518,11 @@ export class CustomMenubarControl extends MenubarControl { } private setupCustomMenubar(firstTime: boolean): void { + // If there is no container, we cannot setup the menubar + if (!this.container) { + return; + } + if (firstTime) { this.menubar = this._register(new MenuBar( this.container, { @@ -526,12 +531,13 @@ export class CustomMenubarControl extends MenubarControl { visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), compactMode: this.currentCompactMenuMode - } - )); + })); this.accessibilityService.alwaysUnderlineAccessKeys().then(val => { this.alwaysOnMnemonics = val; - this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode }); + if (this.menubar) { + this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode }); + } }); this._register(this.menubar.onFocusStateChange(focused => { @@ -557,7 +563,9 @@ export class CustomMenubarControl extends MenubarControl { this._register(attachMenuStyler(this.menubar, this.themeService)); } else { - this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode }); + if (this.menubar) { + this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, disableAltFocus: this.currentDisableMenuBarAltFocus, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, compactMode: this.currentCompactMenuMode }); + } } // Update the menu actions @@ -577,7 +585,9 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, topLevelTitle); - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[topLevelTitle]) }); + if (this.menubar) { + this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[topLevelTitle]) }); + } } }, this)); } @@ -604,7 +614,9 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, title); - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); + if (this.menubar) { + this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); + } } })); } @@ -614,10 +626,12 @@ export class CustomMenubarControl extends MenubarControl { updateActions(menu, actions, title); } - if (!firstTime) { - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); - } else { - this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); + if (this.menubar) { + if (!firstTime) { + this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); + } else { + this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); + } } } } @@ -628,7 +642,9 @@ export class CustomMenubarControl extends MenubarControl { DOM.removeClass(this.container, 'inactive'); } else { DOM.addClass(this.container, 'inactive'); - this.menubar.blur(); + if (this.menubar) { + this.menubar.blur(); + } } } } @@ -648,7 +664,9 @@ export class CustomMenubarControl extends MenubarControl { } this._register(DOM.addDisposableListener(window, DOM.EventType.RESIZE, () => { - this.menubar.blur(); + if (this.menubar) { + this.menubar.blur(); + } })); // Mnemonics require fullscreen in web diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 55bbb16d9ed..43e037e44f2 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -68,19 +68,19 @@ export class TitlebarPart extends Part implements ITitleService { _serviceBrand: undefined; - private title: HTMLElement; - private dragRegion: HTMLElement; - private windowControls: HTMLElement; - private maxRestoreControl: HTMLElement; - private appIcon: HTMLElement; + private title!: HTMLElement; + private dragRegion: HTMLElement | undefined; + private windowControls: HTMLElement | undefined; + private maxRestoreControl: HTMLElement | undefined; + private appIcon: HTMLElement | undefined; private customMenubar: CustomMenubarControl | undefined; private menubar?: HTMLElement; - private resizer: HTMLElement; - private lastLayoutDimensions: Dimension; + private resizer: HTMLElement | undefined; + private lastLayoutDimensions: Dimension | undefined; - private pendingTitle: string; + private pendingTitle: string | undefined; - private isInactive: boolean; + private isInactive: boolean = false; private readonly properties: ITitleProperties = { isPure: true, isAdmin: false }; private readonly activeEditorListeners = this._register(new DisposableStore()); @@ -158,8 +158,10 @@ export class TitlebarPart extends Part implements ITitleService { // Hide title when toggling menu bar if (!isWeb && this.currentMenubarVisibility === 'toggle' && visible) { // Hack to fix issue #52522 with layered webkit-app-region elements appearing under cursor - hide(this.dragRegion); - setTimeout(() => show(this.dragRegion), 50); + if (this.dragRegion) { + hide(this.dragRegion); + setTimeout(() => show(this.dragRegion!), 50); + } } this.adjustTitleMarginToCenter(); @@ -169,7 +171,7 @@ export class TitlebarPart extends Part implements ITitleService { } private onMenubarFocusChanged(focused: boolean) { - if (!isWeb && (isWindows || isLinux) && this.currentMenubarVisibility === 'compact') { + if (!isWeb && (isWindows || isLinux) && this.currentMenubarVisibility === 'compact' && this.dragRegion) { if (focused) { hide(this.dragRegion); } else { @@ -518,9 +520,9 @@ export class TitlebarPart extends Part implements ITitleService { private onUpdateAppIconDragBehavior() { const setting = this.configurationService.getValue('window.doubleClickIconToClose'); - if (setting) { + if (setting && this.appIcon) { (this.appIcon.style as any)['-webkit-app-region'] = 'no-drag'; - } else { + } else if (this.appIcon) { (this.appIcon.style as any)['-webkit-app-region'] = 'drag'; } } @@ -576,14 +578,24 @@ export class TitlebarPart extends Part implements ITitleService { if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') { this.title.style.zoom = `${1 / getZoomFactor()}`; if (!isWeb && (isWindows || isLinux)) { - this.appIcon.style.zoom = `${1 / getZoomFactor()}`; - this.windowControls.style.zoom = `${1 / getZoomFactor()}`; + if (this.appIcon) { + this.appIcon.style.zoom = `${1 / getZoomFactor()}`; + } + + if (this.windowControls) { + this.windowControls.style.zoom = `${1 / getZoomFactor()}`; + } } } else { this.title.style.zoom = null; if (!isWeb && (isWindows || isLinux)) { - this.appIcon.style.zoom = null; - this.windowControls.style.zoom = null; + if (this.appIcon) { + this.appIcon.style.zoom = null; + } + + if (this.windowControls) { + this.windowControls.style.zoom = null; + } } } -- GitLab