From c7c334302f47872ccdd8c8d9cfbac12e4a16f718 Mon Sep 17 00:00:00 2001 From: Brian Leighty Date: Wed, 20 Dec 2023 15:18:27 -0500 Subject: [PATCH] Always request screenshot on initial load, add ability to copy screenshot (#536) * Always request screenshot on initial load, add ability to copy screenshot * Always show copyScreenshot in device view --------- Co-authored-by: Brian Leighty --- package.json | 17 +++++++++ src/commands/VscodeCommand.ts | 1 + src/viewProviders/BaseWebviewViewProvider.ts | 13 +++++-- .../RokuDeviceViewViewProvider.ts | 5 +++ .../RokuDeviceView/RokuDeviceView.svelte | 38 +++++++++++++++---- 5 files changed, 63 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 0ad30633..a0f95bb5 100644 --- a/package.json +++ b/package.json @@ -301,6 +301,11 @@ "when": "view == rokuDeviceView && brightscript.isOnDeviceComponentAvailable && brightscript.rokuDeviceView.isInspectingNodes", "group": "navigation@3" }, + { + "command": "extension.brightscript.rokuDeviceView.copyScreenshot", + "when": "view == rokuDeviceView", + "group": "navigation@4" + }, { "command": "extension.brightscript.rokuAutomationView.startRecording", "when": "view == rokuAutomationView && !brightscript.rokuAutomationView.isRecording", @@ -322,6 +327,12 @@ "group": "navigation@2" } ], + "webview/context": [ + { + "command": "extension.brightscript.rokuDeviceView.copyScreenshot", + "when": "webviewId == rokuDeviceView" + } + ], "commandPalette": [] }, "breakpoints": [ @@ -2838,6 +2849,12 @@ "category": "BrighterScript", "icon": "$(refresh)" }, + { + "command": "extension.brightscript.rokuDeviceView.copyScreenshot", + "title": "Copy Screenshot", + "category": "BrighterScript", + "icon": "$(clippy)" + }, { "command": "extension.brightscript.sceneGraphInspectorView.refreshNodeTree", "title": "Refresh Nodetree", diff --git a/src/commands/VscodeCommand.ts b/src/commands/VscodeCommand.ts index 4c14d3eb..a0d943fd 100644 --- a/src/commands/VscodeCommand.ts +++ b/src/commands/VscodeCommand.ts @@ -2,6 +2,7 @@ export enum VscodeCommand { rokuDeviceViewRefreshScreenshot = 'extension.brightscript.rokuDeviceView.refreshScreenshot', rokuDeviceViewResumeScreenshotCapture = 'extension.brightscript.rokuDeviceView.resumeScreenshotCapture', rokuDeviceViewPauseScreenshotCapture = 'extension.brightscript.rokuDeviceView.pauseScreenshotCapture', + rokuDeviceViewCopyScreenshot = 'extension.brightscript.rokuDeviceView.copyScreenshot', rokuDeviceViewEnableNodeInspector = 'extension.brightscript.rokuDeviceView.enableNodeInspector', rokuDeviceViewDisableNodeInspector = 'extension.brightscript.rokuDeviceView.disableNodeInspector', rokuRegistryExportRegistry = 'extension.brightscript.rokuRegistry.exportRegistry', diff --git a/src/viewProviders/BaseWebviewViewProvider.ts b/src/viewProviders/BaseWebviewViewProvider.ts index 9e6955b3..2797b11c 100644 --- a/src/viewProviders/BaseWebviewViewProvider.ts +++ b/src/viewProviders/BaseWebviewViewProvider.ts @@ -124,14 +124,21 @@ export abstract class BaseWebviewViewProvider implements vscode.WebviewViewProvi }); } - protected registerCommandWithWebViewNotifier(context: vscode.ExtensionContext, command: string) { - context.subscriptions.push(vscode.commands.registerCommand(command, () => { + protected registerCommandWithWebViewNotifier(context: vscode.ExtensionContext, command: string, callback: (() => any) | undefined = undefined) { + this.registerCommand(context, command, async () => { + if (callback) { + await callback(); + } const message = this.createEventMessage(ViewProviderEvent.onVscodeCommandReceived, { commandName: command }); this.postOrQueueMessage(message); - })); + }); + } + + protected registerCommand(context: vscode.ExtensionContext, command: string, callback: (...args: any[]) => any) { + context.subscriptions.push(vscode.commands.registerCommand(command, callback)); } protected onViewReady() { } diff --git a/src/viewProviders/RokuDeviceViewViewProvider.ts b/src/viewProviders/RokuDeviceViewViewProvider.ts index 4b6e6332..b4a3c2aa 100644 --- a/src/viewProviders/RokuDeviceViewViewProvider.ts +++ b/src/viewProviders/RokuDeviceViewViewProvider.ts @@ -15,6 +15,11 @@ export class RokuDeviceViewViewProvider extends BaseRdbViewProvider { this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewRefreshScreenshot); this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewPauseScreenshotCapture); this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewResumeScreenshotCapture); + this.registerCommandWithWebViewNotifier(context, VscodeCommand.rokuDeviceViewCopyScreenshot, () => { + // In order for copy to be successful the webview has to have focus + this.view.show(false); + }); + this.addMessageCommandCallback(ViewProviderCommand.getScreenshot, async (message) => { try { diff --git a/webviews/src/views/RokuDeviceView/RokuDeviceView.svelte b/webviews/src/views/RokuDeviceView/RokuDeviceView.svelte index f2368686..680dd235 100644 --- a/webviews/src/views/RokuDeviceView/RokuDeviceView.svelte +++ b/webviews/src/views/RokuDeviceView/RokuDeviceView.svelte @@ -16,7 +16,10 @@ let deviceAvailable = false; intermediary.observeEvent(ViewProviderEvent.onDeviceAvailabilityChange, (message) => { deviceAvailable = message.context.deviceAvailable; - requestScreenshot(); + if (deviceAvailable) { + // We want to always request a screenshot when someone first opens the panel so we are forcing it + requestScreenshot(true); + } }); let wasRunningScreenshotCaptureBeforeInspect: boolean | undefined; @@ -184,6 +187,8 @@ isInspectingNodes = false; } + let currentScreenshot: Blob; + intermediary.observeEvent(ViewProviderEvent.onVscodeCommandReceived, async (message) => { const name = message.context.commandName; if (name === VscodeCommand.rokuDeviceViewEnableNodeInspector) { @@ -216,23 +221,38 @@ includeBoundingRectInfo: true }); } + } else if (name === VscodeCommand.rokuDeviceViewCopyScreenshot) { + if(!currentScreenshot) { + await requestScreenshot(true); + } + + // Need time for the DOM to become focused before clipboard seems to work + setTimeout(async () => { + const clipboardItem = new ClipboardItem({ + [currentScreenshot.type]: currentScreenshot + }); + await navigator.clipboard.write([clipboardItem]); + }, 100); } }); let screenshotOutOfDateTimeOut; let currentlyCapturingScreenshot = false; - async function requestScreenshot() { - if (!enableScreenshotCapture || currentlyCapturingScreenshot || !deviceAvailable) { - return; + async function requestScreenshot(force = false) { + if (!enableScreenshotCapture || !deviceAvailable) { + if (!force || currentlyCapturingScreenshot) { + return; + } } currentlyCapturingScreenshot = true; try { const {success, arrayBuffer} = await intermediary.sendCommand(ViewProviderCommand.getScreenshot) as any; if (success) { - const newScreenshotUrl = URL.createObjectURL(new Blob( + currentScreenshot = new Blob( [new Uint8Array(arrayBuffer)], - { type: 'image/jpeg' } - )); + { type: 'image/png' } // jpg isn't supported for clipboard and png seems to work for both so ¯\_(ツ)_/¯ + ); + const newScreenshotUrl = URL.createObjectURL(currentScreenshot); URL.revokeObjectURL(screenshotUrl); screenshotUrl = newScreenshotUrl; currentlyCapturingScreenshot = false; @@ -344,7 +364,8 @@ bind:clientWidth={screenshotContainerWidth} bind:clientHeight={screenshotContainerHeight} on:mousemove={onImageMouseMove} - on:mousedown={onMouseDown}> + on:mousedown={onMouseDown} + data-vscode-context={'{"preventDefaultContextMenuItems": true}'}> {#if focusedTreeNode}
@@ -360,6 +381,7 @@ {/if} Screenshot from Roku device