From 316c760b68231257da77b55ba217f43b14c9bc51 Mon Sep 17 00:00:00 2001 From: marechal-p Date: Thu, 8 Nov 2018 12:01:42 -0500 Subject: [PATCH] [core] Add and use application name Add customizable application name. Display application name when no workspace is opened, instead of showing the URL. It was particulary bad on Electron. Remove native menus when creating a new Electron BrowserWindow, which will be re-added by the application when ready. Signed-off-by: marechal-p --- CHANGELOG.md | 1 + .../src/generator/frontend-generator.ts | 9 +++++++-- .../src/application-props.ts | 9 ++++++++- examples/browser/package.json | 7 +++++++ examples/electron/package.json | 7 ++++++- .../src/browser/preference-service.spec.ts | 9 +++++++-- .../src/browser/workspace-service.spec.ts | 18 ++++++++++++++++++ .../workspace/src/browser/workspace-service.ts | 18 +++++++++++++----- 8 files changed, 67 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c61a33067b6b0..d8f933fb5be20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## v0.3.17 - [plug-in] added `languages.registerCodeLensProvider` Plug-in API - [core] `ctrl+alt+a` and `ctrl+alt+d` to switch tabs left/right +- [core] added `theia.applicationName` to application `package.json` and improved window title ## v0.3.16 diff --git a/dev-packages/application-manager/src/generator/frontend-generator.ts b/dev-packages/application-manager/src/generator/frontend-generator.ts index d7bc317d916f7..b2f0ade84d733 100644 --- a/dev-packages/application-manager/src/generator/frontend-generator.ts +++ b/dev-packages/application-manager/src/generator/frontend-generator.ts @@ -104,12 +104,13 @@ process.env.LC_NUMERIC = 'C'; const { join } = require('path'); const { isMaster } = require('cluster'); const { fork } = require('child_process'); -const { app, BrowserWindow, ipcMain } = require('electron'); +const { app, BrowserWindow, ipcMain, Menu } = require('electron'); +const applicationName = \`${this.pck.props.frontend.config.applicationName}\`; const windows = []; function createNewWindow(theUrl) { - const newWindow = new BrowserWindow({ width: 1024, height: 728, show: !!theUrl }); + const newWindow = new BrowserWindow({ width: 1024, height: 728, show: !!theUrl, title: applicationName }); if (windows.length === 0) { newWindow.webContents.on('new-window', (event, url, frameName, disposition, options) => { // If the first electron window isn't visible, then all other new windows will remain invisible. @@ -117,6 +118,7 @@ function createNewWindow(theUrl) { options.show = true; options.width = 1024; options.height = 728; + options.title = applicationName; }); } windows.push(newWindow); @@ -147,6 +149,9 @@ if (isMaster) { createNewWindow(url); }); app.on('ready', () => { + // Remove the default electron menus, waiting for the application to set its own. + Menu.setApplicationMenu(Menu.buildFromTemplate([])); + // Check whether we are in bundled application or development mode. // @ts-ignore const devMode = process.defaultApp || /node_modules[\/]electron[\/]/.test(process.execPath); diff --git a/dev-packages/application-package/src/application-props.ts b/dev-packages/application-package/src/application-props.ts index 0b4aa6bd55d01..5502e1bc486c5 100644 --- a/dev-packages/application-package/src/application-props.ts +++ b/dev-packages/application-package/src/application-props.ts @@ -69,7 +69,9 @@ export namespace ApplicationProps { config: {} }, frontend: { - config: {} + config: { + applicationName: 'Theia' + } } }; @@ -93,6 +95,11 @@ export interface FrontendApplicationConfig extends ApplicationConfig { */ readonly defaultTheme?: string; + /** + * The name of the application. `Theia` by default. + */ + readonly applicationName: string; + } /** diff --git a/examples/browser/package.json b/examples/browser/package.json index 4c3e5e3ccd1b7..56820c5248533 100644 --- a/examples/browser/package.json +++ b/examples/browser/package.json @@ -2,6 +2,13 @@ "private": true, "name": "@theia/example-browser", "version": "0.3.16", + "theia": { + "frontend": { + "config": { + "applicationName": "Theia Browser Example" + } + } + }, "dependencies": { "@theia/callhierarchy": "^0.3.16", "@theia/console": "^0.3.16", diff --git a/examples/electron/package.json b/examples/electron/package.json index a57c90c67df97..cd63b89cd65fb 100644 --- a/examples/electron/package.json +++ b/examples/electron/package.json @@ -3,7 +3,12 @@ "name": "@theia/example-electron", "version": "0.3.16", "theia": { - "target": "electron" + "target": "electron", + "frontend": { + "config": { + "applicationName": "Theia Electron Example" + } + } }, "dependencies": { "@theia/callhierarchy": "^0.3.16", diff --git a/packages/preferences/src/browser/preference-service.spec.ts b/packages/preferences/src/browser/preference-service.spec.ts index 17ec79c8e74b4..c9f7b0c205820 100644 --- a/packages/preferences/src/browser/preference-service.spec.ts +++ b/packages/preferences/src/browser/preference-service.spec.ts @@ -46,6 +46,7 @@ import { MockWorkspaceServer } from '@theia/workspace/lib/common/test/mock-works import { MockWindowService } from '@theia/core/lib/browser/window/test/mock-window-service'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import { WorkspacePreferences, createWorkspacePreferences } from '@theia/workspace/lib/browser/workspace-preferences'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; import * as sinon from 'sinon'; import URI from '@theia/core/lib/common/uri'; @@ -60,7 +61,7 @@ const tempPath = temp.track().openSync().path; const mockUserPreferenceEmitter = new Emitter(); const mockWorkspacePreferenceEmitter = new Emitter(); -before(async () => { +function testContainerSetup() { testContainer = new Container(); bindPreferenceSchemaProvider(testContainer.bind.bind(testContainer)); @@ -130,12 +131,16 @@ before(async () => { /* Logger mock */ testContainer.bind(ILogger).to(MockLogger); -}); +} describe('Preference Service', function () { before(() => { disableJSDOM = enableJSDOM(); + FrontendApplicationConfigProvider.set({ + 'applicationName': 'test', + }); + testContainerSetup(); }); after(() => { diff --git a/packages/workspace/src/browser/workspace-service.spec.ts b/packages/workspace/src/browser/workspace-service.spec.ts index 5ce7e8e01a763..1e3ff4056e28c 100644 --- a/packages/workspace/src/browser/workspace-service.spec.ts +++ b/packages/workspace/src/browser/workspace-service.spec.ts @@ -14,9 +14,13 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import { enableJSDOM } from '@theia/core/lib/browser/test/jsdom'; +let disableJSDOM = enableJSDOM(); + import { Container } from 'inversify'; import { WorkspaceService } from './workspace-service'; import { FileSystem, FileStat } from '@theia/filesystem/lib/common'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; import { FileSystemNode } from '@theia/filesystem/lib/node/node-filesystem'; import { FileSystemWatcher, FileChangeEvent, FileChangeType } from '@theia/filesystem/lib/browser/filesystem-watcher'; import { DefaultWindowService, WindowService } from '@theia/core/lib/browser/window/window-service'; @@ -30,6 +34,8 @@ import * as chai from 'chai'; import URI from '@theia/core/lib/common/uri'; const expect = chai.expect; +disableJSDOM(); + const folderA = Object.freeze({ uri: 'file:///home/folderA', lastModification: 0, @@ -60,6 +66,17 @@ describe('WorkspaceService', () => { let mockILogger: ILogger; let mockPref: WorkspacePreferences; + before(() => { + disableJSDOM = enableJSDOM(); + FrontendApplicationConfigProvider.set({ + 'applicationName': 'test', + }); + }); + + after(() => { + disableJSDOM(); + }); + beforeEach(() => { mockPreferenceValues = {}; mockFilesystem = sinon.createStubInstance(FileSystemNode); @@ -88,6 +105,7 @@ describe('WorkspaceService', () => { wsService = testContainer.get(WorkspaceService); }); + afterEach(() => { wsService['toDisposeOnWorkspace'].dispose(); toRestore.forEach(res => { diff --git a/packages/workspace/src/browser/workspace-service.ts b/packages/workspace/src/browser/workspace-service.ts index e7f10d4590e29..f11bd4e4c8282 100644 --- a/packages/workspace/src/browser/workspace-service.ts +++ b/packages/workspace/src/browser/workspace-service.ts @@ -26,6 +26,7 @@ import { ILogger, Disposable, DisposableCollection, Emitter, Event } from '@thei import { WorkspacePreferences } from './workspace-preferences'; import * as jsoncparser from 'jsonc-parser'; import * as Ajv from 'ajv'; +import { FrontendApplicationConfigProvider } from '@theia/core/lib/browser/frontend-application-config-provider'; export const THEIA_EXT = 'theia-workspace'; export const VSCODE_EXT = 'code-workspace'; @@ -64,6 +65,8 @@ export class WorkspaceService implements FrontendApplicationContribution { @inject(WorkspacePreferences) protected preferences: WorkspacePreferences; + protected applicationName = FrontendApplicationConfigProvider.get().applicationName; + @postConstruct() protected async init(): Promise { const workspaceUri = await this.server.getMostRecentlyUsedWorkspace(); @@ -168,19 +171,24 @@ export class WorkspaceService implements FrontendApplicationContribution { } } - protected updateTitle(): void { + protected formatTitle(title?: string): string { + const name = this.applicationName; + return title ? `${title} — ${name}` : name; + } + + protected updateTitle() { + let title: string | undefined; if (this._workspace) { const uri = new URI(this._workspace.uri); const displayName = uri.displayName; if (!this._workspace.isDirectory && (displayName.endsWith(`.${THEIA_EXT}`) || displayName.endsWith(`.${VSCODE_EXT}`))) { - document.title = displayName.slice(0, displayName.lastIndexOf('.')); + title = displayName.slice(0, displayName.lastIndexOf('.')); } else { - document.title = displayName; + title = displayName; } - } else { - document.title = window.location.href; } + document.title = this.formatTitle(title); } /**