use own keyboard and improve css

This commit is contained in:
hippoz 2022-08-26 15:56:42 +03:00
parent 0f881b668c
commit fa6757b221
No known key found for this signature in database
GPG key ID: 7C52899193467641
9 changed files with 233 additions and 161 deletions

View file

@ -15,7 +15,5 @@
"webpack": "^5.61.0", "webpack": "^5.61.0",
"webpack-cli": "^4.9.1" "webpack-cli": "^4.9.1"
}, },
"dependencies": { "dependencies": {}
"simple-keyboard": "^3.3.16"
}
} }

View file

@ -21,7 +21,7 @@ class Banner {
return; // Already mounted return; // Already mounted
this.element = document.createRange().createContextualFragment(` this.element = document.createRange().createContextualFragment(`
<div class="card small-card center-text"> <div class="Card Card-ui-bottom Card-centered-text">
<h2 id="banner-title"></h2> <h2 id="banner-title"></h2>
<p id="banner-text"></p> <p id="banner-text"></p>
</div> </div>

View file

@ -1,90 +1,122 @@
import SimpleKeyboard from "simple-keyboard";
class KeyboardController { class KeyboardController {
constructor(connection) { constructor(connection) {
this.connection = connection; this.connection = connection;
this.keyboard = null; this.container = null;
this.keyboardDiv = null;
this.layouts = {
default: [
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
["🄰", "z", "x", "c", "v", "b", "n", "m", ".", "⌦"],
["🔢", " ", "↵"]
],
uppercase: [
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
["A", "S", "D", "F", "G", "H", "J", "K", "L"],
["🅰", "Z", "X", "C", "V", "B", "N", "M", ",", "⌦"],
["🔢", " ", "↵"]
],
symbols: [
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
["[", "]", "{", "}", ";", ":", "'", "\"", ",", "<"],
[".", ">", "-", "_", "=", "+", "/", "?", "\\"],
["🄰", "|", "`", "~", "⌦"],
["🔤", " ", "↵"]
]
}
} }
_sendKeyPress(l) { _sendKeyPress(l) {
this.connection.sendMessage("k", [l]); this.connection.sendMessage("k", [l]);
} }
makeKeyboardForLayout(layout) {
queueMicrotask(() => {
while (this.container.firstChild) {
this.container.removeChild(this.container.lastChild);
}
const rowsElement = document.createElement("div");
rowsElement.classList.add("Keyboard-rows");
for (let row = 0; row < layout.length; row++) {
const rowElement = document.createElement("div");
rowElement.classList.add("Keyboard-row");
for (let col = 0; col < layout[row].length; col++) {
const key = layout[row][col];
const colElement = document.createElement("button");
colElement.classList.add("Keyboard-button");
colElement.innerHTML = key;
colElement.addEventListener("click", (event) => {
this.handleKeypress(key);
});
rowElement.appendChild(colElement);
}
rowsElement.appendChild(rowElement);
}
this.container.appendChild(rowsElement);
});
}
handleKeypress(key) {
switch (key) {
case "🄰": {
this.makeKeyboardForLayout(this.layouts.uppercase);
break;
}
case "🅰": {
this.makeKeyboardForLayout(this.layouts.default);
break;
}
case "🔢": {
this.makeKeyboardForLayout(this.layouts.symbols);
break;
}
case "🔤": {
this.makeKeyboardForLayout(this.layouts.default);
break;
}
case " ": {
this._sendKeyPress("{space}");
break;
}
case "⌦": {
this._sendKeyPress("{backspace}");
break;
}
case "↵": {
this._sendKeyPress("{ent}");
break;
}
default: {
this._sendKeyPress(key);
break;
}
}
}
mountOn(element) { mountOn(element) {
if (this.keyboardDiv) if (this.container)
return; // Already mounted; return; // Already mounted;
this.keyboardDiv = document.createElement("div"); this.container = document.createElement("div");
this.keyboardDiv.classList.add("keyboard"); this.container.classList.add("Keyboard");
element.appendChild(this.keyboardDiv); element.appendChild(this.container);
this.keyboard = new SimpleKeyboard(this.keyboardDiv, { this.makeKeyboardForLayout(this.layouts.default);
onKeyPress: this.onKeyPress.bind(this),
mergeDisplay: true,
layoutName: "default",
// refer to: https://hodgef.com/simple-keyboard/demos/?d=mobile
layout: {
default: [
"q w e r t y u i o p",
"a s d f g h j k l",
"{shift} z x c v b n m {backspace}",
"{numbers} {space} {ent}"
],
shift: [
"Q W E R T Y U I O P",
"A S D F G H J K L",
"{shift} Z X C V B N M {backspace}",
"{numbers} {space} {ent}"
],
numbers: ["1 2 3", "4 5 6", "7 8 9", "{abc} 0 {backspace}"]
},
display: {
"{numbers}": "123",
"{ent}": "return",
"{escape}": "esc ⎋",
"{tab}": "tab ⇥",
"{backspace}": "⌫",
"{capslock}": "caps lock ⇪",
"{shift}": "⇧",
"{controlleft}": "ctrl ⌃",
"{controlright}": "ctrl ⌃",
"{altleft}": "alt ⌥",
"{altright}": "alt ⌥",
"{metaleft}": "cmd ⌘",
"{metaright}": "cmd ⌘",
"{abc}": "ABC"
}
});
} }
unmount() { unmount() {
if (!this.keyboardDiv) if (!this.container)
return; // Not mounted - don't do anything return; // Not mounted - don't do anything
this.keyboardDiv.parentElement.removeChild(this.keyboardDiv); this.container.parentElement.removeChild(this.container);
this.keyboardDiv = null; this.container = null;
}
onKeyPress(button) {
if (button === "{shift}" || button === "{lock}")
return this.handleShift();
if (button === "{numbers}" || button === "{abc}")
return this.handleNumbers();
this._sendKeyPress(button);
}
handleShift() {
this.keyboard.setOptions({
layoutName: this.keyboard.options.layoutName === "default" ? "shift" : "default"
});
}
handleNumbers() {
this.keyboard.setOptions({
layoutName: this.keyboard.options.layoutName !== "numbers" ? "numbers" : "default"
});
} }
} }

View file

@ -8,7 +8,7 @@ class LoginPrompt {
return; // Already mounted return; // Already mounted
this.element = document.createRange().createContextualFragment(` this.element = document.createRange().createContextualFragment(`
<div class="card small-card center-text"> <div class="Card Card-ui-bottom Card-centered-text">
<h2>Login</h2> <h2>Login</h2>
<p>You need to enter the login code before you can start controlling your device.</p> <p>You need to enter the login code before you can start controlling your device.</p>
<br> <br>

View file

@ -1,5 +1,5 @@
const HOLDING_THRESHOLD_MS = 300; const HOLDING_THRESHOLD_MS = 300;
const SCROLL_X_DAMPENING = 0.03; const SCROLL_X_DAMPENING = 0.02;
const SCROLL_Y_DAMPENING = 0.09; const SCROLL_Y_DAMPENING = 0.09;
class TouchpadController { class TouchpadController {
@ -65,25 +65,26 @@ class TouchpadController {
return; // Already mounted return; // Already mounted
this.touchpadDiv = document.createElement("div"); this.touchpadDiv = document.createElement("div");
this.touchpadDiv.classList.add("touchpad"); this.touchpadDiv.classList.add("Touchpad");
element.appendChild(this.touchpadDiv);
this.touchpadDiv.addEventListener("touchmove", this.onTouchMove.bind(this)); this.touchpadDiv.addEventListener("touchmove", this.onTouchMove.bind(this));
this.touchpadDiv.addEventListener("touchend", this.onTouchEnd.bind(this)); this.touchpadDiv.addEventListener("touchend", this.onTouchEnd.bind(this));
this.touchpadDiv.addEventListener("touchstart", this.onTouchStart.bind(this)); this.touchpadDiv.addEventListener("touchstart", this.onTouchStart.bind(this));
element.appendChild(this.touchpadDiv);
} }
unmount() { unmount() {
if (!this.touchpadDiv) if (!this.touchpadDiv)
return; // Not mounted - don't do anything return; // Not mounted - don't do anything
this.touchpadDiv.parentElement.removeChild(this.touchpadDiv); this.touchpadDiv.removeEventListener("touchmove", this.onTouchMove.bind(this));
this.touchpadDiv = null; this.touchpadDiv.removeEventListener("touchend", this.onTouchEnd.bind(this));
this.touchpadDiv.removeEventListener("touchstart", this.onTouchStart.bind(this));
} }
onTouchMove(event) { onTouchMove(event) {
const touches = event.changedTouches; const touches = event.changedTouches;
event.preventDefault();
const targetTouch = touches[0]; const targetTouch = touches[0];
@ -125,7 +126,13 @@ class TouchpadController {
} }
if (shouldMarkTouchesAsMoved) { if (shouldMarkTouchesAsMoved) {
for (let i = 0; i < touches.length; i++) { for (let i = 0; i < touches.length; i++) {
this.ongoingTouches[touches[i].identifier].hasMoved = true; const touch = this.ongoingTouches[touches[i].identifier];
touch.hasMoved = true;
if (touch.indicatorElement) {
touch.indicatorElement.style.top = `${targetTouch.pageY}px`;
touch.indicatorElement.style.left = `${targetTouch.pageX}px`;
}
} }
} }
} }
@ -150,6 +157,14 @@ class TouchpadController {
// remove all ended touches // remove all ended touches
for (let i = 0; i < changedTouches.length; i++) { for (let i = 0; i < changedTouches.length; i++) {
const touch = changedTouches[i]; const touch = changedTouches[i];
const knownTouch = this.ongoingTouches[changedTouches[0].identifier];
if (knownTouch && knownTouch.indicatorElement) {
this.touchpadDiv.removeChild(knownTouch.indicatorElement);
knownTouch.indicatorElement = null;
delete knownTouch.indicatorElement;
}
this.ongoingTouches[touch.identifier] = null; this.ongoingTouches[touch.identifier] = null;
delete this.ongoingTouches[touch.identifier]; delete this.ongoingTouches[touch.identifier];
} }
@ -177,12 +192,21 @@ class TouchpadController {
for (let i = 0; i < changedTouches.length; i++) { for (let i = 0; i < changedTouches.length; i++) {
const touch = changedTouches[i]; const touch = changedTouches[i];
let indicatorElement = this.ongoingTouches[touch.identifier] ? this.ongoingTouches[touch.identifier].indicatorElement : null;
if (!indicatorElement) {
indicatorElement = document.createElement("div");
indicatorElement.classList.add("Touchpad-touch-indicator");
indicatorElement.style.top = `${touch.clientY}px`;
indicatorElement.style.left = `${touch.clientX}px`;
this.touchpadDiv.appendChild(indicatorElement);
}
this.ongoingTouches[touch.identifier] = { this.ongoingTouches[touch.identifier] = {
identifier: touch.identifier, identifier: touch.identifier,
clientX: touch.clientX, clientX: touch.clientX,
clientY: touch.clientY, clientY: touch.clientY,
hasMoved: false, hasMoved: false,
gestured: [] gestured: [],
indicatorElement
}; };
} }
} }

View file

@ -2,4 +2,4 @@ import "simple-keyboard/build/css/index.css";
import "./styles/main.css"; import "./styles/main.css";
import App from "./App"; import App from "./App";
new App(document.body); window.__app = new App(document.body);

View file

@ -1,29 +1,55 @@
:root { :root {
--body-bg-color: #2e2e2e; --body-bg-color: #2e2e2e;
--body-bg-color-accent: hsl(0, 0%, 25%);
--accent-bg-color: #d4d3d3; --accent-bg-color: #d4d3d3;
--accent-color: #949494; --accent-color: #949494;
--grayed-text-color: #949494; --grayed-text-color: #949494;
--button-accent-color: #3d3d3d; --button-accent-color: #3d3d3d;
--selected-bg-color: #2e2e2e; --selected-bg-color: #2e2e2e;
--hover-bg-color: #4d4d4d; --hover-bg-color: #4d4d4d;
--card-border-radius: 1rem; --card-border-radius: 1em;
--button-border-radius: 0.5rem; --button-border-radius: 0.5em;
--main-font-weight: 500; --main-font-weight: 500;
--fonts-regular: "Noto Sans", "Liberation Sans", sans-serif; --fonts-regular: "Noto Sans", "Liberation Sans", sans-serif;
} }
body { * {
box-sizing: border-box;
}
html, body {
font-weight: var(--main-font-weight); font-weight: var(--main-font-weight);
font-family: var(--fonts-regular); font-family: var(--fonts-regular);
background-color: var(--body-bg-color); background-color: var(--body-bg-color);
color: var(--accent-bg-color); color: var(--accent-bg-color);
overflow-x: none; overflow: hidden;
margin: 0; margin: 0;
padding: 12px; padding: 0;
height: 100%;
width: 100%;
} }
.card { body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
background: var(--body-bg-color);
background-image: radial-gradient(var(--body-bg-color-accent) 1.4px, transparent 0);
background-size: 35px 35px;
}
/* util */
.full-width {
width: 100%;
}
/* Card */
.Keyboard,
.Card {
margin: 6px; margin: 6px;
padding: 8px; padding: 8px;
background: var(--accent-bg-color); background: var(--accent-bg-color);
@ -31,56 +57,32 @@ body {
border-radius: var(--card-border-radius); border-radius: var(--card-border-radius);
} }
.card.inner-card { .Keyboard,
margin: 4px; .Card-ui-bottom {
margin-bottom: 28px; padding: 14px;
padding: 18px; width: 100%;
border: solid var(--accent-color) 1px; max-width: 610px;
}
.card.layout-card {
margin: 36px auto;
padding: 26px;
width: 80%;
box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40); box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40);
margin: 0;
border-radius: var(--card-border-radius) var(--card-border-radius) 0 0;
padding-top: 16px;
} }
.card.small-card { .Card-centered-text {
margin: 36px auto;
padding: 26px;
width: 350px;
box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40);
}
@media screen and (max-width: 768px) {
.card.small-card {
width: 80%;
}
}
.button-default {
display: block;
box-sizing: border-box;
padding: 12px;
margin: 4px;
text-decoration: none;
border: none;
background-color: var(--accent-bg-color);
border-radius: var(--button-border-radius);
font-size: 18px;
white-space: nowrap;
color: var(--button-accent-color);
cursor: pointer;
outline: none;
min-width: 50px;
text-align: center; text-align: center;
} }
.full-width { /* button */
width: 100%;
}
.center-text { .button-default {
display: block;
padding: 12px;
margin: 4px;
border: none;
background-color: var(--accent-bg-color);
border-radius: var(--button-border-radius);
color: var(--button-accent-color);
min-width: 50px;
text-align: center; text-align: center;
} }
@ -94,6 +96,8 @@ body {
background-color: var(--selected-bg-color); background-color: var(--selected-bg-color);
} }
/* input */
.input { .input {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
@ -104,36 +108,51 @@ body {
padding: 12px; padding: 12px;
} }
.touchpad { /* Touchpad */
height: clamp(5rem, 30rem, 50vh);
.Touchpad {
width: 100%; width: 100%;
background-color: var(--accent-bg-color); height: 100%;
border-radius: var(--card-border-radius);
} }
/* for virtual keyboard */ .Touchpad-touch-indicator {
pointer-events: none;
position: absolute;
width: 28px;
height: 28px;
border-radius: 50%;
background-color: var(--accent-bg-color);
}
.hg-theme-default { /* Keyboard */
font-weight: var(--main-font-weight);
.keyboard-rows {
border-radius: 12px;
display: flex;
flex-direction: column;
}
.Keyboard-row {
display: flex;
flex-direction: row;
flex-grow: 1;
}
.Keyboard-button {
font-family: var(--fonts-regular); font-family: var(--fonts-regular);
} font-weight: var(--main-font-weight);
.keyboard {
padding: 8px; padding: 8px;
color: var(--body-bg-color);
margin-top: 24px;
border-radius: var(--card-border-radius);
background-color: var(--accent-bg-color); background-color: var(--accent-bg-color);
color: #000000;
flex-grow: 1;
min-height: 50px;
width: 20px;
border: none;
display: flex;
align-items: center;
justify-content: center;
} }
.keyboard .hg-rows { .Keyboard-button:active {
margin: 4px; background-color: var(--accent-color);
}
.hg-button {
margin: 2px;
}
.hg-activeButton {
background: var(--accent-color) !important;
} }

View file

@ -14,7 +14,11 @@ module.exports = {
plugins: [ plugins: [
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
title: "Capybara", title: "Capybara",
filename: "app.html" filename: "app.html",
meta: {
"viewport": "width=device-width,height=device-height,initial-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=contain",
"application-name": "Capybara"
}
}), }),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
filename: "[name].[contenthash].css" filename: "[name].[contenthash].css"

View file

@ -1345,11 +1345,6 @@ signal-exit@^3.0.3:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
simple-keyboard@^3.3.16:
version "3.3.16"
resolved "https://registry.yarnpkg.com/simple-keyboard/-/simple-keyboard-3.3.16.tgz#045da7c291d8e973f489d9a686f5872fe8121475"
integrity sha512-fAUNBIeNxh7Rlx3q11Sonq6DLFKYlfQJuRaiqlJkX9vYQeXEqdZGEh/FYyRUMIP0bjVVTI7Slq2H40sQe/DADQ==
source-map-js@^0.6.2: source-map-js@^0.6.2:
version "0.6.2" version "0.6.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"