-
Notifications
You must be signed in to change notification settings - Fork 752
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
Adding support for hotkeys in Emulator (#304) #308
Conversation
Thank you for this, but I am sorry to say that it needs to be done another way. These hotkeys should be implemented as accelerators on menu items. It can be done using Electron's accelerator support. No need for mousetrap. Search the code for 'show-about'. It is an accelerator to show the About Dialog. Follow its implementation. Let me know if you need further clarification. Also, let me know if you are out of time to work on this :) |
@eanders-ms I updated the commit by using the pattern that 'show-about' uses. |
@@ -95,6 +95,10 @@ export class AddressBarTextBox extends React.Component<{}, {}> { | |||
|
|||
componentWillMount() { | |||
this.settingsUnsubscribe = addSettingsListener((settings) => { | |||
if (settings.hotkey.focusAddressBar) { | |||
settings.hotkey.focusAddressBar = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
settings
is immutable. It can only be set in a reducer. A total pain, I know. Let me think about how to do this another way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can set the other values to false e.g. when triggering openMenu: Object.assign({}, state, { openMenu: true, focusAddressBar: false, refreshConversation: false });
Though the current approach was working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a guiding principle of Redux that the state object is immutable. Redux works by returning a new state object each time a change is affected in a reducer. To change the state locally creates an inconsistency between the locally changed state and the actual application state. The flag you're resetting isn't getting reset in the authoritative version of the state.
In fact, you can see it happen:
- in src/server/main.ts, comment out the last line:
require('electron-debug')();
, so that you can test the F5 shortcut without reloading the app, then recompile and run the emulator. - connect to a bot
- press F5 to refresh the conversation
- say something in the conversation
- notice the conversation refreshes again!
I think it can be fixed by clearing the flag via another reducer action, like HotKey_FocusAddressBar_Clear
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you very much for taking the time to share the knowledge. :)
Implemented your suggestion in the latest commit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's getting closer! I noticed two bugs:
- Menu Position vs. Zoom Level: When the zoom level is not the default level, the menu doesn't appear in the correct location when opened using the hotkey. Could you position it so that the top-left corner is in the center of the hamburger icon?
- Double Conversation Refresh: After I send a bot a message, pressing F5 once doesn't refresh the conversation. If pressed a second time, it will refresh. After the refresh, if I say something to the bot, my bot will reply but then the conversation will unexpectedly refresh again.
Getting into more fine-grained feedback on the behavior:
- Some hotkeys should toggle the state, e.g.: When the address bar is focused, pressing F6 should unfocus it. This means some hotkeys are "tri-state", having Unset, Focus, Blur states. I notice the menu hotkey already toggles, but am unsure why. It might be the behavior of
Menu.popup
to hide the menu if it is already showing. The Menu object does have aclosePopup
method.
Some tips for debugging client code
Shift+Ctrl+I
will open up the Chrome debugger, which can be very useful to set breakpoints and stepping through client code, to see when the render calls happen, that the state looks like in reducers, etc.
If you have to comment out the inclusion of electron-debug
, then you can uncomment the call to mainWindow.webContents.openDevTools();
that appears earlier in main.ts. This will just always open the Chrome debugger.
Thank you again for taking this on!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Er, I guess you wouldn't need a tri-state field actually, just toggle existing focus state. Sorry for the bad advice :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@eanders-ms I was checking to create a solution for Menu Position vs Zoom Level but when zoomed in/out the electron API reports the wrong ratio. It is a known issue.
Possible Solution: different ratio per zoom level?
(Simulating click on the menu won't work as by default the menu appears wherever the mouse cursor is.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Latest commit solves all issues:
- Toggling: Affected other components visibility as well so I moved changes up to the parent component AddressBar.
- Double Conversation Refresh: Clicking the
refresh
button the method newConversation is called. I updated hotkeys to call the newConversation method directly. - Menu Position: This solution doesn't make me happy. But couldn't come up or find any better solutions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good! Agreed the menu positioning is pretty rough with the fixed zoom ratios. I might rework the zoom system later to pull all the logic into some kind of zoom handler.
A few minor comments in the code, then we can merge it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all the help along the way :)
src/server/main.ts
Outdated
@@ -201,6 +201,19 @@ const createMainWindow = () => { | |||
windowManager.zoomTo(0); | |||
}); | |||
|
|||
let registerHotkeys = (hotkeys, callback) => hotkeys.forEach(hotkey => | |||
Electron.globalShortcut.register(hotkey, callback)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I recently became aware of this Electron issue, where globally registered shortcuts trigger regardless of whether the app is in focus. I think we need to avoid that, otherwise, when you refreshed a browser window, it would also refresh your bot conversation in the background. A viable workaround would be to take a dependency on 'electron-localshortcut', which circumvents this issue. We'll have to do this for the Zoom shortcuts as well (we'll take care of that in a separate PR. @mgbennet for visibility)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented electron-localshortcut. And tested it. Works like a charm.
onBlur() { | ||
this.hasFocus = false; | ||
AddressBarActions.loseFocus(); | ||
//const settings = getSettings(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is dead code, please remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
} | ||
|
||
getZoomRatio(zoomLevel: number) { | ||
let zoomInRatios = [1, 1.2, 1.437, 1.73, 2.07, 2.49, 2.99, 3.58, 4.3]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please describe how you calculated these values, or where they came from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added comment to it. The zoom in ratios are found very scientifically.
Binary Search and trial&error :P . Tested it in two resolutions 1080p and 1600x900.
getZoomRatio(zoomLevel: number) { | ||
// Below zoom in and out ratios are found by trial and error. | ||
// They fix the wrong coordinates reported on different zoom levels by Element.getBoundingClientRect(). | ||
let zoomInRatios = [1, 1.2, 1.437, 1.73, 2.07, 2.49, 2.99, 3.58, 4.3]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, this was really bugging me so I came up with a mathematical approximation based on your data set:
getZoomRatio(zoomLevel: number): number {
return 0.4819 * Math.exp(0.1824 * (zoomLevel + 4));
}
Could you please make this change? One thing left to do is to make a constant for the 4
literal, but I can do that separately after your changes are in (since I'll want to thread it into the zoom in/out code as well).
* F5 / Ctrl+R - Refresh the conversation. * F10 / Alt+F - Open the menu. * F6 / Ctrl+L - Focus the address bar. The same apply for Mac using ⌘ instead of Ctrl.
} | ||
|
||
getZoomRatio(zoomLevel: number): number { | ||
return 0.4819 * Math.exp(0.1824 * (zoomLevel + 4)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When this is merged in I will comment it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a piece of math 👍
Btw. I wanted to make the 4 a constant but left it better to you as I was coming up with this name REBALANCE_NEGATIVE_ZOOM_LEVEL :p
@rinormaloku thank you for your contribution! It's been a pleasure working with you. |
Support added for following hotkeys:
The same applies for Mac using ⌘ instead of Ctrl.