From 534fd76d069d7fb1616b484266b3240da4986743 Mon Sep 17 00:00:00 2001
From: Himanshu Jangid <himanshujhar@gmail.com>
Date: Sat, 23 Sep 2023 01:17:02 +0530
Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat=20(engine:=20input)=20add=20keybo?=
 =?UTF-8?q?ard=20input=20support=20and=20examples.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 examples/index.html                 |  1 +
 examples/keyboard-inputs/index.html | 82 +++++++++++++++++++++++++++++
 examples/keyboard-inputs/script.ts  | 39 ++++++++++++++
 examples/vite.config.js             |  1 +
 lib/input/input.ts                  | 47 ++++++++++++++++-
 lib/render/engine.ts                |  5 +-
 lib/types/canvas.ts                 |  1 +
 7 files changed, 173 insertions(+), 3 deletions(-)
 create mode 100644 examples/keyboard-inputs/index.html
 create mode 100644 examples/keyboard-inputs/script.ts

diff --git a/examples/index.html b/examples/index.html
index 1e0342c..277aa18 100644
--- a/examples/index.html
+++ b/examples/index.html
@@ -20,6 +20,7 @@ <h2>We got examples for you</h2>
       <li><a href="/drawing/">Drawing</a></li>
       <li><a href="/colors/">Colors</a></li>
       <li><a href="/mouse-inputs/">Mouse Inputs</a></li>
+      <li><a href="/keyboard-inputs/">Keyboard Inputs</a></li>
       <li><a href="/update/">Update</a></li>
     </ul>
     <script type="module" src="/src/main.ts"></script>
diff --git a/examples/keyboard-inputs/index.html b/examples/keyboard-inputs/index.html
new file mode 100644
index 0000000..61938ec
--- /dev/null
+++ b/examples/keyboard-inputs/index.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <link
+      rel="stylesheet"
+      href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css"
+    />
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js"></script>
+
+    <!-- and it's easy to individually load additional languages -->
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/go.min.js"></script>
+    <script src="https://unpkg.com/highlightjs-copy/dist/highlightjs-copy.min.js"></script>
+    <link
+      rel="stylesheet"
+      href="https://unpkg.com/highlightjs-copy/dist/highlightjs-copy.min.css"
+    />
+    <link rel="stylesheet" href="/styles.css" />
+
+    <script>
+      hljs.addPlugin(new CopyButtonPlugin());
+      hljs.highlightAll();
+    </script>
+    <title>MiniPoint Colors</title>
+  </head>
+  <body>
+    <div id="app">
+      Mouse Click Event Examples (Colorful Bees & Gods painting). Toggle
+      checkbox to see difference. Press 'J' to put point on the canvas
+    </div>
+    <label for="">Clear canvas per frame</label
+    ><input type="checkbox" name="checkbox" id="checkbox" checked />
+    <div class="main-area">
+      <canvas id="canvas"></canvas>
+      <pre><code class="language-javascript">
+import { Engine, Point } from '../../lib';
+
+const engine = new Engine(
+  document.getElementById('canvas') as HTMLCanvasElement,
+  {
+    engineOptions: {
+      width: 600,
+      height: 400,
+    },
+  },
+);
+
+const { renderer, input } = engine;
+
+const random = () => {
+  return Math.random();
+};
+
+document.getElementById('checkbox')?.addEventListener('change', (e) => {
+  engine.options.engineOptions!.clearEachFrame = (e.target as any).checked;
+});
+
+engine.update = () => {
+  if (input.keyboard.currentUpKey?.toLowerCase() === 'j') {
+    const p1 = new Point({
+      x: input.mouse.position.x,
+      y: input.mouse.position.y,
+      radius: 6,
+      color: `rgb(${random() * 255}, ${random() * 255}, ${random() * 255})`,
+    });
+
+    p1.update = () => {
+      p1.options.x += random() > 0.5 ? random() * 2 : -random() * 2;
+      p1.options.y += random() > 0.5 ? random() * 2 : -random() * 2;
+    };
+
+    renderer.addObject(p1);
+  }
+};
+
+      </code></pre>
+    </div>
+    <script type="module" src="./script.ts"></script>
+  </body>
+</html>
diff --git a/examples/keyboard-inputs/script.ts b/examples/keyboard-inputs/script.ts
new file mode 100644
index 0000000..f92ba4a
--- /dev/null
+++ b/examples/keyboard-inputs/script.ts
@@ -0,0 +1,39 @@
+import { Engine, Point } from '../../lib';
+
+const engine = new Engine(
+  document.getElementById('canvas') as HTMLCanvasElement,
+  {
+    engineOptions: {
+      width: 600,
+      height: 400,
+    },
+  },
+);
+
+const { renderer, input } = engine;
+
+const random = () => {
+  return Math.random();
+};
+
+document.getElementById('checkbox')?.addEventListener('change', (e) => {
+  engine.options.engineOptions!.clearEachFrame = (e.target as any).checked;
+});
+
+engine.update = () => {
+  if (input.keyboard.currentUpKey?.toLowerCase() === 'j') {
+    const p1 = new Point({
+      x: input.mouse.position.x,
+      y: input.mouse.position.y,
+      radius: 6,
+      color: `rgb(${random() * 255}, ${random() * 255}, ${random() * 255})`,
+    });
+
+    p1.update = () => {
+      p1.options.x += random() > 0.5 ? random() * 2 : -random() * 2;
+      p1.options.y += random() > 0.5 ? random() * 2 : -random() * 2;
+    };
+
+    renderer.addObject(p1);
+  }
+};
diff --git a/examples/vite.config.js b/examples/vite.config.js
index c445fd8..faf955c 100644
--- a/examples/vite.config.js
+++ b/examples/vite.config.js
@@ -12,6 +12,7 @@ export default defineConfig({
         colors: resolve(__dirname, 'colors/index.html'),
         mouse_inputs: resolve(__dirname, 'mouse-inputs/index.html'),
         update: resolve(__dirname, 'update/index.html'),
+        keyboard_inputs: resolve(__dirname, 'keyboard-inputs/index.html'),
       },
     },
   },
