From 34b72625a51fab9c204391913139a92bbc7102b9 Mon Sep 17 00:00:00 2001 From: cpsdqs Date: Fri, 8 Sep 2017 23:37:50 +0200 Subject: [PATCH] Add touch selection --- html_orig/jssrc/soft_keyboard.js | 2 + html_orig/jssrc/term_screen.js | 122 ++++++++++++++++++++++++++----- html_orig/pages/term.php | 3 + html_orig/sass/pages/_term.scss | 14 ++++ 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/html_orig/jssrc/soft_keyboard.js b/html_orig/jssrc/soft_keyboard.js index d0f3619..68e4834 100644 --- a/html_orig/jssrc/soft_keyboard.js +++ b/html_orig/jssrc/soft_keyboard.js @@ -33,4 +33,6 @@ $.ready(() => { input.addEventListener('keypress', e => { e.stopPropagation() }) + + Screen.on('open-soft-keyboard', () => input.focus()) }) diff --git a/html_orig/jssrc/term_screen.js b/html_orig/jssrc/term_screen.js index 43f6a1d..59e7dd4 100644 --- a/html_orig/jssrc/term_screen.js +++ b/html_orig/jssrc/term_screen.js @@ -119,32 +119,121 @@ class TermScreen { this.resetCursorBlink(); let selecting = false; + let selectStart = (x, y) => { + if (selecting) return; + selecting = true; + this.selection.start = this.selection.end = this.screenToGrid(x, y); + this.scheduleDraw(); + }; + let selectMove = (x, y) => { + if (!selecting) return; + this.selection.end = this.screenToGrid(x, y); + this.scheduleDraw(); + }; + let selectEnd = (x, y) => { + if (!selecting) return; + selecting = false; + this.selection.end = this.screenToGrid(x, y); + this.scheduleDraw(); + Object.assign(this.selection, this.getNormalizedSelection()); + }; + this.canvas.addEventListener('mousedown', e => { if (this.selection.selectable || e.altKey) { - let x = e.offsetX; - let y = e.offsetY; - selecting = true; - this.selection.start = this.selection.end = this.screenToGrid(x, y); - this.scheduleDraw() + selectStart(e.offsetX, e.offsetY) } else { Input.onMouseDown(...this.screenToGrid(e.offsetX, e.offsetY), e.button + 1) } }); window.addEventListener('mousemove', e => { - if (selecting) { - this.selection.end = this.screenToGrid(e.offsetX, e.offsetY); - this.scheduleDraw() - } + selectMove(e.offsetX, e.offsetY) }); window.addEventListener('mouseup', e => { + selectEnd(e.offsetX, e.offsetY) + }); + + let touchPosition = null; + let touchDownTime = 0; + let touchSelectMinTime = 500; + let touchDidMove = false; + let getTouchPositionOffset = touch => { + let rect = this.canvas.getBoundingClientRect(); + return [touch.clientX - rect.left, touch.clientY - rect.top]; + } + this.canvas.addEventListener('touchstart', e => { + touchPosition = getTouchPositionOffset(e.touches[0]) + touchDidMove = false; + touchDownTime = Date.now(); + }); + this.canvas.addEventListener('touchmove', e => { + touchPosition = getTouchPositionOffset(e.touches[0]); + + if (!selecting && touchDidMove === false) { + if (touchDownTime < Date.now() - touchSelectMinTime) { + selectStart(...touchPosition); + } + } else if (selecting) { + e.preventDefault(); + selectMove(...touchPosition); + } + + touchDidMove = true; + }); + this.canvas.addEventListener('touchend', e => { + if (e.touches[0]) { + touchPosition = getTouchPositionOffset(e.touches[0]); + } if (selecting) { - selecting = false; - this.selection.end = this.screenToGrid(e.offsetX, e.offsetY); + e.preventDefault(); + selectEnd(...touchPosition); + + let touchSelectMenu = qs('#touch-select-menu') + touchSelectMenu.classList.add('open'); + let rect = touchSelectMenu.getBoundingClientRect() + + // use middle position for x and one line above for y + let selectionPos = this.gridToScreen( + (this.selection.start[0] + this.selection.end[0]) / 2, + this.selection.start[1] - 1 + ); + selectionPos[0] -= rect.width / 2 + selectionPos[1] -= rect.height / 2 + touchSelectMenu.style.transform = `translate(${selectionPos[0]}px, ${ + selectionPos[1]}px)` + } + + if (!touchDidMove) { + this.emit('tap', Object.assign(e, { + x: touchPosition[0], + y: touchPosition[1], + })) + } + + touchPosition = null; + }); + + this.on('tap', e => { + if (this.selection.start[0] !== this.selection.end[0] || + this.selection.start[1] !== this.selection.end[1]) { + // selection is not empty + // reset selection + this.selection.start = this.selection.end = [0, 0]; + qs('#touch-select-menu').classList.remove('open'); this.scheduleDraw(); - Object.assign(this.selection, this.getNormalizedSelection()) + } else { + e.preventDefault(); + this.emit('open-soft-keyboard'); } + }) + + $.ready(() => { + let copyButton = qs('#touch-select-copy-btn') + copyButton.addEventListener('click', () => { + this.copySelectionToClipboard(); + }); }); + this.canvas.addEventListener('mousemove', e => { if (!selecting) { Input.onMouseMove(...this.screenToGrid(e.offsetX, e.offsetY)) @@ -360,7 +449,7 @@ class TermScreen { if (document.execCommand('copy')) { Notify.show('Copied to clipboard'); } else { - console.warn('Copy failed'); + Notify.show('Failed to copy'); // unsuccessful copy } document.body.removeChild(textarea); @@ -410,19 +499,18 @@ class TermScreen { if (underline || strike) { ctx.strokeStyle = inSelection ? SELECTION_FG : this.colors[fg]; - ctx.lineWidth = this.window.devicePixelRatio==1?1:2; + ctx.lineWidth = 1; ctx.lineCap = 'round'; ctx.beginPath(); - let lineY; if (underline) { - lineY = Math.round(y * cellHeight + charSize.height) + 0.5; + let lineY = Math.round(y * cellHeight + charSize.height) + 0.5; ctx.moveTo(x * cellWidth, lineY); ctx.lineTo((x + 1) * cellWidth, lineY); } if (strike) { - lineY = Math.round((y + 0.5) * cellHeight) + 0.5; + let lineY = Math.round((y + 0.5) * cellHeight) + 0.5; ctx.moveTo(x * cellWidth, lineY); ctx.lineTo((x + 1) * cellWidth, lineY); } diff --git a/html_orig/pages/term.php b/html_orig/pages/term.php index 91aec4b..05259f3 100644 --- a/html_orig/pages/term.php +++ b/html_orig/pages/term.php @@ -43,6 +43,9 @@
+
+ +
diff --git a/html_orig/sass/pages/_term.scss b/html_orig/sass/pages/_term.scss index 9771aaa..6c257ab 100755 --- a/html_orig/sass/pages/_term.scss +++ b/html_orig/sass/pages/_term.scss @@ -30,6 +30,8 @@ body.term { cursor: text; } + @include noselect(); + // Dummy input field used to open soft keyboard #softkb-input { position: absolute; @@ -43,6 +45,18 @@ body.term { resize: none; // visible for debugging. do opacity 0 later on } + + #touch-select-menu { + display: none; + position: absolute; + // compensate for padding + top: 6px; + left: 6px; + + &.open { + display: block; + } + } } #action-buttons {