Add touch selection
This commit is contained in:
@@ -33,4 +33,6 @@ $.ready(() => {
|
|||||||
input.addEventListener('keypress', e => {
|
input.addEventListener('keypress', e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Screen.on('open-soft-keyboard', () => input.focus())
|
||||||
})
|
})
|
||||||
|
|||||||
+108
-20
@@ -119,32 +119,121 @@ class TermScreen {
|
|||||||
this.resetCursorBlink();
|
this.resetCursorBlink();
|
||||||
|
|
||||||
let selecting = false;
|
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 => {
|
this.canvas.addEventListener('mousedown', e => {
|
||||||
if (this.selection.selectable || e.altKey) {
|
if (this.selection.selectable || e.altKey) {
|
||||||
let x = e.offsetX;
|
selectStart(e.offsetX, e.offsetY)
|
||||||
let y = e.offsetY;
|
|
||||||
selecting = true;
|
|
||||||
this.selection.start = this.selection.end = this.screenToGrid(x, y);
|
|
||||||
this.scheduleDraw()
|
|
||||||
} 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 => {
|
||||||
if (selecting) {
|
selectEnd(e.offsetX, e.offsetY)
|
||||||
selecting = false;
|
|
||||||
this.selection.end = this.screenToGrid(e.offsetX, e.offsetY);
|
|
||||||
this.scheduleDraw();
|
|
||||||
Object.assign(this.selection, this.getNormalizedSelection())
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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();
|
||||||
|
} 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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user