Skip to content

Detect Backspace and Shift keys #4142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from

Conversation

towerofnix
Copy link
Contributor

@towerofnix towerofnix commented Dec 10, 2023

Resolves

Resolves #1892.

See prior art & discussion in #2247.

Proposed Changes

Adds support for detecting the shift and backspace keys in the "key pressed?" block.

User-apparent details:

  • Like "enter", these options are not included in the block dropdown. They must be accessed by dropping in a reporter that returns the name of the key (specifically, "shift" or "backspace").
  • The "shift" key does not count for the "key any pressed?" or "when any key pressed".
  • The "shift" key responds appropriately to an operating system's "sticky keys" feature, i.e. if sticky keys is enabled then pressing shift once will leave "key shift pressed?" as true until shift is pressed again.

Internal details:

  • A new KEY_NAMES_EXCLUDED_FOR_KEY_ANY_PRESSED array is added (its only item is 'shift'). Scratch keys here are not counted for the "key any pressed?" or "when any key pressed" blocks.
  • getKeyIsDown now performs a .some() call to check if any of the keys currently pressed is not excluded. This will have only minuscule performance overhead - in all cases it will do one of the following:
    • Test zero cases, because the list is empty, so immediately return false. (.some() on an empty array is always false.)
    • Test one case, because the list starts with a valid key. (.some() exits as soon as it matches one true value.)
    • Test one case, because the list only includes 'shift'. (.some() returns false once it's tested every item and none of them was true.)
    • Test two cases, because the list starts with shift and is followed by a valid key.
  • postData now usually emits two events - the existing one remains emit('KEY_PRESSED', scratchKey) and the new one is emit('KEY_ANY_PRESSED'). The former is used for responding to specific keys; the latter for responding to "any" key (in which case it doesn't matter which key is pressed, and so that detail is not emitted).
    • This has similarly tiny performance overhead as it now must check if the key pressed is included in a one-item list. postData isn't called at nearly a high enough frequency for this to be costly.
    • scratch3_event.js is updated to listen to both of these events: the first for starting specific-key "when key pressed" blocks, the second for "when any key pressed" blocks.
    • (scratch3_sensing.js requires no changes because it already calls ioQuery('keyboard', 'getKeyIsDown', ['any']).)
  • No special code logic is required to support sticky keys - the browser/OS does the right job for us.

Reason for Changes

  • Backspace:
    • Backspace is an obvious important key for any "typewriter" project, i.e. any project that displays on-screen input. Without backspace detection, projects have to come up with creative - but clumsy - workarounds. Allowing backspace to be detected means enabling a host of projects that work with any kind of text input. (The example that inspired me to do this PR was someone saying they needed backspace detection for their Wordle project!)
    • Backspace is universally available on all keyboards. It's also universally understood as "the key for getting rid of the character behind the text input cursor" - text input is something we all do. It's used in some games as a "go back" control, as well.
    • While in development, but not anymore, this PR included an option for "delete" as well. But it's controversial as this key isn't available on all keyboards and may cause confusion (the backspace key is actually labelled "delete" on many keyboards!).
  • Shift:
    • Shift is also an extremely important key for any project with text input. Case-sensitive comparison in Scratch is challenging; however, case-sensitive input is outright impossible. The shift key is universally the key for capitalizing the letter being pressed.
    • Symbol detection like "#" or "(" works in current Scratch, but not capital letters, because the "key pressed?" block itself is case-insensitive. Changing that would be a breaking change, but adding support for detecting "shift" on its own should not break anything.
    • "Shift" is a modifier key, but it's not a control key like control or meta/super/command (or arguably alt/option) are. Generally speaking, browsers and operating systems do not have any "special" behavior for pressing shift plus another key, because text entry is such a common practice and shift+letter is such a universal input during text entry. This doubly applies for Scratch because Scratch projects do, very often, detect keyboard input (in a typing context or otherwise).
    • Caveat: It's not practical to detect or supply information about caps lock. So projects can adapt to the shift key, but won't respect caps lock. This is a minor issue IMO, but worth noting.
  • This is not the first time a key has been added to Scratch previously. As mentioned, refer to Add enter key to list of keyboard events detected. #2247 for discussion about the "enter" key.
  • Because "shift" is primarily a modifier key, and is a key which wasn't detected at all before, it's excluded from the "any key pressed?" blocks. This is in acknowledgement of a comment on the "enter" pull request - it's important not to break existing behavior.
  • That said, "backspace" does get counted in "key any pressed?" and "when key any pressed". This is a change to existing projects. However, it seems minor, and in the same vein as "enter", which also changed existing projects. Both "backspace" and "enter" are types of "actions", i.e. pressing backspace represents doing a thing (deleting a character, going back, etc) just like enter (inserting a new line, confirming an action, etc). Modifier keys on the other hand almost always precede an action (insert a capitalized letter, perform an alternate command, etc).
    • Incidentally, this allows for a convenient way for a project to switch between one set of keyboard behavior and another: "when key any pressed: if key shift pressed: say (A key combination was pressed! Ooo!)"

Test Coverage

Tested manually and with unit tests.

Use the script / project linked: Keyboard Project.zip

Screenshot 2023-12-10 at 6 34 27 PM

For unit tests:

  • The existing "ignore modifier key" test has been renamed to "ignore control key" and tests a control key instead of shift.
  • A new test has been added for "shift" specifically, to test that it is detected but does not count for "any".
  • All tests are updated to check the KEY_PRESSED and KEY_ANY_PRESSED events used by the "when key pressed" block, improving test coverage (and verifying an important detail that "shift" shouldn't count for "when any key pressed").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add more keys to "key pressed" dropdown
1 participant