Skip to content
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

Choose Summon keybinding #1489

Closed
wants to merge 23 commits into from
Closed

Conversation

goosewobbler
Copy link
Contributor

@goosewobbler goosewobbler commented Mar 15, 2023

Allowing users to choose the summon keybinding. Key presses are detected and mapped to an accelerator which is registered using the globalShortcut API. We are still using the experimental keyboard API getLayoutMap to better represent shortcut keys to users with different keyboard layouts (though this does not work in all cases)

https://www.w3.org/TR/uievents-code/#code-value-tables
https://www.electronjs.org/docs/latest/api/accelerator
https://www.electronjs.org/docs/latest/api/global-shortcut
https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap

Limitations

Testing

102 key UK keyboard (all platforms) & Macbook Pro UK keyboard (Mac)
Layouts - EN (US), EN (UK), ES (Spain), FR (France), DE (Germany)

Nonfunctional Bindings

Unless we disable these keys for every layout, there is currently no way to detect a layout (outside of specific cases where the event key property maps to a different code when it is likely to be nonfunctional).

  • Quote - nonfunctional on Windows (EN-GB)
  • Backquote - nonfunctional on Windows (EN-GB)

Disabled Keys

Keys are disabled when a matching accelerator does not exist, or if the accelerator is partly or completely non-functional. In these cases a user would be able to successfully bind a key which wouldn't summon or hide Frame. Disabling the keys means that they won't register when the user presses them when trying to configure the shortcut.

It's not feasible to test all of the layouts so we will aim for a decent base level of coverage / disablement of problematic keys and rely on users to report nonfunctional bindings which would benefit their setup with a given keyboard & layout.

Global - disabled on all platforms

IntlBackslash, IntlRo, IntlYen, Pause, NumpadEnter, AltRight

Mac

NumLock

Windows

F12

Disabled when the event key property doesn't match the expected one (most non EN layouts):
Backslash, Backquote, Minus, Equal, BracketLeft, BracketRight, Quote, Comma, Period, Slash, Semicolon

Linux

Backslash, Backquote, Minus, Equal, BracketLeft, BracketRight, Quote, Comma, Period, Slash

Disabled when the event key property doesn't match the expected one (most non EN layouts):
Semicolon


TODO

  • Finalise keyCode => Accelerator mappings (Mac)
  • Finalise keyCode => Accelerator mappings (Windows)
  • Finalise keyCode => Accelerator mappings (Linux)
  • Exempt nonfunctional / problematic keys
  • Disable existing shortcut when entering new one
  • Finish layout testing (Mac)
  • Finish layout testing (Windows)
  • Finish layout testing (Linux)
  • Verify hotkeys is required vs. native methods
  • Improve discoverability (edit button?)
  • Cancel button
  • Tests (migration at a minimum)

@goosewobbler goosewobbler added WIP PRs that are still in progress and not ready for review or merging enhancement New feature or request accessibility window management and removed WIP PRs that are still in progress and not ready for review or merging labels Mar 15, 2023
Copy link
Collaborator

@mholtzman mholtzman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

had a couple issues with running on Ubuntu, though not sure if they're about the code, OS specific, or specific to what I'm running:

  • some meta+ combinations such as Meta+B only hide but dont summon
  • can't assign slash as the key code, which is the default shortcut

in general I think this could be a pretty big rabbit hole if we try to carve out edge cases for every single keyboard layout and locale. if there's no comprehensive way to simply map key codes to strings, maybe we can simplify the problem by only allowing the key code to be a fixed, smaller set of mostly universally-defined keys? possibly something like only letters, numbers, and a small set of punctuation like slashes

@@ -30,7 +32,37 @@ class Settings extends Component {
}

render() {
const { modifierKey, summonKey } = getSummonShortcut(this.store('platform'))
const summonShortcut = this.store('main.shortcuts.summon')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this logic be extracted into its own functional component?

platform,
summonShortcut
)
hotkeys.unbind()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this kind of setup can be done in a useEffect hook that runs when the value of configureShortcut changes instead of every time it renders

const { modifierKey, summonKey } = getSummonShortcut(this.store('platform'))
const summonShortcut = this.store('main.shortcuts.summon')
const platform = this.store('platform')
const { modifierKeys: summonModifierKeys, shortcutKey: summonShortcutKey } = getDisplayShortcut(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems like a bunch of extra work to store the shortcut as a deconstructed object and have to put it together in multiple places. can we just create the accelerator string once when its pressed and pass that around?

Copy link
Contributor Author

@goosewobbler goosewobbler Mar 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this is that the accelerator string will never change according to the key layout of the user when it comes to displaying the shortcut. I thought it better to store the shortcut so that it can be mapped using the keyboard layoutMap API at runtime, rather than having to parse an accelerator each time

hotkeys.unbind()
if (this.state.configureShortcut) {
// disable existing shortcut whilst configuring a new one
link.send('tray:action', 'setShortcut', 'summon', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should disable the shortcut as if the user cancels its now disabled if it was previously enabled

Copy link
Contributor Author

@goosewobbler goosewobbler Mar 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Problem here is that if you try to set the same shortcut again it just hides Frame. Maybe this is less of an issue now we have a cancel button

Copy link
Contributor Author

@goosewobbler goosewobbler Mar 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the same shortcut again edge case definitely problematic so I made the cancel revert the state

const isModifierKey = modifierKeys.includes(event.key)

// ignore modifier key solo keypresses and disabled keys
if (!isModifierKey && !isDisabledKey(event, platform)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic can be moved into something like the getShorcutFromKeyEvent function and that function can simply respond with the accelerator string or undefined if the shortcut is not valid

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may also be able to leverage something like this: https://github.com/brrd/electron-is-accelerator

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this logic can be moved into something like the getShorcutFromKeyEvent function and that function can simply respond with the accelerator string or undefined if the shortcut is not valid

This works if we are storing the accelerator but I think it's better to keep the key code + modifiers as a single source of truth and create the accelerator from that

we may also be able to leverage something like this: brrd/electron-is-accelerator

Not been updated in a while, wouldn't want to rely on it

@mholtzman
Copy link
Collaborator

@goosewobbler also lets target canary as the base branch

@mholtzman mholtzman force-pushed the choose-summon-keybinding branch from b8032e9 to cc7c63b Compare March 21, 2023 15:33
@mholtzman mholtzman changed the base branch from develop to canary March 21, 2023 15:37
@goosewobbler
Copy link
Contributor Author

Closed in favour of #1494

@mholtzman mholtzman deleted the choose-summon-keybinding branch April 14, 2023 21:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants