Browse Source

screenshot: copy to clipboard

David Sehnal 4 years ago
parent
commit
a6c25551dd

+ 0 - 2
src/extensions/mp4-export/encoder.ts

@@ -7,7 +7,6 @@ import { PluginContext } from '../../mol-plugin/context';
 import { RuntimeContext } from '../../mol-task';
 import { Color } from '../../mol-util/color';
 
-
 export interface Mp4EncoderParams<A extends PluginStateAnimation = PluginStateAnimation> {
     pass: ImagePass,
     customBackground?: Color,
@@ -35,7 +34,6 @@ export async function encodeMp4Animation<A extends PluginStateAnimation>(plugin:
     const { width, height } = params;
     const vw = params.viewport?.width ?? width, vh = params.viewport?.height ?? height;
 
-
     encoder.width = vw;
     encoder.height = vh;
     if (params.quantizationParameter) encoder.quantizationParameter = params.quantizationParameter;

+ 2 - 0
src/mol-plugin-ui/controls/icons.tsx

@@ -97,6 +97,8 @@ const _CloudUpload = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d
 export function CloudUploadSvg() { return _CloudUpload; }
 const _Code = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z' /></svg>;
 export function CodeSvg() { return _Code; }
+const _Copy = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4H8c-1.1 0-1.99.9-1.99 2L6 21c0 1.1.89 2 1.99 2H19c1.1 0 2-.9 2-2V11l-6-6zM8 21V7h6v5h5v9H8z' /></svg>;
+export function CopySvg() { return _Copy; }
 const _DeleteOutlined = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M16 9v10H8V9h8m-1.5-6h-5l-1 1H5v2h14V4h-3.5l-1-1zM18 7H6v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7z' /></svg>;
 export function DeleteOutlinedSvg() { return _DeleteOutlined; }
 const _Delete = <svg width='24px' height='24px' viewBox='0 0 24 24'><path d='M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z' /></svg>;

+ 9 - 21
src/mol-plugin-ui/viewport/screenshot.tsx

@@ -16,7 +16,7 @@ import { Button, ExpandGroup } from '../controls/common';
 import { CameraHelperProps } from '../../mol-canvas3d/helper/camera-helper';
 import { PluginCommands } from '../../mol-plugin/commands';
 import { StateExportImportControls, LocalStateSnapshotParams } from '../state/snapshots';
-import { GetAppSvg, LaunchSvg } from '../controls/icons';
+import { CopySvg, GetAppSvg, LaunchSvg } from '../controls/icons';
 
 interface ImageControlsState {
     showPreview: boolean
@@ -49,26 +49,14 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
         this.props.close();
     }
 
-    private openTab = () => {
-        // modified from https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript/16245768#16245768
-
-        const base64 = this.imgRef.current!.src;
-        const byteCharacters = atob(base64.substr(`data:image/png;base64,`.length));
-        const byteArrays = [];
-
-        const sliceSize = Math.min(byteCharacters.length, 1024 * 1024);
-        for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
-            const byteNumbers = new Uint8Array(Math.min(sliceSize, byteCharacters.length - offset));
-            for (let i = 0, _i = byteNumbers.length; i < _i; i++) {
-                byteNumbers[i] = byteCharacters.charCodeAt(offset + i);
-            }
-            byteArrays.push(byteNumbers);
-        }
-        const blob = new Blob(byteArrays, { type: 'image/png' });
-        const blobUrl = URL.createObjectURL(blob);
-
-        window.open(blobUrl, '_blank');
+    private copy = async () => {
+        await this.plugin.helpers.viewportScreenshot?.copyToClipboard();
         this.props.close();
+        PluginCommands.Toast.Show(this.plugin, {
+            message: 'Copied to clipboard.',
+            title: 'Screenshot',
+            timeoutMs: 1500
+        });
     }
 
     private handlePreview() {
@@ -140,8 +128,8 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
                 <span>Right-click the image to Copy.</span>
             </div>
             <div className='msp-flex-row'>
+                {!(navigator.clipboard as any).write && <Button icon={CopySvg} onClick={this.copy} disabled={this.state.isDisabled}>Copy</Button>}
                 <Button icon={GetAppSvg} onClick={this.download} disabled={this.state.isDisabled}>Download</Button>
-                <Button icon={LaunchSvg} onClick={this.openTab} disabled={this.state.isDisabled}>Open in new Tab</Button>
             </div>
             <ParameterControls params={this.plugin.helpers.viewportScreenshot!.params} values={this.plugin.helpers.viewportScreenshot!.values} onChange={this.setProps} isDisabled={this.state.isDisabled} />
             <ExpandGroup header='State'>

+ 18 - 1
src/mol-plugin/util/viewport-screenshot.ts

@@ -160,6 +160,21 @@ class ViewportScreenshotHelper {
         return;
     }
 
+    async copyToClipboard() {
+        const cb = navigator.clipboard as any;
+
+        if (!cb.write) {
+            this.plugin.log.error('clipboard.write not supported!');
+            return;
+        }
+
+        const blob = await canvasToBlob(this.canvas, 'png');
+        const item = new ClipboardItem({ 'image/png': blob });
+        cb.write([item]);
+
+        this.plugin.log.message('Image copied to clipboard.');
+    }
+
     private downloadTask() {
         return Task.create('Download Image', async ctx => {
             this.draw(ctx);
@@ -189,4 +204,6 @@ class ViewportScreenshotHelper {
     constructor(private plugin: PluginContext) {
 
     }
-}
+}
+
+declare const ClipboardItem: any;