fluid-lab / gamepad-navigator

GSoC 2020 project
Other
6 stars 10 forks source link

Refactor handling of "discrete" vs. "continuous" controls. #108

Closed duhrer closed 5 months ago

duhrer commented 8 months ago

Currently, we have two kinds of controls. The first are "discrete" controls, that fire when the control value is above the cutoff. Because there is no checking for "already on", these controls can be called repeatedly (and rapidly) when an analog button like a trigger is used.

The second are "continuous" controls, which poll for updates and keep firing the action as long as the value is still above the cutoff threshold. These can only be bound to "axes" at the moment. In cases like scrolling, the value is also used to determine how hard/fast to move, so that gentle thumb stick deflection scrolls slowly, and full deflection scrolls quickly.

For both types of action, each action models its own value handling, which results in a lot of duplicated code and inconsistent behaviour. We should refactor such that:

  1. Each type of control extends a parent grade that enforces the basic contract (jitter cutoff in the case of "discrete" controls, for example, and polling in the case of "continuous" controls).
  2. Both types of actions are available for all controls, such that:
    • Buttons can be used with "continuous" controls. For example, we should be able to scroll repeatedly by holding a d-pad directional button, or navigate continuously by holding a bumper.
    • Thumb sticks and triggers can be used with "discrete" actions without constantly repeating.

This would be a good time to standardise the contract for actions in general, so that we don't have a method signature with every possible parameter, and instead pass an action options object for each call.

duhrer commented 7 months ago

The supported parameters should be part of the new action contract. Previously we've used things like JSON schemas to model parameters, which allows both input validation and UI generation.

Given that the range of options is somewhat more constrained here, we might try for a simpler definition, which describes:

  1. Which fields an action supports.
  2. The allowable values for the field (i.e. boolean vs. number, maximum and minimum values).
  3. The default value for the field.

We could also use our FSS variation on JSON schemas informally without using the validation infrastructure, i.e. only using it for UI generation. We could add a schema-linting step to our tests as a safety check, without having to inject AJV and a bunch of fluid-json-schema code into the page.

duhrer commented 7 months ago

The following should only support "discrete" operation:

  1. openActionLauncher
  2. openSearchKeyboard
  3. openNewWindow
  4. openNewTab
  5. closeCurrentTab
  6. closeCurrentWindow
  7. reopenTabOrWindow
  8. maximizeWindow
  9. restoreWindowSize
  10. click

The rest should support both "discrete" and "continuous" operation:

  1. goToPreviousWindow
  2. goToNextWindow
  3. goToPreviousTab
  4. goToNextTab
  5. previousPageInHistory
  6. nextPageInHistory
  7. reverseTab
  8. forwardTab
  9. scrollLeft,
  10. scrollRight
  11. scrollUp
  12. scrollDown
  13. zoomIn
  14. zoomOut
  15. sendArrowLeft
  16. sendArrowRight
  17. sendArrowUp
  18. sendArrowDown

The following "axes" actions should be reviewed to ensure that they are basically triggering the same code as their "discrete" equivalents:

  1. scrollHorizontally
  2. scrollVertically
  3. thumbstickHistoryNavigation
  4. thumbstickTabbing
  5. thumbstickZoom
  6. thumbstickWindowSize
  7. thumbstickHorizontalArrows
  8. thumbstickVerticalArrows
  9. (No thumb stick function for previous/next window, should add one.)
  10. (No thumb stick function for previous/next tab, should add one).

This suggests making an axis simply a predefined "paired action" grouping. These groupings should still be capable of operating in "discrete" mode, so that users who can only slowly release a thumb stick have the option to only have the action for a given direction trigger once.

duhrer commented 7 months ago

I ended up refactoring all action handling so that only the produceNavigation method handles scheduling of repeating events. I also moved the "analog cutoff" detection to the same area, which allowed me to remove a lot of code from individual action functions.

The net result is that nearly all thumb stick and button actions are repeatable (actions like "click" and "open new window" are intentionally not repeatable). For actions that are allowed to repeat, the user can configure whether (and how often) they repeat.