diff --git a/README.md b/README.md index 70a3926..769a60c 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ const { held, prompt } = useKeyboardSwitches({ - Keys pass through while focus is in a text field, unless you set `ignoreWhileTyping: false`. - Escape stops scanning. - `interceptPress(switchId)` runs when a switch goes down. Return true to consume the whole press, for example to let any press stop something the app is repeating. -- Automatic movement pauses while a switch is held. +- Automatic movement pauses while a switch is held. Call `controller.holdMovement(reason, held)` to hold it for reasons of your own, for example while something the app started is still repeating. - `validateSwitchSettings` reports settings that can't drive scanning, for example ones with no Select, or manual scanning without Next and Previous. ## React Native diff --git a/package.json b/package.json index dce87b6..5475659 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@switchify/scanning", - "version": "0.4.0", + "version": "0.5.0", "description": "Switch scanning for React and React Native, ported from Switchify PC.", "license": "AGPL-3.0-only", "type": "module", diff --git a/src/core/controller.ts b/src/core/controller.ts index 2707594..6680257 100644 --- a/src/core/controller.ts +++ b/src/core/controller.ts @@ -143,7 +143,7 @@ export class ScanController { private sequence = 0; private active = false; private paused = false; - private switchHeld = false; + private readonly holds = new Set(); private clock: unknown; private last = 0; private snapshot: ScanSnapshot = INACTIVE; @@ -251,15 +251,24 @@ export class ScanController { this.contentChanged(); } + /** + * Holds automatic movement while `reason` applies, for example while something the + * app started is still running. Movement continues from where it paused once every + * reason is released. + */ + holdMovement(reason: string, held: boolean): void { + if (this.holds.has(reason) === held) return; + if (held) this.holds.add(reason); + else this.holds.delete(reason); + this.update(); + } + /** * Holds automatic movement while a switch is down, as Switchify PC does, so the - * highlight cannot move away from the item being selected. Movement continues - * from where it paused once the switch is released. + * highlight cannot move away from the item being selected. */ setSwitchHeld(held: boolean): void { - if (this.switchHeld === held) return; - this.switchHeld = held; - this.update(); + this.holdMovement("switch", held); } start(): void { @@ -416,7 +425,7 @@ export class ScanController { return ( this.active && !this.paused && - !this.switchHeld && + this.holds.size === 0 && this.scanner.options.automatic && !this.scanner.suspended && !this.scanner.pending() diff --git a/test/controller.test.ts b/test/controller.test.ts index 0e8dd1b..a0edc80 100644 --- a/test/controller.test.ts +++ b/test/controller.test.ts @@ -299,3 +299,17 @@ test("options left undefined keep their current values", () => { controller.start(); expect(clock.running()).toBe(false); }); + +test("movement stays held until every reason is released", () => { + const { controller, clock } = setup({ options: { intervalMs: 500 } }); + controller.registerItem("a"); + controller.registerItem("b"); + controller.flush(); + controller.start(); + controller.holdMovement("repeat", true); + controller.setSwitchHeld(true); + controller.setSwitchHeld(false); + expect(clock.running()).toBe(false); + controller.holdMovement("repeat", false); + expect(clock.running()).toBe(true); +});