Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: restore window.titleBarStyle and breadcrumbs.enabled #1870

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions arduino-ide-extension/src/browser/style/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@
background-color: var(--theia-titleBar-activeBackground);
}

#arduino-toolbar-panel {
background: var(--theia-titleBar-activeBackground);
color: var(--theia-titleBar-activeForeground);
display: flex;
min-height: var(--theia-private-menubar-height);
border-bottom: 1px solid var(--theia-titleBar-border);
}
#arduino-toolbar-panel:window-inactive,
#arduino-toolbar-panel:-moz-window-inactive {
background: var(--theia-titleBar-inactiveBackground);
color: var(--theia-titleBar-inactiveForeground);
}

#arduino-toolbar-container {
display: flex;
width: 100%;
Expand Down
61 changes: 58 additions & 3 deletions arduino-ide-extension/src/browser/theia/core/application-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
SHELL_TABBAR_CONTEXT_MENU,
TabBar,
Widget,
Layout,
SplitPanel,
} from '@theia/core/lib/browser';
import {
ConnectionStatus,
Expand All @@ -17,17 +19,23 @@ import { MessageService } from '@theia/core/lib/common/message-service';
import { inject, injectable } from '@theia/core/shared/inversify';
import { ToolbarAwareTabBar } from './tab-bars';

interface WidgetOptions
extends Omit<TheiaApplicationShell.WidgetOptions, 'area'> {
area?: TheiaApplicationShell.Area | 'toolbar';
}

@injectable()
export class ApplicationShell extends TheiaApplicationShell {
@inject(MessageService)
private readonly messageService: MessageService;

@inject(ConnectionStatusService)
private readonly connectionStatusService: ConnectionStatusService;
private toolbarPanel: Panel;

override async addWidget(
widget: Widget,
options: Readonly<TheiaApplicationShell.WidgetOptions> = {}
options: Readonly<WidgetOptions> = {}
): Promise<void> {
// By default, Theia open a widget **next** to the currently active in the target area.
// Instead of this logic, we want to open the new widget after the last of the target area.
Expand All @@ -37,8 +45,12 @@ export class ApplicationShell extends TheiaApplicationShell {
);
return;
}
if (options.area === 'toolbar') {
this.toolbarPanel.addWidget(widget);
return;
}
const area = options.area || 'main';
let ref: Widget | undefined = options.ref;
const area: TheiaApplicationShell.Area = options.area || 'main';
if (!ref && (area === 'main' || area === 'bottom')) {
const tabBar = this.getTabBarFor(area);
if (tabBar) {
Expand All @@ -48,14 +60,57 @@ export class ApplicationShell extends TheiaApplicationShell {
}
}
}
return super.addWidget(widget, { ...options, ref });
return super.addWidget(widget, {
...(<TheiaApplicationShell.WidgetOptions>options),
ref,
});
}

override handleEvent(): boolean {
// NOOP, dragging has been disabled
return false;
}

protected override initializeShell(): void {
this.toolbarPanel = this.createToolbarPanel();
super.initializeShell();
}

private createToolbarPanel(): Panel {
const toolbarPanel = new Panel();
toolbarPanel.id = 'arduino-toolbar-panel';
toolbarPanel.show();
return toolbarPanel;
}

protected override createLayout(): Layout {
const bottomSplitLayout = this.createSplitLayout(
[this.mainPanel, this.bottomPanel],
[1, 0],
{ orientation: 'vertical', spacing: 0 }
);
const panelForBottomArea = new SplitPanel({ layout: bottomSplitLayout });
panelForBottomArea.id = 'theia-bottom-split-panel';

const leftRightSplitLayout = this.createSplitLayout(
[
this.leftPanelHandler.container,
panelForBottomArea,
this.rightPanelHandler.container,
],
[0, 1, 0],
{ orientation: 'horizontal', spacing: 0 }
);
const panelForSideAreas = new SplitPanel({ layout: leftRightSplitLayout });
panelForSideAreas.id = 'theia-left-right-split-panel';

return this.createBoxLayout(
[this.topPanel, this.toolbarPanel, panelForSideAreas, this.statusBar],
[0, 0, 1, 0],
{ direction: 'top-to-bottom', spacing: 0 }
);
}

// Avoid hiding top panel as we use it for arduino toolbar
protected override createTopPanel(): Panel {
const topPanel = super.createTopPanel();
Expand Down
5 changes: 0 additions & 5 deletions arduino-ide-extension/src/browser/theia/core/tab-bars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ export class TabBarRenderer extends TheiaTabBarRenderer {
}

export class ToolbarAwareTabBar extends TheiaToolbarAwareTabBar {
protected override async updateBreadcrumbs(): Promise<void> {
// NOOP
// IDE2 does not use breadcrumbs.
}

private readonly doUpdateToolbar = debounce(() => super.updateToolbar(), 500);
protected override updateToolbar(): void {
// Unlike Theia, IDE2 debounces the toolbar updates with 500ms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export class ArduinoToolbarContainer extends Widget {
this.toolbars = toolbars;
}

override onAfterAttach(msg: Message) {
override onAfterAttach(msg: Message): void {
super.onAfterAttach(msg);
for (const toolbar of this.toolbars) {
Widget.attach(toolbar, this.node);
}
Expand Down Expand Up @@ -56,9 +57,11 @@ export class ArduinoToolbarContribution
);
}

onStart(app: FrontendApplication) {
app.shell.addWidget(this.arduinoToolbarContainer, {
area: 'top',
});
onStart(app: FrontendApplication): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const options = <any>{
area: 'toolbar',
};
app.shell.addWidget(this.arduinoToolbarContainer, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
CommandMenuNode,
CompoundMenuNode,
CompoundMenuNodeRole,
MAIN_MENU_BAR,
MenuNode,
MenuPath,
} from '@theia/core/lib/common/menu';
Expand Down Expand Up @@ -39,18 +38,9 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
});
}

override createElectronMenuBar(): Electron.Menu {
override createElectronMenuBar(): Electron.Menu | null {
this._toggledCommands.clear(); // https://github.com/eclipse-theia/theia/issues/8977
const menuModel = this.menuProvider.getMenu(MAIN_MENU_BAR);
const template = this.fillMenuTemplate([], menuModel, [], {
rootMenuPath: MAIN_MENU_BAR,
});
if (isOSX) {
template.unshift(this.createOSXMenu());
}
const menu = remote.Menu.buildFromTemplate(this.escapeAmpersand(template));
this._menu = menu;
return menu;
return super.createElectronMenuBar();
}

override async setMenuBar(): Promise<void> {
Expand All @@ -61,13 +51,7 @@ export class ElectronMainMenuFactory extends TheiaElectronMainMenuFactory {
this.updateWhenReady = true;
return;
}
await this.preferencesService.ready;
const createdMenuBar = this.createElectronMenuBar();
if (isOSX) {
remote.Menu.setApplicationMenu(createdMenuBar);
} else {
remote.getCurrentWindow().setMenu(createdMenuBar);
}
return super.setMenuBar();
}

override createElectronContextMenu(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,38 @@
import { inject, injectable } from '@theia/core/shared/inversify';
import {
getCurrentWebContents,
getCurrentWindow,
} from '@theia/core/electron-shared/@electron/remote';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
import { PreferenceScope } from '@theia/core/lib/browser/preferences/preference-scope';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { MenuModelRegistry } from '@theia/core/lib/common/menu';
import { KeybindingRegistry } from '@theia/core/lib/browser/keybinding';
import {
ElectronMenuContribution as TheiaElectronMenuContribution,
ElectronCommands,
ElectronMenuContribution as TheiaElectronMenuContribution,
} from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
import { MainMenuManager } from '../../../common/main-menu-manager';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
import { FrontendApplication } from '@theia/core/lib/browser/frontend-application';
import { ZoomLevel } from '@theia/core/lib/electron-browser/window/electron-window-preferences';
import { PreferenceScope } from '@theia/core/lib/browser/preferences/preference-scope';
import {
getCurrentWindow,
getCurrentWebContents,
} from '@theia/core/electron-shared/@electron/remote';
import { injectable } from '@theia/core/shared/inversify';
import { MainMenuManager } from '../../../common/main-menu-manager';

@injectable()
export class ElectronMenuContribution
extends TheiaElectronMenuContribution
implements MainMenuManager
{
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;

// private appReady = false;
// private updateWhenReady = false;
private app: FrontendApplication;

override onStart(app: FrontendApplication): void {
this.app = app;
super.onStart(app);
this.appStateService.reachedState('ready').then(() => {
// this.appReady = true;
// if (this.updateWhenReady) {
// this.update();
// }
});
}

protected override hideTopPanel(): void {
// NOOP
// We reuse the `div` for the Arduino toolbar.
}

update(): void {
// if (this.appReady) {
(this as any).setMenu();
// } else {
// this.updateWhenReady = true;
// }
// no menu updates before `onStart`
if (!this.app) {
return;
}
this.setMenu(this.app);
}

override registerCommands(registry: CommandRegistry): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ import {
} from '@theia/core/lib/electron-main/electron-main-application';
import { URI } from '@theia/core/shared/vscode-uri';
import { Deferred } from '@theia/core/lib/common/promise-util';
import * as os from '@theia/core/lib/common/os';
import { Restart } from '@theia/core/lib/electron-common/messaging/electron-messages';
import { isOSX } from '@theia/core/lib/common/os';
import {
RequestTitleBarStyle,
Restart,
TitleBarStyleAtStartup,
TitleBarStyleChanged,
} from '@theia/core/lib/electron-common/messaging/electron-messages';
import { TheiaBrowserWindowOptions } from '@theia/core/lib/electron-main/theia-electron-window';
import { IsTempSketch } from '../../node/is-temp-sketch';
import {
Expand Down Expand Up @@ -176,7 +181,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {

private attachFileAssociations(cwd: string): void {
// OSX: register open-file event
if (os.isOSX) {
if (isOSX) {
app.on('open-file', async (event, path) => {
event.preventDefault();
const resolvedPath = await this.resolvePath(path, cwd);
Expand Down Expand Up @@ -330,10 +335,19 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
}

protected override getTitleBarStyle(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_config: FrontendApplicationConfig
config: FrontendApplicationConfig
): 'native' | 'custom' {
return 'native';
const storedFrame = this.electronStore.get('windowstate')?.frame;
if (storedFrame !== undefined) {
return !!storedFrame ? 'native' : 'custom';
}
if (config.preferences && config.preferences['window.titleBarStyle']) {
const titleBarStyle = config.preferences['window.titleBarStyle'];
if (titleBarStyle === 'native' || titleBarStyle === 'custom') {
return titleBarStyle;
}
}
return 'custom';
}

protected override hookApplicationEvents(): void {
Expand All @@ -351,6 +365,21 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
this.delete(sketch);
}
});
ipcMain.on(TitleBarStyleChanged, ({ sender }, titleBarStyle: string) => {
this.useNativeWindowFrame = isOSX || titleBarStyle === 'native';
const browserWindow = BrowserWindow.fromId(sender.id);
if (browserWindow) {
this.saveWindowState(browserWindow);
} else {
console.warn(`no BrowserWindow with id: ${sender.id}`);
}
});
ipcMain.on(RequestTitleBarStyle, ({ sender }) => {
sender.send(
TitleBarStyleAtStartup,
this.didUseNativeWindowFrameOnStart.get(sender.id) ? 'native' : 'custom'
);
});
}

protected override async onSecondInstance(
Expand Down