mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 17:22:51 -05:00
* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c * remove files we don't want * fix hygiene * update distro * update distro * fix hygiene * fix strict nulls * distro * distro * fix tests * fix tests * add another edit * fix viewlet icon * fix azure dialog * fix some padding * fix more padding issues
257 lines
8.0 KiB
TypeScript
257 lines
8.0 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as vscode from 'vscode';
|
|
import * as nls from 'vscode-nls';
|
|
import { Disposable } from './dispose';
|
|
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
|
|
import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry';
|
|
import { BinarySizeStatusBarEntry } from './binarySizeStatusBarEntry';
|
|
|
|
const localize = nls.loadMessageBundle();
|
|
|
|
|
|
export class PreviewManager implements vscode.WebviewEditorProvider {
|
|
|
|
public static readonly viewType = 'imagePreview.previewEditor';
|
|
|
|
private readonly _previews = new Set<Preview>();
|
|
private _activePreview: Preview | undefined;
|
|
|
|
constructor(
|
|
private readonly extensionRoot: vscode.Uri,
|
|
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
|
|
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
|
|
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
|
|
) { }
|
|
|
|
public async resolveWebviewEditor(
|
|
input: { readonly resource: vscode.Uri, },
|
|
webviewEditor: vscode.WebviewPanel,
|
|
): Promise<vscode.WebviewEditorCapabilities> {
|
|
const preview = new Preview(this.extensionRoot, input.resource, webviewEditor, this.sizeStatusBarEntry, this.binarySizeStatusBarEntry, this.zoomStatusBarEntry);
|
|
this._previews.add(preview);
|
|
this.setActivePreview(preview);
|
|
|
|
webviewEditor.onDidDispose(() => { this._previews.delete(preview); });
|
|
|
|
webviewEditor.onDidChangeViewState(() => {
|
|
if (webviewEditor.active) {
|
|
this.setActivePreview(preview);
|
|
} else if (this._activePreview === preview && !webviewEditor.active) {
|
|
this.setActivePreview(undefined);
|
|
}
|
|
});
|
|
|
|
return {};
|
|
}
|
|
|
|
public get activePreview() { return this._activePreview; }
|
|
|
|
private setActivePreview(value: Preview | undefined): void {
|
|
this._activePreview = value;
|
|
this.setPreviewActiveContext(!!value);
|
|
}
|
|
|
|
private setPreviewActiveContext(value: boolean) {
|
|
vscode.commands.executeCommand('setContext', 'imagePreviewFocus', value);
|
|
}
|
|
}
|
|
|
|
const enum PreviewState {
|
|
Disposed,
|
|
Visible,
|
|
Active,
|
|
}
|
|
|
|
class Preview extends Disposable {
|
|
|
|
private readonly id: string = `${Date.now()}-${Math.random().toString()}`;
|
|
|
|
private _previewState = PreviewState.Visible;
|
|
private _imageSize: string | undefined;
|
|
private _imageBinarySize: number | undefined;
|
|
private _imageZoom: Scale | undefined;
|
|
|
|
constructor(
|
|
private readonly extensionRoot: vscode.Uri,
|
|
private readonly resource: vscode.Uri,
|
|
private readonly webviewEditor: vscode.WebviewPanel,
|
|
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
|
|
private readonly binarySizeStatusBarEntry: BinarySizeStatusBarEntry,
|
|
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
|
|
) {
|
|
super();
|
|
const resourceRoot = resource.with({
|
|
path: resource.path.replace(/\/[^\/]+?\.\w+$/, '/'),
|
|
});
|
|
|
|
webviewEditor.webview.options = {
|
|
enableScripts: true,
|
|
localResourceRoots: [
|
|
resourceRoot,
|
|
extensionRoot,
|
|
]
|
|
};
|
|
|
|
this._register(webviewEditor.webview.onDidReceiveMessage(message => {
|
|
switch (message.type) {
|
|
case 'size':
|
|
{
|
|
this._imageSize = message.value;
|
|
this.update();
|
|
break;
|
|
}
|
|
case 'zoom':
|
|
{
|
|
this._imageZoom = message.value;
|
|
this.update();
|
|
break;
|
|
}
|
|
}
|
|
}));
|
|
|
|
this._register(zoomStatusBarEntry.onDidChangeScale(e => {
|
|
if (this._previewState === PreviewState.Active) {
|
|
this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale });
|
|
}
|
|
}));
|
|
|
|
this._register(webviewEditor.onDidChangeViewState(() => {
|
|
this.update();
|
|
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
|
|
}));
|
|
|
|
this._register(webviewEditor.onDidDispose(() => {
|
|
if (this._previewState === PreviewState.Active) {
|
|
this.sizeStatusBarEntry.hide(this.id);
|
|
this.binarySizeStatusBarEntry.hide(this.id);
|
|
this.zoomStatusBarEntry.hide(this.id);
|
|
}
|
|
this._previewState = PreviewState.Disposed;
|
|
}));
|
|
|
|
const watcher = this._register(vscode.workspace.createFileSystemWatcher(resource.fsPath));
|
|
this._register(watcher.onDidChange(e => {
|
|
if (e.toString() === this.resource.toString()) {
|
|
this.render();
|
|
}
|
|
}));
|
|
this._register(watcher.onDidDelete(e => {
|
|
if (e.toString() === this.resource.toString()) {
|
|
this.webviewEditor.dispose();
|
|
}
|
|
}));
|
|
|
|
vscode.workspace.fs.stat(resource).then(({ size }) => {
|
|
this._imageBinarySize = size;
|
|
this.update();
|
|
});
|
|
|
|
this.render();
|
|
this.update();
|
|
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
|
|
}
|
|
|
|
public zoomIn() {
|
|
if (this._previewState === PreviewState.Active) {
|
|
this.webviewEditor.webview.postMessage({ type: 'zoomIn' });
|
|
}
|
|
}
|
|
|
|
public zoomOut() {
|
|
if (this._previewState === PreviewState.Active) {
|
|
this.webviewEditor.webview.postMessage({ type: 'zoomOut' });
|
|
}
|
|
}
|
|
|
|
private render() {
|
|
if (this._previewState !== PreviewState.Disposed) {
|
|
this.webviewEditor.webview.html = this.getWebiewContents();
|
|
}
|
|
}
|
|
|
|
private update() {
|
|
if (this._previewState === PreviewState.Disposed) {
|
|
return;
|
|
}
|
|
|
|
if (this.webviewEditor.active) {
|
|
this._previewState = PreviewState.Active;
|
|
this.sizeStatusBarEntry.show(this.id, this._imageSize || '');
|
|
this.binarySizeStatusBarEntry.show(this.id, this._imageBinarySize);
|
|
this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit');
|
|
} else {
|
|
if (this._previewState === PreviewState.Active) {
|
|
this.sizeStatusBarEntry.hide(this.id);
|
|
this.binarySizeStatusBarEntry.hide(this.id);
|
|
this.zoomStatusBarEntry.hide(this.id);
|
|
}
|
|
this._previewState = PreviewState.Visible;
|
|
}
|
|
}
|
|
|
|
private getWebiewContents(): string {
|
|
const version = Date.now().toString();
|
|
const settings = {
|
|
isMac: process.platform === 'darwin',
|
|
src: this.getResourcePath(this.webviewEditor, this.resource, version),
|
|
};
|
|
|
|
const nonce = Date.now().toString();
|
|
|
|
return /* html */`<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
|
|
<!-- Disable pinch zooming -->
|
|
<meta name="viewport"
|
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
|
|
|
<title>Image Preview</title>
|
|
|
|
<link rel="stylesheet" href="${escapeAttribute(this.extensionResource('/media/main.css'))}" type="text/css" media="screen" nonce="${nonce}">
|
|
|
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' data: ${this.webviewEditor.webview.cspSource}; script-src 'nonce-${nonce}'; style-src 'self' 'nonce-${nonce}';">
|
|
<meta id="image-preview-settings" data-settings="${escapeAttribute(JSON.stringify(settings))}">
|
|
</head>
|
|
<body class="container image scale-to-fit loading">
|
|
<div class="loading-indicator"></div>
|
|
<div class="image-load-error-message">${localize('preview.imageLoadError', "An error occurred while loading the image")}</div>
|
|
<script src="${escapeAttribute(this.extensionResource('/media/main.js'))}" nonce="${nonce}"></script>
|
|
</body>
|
|
</html>`;
|
|
}
|
|
|
|
private getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string) {
|
|
switch (resource.scheme) {
|
|
case 'data':
|
|
return resource.toString(true);
|
|
|
|
case 'git':
|
|
// Show blank image
|
|
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==';
|
|
|
|
default:
|
|
// Avoid adding cache busting if there is already a query string
|
|
if (resource.query) {
|
|
return webviewEditor.webview.asWebviewUri(resource).toString(true);
|
|
}
|
|
return webviewEditor.webview.asWebviewUri(resource).with({ query: `version=${version}` }).toString(true);
|
|
}
|
|
}
|
|
|
|
private extensionResource(path: string) {
|
|
return this.webviewEditor.webview.asWebviewUri(this.extensionRoot.with({
|
|
path: this.extensionRoot.path + path
|
|
}));
|
|
}
|
|
}
|
|
|
|
function escapeAttribute(value: string | vscode.Uri): string {
|
|
return value.toString().replace(/"/g, '"');
|
|
}
|