mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -1,3 +1,17 @@
|
||||
# Image Preview
|
||||
|
||||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
||||
|
||||
## Features
|
||||
|
||||
This extension provides VS Code's built-in image preview functionality.
|
||||
|
||||
Supported image formats:
|
||||
|
||||
- `*.jpg`, `*.jpe`, `*.jpeg`
|
||||
- `*.png`
|
||||
- `*.bmp`
|
||||
- `*.gif`
|
||||
- `*.ico`
|
||||
- `*.tga`
|
||||
- `*.webp`
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body img {
|
||||
@@ -77,22 +78,38 @@ body img {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.loading {
|
||||
position: fixed;
|
||||
.container.loading,
|
||||
.container.error {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading-indicator {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -15px;
|
||||
margin-left: -15px;
|
||||
background-image: url('./loading.svg');
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.vscode-dark .loading {
|
||||
.loading-indicator,
|
||||
.image-load-error-message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.loading .loading-indicator,
|
||||
.error .image-load-error-message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.image-load-error-message {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.vscode-dark .loading-indicator {
|
||||
background-image: url('./loading-dark.svg');
|
||||
}
|
||||
|
||||
.vscode-high-contrast .loading {
|
||||
.vscode-high-contrast .loading-indicator {
|
||||
background-image: url('./loading-hc.svg');
|
||||
}
|
||||
|
||||
@@ -70,9 +70,10 @@
|
||||
let ctrlPressed = false;
|
||||
let altPressed = false;
|
||||
let hasLoadedImage = false;
|
||||
let consumeClick = false;
|
||||
|
||||
// Elements
|
||||
const container = /** @type {HTMLElement} */(document.querySelector('body'));
|
||||
const container = document.body;
|
||||
const image = document.createElement('img');
|
||||
|
||||
function updateScale(newScale) {
|
||||
@@ -88,9 +89,6 @@
|
||||
image.style.width = 'auto';
|
||||
vscode.setState(undefined);
|
||||
} else {
|
||||
const oldWidth = image.width;
|
||||
const oldHeight = image.height;
|
||||
|
||||
scale = clamp(newScale, MIN_SCALE, MAX_SCALE);
|
||||
if (scale >= PIXELATION_THRESHOLD) {
|
||||
image.classList.add('pixelated');
|
||||
@@ -98,25 +96,19 @@
|
||||
image.classList.remove('pixelated');
|
||||
}
|
||||
|
||||
const { scrollTop, scrollLeft } = image.parentElement;
|
||||
const dx = (scrollLeft + image.parentElement.clientWidth / 2) / image.parentElement.scrollWidth;
|
||||
const dy = (scrollTop + image.parentElement.clientHeight / 2) / image.parentElement.scrollHeight;
|
||||
const dx = (window.scrollX + container.clientWidth / 2) / container.scrollWidth;
|
||||
const dy = (window.scrollY + container.clientHeight / 2) / container.scrollHeight;
|
||||
|
||||
image.classList.remove('scale-to-fit');
|
||||
image.style.minWidth = `${(image.naturalWidth * scale)}px`;
|
||||
image.style.width = `${(image.naturalWidth * scale)}px`;
|
||||
|
||||
const newWidth = image.width;
|
||||
const scaleFactor = (newWidth - oldWidth) / oldWidth;
|
||||
const newScrollX = container.scrollWidth * dx - container.clientWidth / 2;
|
||||
const newScrollY = container.scrollHeight * dy - container.clientHeight / 2;
|
||||
|
||||
const newScrollLeft = ((oldWidth * scaleFactor * dx) + scrollLeft);
|
||||
const newScrollTop = ((oldHeight * scaleFactor * dy) + scrollTop);
|
||||
// scrollbar.setScrollPosition({
|
||||
// scrollLeft: newScrollLeft,
|
||||
// scrollTop: newScrollTop,
|
||||
// });
|
||||
window.scrollTo(newScrollX, newScrollY);
|
||||
|
||||
vscode.setState({ scale: scale, offsetX: newScrollLeft, offsetY: newScrollTop });
|
||||
vscode.setState({ scale: scale, offsetX: newScrollX, offsetY: newScrollY });
|
||||
}
|
||||
|
||||
vscode.postMessage({
|
||||
@@ -125,6 +117,18 @@
|
||||
});
|
||||
}
|
||||
|
||||
function changeActive(value) {
|
||||
if (value) {
|
||||
container.classList.add('zoom-in');
|
||||
consumeClick = true;
|
||||
} else {
|
||||
ctrlPressed = false;
|
||||
altPressed = false;
|
||||
container.classList.remove('zoom-out');
|
||||
container.classList.remove('zoom-in');
|
||||
}
|
||||
}
|
||||
|
||||
function firstZoom() {
|
||||
if (!image || !hasLoadedImage) {
|
||||
return;
|
||||
@@ -161,6 +165,18 @@
|
||||
}
|
||||
});
|
||||
|
||||
container.addEventListener('mousedown', (/** @type {MouseEvent} */ e) => {
|
||||
if (!image || !hasLoadedImage) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
consumeClick = false;
|
||||
});
|
||||
|
||||
container.addEventListener('click', (/** @type {MouseEvent} */ e) => {
|
||||
if (!image || !hasLoadedImage) {
|
||||
return;
|
||||
@@ -170,6 +186,18 @@
|
||||
return;
|
||||
}
|
||||
|
||||
ctrlPressed = e.ctrlKey;
|
||||
altPressed = e.altKey;
|
||||
|
||||
if (isMac ? altPressed : ctrlPressed) {
|
||||
container.classList.remove('zoom-in');
|
||||
container.classList.add('zoom-out');
|
||||
}
|
||||
|
||||
if (consumeClick) {
|
||||
consumeClick = false;
|
||||
return;
|
||||
}
|
||||
// left click
|
||||
if (scale === 'fit') {
|
||||
firstZoom();
|
||||
@@ -227,24 +255,19 @@
|
||||
});
|
||||
|
||||
container.classList.add('image');
|
||||
container.classList.add('zoom-in');
|
||||
|
||||
image.classList.add('scale-to-fit');
|
||||
|
||||
image.addEventListener('load', () => {
|
||||
document.querySelector('.loading').remove();
|
||||
hasLoadedImage = true;
|
||||
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
vscode.postMessage({
|
||||
type: 'size',
|
||||
value: `${image.naturalWidth}x${image.naturalHeight}`,
|
||||
});
|
||||
|
||||
container.classList.add('ready');
|
||||
document.body.classList.remove('loading');
|
||||
document.body.classList.add('ready');
|
||||
document.body.append(image);
|
||||
|
||||
updateScale(scale);
|
||||
@@ -254,6 +277,12 @@
|
||||
}
|
||||
});
|
||||
|
||||
image.addEventListener('error', () => {
|
||||
hasLoadedImage = true;
|
||||
document.body.classList.add('error');
|
||||
document.body.classList.remove('loading');
|
||||
});
|
||||
|
||||
image.src = decodeURI(settings.src);
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
@@ -261,6 +290,9 @@
|
||||
case 'setScale':
|
||||
updateScale(e.data.scale);
|
||||
break;
|
||||
case 'setActive':
|
||||
changeActive(e.data.value);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}());
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
{
|
||||
"viewType": "imagePreview.previewEditor",
|
||||
"displayName": "%webviewEditors.displayName%",
|
||||
"priority": "builtin",
|
||||
"selector": [
|
||||
{
|
||||
"filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,tif,tiff,webp}",
|
||||
"filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,webp}",
|
||||
"mime": "image/*"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"displayName": "Image Preview",
|
||||
"description": "Previews images.",
|
||||
"description": "Provides VS Code's built-in image preview",
|
||||
"webviewEditors.displayName": "Image Preview"
|
||||
}
|
||||
|
||||
@@ -4,19 +4,32 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
|
||||
import { ZoomStatusBarEntry } from './zoomStatusBarEntry';
|
||||
import * as nls from 'vscode-nls';
|
||||
import { Disposable } from './dispose';
|
||||
import { SizeStatusBarEntry } from './sizeStatusBarEntry';
|
||||
import { Scale, ZoomStatusBarEntry } from './zoomStatusBarEntry';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const enum PreviewState {
|
||||
Disposed,
|
||||
Visible,
|
||||
Active,
|
||||
}
|
||||
|
||||
export class Preview extends Disposable {
|
||||
|
||||
public static readonly viewType = 'imagePreview.previewEditor';
|
||||
|
||||
private _active = true;
|
||||
private readonly id: string = `${Date.now()}-${Math.random().toString()}`;
|
||||
|
||||
private _previewState = PreviewState.Visible;
|
||||
private _imageSize: string | undefined;
|
||||
private _imageZoom: Scale | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly extensionRoot: vscode.Uri,
|
||||
resource: vscode.Uri,
|
||||
private readonly resource: vscode.Uri,
|
||||
private readonly webviewEditor: vscode.WebviewEditor,
|
||||
private readonly sizeStatusBarEntry: SizeStatusBarEntry,
|
||||
private readonly zoomStatusBarEntry: ZoomStatusBarEntry,
|
||||
@@ -34,56 +47,91 @@ export class Preview extends Disposable {
|
||||
]
|
||||
};
|
||||
|
||||
webviewEditor.webview.html = this.getWebiewContents(webviewEditor, resource);
|
||||
|
||||
this._register(webviewEditor.webview.onDidReceiveMessage(message => {
|
||||
switch (message.type) {
|
||||
case 'size':
|
||||
{
|
||||
this.sizeStatusBarEntry.update(message.value);
|
||||
this._imageSize = message.value;
|
||||
this.update();
|
||||
break;
|
||||
}
|
||||
case 'zoom':
|
||||
{
|
||||
this.zoomStatusBarEntry.update(message.value);
|
||||
this._imageZoom = message.value;
|
||||
this.update();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(zoomStatusBarEntry.onDidChangeScale(e => {
|
||||
this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale });
|
||||
if (this._previewState === PreviewState.Active) {
|
||||
this.webviewEditor.webview.postMessage({ type: 'setScale', scale: e.scale });
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(webviewEditor.onDidChangeViewState(() => {
|
||||
this.update();
|
||||
}));
|
||||
|
||||
this._register(webviewEditor.onDidDispose(() => {
|
||||
if (this._active) {
|
||||
this.sizeStatusBarEntry.hide();
|
||||
this.zoomStatusBarEntry.hide();
|
||||
if (this._previewState === PreviewState.Active) {
|
||||
this.sizeStatusBarEntry.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();
|
||||
}
|
||||
}));
|
||||
|
||||
this.render();
|
||||
this.update();
|
||||
}
|
||||
|
||||
private update() {
|
||||
this._active = this.webviewEditor.active;
|
||||
if (this._active) {
|
||||
this.sizeStatusBarEntry.show();
|
||||
this.zoomStatusBarEntry.show();
|
||||
} else {
|
||||
this.sizeStatusBarEntry.hide();
|
||||
this.zoomStatusBarEntry.hide();
|
||||
private render() {
|
||||
if (this._previewState !== PreviewState.Disposed) {
|
||||
this.webviewEditor.webview.html = this.getWebiewContents();
|
||||
}
|
||||
}
|
||||
|
||||
private getWebiewContents(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri): string {
|
||||
private update() {
|
||||
if (this._previewState === PreviewState.Disposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.webviewEditor.active) {
|
||||
this._previewState = PreviewState.Active;
|
||||
this.sizeStatusBarEntry.show(this.id, this._imageSize || '');
|
||||
this.zoomStatusBarEntry.show(this.id, this._imageZoom || 'fit');
|
||||
} else {
|
||||
if (this._previewState === PreviewState.Active) {
|
||||
this.sizeStatusBarEntry.hide(this.id);
|
||||
this.zoomStatusBarEntry.hide(this.id);
|
||||
}
|
||||
this._previewState = PreviewState.Visible;
|
||||
}
|
||||
this.webviewEditor.webview.postMessage({ type: 'setActive', value: this.webviewEditor.active });
|
||||
}
|
||||
|
||||
private getWebiewContents(): string {
|
||||
const version = Date.now().toString();
|
||||
const settings = {
|
||||
isMac: process.platform === 'darwin',
|
||||
src: this.getResourcePath(webviewEditor, resource)
|
||||
src: this.getResourcePath(this.webviewEditor, this.resource, version),
|
||||
};
|
||||
|
||||
const nonce = Date.now().toString();
|
||||
|
||||
return /* html */`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@@ -91,23 +139,33 @@ export class Preview extends Disposable {
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Image Preview</title>
|
||||
<link rel="stylesheet" class="code-user-style" href="${escapeAttribute(this.extensionResource('/media/main.css'))}" type="text/css" media="screen">
|
||||
|
||||
<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">
|
||||
<div class='loading'></div>
|
||||
<script src="${escapeAttribute(this.extensionResource('/media/main.js'))}"></script>
|
||||
<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.WebviewEditor, resource: vscode.Uri) {
|
||||
if (resource.scheme === 'data') {
|
||||
return encodeURI(resource.toString(true));
|
||||
}
|
||||
private getResourcePath(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri, version: string) {
|
||||
switch (resource.scheme) {
|
||||
case 'data':
|
||||
return encodeURI(resource.toString(true));
|
||||
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true));
|
||||
case 'git':
|
||||
// Show blank image
|
||||
return encodeURI('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAEElEQVR42gEFAPr/AP///wAI/AL+Sr4t6gAAAABJRU5ErkJggg==');
|
||||
|
||||
|
||||
default:
|
||||
return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true) + `?version=${version}`);
|
||||
}
|
||||
}
|
||||
|
||||
private extensionResource(path: string) {
|
||||
|
||||
@@ -5,29 +5,35 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Disposable } from './dispose';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class SizeStatusBarEntry extends Disposable {
|
||||
private readonly _entry: vscode.StatusBarItem;
|
||||
|
||||
private _showingOwner: string | undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._entry = this._register(vscode.window.createStatusBarItem({
|
||||
id: 'imagePreview.size',
|
||||
name: 'Image Size',
|
||||
name: localize('sizeStatusBar.name', "Image Size"),
|
||||
alignment: vscode.StatusBarAlignment.Right,
|
||||
priority: 101 /* to the left of editor status (100) */,
|
||||
}));
|
||||
}
|
||||
|
||||
public show() {
|
||||
public show(owner: string, text: string) {
|
||||
this._showingOwner = owner;
|
||||
this._entry.text = text;
|
||||
this._entry.show();
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this._entry.hide();
|
||||
}
|
||||
|
||||
public update(text: string) {
|
||||
this._entry.text = text;
|
||||
public hide(owner: string) {
|
||||
if (owner === this._showingOwner) {
|
||||
this._entry.hide();
|
||||
this._showingOwner = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ const localize = nls.loadMessageBundle();
|
||||
|
||||
const selectZoomLevelCommandId = '_imagePreview.selectZoomLevel';
|
||||
|
||||
type Scale = number | 'fit';
|
||||
export type Scale = number | 'fit';
|
||||
|
||||
export class ZoomStatusBarEntry extends Disposable {
|
||||
private readonly _entry: vscode.StatusBarItem;
|
||||
@@ -19,11 +19,13 @@ export class ZoomStatusBarEntry extends Disposable {
|
||||
private readonly _onDidChangeScale = this._register(new vscode.EventEmitter<{ scale: Scale }>());
|
||||
public readonly onDidChangeScale = this._onDidChangeScale.event;
|
||||
|
||||
private _showOwner: string | undefined;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._entry = this._register(vscode.window.createStatusBarItem({
|
||||
id: 'imagePreview.zoom',
|
||||
name: 'Image Zoom',
|
||||
name: localize('zoomStatusBar.name', "Image Zoom"),
|
||||
alignment: vscode.StatusBarAlignment.Right,
|
||||
priority: 102 /* to the left of editor size entry (101) */,
|
||||
}));
|
||||
@@ -48,16 +50,17 @@ export class ZoomStatusBarEntry extends Disposable {
|
||||
this._entry.command = selectZoomLevelCommandId;
|
||||
}
|
||||
|
||||
public show() {
|
||||
public show(owner: string, scale: Scale) {
|
||||
this._showOwner = owner;
|
||||
this._entry.text = this.zoomLabel(scale);
|
||||
this._entry.show();
|
||||
}
|
||||
|
||||
public hide() {
|
||||
this._entry.hide();
|
||||
}
|
||||
|
||||
public update(scale: Scale) {
|
||||
this._entry.text = this.zoomLabel(scale);
|
||||
public hide(owner: string) {
|
||||
if (owner === this._showOwner) {
|
||||
this._entry.hide();
|
||||
this._showOwner = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private zoomLabel(scale: Scale): string {
|
||||
|
||||
Reference in New Issue
Block a user