Add WebviewPanel.iconPath (#54912)

* Add WebviewPanel.iconPath

Allows webviews to provide icons used in UI. Adds a new `WebviewPanel.iconPath` property for this.

Replaces the static contribution approach from #49657

Fixes #48864

* Fix doc

* Move icon into mainthreadwebview

* Cleaning up implementation

* Cleaning up implementation
This commit is contained in:
Matt Bierner
2018-07-24 15:08:46 -07:00
committed by GitHub
parent b94c44e719
commit 4be0f07230
10 changed files with 132 additions and 22 deletions

View File

@@ -2,23 +2,24 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as dom from 'vs/base/browser/dom';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import * as map from 'vs/base/common/map';
import URI, { UriComponents } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { localize } from 'vs/nls';
import { EditorViewColumn, viewColumnToEditorGroup, editorGroupToViewColumn } from 'vs/workbench/api/shared/editor';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle } from 'vs/workbench/api/node/extHost.protocol';
import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/shared/editor';
import { WebviewEditor } from 'vs/workbench/parts/webview/electron-browser/webviewEditor';
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
import { IWebviewEditorService, WebviewInputOptions, WebviewReviver, ICreateWebViewShowOptions } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService';
import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions, WebviewReviver } from 'vs/workbench/parts/webview/electron-browser/webviewEditorService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { extHostNamedCustomer } from './extHostCustomers';
import * as vscode from 'vscode';
import { extHostNamedCustomer } from './extHostCustomers';
@extHostNamedCustomer(MainContext.MainThreadWebviews)
export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviver {
@@ -29,6 +30,39 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
private static revivalPool = 0;
private static _styleElement?: HTMLStyleElement;
private static _icons = new Map<number, { light: URI, dark: URI }>();
private static updateStyleElement(
webview: WebviewEditorInput,
iconPath: { light: URI, dark: URI } | undefined
) {
const id = webview.getId();
if (!this._styleElement) {
this._styleElement = dom.createStyleSheet();
this._styleElement.className = 'webview-icons';
}
if (!iconPath) {
this._icons.delete(id);
} else {
this._icons.set(id, iconPath);
}
const cssRules: string[] = [];
this._icons.forEach((value, key) => {
const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`;
if (URI.isUri(value)) {
cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.toString()}); }`);
} else {
cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.light.toString()}); }`);
cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${value.dark.toString()}); }`);
}
});
this._styleElement.innerHTML = cssRules.join('\n');
}
private _toDispose: IDisposable[] = [];
private readonly _proxy: ExtHostWebviewsShape;
@@ -96,6 +130,11 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
webview.setName(value);
}
public $setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void {
const webview = this.getWebview(handle);
MainThreadWebviews.updateStyleElement(webview, reviveWebviewIcon(value));
}
public $setHtml(handle: WebviewPanelHandle, value: string): void {
const webview = this.getWebview(handle);
webview.html = value;
@@ -185,9 +224,16 @@ export class MainThreadWebviews implements MainThreadWebviewsShape, WebviewReviv
onDidClickLink: uri => this.onDidClickLink(handle, uri),
onMessage: message => this._proxy.$onMessage(handle, message),
onDispose: () => {
const cleanUp = () => {
const webview = this._webviews.get(handle);
if (webview) {
MainThreadWebviews.updateStyleElement(webview, undefined);
}
this._webviews.delete(handle);
};
this._proxy.$onDidDisposeWebviewPanel(handle).then(
() => this._webviews.delete(handle),
() => this._webviews.delete(handle));
cleanUp,
cleanUp);
}
};
}
@@ -297,3 +343,16 @@ function reviveWebviewOptions(options: WebviewInputOptions): WebviewInputOptions
localResourceRoots: Array.isArray(options.localResourceRoots) ? options.localResourceRoots.map(URI.revive) : undefined
};
}
function reviveWebviewIcon(
value: { light: UriComponents, dark: UriComponents } | undefined
): { light: URI, dark: URI } | undefined {
if (!value) {
return undefined;
}
return {
light: URI.revive(value.light),
dark: URI.revive(value.dark)
};
}

View File

@@ -430,6 +430,7 @@ export interface MainThreadWebviewsShape extends IDisposable {
$disposeWebview(handle: WebviewPanelHandle): void;
$reveal(handle: WebviewPanelHandle, viewColumn: EditorViewColumn | null, preserveFocus: boolean): void;
$setTitle(handle: WebviewPanelHandle, value: string): void;
$setIconPath(handle: WebviewPanelHandle, value: { light: UriComponents, dark: UriComponents } | undefined): void;
$setHtml(handle: WebviewPanelHandle, value: string): void;
$setOptions(handle: WebviewPanelHandle, options: vscode.WebviewOptions): void;
$postMessage(handle: WebviewPanelHandle, value: any): Thenable<boolean>;

View File

@@ -3,14 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MainContext, MainThreadWebviewsShape, IMainContext, ExtHostWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import * as vscode from 'vscode';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import * as typeConverters from 'vs/workbench/api/node/extHostTypeConverters';
import { EditorViewColumn } from 'vs/workbench/api/shared/editor';
import { TPromise } from 'vs/base/common/winjs.base';
import * as vscode from 'vscode';
import { ExtHostWebviewsShape, IMainContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelViewState } from './extHost.protocol';
import { Disposable } from './extHostTypes';
import URI from 'vs/base/common/uri';
type IconPath = URI | { light: URI, dark: URI };
export class ExtHostWebview implements vscode.Webview {
private readonly _handle: WebviewPanelHandle;
@@ -78,6 +81,7 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
private readonly _proxy: MainThreadWebviewsShape;
private readonly _viewType: string;
private _title: string;
private _iconPath: IconPath;
private readonly _options: vscode.WebviewPanelOptions;
private readonly _webview: ExtHostWebview;
@@ -150,6 +154,20 @@ export class ExtHostWebviewPanel implements vscode.WebviewPanel {
}
}
get iconPath(): IconPath | undefined {
this.assertNotDisposed();
return this._iconPath;
}
set iconPath(value: IconPath | undefined) {
this.assertNotDisposed();
if (this._iconPath !== value) {
this._iconPath = value;
this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value);
}
}
get options() {
return this._options;
}