未验证 提交 4be0f072 编写于 作者: M Matt Bierner 提交者: GitHub

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
上级 b94c44e7
......@@ -24,7 +24,7 @@ export function activate(context: vscode.ExtensionContext) {
const telemetryReporter = loadDefaultTelemetryReporter();
context.subscriptions.push(telemetryReporter);
const contributions = getMarkdownExtensionContributions();
const contributions = getMarkdownExtensionContributions(context);
const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState);
const engine = new MarkdownEngine(contributions, githubSlugifier);
......
......@@ -271,6 +271,14 @@ export class MarkdownPreview {
this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked);
}
private get iconPath() {
const root = path.join(this._contributions.extensionPath, 'media');
return {
light: vscode.Uri.file(path.join(root, 'Preview.svg')),
dark: vscode.Uri.file(path.join(root, 'Preview_inverse.svg'))
};
}
private isPreviewOf(resource: vscode.Uri): boolean {
return this._resource.fsPath === resource.fsPath;
}
......@@ -327,6 +335,7 @@ export class MarkdownPreview {
const content = await this._contentProvider.provideTextDocumentContent(document, this._previewConfigurations, this.line, this.state);
if (this._resource === resource) {
this.editor.title = MarkdownPreview.getPreviewTitle(this._resource, this._locked);
this.editor.iconPath = this.iconPath;
this.editor.webview.options = MarkdownPreview.getWebviewOptions(resource, this._contributions);
this.editor.webview.html = content;
}
......
......@@ -26,6 +26,7 @@ const resolveExtensionResources = (extension: vscode.Extension<any>, resourcePat
};
export interface MarkdownContributions {
readonly extensionPath: string;
readonly previewScripts: vscode.Uri[];
readonly previewStyles: vscode.Uri[];
readonly markdownItPlugins: Thenable<(md: any) => any>[];
......@@ -40,6 +41,10 @@ class MarkdownExtensionContributions implements MarkdownContributions {
private _loaded = false;
public constructor(
public readonly extensionPath: string,
) { }
public get previewScripts(): vscode.Uri[] {
this.ensureLoaded();
return this._scripts;
......@@ -111,6 +116,6 @@ class MarkdownExtensionContributions implements MarkdownContributions {
}
}
export function getMarkdownExtensionContributions(): MarkdownContributions {
return new MarkdownExtensionContributions();
export function getMarkdownExtensionContributions(context: vscode.ExtensionContext): MarkdownContributions {
return new MarkdownExtensionContributions(context.extensionPath);
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ import { MarkdownContributions } from '../markdownExtensions';
import { githubSlugifier } from '../slugify';
const emptyContributions = new class implements MarkdownContributions {
readonly extensionPath = '';
readonly previewScripts: vscode.Uri[] = [];
readonly previewStyles: vscode.Uri[] = [];
readonly previewResourceRoots: vscode.Uri[] = [];
......
......@@ -5500,6 +5500,11 @@ declare module 'vscode' {
*/
title: string;
/**
* Icon for the panel shown in UI.
*/
iconPath?: Uri | { light: Uri; dark: Uri };
/**
* Webview belonging to the panel.
*/
......
......@@ -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)
};
}
\ No newline at end of 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>;
......
......@@ -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;
}
......
......@@ -5,6 +5,7 @@
import * as DOM from 'vs/base/browser/dom';
import { domEvent } from 'vs/base/browser/event';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
......@@ -14,13 +15,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { EditorOptions } from 'vs/workbench/common/editor';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { WebviewEditorInput } from 'vs/workbench/parts/webview/electron-browser/webviewEditorInput';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { BaseWebviewEditor, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FIND_WIDGET_INPUT_FOCUSED, KEYBINDING_CONTEXT_WEBVIEWEDITOR_FOCUS, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE } from './baseWebviewEditor';
import { WebviewElement } from './webviewElement';
import { CancellationToken } from 'vs/base/common/cancellation';
export class WebviewEditor extends BaseWebviewEditor {
......
......@@ -3,15 +3,16 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { EditorInput, EditorModel, IEditorInput, GroupIdentifier } from 'vs/workbench/common/editor';
import { EditorInput, EditorModel, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import * as vscode from 'vscode';
import { WebviewEvents, WebviewInputOptions, WebviewReviver } from './webviewEditorService';
import { WebviewElement } from './webviewElement';
import * as vscode from 'vscode';
export class WebviewEditorInput extends EditorInput {
private static handlePool = 0;
......@@ -35,6 +36,7 @@ export class WebviewEditorInput extends EditorInput {
private _revived: boolean = false;
public readonly extensionLocation: URI | undefined;
private readonly _id: number;
constructor(
public readonly viewType: string,
......@@ -47,6 +49,7 @@ export class WebviewEditorInput extends EditorInput {
@IPartService private readonly _partService: IPartService,
) {
super();
this._id = WebviewEditorInput.handlePool++;
this._name = name;
this._options = options;
this._events = events;
......@@ -58,6 +61,13 @@ export class WebviewEditorInput extends EditorInput {
return WebviewEditorInput.typeId;
}
public getId(): number {
return this._id;
}
private readonly _onDidChangeIcon = this._register(new Emitter<void>());
public readonly onDidChangeIcon = this._onDidChangeIcon.event;
public dispose() {
this.disposeWebview();
......@@ -76,7 +86,10 @@ export class WebviewEditorInput extends EditorInput {
}
public getResource(): URI {
return null;
return URI.from({
scheme: 'webview-panel',
path: `webview-panel/webview-${this._id}`
});
}
public getName(): string {
......@@ -169,9 +182,8 @@ export class WebviewEditorInput extends EditorInput {
public get container(): HTMLElement {
if (!this._container) {
const id = WebviewEditorInput.handlePool++;
this._container = document.createElement('div');
this._container.id = `webview-${id}`;
this._container.id = `webview-${this._id}`;
this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container);
}
return this._container;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册