Add touch selection

http-comm
cpsdqs 7 years ago committed by Ondřej Hruška
parent a3567ec338
commit 34b72625a5
  1. 2
      html_orig/jssrc/soft_keyboard.js
  2. 122
      html_orig/jssrc/term_screen.js
  3. 3
      html_orig/pages/term.php
  4. 14
      html_orig/sass/pages/_term.scss

@ -33,4 +33,6 @@ $.ready(() => {
input.addEventListener('keypress', e => { input.addEventListener('keypress', e => {
e.stopPropagation() e.stopPropagation()
}) })
Screen.on('open-soft-keyboard', () => input.focus())
}) })

@ -119,32 +119,121 @@ class TermScreen {
this.resetCursorBlink(); this.resetCursorBlink();
let selecting = false; let selecting = false;
this.canvas.addEventListener('mousedown', e => { let selectStart = (x, y) => {
if (this.selection.selectable || e.altKey) { if (selecting) return;
let x = e.offsetX;
let y = e.offsetY;
selecting = true; selecting = true;
this.selection.start = this.selection.end = this.screenToGrid(x, y); this.selection.start = this.selection.end = this.screenToGrid(x, y);
this.scheduleDraw() 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) {
selectStart(e.offsetX, e.offsetY)
} else { } else {
Input.onMouseDown(...this.screenToGrid(e.offsetX, e.offsetY), Input.onMouseDown(...this.screenToGrid(e.offsetX, e.offsetY),
e.button + 1) e.button + 1)
} }
}); });
window.addEventListener('mousemove', e => { window.addEventListener('mousemove', e => {
if (selecting) { selectMove(e.offsetX, e.offsetY)
this.selection.end = this.screenToGrid(e.offsetX, e.offsetY);
this.scheduleDraw()
}
}); });
window.addEventListener('mouseup', e => { 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) { if (selecting) {
selecting = false; e.preventDefault();
this.selection.end = this.screenToGrid(e.offsetX, e.offsetY); 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(); 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 => { this.canvas.addEventListener('mousemove', e => {
if (!selecting) { if (!selecting) {
Input.onMouseMove(...this.screenToGrid(e.offsetX, e.offsetY)) Input.onMouseMove(...this.screenToGrid(e.offsetX, e.offsetY))
@ -360,7 +449,7 @@ class TermScreen {
if (document.execCommand('copy')) { if (document.execCommand('copy')) {
Notify.show('Copied to clipboard'); Notify.show('Copied to clipboard');
} else { } else {
console.warn('Copy failed'); Notify.show('Failed to copy');
// unsuccessful copy // unsuccessful copy
} }
document.body.removeChild(textarea); document.body.removeChild(textarea);
@ -410,19 +499,18 @@ class TermScreen {
if (underline || strike) { if (underline || strike) {
ctx.strokeStyle = inSelection ? SELECTION_FG : this.colors[fg]; ctx.strokeStyle = inSelection ? SELECTION_FG : this.colors[fg];
ctx.lineWidth = this.window.devicePixelRatio==1?1:2; ctx.lineWidth = 1;
ctx.lineCap = 'round'; ctx.lineCap = 'round';
ctx.beginPath(); ctx.beginPath();
let lineY;
if (underline) { 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.moveTo(x * cellWidth, lineY);
ctx.lineTo((x + 1) * cellWidth, lineY); ctx.lineTo((x + 1) * cellWidth, lineY);
} }
if (strike) { 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.moveTo(x * cellWidth, lineY);
ctx.lineTo((x + 1) * cellWidth, lineY); ctx.lineTo((x + 1) * cellWidth, lineY);
} }

@ -43,6 +43,9 @@
<div id="term-wrap"> <div id="term-wrap">
<div id="screen" class="theme-%theme%"> <div id="screen" class="theme-%theme%">
<textarea id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea> <textarea id="softkb-input" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea>
<div id="touch-select-menu">
<button id="touch-select-copy-btn">Copy</button>
</div>
</div> </div>
<div id="action-buttons"> <div id="action-buttons">

@ -30,6 +30,8 @@ body.term {
cursor: text; cursor: text;
} }
@include noselect();
// Dummy input field used to open soft keyboard // Dummy input field used to open soft keyboard
#softkb-input { #softkb-input {
position: absolute; position: absolute;
@ -43,6 +45,18 @@ body.term {
resize: none; resize: none;
// visible for debugging. do opacity 0 later on // 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 { #action-buttons {

Loading…
Cancel
Save