Hotkey Submenus

Hey everyone! The new hotkey system is looking wonderful! I’ve been digging through the code to see what it can do, and I have a certain capability in mind that I feel should be implemented, but I cannot seem to do it myself, so any advice would be appreciated.

Submenus Hotkeys

Take, for instance, the mine tool. Currently, that button is contained in the harvest menu, but any time you hit the “M” key, it will pop straight to the mine tool.

With submenuing capability, you could define a hotkey as something like: “category:harvest>keym” and this would require you to have the harvest menu open, then hit the “M” key to activate the mining tool. This is much like how traditional RTS hotkeys will have, say, a “Build” button that opens a menu with the actual building options, each with their own hotkey.

My current issue …

  1. In hotkey_manager.js _eventMatchesCombo (e, combo) function, I cannot seem to find a way to attach custom fields to the e object that I have defines in hotkeys.json. For instance, my hotkeys,json object may look like:

“harvest:mine” : {
“display_name” : “i18n(”,
“category” : “harvest”,
“ordinal” : 20,
“combo1” : “keym”,
“combo2” : “”,
“require_category_menu”: true

but “require_category_menu” never gets attached to e, and I don’t understand how works, nor where to find ‘radiant:get_hotkey_definitions’ function. What’s more, is hotkey_manager.js even the right class to modify? Or should it be handled in the hotkey_service.lua? input_service.lua perhaps?

Additionally, I have this annoyance when using Decoda where if Im watching a variable, let’s say the KeyboardEvent e, if I specify “e” on the watchlist, all it will tell me is that is a [KeyboardEvent] object, not what variables and functions it has, is there any way to expose this info?


Ok, to address your specific questions:

  • hotkeys.json is read on the C++ side, so the format isn’t really moddable.
  • Almost all hotkeys are handled in the JavaScript/HTML, and go through hotkey_manager.js. The Lua side is only used for camera movement, and perhaps some debug keys.
  • You can’t really see the details of KeyboardEvent in Decoda, since it’s a C++ class, but as per above, you shouldn’t need to mess with that much. For JavaScript, you can use the excellent Chrome debugger which shows all fields and methods. You can access it at http://localhost:1338/ while the game is running.

As for getting hotkeys that only work in specific modes, that shouldn’t be too hard to achieve. Every hotkey listener is created in StonehearthHotkeyManager.bindActionsWithin(), and removed in StonehearthHotkeyManager.unbindActionsWithin() (in hotkey_manager.js). By default, whenever a view is rendered, it automatically calls bindActionsWithin() (through _addHotkeys()) in App.View.didInsertElement() (in view.js). If you want to override that, call unbindActionsWithin() on the elements containing the buttons that you don’t want to be always active, then in the code that opens/closes those menus, you can rebind/unbind them.

1 Like

Awesome! This is exactly the info I was looking for! You’re awesome Max :smiley: :smiley: Im gonna mess around with this and get back to you on the progress. :slightly_smiling_face:

You’ve been a big help already, but maybe you can help me out here :slightly_smiling_face:

Here’s the only file in my mod (filename: init.js):

$(top).on('start_menu_activated', function(i, item) {
   var stonehearthMenu = $('.stonehearthMenu');
   if (stonehearthMenu[0]) stonehearthMenu = stonehearthMenu[0];

   if (!$(stonehearthMenu).hasClass('unbound')){

And here’s what I know:

  1. The file is definitely executing
  2. the div for .stonehearthMenu is being passed to the unbindActionsWithin() function
  3. The function goes through the children of stonehearthMenu (harvest_menu, zone_menu, rootgroup, etc.)
  4. Those element’s hotkeys are still bound even after this.

Any idea where I’m going wrong?

start_menu_activated is triggered when you select an option from the “start menu” (the main toolbar). So with your code, the hotkeys will be unbound the first time you open a submenu, which is kind of the opposite of what you want. You’ll probably need a timer/interval to wait until the menu is rendered. As soon as it is rendered (i.e. .stonehearthMenu exists), do the unbinding, then rebind the right buttons when start_menu_activated is triggered. You’ll also need to unbind them when the menu is closed, but I don’t remember if we have an event for that. You can always do the hacky version of a timer/interval ticking while it’s open and detecting when it’s been closed.