From 24bed01fa40ab57cca036a20e6c4222cc2fa7f71 Mon Sep 17 00:00:00 2001 From: Daniel Imms Date: Thu, 25 May 2017 16:17:43 -0700 Subject: [PATCH] Support copy and paste via context menu --- src/SelectionModel.ts | 4 ++ src/handlers/Clipboard.ts | 84 +++++++++++---------------------------- src/xterm.js | 12 +++--- 3 files changed, 33 insertions(+), 67 deletions(-) diff --git a/src/SelectionModel.ts b/src/SelectionModel.ts index ca69799f..bcb7a9ce 100644 --- a/src/SelectionModel.ts +++ b/src/SelectionModel.ts @@ -52,6 +52,10 @@ export class SelectionModel { * word selection and triple click line selection. */ public get finalSelectionEnd(): [number, number] { + if (!this.selectionStart) { + return null; + } + if (this.isSelectAllActive) { return [this._terminal.cols - 1, this._terminal.ydisp + this._terminal.rows - 1]; } diff --git a/src/handlers/Clipboard.ts b/src/handlers/Clipboard.ts index 2d1f5bc2..26305602 100644 --- a/src/handlers/Clipboard.ts +++ b/src/handlers/Clipboard.ts @@ -99,67 +99,31 @@ export function pasteHandler(ev: ClipboardEvent, term: ITerminal) { /** * Bind to right-click event and allow right-click copy and paste. - * - * **Logic** - * If text is selected and right-click happens on selected text, then - * do nothing to allow seamless copying. - * If no text is selected or right-click is outside of the selection - * area, then bring the terminal's input below the cursor, in order to - * trigger the event on the textarea and allow-right click paste, without - * caring about disappearing selection. - * @param {MouseEvent} ev The original right click event to be handled - * @param {Terminal} term The terminal on which to apply the handled paste event + * @param ev The original right click event to be handled + * @param term The terminal on which to apply the handled paste event + * @param selectionManager The terminal's selection manager. */ -export function rightClickHandler(ev: MouseEvent, term: ITerminal) { - let s = document.getSelection(), - selectedText = prepareTextForClipboard(s.toString()), - clickIsOnSelection = false, - x = ev.clientX, - y = ev.clientY; - - if (s.rangeCount) { - let r = s.getRangeAt(0), - cr = r.getClientRects(); - - for (let i = 0; i < cr.length; i++) { - let rect = cr[i]; - - clickIsOnSelection = ( - (x > rect.left) && (x < rect.right) && - (y > rect.top) && (y < rect.bottom) - ); - - if (clickIsOnSelection) { - break; - } - } - // If we clicked on selection and selection is not a single space, - // then mark the right click as copy-only. We check for the single - // space selection, as this can happen when clicking on an   - // and there is not much pointing in copying a single space. - if (selectedText.match(/^\s$/) || !selectedText.length) { - clickIsOnSelection = false; - } - } - +export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager) { // Bring textarea at the cursor position - if (!clickIsOnSelection) { - term.textarea.style.position = 'fixed'; - term.textarea.style.width = '20px'; - term.textarea.style.height = '20px'; - term.textarea.style.left = (x - 10) + 'px'; - term.textarea.style.top = (y - 10) + 'px'; - term.textarea.style.zIndex = '1000'; - term.textarea.focus(); + textarea.style.position = 'fixed'; + textarea.style.width = '20px'; + textarea.style.height = '20px'; + textarea.style.left = (ev.clientX - 10) + 'px'; + textarea.style.top = (ev.clientY - 10) + 'px'; + textarea.style.zIndex = '1000'; - // Reset the terminal textarea's styling - setTimeout(function () { - term.textarea.style.position = null; - term.textarea.style.width = null; - term.textarea.style.height = null; - term.textarea.style.left = null; - term.textarea.style.top = null; - term.textarea.style.zIndex = null; - }, 4); - } + // Get textarea ready to copy from the context menu + textarea.value = selectionManager.selectionText; + textarea.focus(); + textarea.select(); + + // Reset the terminal textarea's styling + setTimeout(function () { + textarea.style.position = null; + textarea.style.width = null; + textarea.style.height = null; + textarea.style.left = null; + textarea.style.top = null; + textarea.style.zIndex = null; + }, 4); } diff --git a/src/xterm.js b/src/xterm.js index 94979d8b..d2232300 100644 --- a/src/xterm.js +++ b/src/xterm.js @@ -532,18 +532,16 @@ Terminal.prototype.initGlobal = function() { pasteHandler.call(this, ev, term); }); - function rightClickHandlerWrapper (ev) { - rightClickHandler.call(this, ev, term); - } - if (term.browser.isFirefox) { - on(this.element, 'mousedown', function (ev) { + on(this.element, 'mousedown', event => { if (ev.button == 2) { - rightClickHandlerWrapper(ev); + rightClickHandler(event, this.textarea, this.selectionManager); } }); } else { - on(this.element, 'contextmenu', rightClickHandlerWrapper); + on(this.element, 'contextmenu', event => { + rightClickHandler(event, this.textarea, this.selectionManager); + }); } };