diff --git a/lib/input/input.ts b/lib/input/input.ts
index d60dd5f..315955f 100644
--- a/lib/input/input.ts
+++ b/lib/input/input.ts
@@ -71,5 +71,50 @@ export class MouseInput {
 }
 
 export class KeyBoardInput {
-  constructor(_engine: Engine) {}
+  currentDownKey: string = '';
+  currentUpKey: string = '';
+  currentPressedKey: string = '';
+  shiftKey?: boolean;
+  altKey?: boolean;
+  ctrlKey?: boolean;
+
+  engine: Engine;
+
+  constructor(engine: Engine) {
+    this.engine = engine;
+    window.addEventListener('keydown', (event) => {
+      this.currentDownKey = this.getKey(event.key);
+      this.currentUpKey = '';
+      this.altKey = event.altKey;
+      this.shiftKey = event.shiftKey;
+      this.ctrlKey = event.ctrlKey;
+    });
+
+    window.addEventListener('keypress', (event) => {
+      this.currentDownKey = this.getKey(event.key);
+      this.currentUpKey = '';
+      this.altKey = event.altKey;
+      this.shiftKey = event.shiftKey;
+      this.ctrlKey = event.ctrlKey;
+    });
+
+    window.addEventListener('keyup', (event) => {
+      this.altKey = event.altKey;
+      this.shiftKey = event.shiftKey;
+      this.ctrlKey = event.ctrlKey;
+      this.currentUpKey = this.getKey(event.key);
+      this.currentDownKey = '';
+      this.currentPressedKey = '';
+    });
+  }
+
+  getKey(key: string) {
+    if (!key) return '';
+
+    if (this.engine.options.engineOptions?.preserveKeyboardInputCase) {
+      return key;
+    }
+
+    return key.toLowerCase();
+  }
 }
diff --git a/lib/render/engine.ts b/lib/render/engine.ts
index 0813190..5b295ba 100644
--- a/lib/render/engine.ts
+++ b/lib/render/engine.ts
@@ -56,8 +56,8 @@ export class Engine {
     // the initial position is calculated based on scrolled window.
     // otherwise, the position may be unexpected
     this.position = new Vector(x - window.scrollX, y - -window.scrollY);
-    this.width = this.canvas.width;
-    this.height = this.canvas.height;
+    this.width = this.options.engineOptions?.width!;
+    this.height = this.options.engineOptions?.height!;
     this.canvas.style.border = options.engineOptions!.border!;
     this.canvas.style.background = options.engineOptions!.bg!;
     this.canvas.style.width = options.engineOptions!.width!.toString() + 'px';
@@ -101,5 +101,6 @@ export class Engine {
   resetEvent() {
     this.input.mouse.click = false;
     this.input.mouse.move = false;
+    this.input.keyboard.currentUpKey = '';
   }
 }
diff --git a/lib/types/canvas.ts b/lib/types/canvas.ts
index aa3a519..f85a697 100644
--- a/lib/types/canvas.ts
+++ b/lib/types/canvas.ts
@@ -18,6 +18,7 @@ export type EngineOptions = {
   // TODO: not yet decided
   engineOptions?: {
     clearEachFrame?: boolean;
+    preserveKeyboardInputCase?: boolean;
     border?: string;
     bg?: string;
     width?: number;