diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 44dc6e17e3af..b4026776ceac 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -163,6 +163,10 @@ "message": "Copy Link", "description": "Shown in the context menu for a link to indicate that the user can copy the link" }, + "contextMenuCopyImage": { + "message": "Copy Image", + "description": "Shown in the context menu for an image to indicate that the user can copy the image" + }, "contextMenuNoSuggestions": { "message": "No Suggestions", "description": "Shown in the context menu for a misspelled word to indicate that there are no suggestions to replace the misspelled word" diff --git a/app/spell_check.js b/app/spell_check.js index 07f1208b940f..6adfa9cef052 100644 --- a/app/spell_check.js +++ b/app/spell_check.js @@ -1,8 +1,9 @@ /* eslint-disable strict */ -const { Menu, clipboard } = require('electron'); +const { Menu, clipboard, nativeImage } = require('electron'); const osLocale = require('os-locale'); const { uniq } = require('lodash'); +const url = require('url'); function getLanguages(userLocale, availableLocales) { const baseLocale = userLocale.split('-')[0]; @@ -37,7 +38,10 @@ exports.setup = (browserWindow, messages) => { const { editFlags } = params; const isMisspelled = Boolean(params.misspelledWord); const isLink = Boolean(params.linkURL); - const showMenu = params.isEditable || editFlags.canCopy || isLink; + const isImage = + params.mediaType === 'image' && params.hasImageContents && params.srcURL; + const showMenu = + params.isEditable || editFlags.canCopy || isLink || isImage; // Popup editor menu if (showMenu) { @@ -79,25 +83,43 @@ exports.setup = (browserWindow, messages) => { } } - if (editFlags.canCopy || isLink) { + if (editFlags.canCopy || isLink || isImage) { + let click; + let label; + + if (isLink) { + click = () => { + clipboard.writeText(params.linkURL); + }; + label = messages.contextMenuCopyLink.message; + } else if (isImage) { + click = () => { + if (url.parse(params.srcURL).protocol !== 'file:') { + return; + } + + const image = nativeImage.createFromPath( + url.fileURLToPath(params.srcURL) + ); + clipboard.writeImage(image); + }; + label = messages.contextMenuCopyImage.message; + } else { + label = messages.editMenuCopy.message; + } + template.push({ - label: isLink - ? messages.contextMenuCopyLink.message - : messages.editMenuCopy.message, - role: isLink ? undefined : 'copy', - click: isLink - ? () => { - clipboard.writeText(params.linkURL); - } - : undefined, + label, + role: isLink || isImage ? undefined : 'copy', + click, }); } - if (editFlags.canPaste) { + if (editFlags.canPaste && !isImage) { template.push({ label: messages.editMenuPaste.message, role: 'paste' }); } - if (editFlags.canPaste) { + if (editFlags.canPaste && !isImage) { template.push({ label: messages.editMenuPasteAndMatchStyle.message, role: 'pasteAndMatchStyle', diff --git a/preload.js b/preload.js index 3f8549e8e53c..d5f42d9b36f7 100644 --- a/preload.js +++ b/preload.js @@ -544,7 +544,8 @@ try { ); const link = e.target.closest('a'); const selection = Boolean(window.getSelection().toString()); - if (!editable && !selection && !link) { + const image = e.target.closest('.module-lightbox img'); + if (!editable && !selection && !link && !image) { e.preventDefault(); } }); diff --git a/ts/components/Lightbox.tsx b/ts/components/Lightbox.tsx index b7a9231f3169..b9f38bad0eca 100644 --- a/ts/components/Lightbox.tsx +++ b/ts/components/Lightbox.tsx @@ -376,6 +376,7 @@ export class Lightbox extends React.Component { alt={i18n('lightboxImageAlt')} style={styles.img} src={objectURL} + onContextMenu={this.onContextMenu} /> ); @@ -415,6 +416,15 @@ export class Lightbox extends React.Component { ); }; + private readonly onContextMenu = (event: React.MouseEvent) => { + const { contentType } = this.props; + + // These are the only image types supported by Electron's NativeImage + if (contentType !== "image/png" && contentType !== "image/jpg") { + event?.preventDefault(); + } + } + private readonly onClose = () => { const { close } = this.props; if (!close) {