mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-09 20:36:32 +00:00
Can check if the current window is the first one.
Closes #1070 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
bf193b1cac
commit
36ac47b975
@ -150,6 +150,10 @@
|
||||
"frontend": "lib/browser/theia/core/browser-menu-module",
|
||||
"frontendElectron": "lib/electron-browser/theia/core/electron-menu-module"
|
||||
},
|
||||
{
|
||||
"frontend": "lib/browser/theia/core/browser-window-module",
|
||||
"frontendElectron": "lib/electron-browser/theia/core/electron-window-module"
|
||||
},
|
||||
{
|
||||
"electronMain": "lib/electron-main/arduino-electron-main-module"
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ export class FirstStartupInstaller extends Contribution {
|
||||
// If arduino:avr installation fails because it's already installed we don't want to retry on next start-up
|
||||
console.error(e);
|
||||
} else {
|
||||
// But if there is any other error (e.g.: no interntet cconnection), we want to retry next time
|
||||
// But if there is any other error (e.g.: no Internet connection), we want to retry next time
|
||||
avrPackageError = e;
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ export class FirstStartupInstaller extends Contribution {
|
||||
// If Arduino_BuiltIn installation fails because it's already installed we don't want to retry on next start-up
|
||||
console.log('error installing core', e);
|
||||
} else {
|
||||
// But if there is any other error (e.g.: no interntet cconnection), we want to retry next time
|
||||
// But if there is any other error (e.g.: no Internet connection), we want to retry next time
|
||||
builtInLibraryError = e;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { DefaultWindowService } from './default-window-service';
|
||||
import { WindowServiceExt } from './window-service-ext';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(DefaultWindowService).toSelf().inSingletonScope();
|
||||
rebind(TheiaDefaultWindowService).toService(DefaultWindowService);
|
||||
bind(WindowServiceExt).toService(DefaultWindowService);
|
||||
});
|
@ -0,0 +1,17 @@
|
||||
import { DefaultWindowService as TheiaDefaultWindowService } from '@theia/core/lib/browser/window/default-window-service';
|
||||
import { injectable } from '@theia/core/shared/inversify';
|
||||
import { WindowServiceExt } from './window-service-ext';
|
||||
|
||||
@injectable()
|
||||
export class DefaultWindowService
|
||||
extends TheiaDefaultWindowService
|
||||
implements WindowServiceExt
|
||||
{
|
||||
/**
|
||||
* The default implementation always resolves to `true`.
|
||||
* IDE2 does not use it. It's currently an electron-only app.
|
||||
*/
|
||||
async isFirstWindow(): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
export const WindowServiceExt = Symbol('WindowServiceExt');
|
||||
export interface WindowServiceExt {
|
||||
/**
|
||||
* Returns with a promise that resolves to `true` if the current window is the first window.
|
||||
*/
|
||||
isFirstWindow(): Promise<boolean>;
|
||||
}
|
@ -1,14 +1,7 @@
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ElectronMainMenuFactory as TheiaElectronMainMenuFactory } from '@theia/core/lib/electron-browser/menu/electron-main-menu-factory';
|
||||
import { ElectronMenuContribution as TheiaElectronMenuContribution } from '@theia/core/lib/electron-browser/menu/electron-menu-contribution';
|
||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
||||
import {
|
||||
SplashService,
|
||||
splashServicePath,
|
||||
} from '../../../electron-common/splash-service';
|
||||
import { MainMenuManager } from '../../../common/main-menu-manager';
|
||||
import { ElectronWindowService } from '../../electron-window-service';
|
||||
import { ElectronMainMenuFactory } from './electron-main-menu-factory';
|
||||
import { ElectronMenuContribution } from './electron-menu-contribution';
|
||||
import { nls } from '@theia/core/lib/common/nls';
|
||||
@ -16,23 +9,25 @@ import { nls } from '@theia/core/lib/common/nls';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import * as dialogs from '@theia/core/lib/browser/dialogs';
|
||||
|
||||
|
||||
Object.assign(dialogs, {
|
||||
confirmExit: async () => {
|
||||
const messageBoxResult = await remote.dialog.showMessageBox(
|
||||
remote.getCurrentWindow(),
|
||||
{
|
||||
message: nls.localize('theia/core/quitMessage', 'Any unsaved changes will not be saved.'),
|
||||
title: nls.localize('theia/core/quitTitle', 'Are you sure you want to quit?'),
|
||||
message: nls.localize(
|
||||
'theia/core/quitMessage',
|
||||
'Any unsaved changes will not be saved.'
|
||||
),
|
||||
title: nls.localize(
|
||||
'theia/core/quitTitle',
|
||||
'Are you sure you want to quit?'
|
||||
),
|
||||
type: 'question',
|
||||
buttons: [
|
||||
dialogs.Dialog.CANCEL,
|
||||
dialogs.Dialog.YES,
|
||||
],
|
||||
buttons: [dialogs.Dialog.CANCEL, dialogs.Dialog.YES],
|
||||
}
|
||||
)
|
||||
);
|
||||
return messageBoxResult.response === 1;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
@ -41,14 +36,4 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaElectronMenuContribution).toService(ElectronMenuContribution);
|
||||
bind(ElectronMainMenuFactory).toSelf().inSingletonScope();
|
||||
rebind(TheiaElectronMainMenuFactory).toService(ElectronMainMenuFactory);
|
||||
bind(ElectronWindowService).toSelf().inSingletonScope();
|
||||
rebind(WindowService).toService(ElectronWindowService);
|
||||
bind(SplashService)
|
||||
.toDynamicValue((context) =>
|
||||
ElectronIpcConnectionProvider.createProxy(
|
||||
context.container,
|
||||
splashServicePath
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
@ -0,0 +1,32 @@
|
||||
import { WindowService } from '@theia/core/lib/browser/window/window-service';
|
||||
import { ElectronIpcConnectionProvider } from '@theia/core/lib/electron-browser/messaging/electron-ipc-connection-provider';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
||||
import {
|
||||
ElectronMainWindowServiceExt,
|
||||
electronMainWindowServiceExtPath,
|
||||
} from '../../../electron-common/electron-main-window-service-ext';
|
||||
import {
|
||||
SplashService,
|
||||
splashServicePath,
|
||||
} from '../../../electron-common/splash-service';
|
||||
import { ElectronWindowService } from './electron-window-service';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ElectronWindowService).toSelf().inSingletonScope();
|
||||
rebind(WindowService).toService(ElectronWindowService);
|
||||
bind(WindowServiceExt).toService(ElectronWindowService);
|
||||
bind(ElectronMainWindowServiceExt)
|
||||
.toDynamicValue(({ container }) =>
|
||||
ElectronIpcConnectionProvider.createProxy(
|
||||
container,
|
||||
electronMainWindowServiceExtPath
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
bind(SplashService)
|
||||
.toDynamicValue(({ container }) =>
|
||||
ElectronIpcConnectionProvider.createProxy(container, splashServicePath)
|
||||
)
|
||||
.inSingletonScope();
|
||||
});
|
@ -1,4 +1,8 @@
|
||||
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
inject,
|
||||
injectable,
|
||||
postConstruct,
|
||||
} from '@theia/core/shared/inversify';
|
||||
import * as remote from '@theia/core/electron-shared/@electron/remote';
|
||||
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
|
||||
import {
|
||||
@ -6,19 +10,27 @@ import {
|
||||
ConnectionStatusService,
|
||||
} from '@theia/core/lib/browser/connection-status-service';
|
||||
import { ElectronWindowService as TheiaElectronWindowService } from '@theia/core/lib/electron-browser/window/electron-window-service';
|
||||
import { SplashService } from '../electron-common/splash-service';
|
||||
import { SplashService } from '../../../electron-common/splash-service';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { WindowServiceExt } from '../../../browser/theia/core/window-service-ext';
|
||||
import { ElectronMainWindowServiceExt } from '../../../electron-common/electron-main-window-service-ext';
|
||||
|
||||
@injectable()
|
||||
export class ElectronWindowService extends TheiaElectronWindowService {
|
||||
export class ElectronWindowService
|
||||
extends TheiaElectronWindowService
|
||||
implements WindowServiceExt
|
||||
{
|
||||
@inject(ConnectionStatusService)
|
||||
protected readonly connectionStatusService: ConnectionStatusService;
|
||||
private readonly connectionStatusService: ConnectionStatusService;
|
||||
|
||||
@inject(SplashService)
|
||||
protected readonly splashService: SplashService;
|
||||
private readonly splashService: SplashService;
|
||||
|
||||
@inject(FrontendApplicationStateService)
|
||||
protected readonly appStateService: FrontendApplicationStateService;
|
||||
private readonly appStateService: FrontendApplicationStateService;
|
||||
|
||||
@inject(ElectronMainWindowServiceExt)
|
||||
private readonly mainWindowServiceExt: ElectronMainWindowServiceExt;
|
||||
|
||||
@postConstruct()
|
||||
protected override init(): void {
|
||||
@ -55,4 +67,15 @@ export class ElectronWindowService extends TheiaElectronWindowService {
|
||||
});
|
||||
return response === 0; // 'Yes', close the window.
|
||||
}
|
||||
|
||||
private _firstWindow: boolean | undefined;
|
||||
async isFirstWindow(): Promise<boolean> {
|
||||
if (this._firstWindow === undefined) {
|
||||
const windowId = remote.getCurrentWindow().id; // This is expensive and synchronous so we check it once per FE.
|
||||
this._firstWindow = await this.mainWindowServiceExt.isFirstWindow(
|
||||
windowId
|
||||
);
|
||||
}
|
||||
return this._firstWindow;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
export const electronMainWindowServiceExtPath = '/services/electron-window-ext';
|
||||
export const ElectronMainWindowServiceExt = Symbol(
|
||||
'ElectronMainWindowServiceExt'
|
||||
);
|
||||
export interface ElectronMainWindowServiceExt {
|
||||
isFirstWindow(windowId: number): Promise<boolean>;
|
||||
}
|
@ -1,31 +1,31 @@
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import { JsonRpcConnectionHandler } from '@theia/core/lib/common/messaging/proxy-factory';
|
||||
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
|
||||
import { ElectronMainWindowService } from '@theia/core/lib/electron-common/electron-main-window-service';
|
||||
import { ElectronConnectionHandler } from '@theia/core/lib/electron-common/messaging/electron-connection-handler';
|
||||
import {
|
||||
ElectronMainApplication as TheiaElectronMainApplication,
|
||||
ElectronMainApplicationContribution,
|
||||
} from '@theia/core/lib/electron-main/electron-main-application';
|
||||
import {
|
||||
SplashService,
|
||||
splashServicePath,
|
||||
} from '../electron-common/splash-service';
|
||||
import { SplashServiceImpl } from './splash/splash-service-impl';
|
||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { ContainerModule } from '@theia/core/shared/inversify';
|
||||
import {
|
||||
IDEUpdater,
|
||||
IDEUpdaterClient,
|
||||
IDEUpdaterPath,
|
||||
} from '../common/protocol/ide-updater';
|
||||
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
||||
import { TheiaElectronWindow } from './theia/theia-electron-window';
|
||||
import { TheiaElectronWindow as DefaultTheiaElectronWindow } from '@theia/core/lib/electron-main/theia-electron-window';
|
||||
import { SurveyNotificationServiceImpl } from '../node/survey-service-impl';
|
||||
import {
|
||||
SurveyNotificationService,
|
||||
SurveyNotificationServicePath,
|
||||
} from '../common/protocol/survey-service';
|
||||
ElectronMainWindowServiceExt,
|
||||
electronMainWindowServiceExtPath,
|
||||
} from '../electron-common/electron-main-window-service-ext';
|
||||
import {
|
||||
SplashService,
|
||||
splashServicePath,
|
||||
} from '../electron-common/splash-service';
|
||||
import { ElectronMainWindowServiceExtImpl } from './electron-main-window-service-ext-impl';
|
||||
import { IDEUpdaterImpl } from './ide-updater/ide-updater-impl';
|
||||
import { SplashServiceImpl } from './splash/splash-service-impl';
|
||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||
import { ElectronMainWindowServiceImpl } from './theia/electron-main-window-service';
|
||||
import { TheiaElectronWindow } from './theia/theia-electron-window';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(ElectronMainApplication).toSelf().inSingletonScope();
|
||||
@ -67,19 +67,14 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(TheiaElectronWindow).toSelf();
|
||||
rebind(DefaultTheiaElectronWindow).toService(TheiaElectronWindow);
|
||||
|
||||
// Survey notification bindings
|
||||
bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope();
|
||||
bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl);
|
||||
bind(ElectronMainApplicationContribution).toService(
|
||||
SurveyNotificationService
|
||||
);
|
||||
bind(ElectronMainWindowServiceExt)
|
||||
.to(ElectronMainWindowServiceExtImpl)
|
||||
.inSingletonScope();
|
||||
bind(ElectronConnectionHandler)
|
||||
.toDynamicValue(
|
||||
(context) =>
|
||||
new JsonRpcConnectionHandler(SurveyNotificationServicePath, () =>
|
||||
context.container.get<SurveyNotificationService>(
|
||||
SurveyNotificationService
|
||||
)
|
||||
new JsonRpcConnectionHandler(electronMainWindowServiceExtPath, () =>
|
||||
context.container.get(ElectronMainWindowServiceExt)
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { inject, injectable } from '@theia/core/shared/inversify';
|
||||
import { ElectronMainWindowServiceExt } from '../electron-common/electron-main-window-service-ext';
|
||||
import { ElectronMainApplication } from './theia/electron-main-application';
|
||||
|
||||
@injectable()
|
||||
export class ElectronMainWindowServiceExtImpl
|
||||
implements ElectronMainWindowServiceExt
|
||||
{
|
||||
@inject(ElectronMainApplication)
|
||||
private readonly app: ElectronMainApplication;
|
||||
|
||||
async isFirstWindow(windowId: number): Promise<boolean> {
|
||||
return this.app.firstWindowId === windowId;
|
||||
}
|
||||
}
|
@ -66,11 +66,12 @@ const APP_STARTED_WITH_CONTENT_TRACE =
|
||||
|
||||
@injectable()
|
||||
export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
protected startup = false;
|
||||
protected openFilePromise = new Deferred();
|
||||
private startup = false;
|
||||
private _firstWindowId: number | undefined;
|
||||
private openFilePromise = new Deferred();
|
||||
|
||||
@inject(SplashServiceImpl)
|
||||
protected readonly splashService: SplashServiceImpl;
|
||||
private readonly splashService: SplashServiceImpl;
|
||||
|
||||
override async start(config: FrontendApplicationConfig): Promise<void> {
|
||||
// Explicitly set the app name to have better menu items on macOS. ("About", "Hide", and "Quit")
|
||||
@ -142,7 +143,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
})();
|
||||
}
|
||||
|
||||
attachFileAssociations() {
|
||||
private attachFileAssociations(): void {
|
||||
// OSX: register open-file event
|
||||
if (os.isOSX) {
|
||||
app.on('open-file', async (event, uri) => {
|
||||
@ -158,7 +159,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
|
||||
protected async isValidSketchPath(uri: string): Promise<boolean | undefined> {
|
||||
private async isValidSketchPath(uri: string): Promise<boolean | undefined> {
|
||||
return typeof uri === 'string' && (await fs.pathExists(uri));
|
||||
}
|
||||
|
||||
@ -201,7 +202,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
|
||||
protected async launchFromArgs(
|
||||
private async launchFromArgs(
|
||||
params: ElectronMainExecutionParams
|
||||
): Promise<boolean> {
|
||||
// Copy to prevent manipulation of original array
|
||||
@ -223,7 +224,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected async openSketch(
|
||||
private async openSketch(
|
||||
workspace: WorkspaceOptions | string
|
||||
): Promise<BrowserWindow> {
|
||||
const options = await this.getLastWindowOptions();
|
||||
@ -257,7 +258,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
|
||||
protected override getTitleBarStyle(
|
||||
config: FrontendApplicationConfig
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_config: FrontendApplicationConfig
|
||||
): 'native' | 'custom' {
|
||||
return 'native';
|
||||
}
|
||||
@ -354,6 +356,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
electronWindow.webContents.openDevTools();
|
||||
}
|
||||
this.attachListenersToWindow(electronWindow);
|
||||
if (this._firstWindowId === undefined) {
|
||||
this._firstWindowId = electronWindow.id;
|
||||
}
|
||||
return electronWindow;
|
||||
}
|
||||
|
||||
@ -389,7 +394,7 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
});
|
||||
event.newGuest = new BrowserWindow(options);
|
||||
event.newGuest.setMenu(null);
|
||||
event.newGuest?.on('closed', (e: any) => {
|
||||
event.newGuest?.on('closed', () => {
|
||||
electronWindow?.webContents.send('CLOSE_CHILD_WINDOW');
|
||||
});
|
||||
event.newGuest?.loadURL(url);
|
||||
@ -462,9 +467,9 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
}
|
||||
}
|
||||
|
||||
protected closedWorkspaces: WorkspaceOptions[] = [];
|
||||
private closedWorkspaces: WorkspaceOptions[] = [];
|
||||
|
||||
protected attachClosedWorkspace(window: BrowserWindow): void {
|
||||
private attachClosedWorkspace(window: BrowserWindow): void {
|
||||
// Since the `before-quit` event is only fired when closing the *last* window
|
||||
// We need to keep track of recently closed windows/workspaces manually
|
||||
window.on('close', () => {
|
||||
@ -522,4 +527,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
|
||||
get browserWindows(): BrowserWindow[] {
|
||||
return Array.from(this.windows.values()).map(({ window }) => window);
|
||||
}
|
||||
|
||||
get firstWindowId(): number | undefined {
|
||||
return this._firstWindowId;
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,11 @@ import { ClangFormatter } from './clang-formatter';
|
||||
import { FormatterPath } from '../common/protocol/formatter';
|
||||
import { LocalizationBackendContribution } from './i18n/localization-backend-contribution';
|
||||
import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution';
|
||||
import { SurveyNotificationServiceImpl } from './survey-service-impl';
|
||||
import {
|
||||
SurveyNotificationService,
|
||||
SurveyNotificationServicePath,
|
||||
} from '../common/protocol/survey-service';
|
||||
|
||||
export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(BackendApplication).toSelf().inSingletonScope();
|
||||
@ -401,4 +406,17 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaLocalizationBackendContribution).toService(
|
||||
LocalizationBackendContribution
|
||||
);
|
||||
|
||||
// Survey notification bindings
|
||||
// It's currently unused. https://github.com/arduino/arduino-ide/pull/1150
|
||||
bind(SurveyNotificationServiceImpl).toSelf().inSingletonScope();
|
||||
bind(SurveyNotificationService).toService(SurveyNotificationServiceImpl);
|
||||
bind(ConnectionHandler)
|
||||
.toDynamicValue(
|
||||
({ container }) =>
|
||||
new JsonRpcConnectionHandler(SurveyNotificationServicePath, () =>
|
||||
container.get<SurveyNotificationService>(SurveyNotificationService)
|
||||
)
|
||||
)
|
||||
.inSingletonScope();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user