@theia/core 1.77.0-next.0 → 1.77.0-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/catalog.json +206 -5
- package/lib/browser/menu/browser-menu-plugin.d.ts +42 -1
- package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
- package/lib/browser/menu/browser-menu-plugin.js +152 -5
- package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
- package/lib/browser/shell/application-shell.js +1 -1
- package/lib/browser/shell/application-shell.js.map +1 -1
- package/lib/common/core-preferences.d.ts +1 -1
- package/lib/common/core-preferences.d.ts.map +1 -1
- package/lib/common/core-preferences.js +2 -1
- package/lib/common/core-preferences.js.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +5 -0
- package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-main-menu-factory.js +39 -8
- package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.d.ts +8 -1
- package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
- package/lib/electron-browser/menu/electron-menu-contribution.js +50 -4
- package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
- package/lib/electron-browser/preload.d.ts.map +1 -1
- package/lib/electron-browser/preload.js +1 -0
- package/lib/electron-browser/preload.js.map +1 -1
- package/lib/electron-common/electron-api.d.ts +2 -0
- package/lib/electron-common/electron-api.d.ts.map +1 -1
- package/lib/electron-common/electron-api.js +2 -1
- package/lib/electron-common/electron-api.js.map +1 -1
- package/lib/electron-main/electron-api-main.d.ts.map +1 -1
- package/lib/electron-main/electron-api-main.js +15 -0
- package/lib/electron-main/electron-api-main.js.map +1 -1
- package/package.json +4 -4
- package/src/browser/menu/browser-menu-plugin.ts +169 -6
- package/src/browser/shell/application-shell.ts +1 -1
- package/src/common/core-preferences.ts +3 -2
- package/src/electron-browser/menu/electron-main-menu-factory.ts +45 -7
- package/src/electron-browser/menu/electron-menu-contribution.ts +50 -5
- package/src/electron-browser/preload.ts +3 -1
- package/src/electron-common/electron-api.ts +2 -0
- package/src/electron-main/electron-api-main.ts +16 -1
|
@@ -18,7 +18,7 @@ import { injectable, inject } from 'inversify';
|
|
|
18
18
|
import { Menu, MenuBar, Menu as MenuWidget, Widget } from '@lumino/widgets';
|
|
19
19
|
import { CommandRegistry as LuminoCommandRegistry } from '@lumino/commands';
|
|
20
20
|
import {
|
|
21
|
-
environment, DisposableCollection,
|
|
21
|
+
environment, Disposable, DisposableCollection,
|
|
22
22
|
AcceleratorSource,
|
|
23
23
|
ArrayUtils,
|
|
24
24
|
PreferenceService
|
|
@@ -92,7 +92,7 @@ export class BrowserMainMenuFactory implements MenuWidgetFactory {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
protected showMenuBar(menuBar: DynamicMenuBarWidget, preference = this.getMenuBarVisibility()): void {
|
|
95
|
-
if (preference && ['classic', 'visible'].includes(preference)) {
|
|
95
|
+
if (preference && ['classic', 'visible', 'toggle'].includes(preference)) {
|
|
96
96
|
menuBar.clearMenus();
|
|
97
97
|
this.fillMenuBar(menuBar);
|
|
98
98
|
} else {
|
|
@@ -432,6 +432,11 @@ export class BrowserMenuBarContribution implements FrontendApplicationContributi
|
|
|
432
432
|
@inject(PreferenceService)
|
|
433
433
|
protected readonly preferenceService: PreferenceService;
|
|
434
434
|
|
|
435
|
+
protected toggleModeListeners = new DisposableCollection();
|
|
436
|
+
|
|
437
|
+
protected menuWidget?: MenuBarWidget;
|
|
438
|
+
protected logoWidget?: Widget;
|
|
439
|
+
|
|
435
440
|
constructor(
|
|
436
441
|
@inject(BrowserMainMenuFactory) protected readonly factory: BrowserMainMenuFactory
|
|
437
442
|
) { }
|
|
@@ -449,20 +454,178 @@ export class BrowserMenuBarContribution implements FrontendApplicationContributi
|
|
|
449
454
|
shell.addWidget(logo, { area: 'top' });
|
|
450
455
|
const menu = this.factory.createMenuBar();
|
|
451
456
|
shell.addWidget(menu, { area: 'top' });
|
|
452
|
-
|
|
453
|
-
|
|
457
|
+
this.menuWidget = menu;
|
|
458
|
+
this.logoWidget = logo;
|
|
454
459
|
if (environment.electron.is()) {
|
|
455
460
|
this.preferenceService.ready.then(() => {
|
|
456
|
-
|
|
461
|
+
this.updateElectronMenuToggleMode(menu, logo);
|
|
462
|
+
});
|
|
463
|
+
this.preferenceService.onPreferenceChanged(change => {
|
|
464
|
+
if (change.preferenceName === 'window.menuBarVisibility') {
|
|
465
|
+
this.updateElectronMenuToggleMode(menu, logo);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
} else {
|
|
469
|
+
// In the browser, the whole top panel is hidden for compact/hidden/toggle
|
|
470
|
+
// (handled by ApplicationShell). For toggle mode, this installs an Alt-key
|
|
471
|
+
// listener that temporarily shows the top panel.
|
|
472
|
+
this.preferenceService.ready.then(() => {
|
|
473
|
+
this.updateBrowserMenuToggleMode(menu);
|
|
457
474
|
});
|
|
458
475
|
this.preferenceService.onPreferenceChanged(change => {
|
|
459
476
|
if (change.preferenceName === 'window.menuBarVisibility') {
|
|
460
|
-
|
|
477
|
+
this.updateBrowserMenuToggleMode(menu);
|
|
461
478
|
}
|
|
462
479
|
});
|
|
463
480
|
}
|
|
464
481
|
}
|
|
465
482
|
|
|
483
|
+
/**
|
|
484
|
+
* Manages Lumino menu bar visibility and Alt-key toggling in Electron.
|
|
485
|
+
*
|
|
486
|
+
* With native title bar, the native Electron menu bar handles toggle via
|
|
487
|
+
* `autoHideMenuBar`, so the Lumino menu bar is simply hidden.
|
|
488
|
+
*
|
|
489
|
+
* With custom title bar, the Lumino menu bar is the only menu bar visible
|
|
490
|
+
* to the user. For 'toggle' mode, an Alt-key listener is installed to
|
|
491
|
+
* show/hide the menu bar widget.
|
|
492
|
+
*/
|
|
493
|
+
protected updateElectronMenuToggleMode(menu: MenuBarWidget, logo: Widget): void {
|
|
494
|
+
this.toggleModeListeners.dispose();
|
|
495
|
+
const pref = this.preferenceService.get<string>('window.menuBarVisibility', 'classic');
|
|
496
|
+
this.applyElectronMenuBarVisibility(menu, logo, pref);
|
|
497
|
+
if (pref === 'toggle') {
|
|
498
|
+
this.toggleModeListeners = this.installAltKeyToggle(menu, () => this.getElectronToggleTargets(menu, logo));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Establishes the resting visibility of the menu bar widgets in Electron, i.e. the state
|
|
504
|
+
* a clean Alt press toggles away from in 'toggle' mode.
|
|
505
|
+
*/
|
|
506
|
+
protected applyElectronMenuBarVisibility(menu: MenuBarWidget, logo: Widget, pref: string): void {
|
|
507
|
+
const shouldHide = ['compact', 'hidden', 'toggle'].includes(pref);
|
|
508
|
+
menu.setHidden(shouldHide);
|
|
509
|
+
logo.setHidden(shouldHide);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* The widgets a clean Alt press shows and hides in 'toggle' mode in Electron.
|
|
514
|
+
*/
|
|
515
|
+
protected getElectronToggleTargets(menu: MenuBarWidget, logo: Widget): Widget[] {
|
|
516
|
+
return [menu, logo];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Manages Alt-key toggle behavior for 'toggle' mode in the browser.
|
|
521
|
+
* The top panel's initial visibility is handled by {@link ApplicationShell.setTopPanelVisibility};
|
|
522
|
+
* this method only installs or removes the Alt-key listener.
|
|
523
|
+
*/
|
|
524
|
+
protected updateBrowserMenuToggleMode(menu: MenuBarWidget): void {
|
|
525
|
+
this.toggleModeListeners.dispose();
|
|
526
|
+
const pref = this.preferenceService.get<string>('window.menuBarVisibility', 'classic');
|
|
527
|
+
if (pref === 'toggle') {
|
|
528
|
+
this.toggleModeListeners = this.installAltKeyToggle(menu, () => [this.shell.topPanel]);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Installs Alt-key listeners to toggle menu bar visibility.
|
|
534
|
+
* A clean Alt press (press and release without other keys) shows the
|
|
535
|
+
* given toggle targets and focuses the first menu. Pressing Alt again
|
|
536
|
+
* or clicking outside hides them.
|
|
537
|
+
*
|
|
538
|
+
* @param menu the menu bar widget to focus when shown
|
|
539
|
+
* @param toggleTargets supplies the widgets to show/hide on Alt toggle. It is consulted on
|
|
540
|
+
* each gesture, because the targets can depend on state that changes while the listener is
|
|
541
|
+
* installed, e.g. whether the window is in full screen.
|
|
542
|
+
*/
|
|
543
|
+
protected installAltKeyToggle(menu: MenuBarWidget, toggleTargets: () => Widget[]): DisposableCollection {
|
|
544
|
+
const disposables = new DisposableCollection();
|
|
545
|
+
let altKeyPressed = false;
|
|
546
|
+
let lastKeyWasAlt = false;
|
|
547
|
+
|
|
548
|
+
const isHidden = (): boolean => toggleTargets().some(w => w.isHidden);
|
|
549
|
+
|
|
550
|
+
const hide = (): void => {
|
|
551
|
+
for (const w of toggleTargets()) {
|
|
552
|
+
w.setHidden(true);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const show = (): void => {
|
|
557
|
+
for (const w of toggleTargets()) {
|
|
558
|
+
w.setHidden(false);
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
const onKeyDown = (e: KeyboardEvent): void => {
|
|
563
|
+
if (e.key === 'Alt') {
|
|
564
|
+
if (!e.repeat) {
|
|
565
|
+
altKeyPressed = true;
|
|
566
|
+
lastKeyWasAlt = true;
|
|
567
|
+
}
|
|
568
|
+
} else {
|
|
569
|
+
// Any other key invalidates the Alt-only gesture
|
|
570
|
+
lastKeyWasAlt = false;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// Mouse interaction while Alt is held is a modifier gesture of its own, e.g. Alt+click to add a
|
|
575
|
+
// cursor or Alt+scroll to scroll faster, so it invalidates the Alt-only gesture as any other key does.
|
|
576
|
+
const onPointerInteraction = (): void => {
|
|
577
|
+
lastKeyWasAlt = false;
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const onKeyUp = (e: KeyboardEvent): void => {
|
|
581
|
+
if (e.key !== 'Alt') {
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const wasCleanAltPress = altKeyPressed && lastKeyWasAlt;
|
|
585
|
+
altKeyPressed = false;
|
|
586
|
+
lastKeyWasAlt = false;
|
|
587
|
+
if (!wasCleanAltPress) {
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
// Keep the browser from acting on the solitary Alt press itself: Firefox, for
|
|
591
|
+
// instance, focuses its own menu bar, which would then toggle along with ours.
|
|
592
|
+
e.preventDefault();
|
|
593
|
+
if (isHidden()) {
|
|
594
|
+
show();
|
|
595
|
+
menu.activeIndex = 0;
|
|
596
|
+
menu.node.focus();
|
|
597
|
+
} else {
|
|
598
|
+
hide();
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// When a menu closes and focus leaves the menu bar, hide it again
|
|
603
|
+
const onFocusOut = (): void => {
|
|
604
|
+
// Use setTimeout to allow focus to settle (e.g. focus moving between menu items)
|
|
605
|
+
setTimeout(() => {
|
|
606
|
+
if (!isHidden() && !menu.node.contains(document.activeElement)) {
|
|
607
|
+
hide();
|
|
608
|
+
}
|
|
609
|
+
}, 100);
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
document.addEventListener('keydown', onKeyDown, true);
|
|
613
|
+
document.addEventListener('keyup', onKeyUp, true);
|
|
614
|
+
document.addEventListener('mousedown', onPointerInteraction, true);
|
|
615
|
+
document.addEventListener('wheel', onPointerInteraction, { capture: true, passive: true });
|
|
616
|
+
menu.node.addEventListener('focusout', onFocusOut);
|
|
617
|
+
|
|
618
|
+
disposables.push(Disposable.create(() => {
|
|
619
|
+
document.removeEventListener('keydown', onKeyDown, true);
|
|
620
|
+
document.removeEventListener('keyup', onKeyUp, true);
|
|
621
|
+
document.removeEventListener('mousedown', onPointerInteraction, true);
|
|
622
|
+
document.removeEventListener('wheel', onPointerInteraction, true);
|
|
623
|
+
menu.node.removeEventListener('focusout', onFocusOut);
|
|
624
|
+
}));
|
|
625
|
+
|
|
626
|
+
return disposables;
|
|
627
|
+
}
|
|
628
|
+
|
|
466
629
|
protected createLogo(): Widget {
|
|
467
630
|
const logo = new Widget();
|
|
468
631
|
logo.id = 'theia:icon';
|
|
@@ -422,7 +422,7 @@ export class ApplicationShell extends Widget {
|
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
protected setTopPanelVisibility(preference: string): void {
|
|
425
|
-
const hiddenPreferences = ['compact', 'hidden'];
|
|
425
|
+
const hiddenPreferences = ['compact', 'hidden', 'toggle'];
|
|
426
426
|
this.topPanel.setHidden(hiddenPreferences.includes(preference));
|
|
427
427
|
}
|
|
428
428
|
|
|
@@ -92,10 +92,11 @@ export const corePreferenceSchema: PreferenceSchema = {
|
|
|
92
92
|
},
|
|
93
93
|
'window.menuBarVisibility': {
|
|
94
94
|
type: 'string',
|
|
95
|
-
enum: ['classic', 'visible', 'hidden', 'compact'],
|
|
95
|
+
enum: ['classic', 'visible', 'toggle', 'hidden', 'compact'],
|
|
96
96
|
markdownEnumDescriptions: [
|
|
97
97
|
nls.localizeByDefault('Menu is displayed at the top of the window and only hidden in full screen mode.'),
|
|
98
98
|
nls.localizeByDefault('Menu is always visible at the top of the window even in full screen mode.'),
|
|
99
|
+
nls.localizeByDefault('Menu is hidden but can be displayed at the top of the window via the Alt key.'),
|
|
99
100
|
nls.localizeByDefault('Menu is always hidden.'),
|
|
100
101
|
environment.electron.is()
|
|
101
102
|
// we do not support the window.menuStyle setting yet, so we do not use the default string in this case yet
|
|
@@ -314,7 +315,7 @@ export interface CoreConfiguration {
|
|
|
314
315
|
'files.encoding': string;
|
|
315
316
|
'keyboard.dispatch': 'code' | 'keyCode';
|
|
316
317
|
'window.tabbar.enhancedPreview': 'classic' | 'enhanced' | 'visual';
|
|
317
|
-
'window.menuBarVisibility': 'classic' | 'visible' | 'hidden' | 'compact';
|
|
318
|
+
'window.menuBarVisibility': 'classic' | 'visible' | 'toggle' | 'hidden' | 'compact';
|
|
318
319
|
'window.title': string;
|
|
319
320
|
'window.titleSeparator': string;
|
|
320
321
|
'window.tabCloseIconPlacement': 'end' | 'start';
|
|
@@ -91,6 +91,12 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
|
|
|
91
91
|
@inject(PreferenceService)
|
|
92
92
|
protected preferencesService: PreferenceService;
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* The title bar style ('native' or 'custom'). Set by {@link ElectronMenuContribution}
|
|
96
|
+
* after querying the main process for the active style.
|
|
97
|
+
*/
|
|
98
|
+
titleBarStyle?: string;
|
|
99
|
+
|
|
94
100
|
setMenuBar = debounce(() => this.doSetMenuBar(), 100);
|
|
95
101
|
|
|
96
102
|
@postConstruct()
|
|
@@ -140,13 +146,45 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
|
|
|
140
146
|
|
|
141
147
|
doSetMenuBar(): void {
|
|
142
148
|
const preference = this.preferencesService.get<string>('window.menuBarVisibility') || 'classic';
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
149
|
+
const isFullScreen = window.electronTheiaCore.isFullScreen();
|
|
150
|
+
const isCustomTitleBar = this.titleBarStyle === 'custom';
|
|
151
|
+
|
|
152
|
+
switch (preference) {
|
|
153
|
+
case 'visible':
|
|
154
|
+
this.menu = this.createElectronMenuBar();
|
|
155
|
+
window.electronTheiaCore.setMenu(this.menu);
|
|
156
|
+
window.electronTheiaCore.setMenuBarVisible(true);
|
|
157
|
+
window.electronTheiaCore.setAutoHideMenuBar(false);
|
|
158
|
+
break;
|
|
159
|
+
case 'toggle':
|
|
160
|
+
if (isCustomTitleBar) {
|
|
161
|
+
// With custom title bar, the Lumino menu bar handles toggle via Alt-key listener.
|
|
162
|
+
// Do not set autoHideMenuBar on the native window to prevent it from appearing.
|
|
163
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
164
|
+
window.electronTheiaCore.setAutoHideMenuBar(false);
|
|
165
|
+
} else {
|
|
166
|
+
// With native title bar, Electron's autoHideMenuBar handles Alt toggling.
|
|
167
|
+
this.menu = this.createElectronMenuBar();
|
|
168
|
+
window.electronTheiaCore.setMenu(this.menu);
|
|
169
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
170
|
+
window.electronTheiaCore.setAutoHideMenuBar(true);
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
case 'hidden':
|
|
174
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
175
|
+
window.electronTheiaCore.setAutoHideMenuBar(false);
|
|
176
|
+
break;
|
|
177
|
+
case 'compact':
|
|
178
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
179
|
+
window.electronTheiaCore.setAutoHideMenuBar(false);
|
|
180
|
+
break;
|
|
181
|
+
case 'classic':
|
|
182
|
+
default:
|
|
183
|
+
this.menu = this.createElectronMenuBar();
|
|
184
|
+
window.electronTheiaCore.setMenu(this.menu);
|
|
185
|
+
window.electronTheiaCore.setMenuBarVisible(!isFullScreen);
|
|
186
|
+
window.electronTheiaCore.setAutoHideMenuBar(isFullScreen);
|
|
187
|
+
break;
|
|
150
188
|
}
|
|
151
189
|
}
|
|
152
190
|
|
|
@@ -27,7 +27,7 @@ import { ElectronMainMenuFactory } from './electron-main-menu-factory';
|
|
|
27
27
|
import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state';
|
|
28
28
|
import { FrontendApplicationConfigProvider } from '../../browser/frontend-application-config-provider';
|
|
29
29
|
import { PREF_WINDOW_ZOOM_LEVEL, ZoomLevel } from '../../electron-common/electron-window-preferences';
|
|
30
|
-
import { BrowserMenuBarContribution } from '../../browser/menu/browser-menu-plugin';
|
|
30
|
+
import { BrowserMenuBarContribution, MenuBarWidget } from '../../browser/menu/browser-menu-plugin';
|
|
31
31
|
import { WindowService } from '../../browser/window/window-service';
|
|
32
32
|
import { WindowTitleService } from '../../browser/window/window-title-service';
|
|
33
33
|
|
|
@@ -152,8 +152,9 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
|
|
|
152
152
|
|
|
153
153
|
handleTitleBarStyling(app: FrontendApplication): void {
|
|
154
154
|
this.hideTopPanel(app);
|
|
155
|
-
window.electronTheiaCore.getTitleBarStyleAtStartup().then(style => {
|
|
155
|
+
const titleBarStyleAtStartup = window.electronTheiaCore.getTitleBarStyleAtStartup().then(style => {
|
|
156
156
|
this.titleBarStyle = style;
|
|
157
|
+
this.factory.titleBarStyle = style;
|
|
157
158
|
this.setMenu(app);
|
|
158
159
|
this.preferenceService.ready.then(() => {
|
|
159
160
|
const current = this.preferenceService.inspect('window.titleBarStyle');
|
|
@@ -171,8 +172,17 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
|
|
|
171
172
|
});
|
|
172
173
|
});
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
// `titleBarStyle` decides whether the native menu bar is used at all, so wait for it:
|
|
176
|
+
// preferences can become ready first, in which case it would still be `undefined` here.
|
|
177
|
+
Promise.all([titleBarStyleAtStartup, this.preferenceService.ready]).then(() => {
|
|
178
|
+
const pref = this.preferenceService.get<string>('window.menuBarVisibility', 'classic');
|
|
179
|
+
if (pref === 'toggle' && this.titleBarStyle !== 'custom') {
|
|
180
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
181
|
+
window.electronTheiaCore.setAutoHideMenuBar(true);
|
|
182
|
+
} else {
|
|
183
|
+
window.electronTheiaCore.setMenuBarVisible(['classic', 'visible'].includes(pref));
|
|
184
|
+
window.electronTheiaCore.setAutoHideMenuBar(false);
|
|
185
|
+
}
|
|
176
186
|
});
|
|
177
187
|
|
|
178
188
|
this.preferenceService.onPreferenceChanged(change => {
|
|
@@ -424,7 +434,19 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
|
|
|
424
434
|
protected handleFullScreen(menuBarVisibility: string): void {
|
|
425
435
|
const shouldShowTop = !window.electronTheiaCore.isFullScreen() || menuBarVisibility === 'visible';
|
|
426
436
|
if (this.titleBarStyle === 'native') {
|
|
427
|
-
|
|
437
|
+
if (menuBarVisibility === 'toggle') {
|
|
438
|
+
window.electronTheiaCore.setMenuBarVisible(false);
|
|
439
|
+
window.electronTheiaCore.setAutoHideMenuBar(true);
|
|
440
|
+
} else {
|
|
441
|
+
window.electronTheiaCore.setMenuBarVisible(shouldShowTop);
|
|
442
|
+
// Match `ElectronMainMenuFactory.doSetMenuBar`: in 'classic' the menu is hidden in
|
|
443
|
+
// full screen but Alt still reveals it. Without this the two disagree and the
|
|
444
|
+
// effective behaviour depends on which of them ran last.
|
|
445
|
+
window.electronTheiaCore.setAutoHideMenuBar(!shouldShowTop && menuBarVisibility === 'classic');
|
|
446
|
+
}
|
|
447
|
+
} else if (menuBarVisibility === 'toggle' && this.menuWidget && this.logoWidget) {
|
|
448
|
+
// Which widget Alt reveals depends on the full screen state, so re-establish it.
|
|
449
|
+
this.updateElectronMenuToggleMode(this.menuWidget, this.logoWidget);
|
|
428
450
|
} else if (shouldShowTop) {
|
|
429
451
|
this.shell.topPanel.show();
|
|
430
452
|
} else {
|
|
@@ -432,6 +454,29 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
|
|
|
432
454
|
}
|
|
433
455
|
}
|
|
434
456
|
|
|
457
|
+
/**
|
|
458
|
+
* Outside of full screen the top panel also carries the window controls, so it has to stay
|
|
459
|
+
* visible and Alt toggles the menu widgets inside it. In full screen the panel is hidden
|
|
460
|
+
* altogether, so Alt toggles the panel itself and the widgets inside it stay visible.
|
|
461
|
+
*/
|
|
462
|
+
protected override applyElectronMenuBarVisibility(menu: MenuBarWidget, logo: Widget, pref: string): void {
|
|
463
|
+
if (pref === 'toggle' && this.titleBarStyle === 'custom') {
|
|
464
|
+
const isFullScreen = window.electronTheiaCore.isFullScreen();
|
|
465
|
+
menu.setHidden(!isFullScreen);
|
|
466
|
+
logo.setHidden(!isFullScreen);
|
|
467
|
+
this.shell.topPanel.setHidden(isFullScreen);
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
super.applyElectronMenuBarVisibility(menu, logo, pref);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
protected override getElectronToggleTargets(menu: MenuBarWidget, logo: Widget): Widget[] {
|
|
474
|
+
if (this.titleBarStyle === 'custom' && window.electronTheiaCore.isFullScreen()) {
|
|
475
|
+
return [this.shell.topPanel];
|
|
476
|
+
}
|
|
477
|
+
return super.getElectronToggleTargets(menu, logo);
|
|
478
|
+
}
|
|
479
|
+
|
|
435
480
|
protected handleThemeChange(e: ThemeChangeEvent): void {
|
|
436
481
|
const backgroundColor = window.getComputedStyle(document.body).backgroundColor;
|
|
437
482
|
window.electronTheiaCore.setBackgroundColor(backgroundColor);
|
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
CHANNEL_KEYBOARD_LAYOUT_CHANGED, CHANNEL_IPC_CONNECTION, InternalMenuDto, CHANNEL_REQUEST_SECONDARY_CLOSE, CHANNEL_SET_BACKGROUND_COLOR,
|
|
29
29
|
CHANNEL_WC_METADATA, CHANNEL_ABOUT_TO_CLOSE, CHANNEL_OPEN_WITH_SYSTEM_APP,
|
|
30
30
|
CHANNEL_OPEN_URL, CHANNEL_SET_THEME, CHANNEL_OPEN_DEVTOOLS_FOR_WINDOW,
|
|
31
|
-
CHANNEL_UPDATE_RECENT_WORKSPACES
|
|
31
|
+
CHANNEL_UPDATE_RECENT_WORKSPACES,
|
|
32
|
+
CHANNEL_SET_AUTO_HIDE_MENU_BAR
|
|
32
33
|
} from '../electron-common/electron-api';
|
|
33
34
|
|
|
34
35
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
@@ -80,6 +81,7 @@ function convertMenu(menu: MenuDto[] | undefined, handlerMap: Map<number, () =>
|
|
|
80
81
|
const api: TheiaCoreAPI = {
|
|
81
82
|
WindowMetadata: { webcontentId: 'none' },
|
|
82
83
|
setMenuBarVisible: (visible: boolean, windowName?: string) => ipcRenderer.send(CHANNEL_SET_MENU_BAR_VISIBLE, visible, windowName),
|
|
84
|
+
setAutoHideMenuBar: (enabled: boolean, windowName?: string) => ipcRenderer.send(CHANNEL_SET_AUTO_HIDE_MENU_BAR, enabled, windowName),
|
|
83
85
|
setMenu: (menu: MenuDto[] | undefined) => {
|
|
84
86
|
commandHandlers.delete(mainMenuId);
|
|
85
87
|
const handlers = new Map<number, () => void>();
|
|
@@ -59,6 +59,7 @@ export interface TheiaCoreAPI {
|
|
|
59
59
|
attachSecurityToken: (endpoint: string) => Promise<void>;
|
|
60
60
|
|
|
61
61
|
setMenuBarVisible(visible: boolean, windowName?: string): void;
|
|
62
|
+
setAutoHideMenuBar(enabled: boolean, windowName?: string): void;
|
|
62
63
|
setMenu(menu: MenuDto[] | undefined): void;
|
|
63
64
|
|
|
64
65
|
popup(menu: MenuDto[], x: number, y: number, onClosed: () => void, windowName?: string): Promise<number>;
|
|
@@ -127,6 +128,7 @@ declare global {
|
|
|
127
128
|
export const CHANNEL_WC_METADATA = 'WebContentMetadata';
|
|
128
129
|
export const CHANNEL_SET_MENU = 'SetMenu';
|
|
129
130
|
export const CHANNEL_SET_MENU_BAR_VISIBLE = 'SetMenuBarVisible';
|
|
131
|
+
export const CHANNEL_SET_AUTO_HIDE_MENU_BAR = 'SetAutoHideMenuBar';
|
|
130
132
|
export const CHANNEL_INVOKE_MENU = 'InvokeMenu';
|
|
131
133
|
export const CHANNEL_OPEN_POPUP = 'OpenPopup';
|
|
132
134
|
export const CHANNEL_ON_CLOSE_POPUP = 'OnClosePopup';
|
|
@@ -59,7 +59,8 @@ import {
|
|
|
59
59
|
CHANNEL_OPEN_URL,
|
|
60
60
|
CHANNEL_SET_THEME,
|
|
61
61
|
CHANNEL_OPEN_DEVTOOLS_FOR_WINDOW,
|
|
62
|
-
CHANNEL_UPDATE_RECENT_WORKSPACES
|
|
62
|
+
CHANNEL_UPDATE_RECENT_WORKSPACES,
|
|
63
|
+
CHANNEL_SET_AUTO_HIDE_MENU_BAR
|
|
63
64
|
} from '../electron-common/electron-api';
|
|
64
65
|
import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application';
|
|
65
66
|
import { Disposable, DisposableCollection, isOSX, isWindows, MaybePromise, URI } from '../common';
|
|
@@ -128,6 +129,20 @@ export class TheiaMainApi implements ElectronMainApplicationContribution {
|
|
|
128
129
|
}
|
|
129
130
|
});
|
|
130
131
|
|
|
132
|
+
ipcMain.on(CHANNEL_SET_AUTO_HIDE_MENU_BAR, (event, enabled: boolean, windowName: string | undefined) => {
|
|
133
|
+
let electronWindow;
|
|
134
|
+
if (windowName) {
|
|
135
|
+
electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
|
|
136
|
+
} else {
|
|
137
|
+
electronWindow = BrowserWindow.fromWebContents(event.sender);
|
|
138
|
+
}
|
|
139
|
+
if (electronWindow) {
|
|
140
|
+
electronWindow.autoHideMenuBar = enabled;
|
|
141
|
+
} else {
|
|
142
|
+
console.warn(`There is no known secondary window '${windowName}'. Thus, autoHideMenuBar could not be set.`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
131
146
|
// popup menu
|
|
132
147
|
ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y, windowName?: string) => {
|
|
133
148
|
const zoom = event.sender.getZoomFactor();
|