Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
23 changes: 16 additions & 7 deletions src/core/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export class ScanController {
private sequence = 0;
private active = false;
private paused = false;
private switchHeld = false;
private readonly holds = new Set<string>();
private clock: unknown;
private last = 0;
private snapshot: ScanSnapshot = INACTIVE;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()
Expand Down
14 changes: 14 additions & 0 deletions test/controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
Loading