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

Add support for customizable keyboard shortcuts #1938

Merged
merged 42 commits into from
Aug 25, 2020

Conversation

nilaymaj
Copy link
Contributor

This PR adds support for customizable keyboard shortcuts to both the web and desktop app. Every shortcut is linked to a corresponding command, and the default shortcuts can be overridden by the user in a new "Keyboard Shortcuts" tab in preferences dialog.

Added logic for checking whether user is typing something. This unblocks
shortcut possibilities that earlier clashed with text entry, thus a lot
of keytype-specific restrictions have been removed.
@nilaymaj nilaymaj requested a review from 4ian as a code owner August 24, 2020 10:39
newIDE/app/yarn.lock Outdated Show resolved Hide resolved
Comment on lines 68 to 86
{areaWiseCommands[areaName].map(commandName => {
// Get default and user-set shortcuts
const userShortcut = props.userShortcutMap[commandName];
const defaultShortcut = defaultShortcuts[commandName] || '';
const shortcutString = userShortcut || defaultShortcut;
const shortcutDisplayName = getShortcutDisplayName(
shortcutString
);
// Check if shortcut clashes with another command
const clashingCommandName = Object.keys(commandsList)
.filter(commandName => !commandsList[commandName].noShortcut)
.find(otherCommandName => {
if (otherCommandName === commandName) return false;
const otherShortcut =
props.userShortcutMap[otherCommandName] ||
defaultShortcuts[otherCommandName] ||
'';
if (shortcutString !== otherShortcut) return false;
if (shortcutString === '') return false;
Copy link
Owner

Choose a reason for hiding this comment

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

In this logic, it seems that the complexity is more important than what it could be.
You iterate on all commands (well, on all areas, then on each commands of this area, so at the end it's on all commands). For each command, you get its shortcutString (so far so good).
Then inside this loop, you're iterating again on the commandsList, computing for each one its shortcutString.

At the end, this means that you're roughly doing n^2 the work of computing the shortcut strings and comparing them. When we'll have 200 commands, this means 40000 operations, which start to make a lot for some low end or mobile devices :)

Instead, consider the approach of using an object as a map of used shortcutString. In pseudo code:

const shortcutStringToCommands = {};
for each command:
  // Get userShortcut and defaultShortcut as before
  const shortcutString = userShortcut || defaultShortcut
  shortcutStringToCommands[shortcutString] = (shortcutStringToCommands[shortcutString] || []).concat(command);

This is doing a single loop on commands, so complexity is n. Better, you can put this in a separate function outside of the render.

Then in the render, you iterate on commands again, and for each you look into shortcutStringToCommands, with the shortcutString of the command. If the array contains just your command, you're fine. If it contains something else, you have one (or more!) clashing commands :)

At the end, this is done in roughly 2*n operations (2 separate loops), so even with 200 commands, we'll have 400 operations to do - it should be more than fast enough even for the future and low end devices :)

@4ian
Copy link
Owner

4ian commented Aug 24, 2020

Added a few comments but seems we're on a good path!

@4ian 4ian added the 🏁PR almost ready: final fixes The PR is almost ready and needs final fixes and/or polishing before merge label Aug 24, 2020
Copy link
Contributor Author

@nilaymaj nilaymaj left a comment

Choose a reason for hiding this comment

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

I've made the fixes you suggested - please have a look!

@nilaymaj
Copy link
Contributor Author

I've added a DismissableAlertMessage for the command palette hint in the shortcuts list (which came along with a flag in "Hints and Explanations" tab in preferences). Looks like this:

Screenshot_20200825_192647

Is this what you had in mind?

@4ian
Copy link
Owner

4ian commented Aug 25, 2020 via email

@4ian
Copy link
Owner

4ian commented Aug 25, 2020

This looks good to me, merging now!🎉
Again huge thanks for working on this, this will be super useful for power users that will now benefit from a speed up when using GDevelop thanks to these customizable shortcut. I believe it will be super helpful for preview, for quickly jumping to an object/tab using the command palette, and I think we'll be able to add even more commands in the future! (I can almost see GDevelop being able to be entirely keyboard driven with commands :))

Very nice work, the code is of great quality! As the last time, I appreciate your attention to details when working on this 👏

@4ian 4ian merged commit 8957050 into 4ian:master Aug 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏁PR almost ready: final fixes The PR is almost ready and needs final fixes and/or polishing before merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants