From 89eb75acd9d35df3c6845110536e5d52d69a8fee Mon Sep 17 00:00:00 2001 From: Rob Date: Tue, 11 Oct 2022 22:22:46 -0700 Subject: [PATCH 1/2] Android: option to vibrate on button press --- runtimes/web/src/ui/app.ts | 22 ++++++++++++++++++++ runtimes/web/src/ui/menu-overlay.ts | 28 ++++++++++++++++++++++---- runtimes/web/src/ui/virtual-gamepad.ts | 7 +++++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/runtimes/web/src/ui/app.ts b/runtimes/web/src/ui/app.ts index 8fb03231..d8f0e2a2 100644 --- a/runtimes/web/src/ui/app.ts +++ b/runtimes/web/src/ui/app.ts @@ -19,6 +19,13 @@ class InputState { mouseButtons = 0; } +const vibrateLevels: [string, number][] = [ + ["OFF", 0], + ["LOW", 1], + ["MEDIUM", 2], + ["HIGH", 4] +]; + @customElement("wasm4-app") export class App extends LitElement { static styles = css` @@ -76,6 +83,11 @@ export class App extends LitElement { private readonly diskPrefix: string; + private vibrateLevelIdx = 0; + get vibrateLevel (): [string, number] { + return vibrateLevels[this.vibrateLevelIdx]; + } + readonly onPointerUp = (event: PointerEvent) => { if (event.pointerType == "touch") { // Try to go fullscreen on mobile @@ -666,6 +678,16 @@ export class App extends LitElement { return netplay; } + canVibrate (): boolean { + // No reliable way to tell, so best guess + return 'ontouchstart' in window && navigator.vibrate !== undefined; + } + + cycleVibrateLevel (): [string, number] { + this.vibrateLevelIdx = (this.vibrateLevelIdx + 1) % vibrateLevels.length; + return this.vibrateLevel; + } + getNetplaySummary () { return this.netplay ? this.netplay.getSummary() : []; } diff --git a/runtimes/web/src/ui/menu-overlay.ts b/runtimes/web/src/ui/menu-overlay.ts index e5ce13be..6889d1b4 100644 --- a/runtimes/web/src/ui/menu-overlay.ts +++ b/runtimes/web/src/ui/menu-overlay.ts @@ -3,6 +3,7 @@ import { customElement, state } from 'lit/decorators.js'; import { map } from 'lit/directives/map.js'; import { App } from "./app"; +import { VirtualGamepad } from "./virtual-gamepad"; import * as constants from "../constants"; const optionContext = { @@ -15,10 +16,11 @@ const optionIndex = [ CONTINUE: 0, SAVE_STATE: 1, LOAD_STATE: 2, - DISK_OPTIONS: 3, + BUTTON_VIBRATE: 3, + DISK_OPTIONS: 4, // OPTIONS: null, - COPY_NETPLAY_LINK: 4, - RESET_CART: 5, + COPY_NETPLAY_LINK: 5, + RESET_CART: 6, }, { BACK: 0, @@ -33,6 +35,7 @@ const options = [ "CONTINUE", "SAVE STATE", "LOAD STATE", + "VIBRATE:", "DISK OPTIONS", // "OPTIONS", "COPY NETPLAY URL", @@ -115,6 +118,7 @@ export class MenuOverlay extends LitElement { @state() private selectedIdx = 0; @state() private netplaySummary: { playerIdx: number, ping: number }[] = []; + @state() private vibrateLevel = ""; private netplayPollInterval?: number; @@ -183,6 +187,9 @@ export class MenuOverlay extends LitElement { this.app.loadGameState(); this.app.closeMenu(); break; + case this.optionIndex.BUTTON_VIBRATE: + this.vibrateLevel = this.app.cycleVibrateLevel()[0]; + break; case this.optionIndex.DISK_OPTIONS: this.switchContext(optionContext.DISK); break; @@ -219,9 +226,17 @@ export class MenuOverlay extends LitElement { if (pressedThisFrame & constants.BUTTON_DOWN) { this.selectedIdx++; + if (this.optionContext === optionContext.DEFAULT && this.selectedIdx === this.optionIndex.BUTTON_VIBRATE && + !this.app.canVibrate()) { + this.selectedIdx++; + } } if (pressedThisFrame & constants.BUTTON_UP) { this.selectedIdx--; + if (this.optionContext === optionContext.DEFAULT && this.selectedIdx === this.optionIndex.BUTTON_VIBRATE && + !this.app.canVibrate()) { + this.selectedIdx--; + } } this.selectedIdx = (this.selectedIdx + this.options.length) % this.options.length; } @@ -243,11 +258,16 @@ export class MenuOverlay extends LitElement { } render () { + if (!this.vibrateLevel) { + this.vibrateLevel = this.app.vibrateLevel[0]; + } return html`