Skip to content

Configurable hotkeys + fastforward hotkey as example #1519

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 3 commits into
base: main
Choose a base branch
from

Conversation

Anime37
Copy link

@Anime37 Anime37 commented Mar 19, 2025

Demos (old UI):

Implemented configurable hotkeys and tried to make it as easy as possible to add new hotkeys.
Second commit is an example of how to add a new hotkey and it's also a pretty useful hotkey in my opinion.

im bad at design but it works:
image
image

keyboard hotkeys also support modifiers:
image

can also add non-configurable hotkeys (as example, added toggle_fullscreen_alt and exit_fullscreen, since i doubt anyone would want to change those)

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 8b4d7f5 to 3baf0a0 Compare March 19, 2025 14:43
@Anime37
Copy link
Author

Anime37 commented Mar 19, 2025

fixed modifiers not saving to config + cut down hotkey values to 2bytes instead of 4bytes.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 3baf0a0 to b22cae4 Compare March 19, 2025 14:53
@Anime37
Copy link
Author

Anime37 commented Mar 19, 2025

added missing init call (needed for mapping configured hotkeys on startup)

@chriztr
Copy link

chriztr commented Mar 19, 2025

Question;
Could a hotkey for exiting cemu be added?
And can these be mapped to your controller?

@Anime37
Copy link
Author

Anime37 commented Mar 19, 2025

Question; Could a hotkey for exiting cemu be added? And can these be mapped to your controller?

if this gets approved, either me or anyone else could add a hotkey for exiting the emulator.
currently this is only capturing keyboard events, so can't bind controller buttons. that's the next thing i'll try to implement.

Comment on lines 9 to 18
static std::unordered_map<uHotkey*, std::function<void(wxFrame&)>> cfg_hotkey_to_func_map{
{&cfg_hotkeys.toggle_fullscreen, [](wxFrame& frame) {frame.ShowFullScreen(!frame.IsFullScreen());}},
{&cfg_hotkeys.toggle_fullscreen_alt, [](wxFrame& frame) {frame.ShowFullScreen(!frame.IsFullScreen());}},
{&cfg_hotkeys.exit_fullscreen, [](wxFrame& frame) {frame.ShowFullScreen(false);}},
{&cfg_hotkeys.take_screenshot, [](wxFrame&) {g_window_info.has_screenshot_request = true;}},
{&cfg_hotkeys.toggle_fastforward, [](wxFrame& frame) {ActiveSettings::SetTimerShiftFactor((ActiveSettings::GetTimerShiftFactor() < 3) ? 3 : 1);}},
};
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 arbitrary to have the MainWindow frame passed as an argument for all hotkey functions when not all of them use it. Could you call the frame functions via some other means?

Copy link
Author

Choose a reason for hiding this comment

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

hm ya, mainly did this for fullscreen hotkeys. will see if i can store a static pointer to mainwindow on init or something.

Copy link
Collaborator

@goeiecool9999 goeiecool9999 Mar 20, 2025

Choose a reason for hiding this comment

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

I suggest moving the init call to CemuApp::OnInit() I see you already did!

Copy link
Author

Choose a reason for hiding this comment

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

switched to storing pointer to main window on init.

wxFlexGridSizer* m_sizer;
std::vector<HotkeyEntry> m_hotkeys;
std::unordered_map<wxButton*, uHotkey*> m_input_to_cfg_map;
bool need_to_save = false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

General note: Please conform to the style guidelines. Admittedly some of Cemu's code already strays from it, but at the very least need_to_save should have an m_ prefix for consistency. I don't know how strictly we should enforce the style guidelines, but I think it's best to adhere to it for new code. I'll defer to @Exzap on that.

Copy link
Author

Choose a reason for hiding this comment

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

that's my style too, somehow missed this one though

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Comment on lines 101 to 289
if (new_hotkey.raw != old_hotkey.raw)
{
auto& cfg_hotkey = *m_input_to_cfg_map[obj];
need_to_save |= (new_hotkey.raw != old_hotkey.raw);
cfg_hotkey = new_hotkey;
hotkey_to_func_map.erase(old_hotkey.raw);
hotkey_to_func_map[cfg_hotkey.raw] = cfg_hotkey_to_func_map[&cfg_hotkey];
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

m_input_to_cfg_map can be eliminated. wxButton inherits from wxEvtHandler which means you can call SetClientData() to store any arbitrary pointer, which can later be retrieved with GetClientData(). As wxWidgets' documentation points out Normally, any extra data the programmer wishes to associate with the object should be made available by deriving a new class with new data members. but I think in this case defining a whole new button class is more trouble than it's worth.

Copy link
Author

@Anime37 Anime37 Mar 20, 2025

Choose a reason for hiding this comment

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

oh, that's exactly what i was looking for and was surprised when i wasn't able to find it. thanks.

Copy link
Author

Choose a reason for hiding this comment

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

removed m_input_to_cfg_map and done everything with SetClientData and GetClientData as was suggested.

Comment on lines 106 to 110
hotkey_to_func_map.erase(old_hotkey.raw);
hotkey_to_func_map[cfg_hotkey.raw] = cfg_hotkey_to_func_map[&cfg_hotkey];
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this account for an edge case where a user selects the same key combination for multiple hotkeys? Should that be allowed?

Copy link
Author

Choose a reason for hiding this comment

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

good point, it doesn't. probably will block duplicate setups (to avoid having to parse vectors during key press captures).

Copy link
Author

Choose a reason for hiding this comment

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

blocked attempts at trying to configure duplicate hotkeys.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch 2 times, most recently from e99dcae to 6403f0c Compare March 20, 2025 14:49
@Anime37
Copy link
Author

Anime37 commented Mar 20, 2025

fixed alternative hardcoded fullscreen hotkey (apparently ENTER key in wxWidgets is WXK_CONTROL_M, should have just looked up unicodes)

Comment on lines 10 to 18
static std::unordered_map<uHotkey*, std::function<void(void)>> cfg_hotkey_to_func_map{
{&cfg_hotkeys.toggle_fullscreen, [](void) {s_main_window->ShowFullScreen(!s_main_window->IsFullScreen());}},
{&cfg_hotkeys.toggle_fullscreen_alt, [](void) {s_main_window->ShowFullScreen(!s_main_window->IsFullScreen());}},
{&cfg_hotkeys.exit_fullscreen, [](void) {s_main_window->ShowFullScreen(false);}},
{&cfg_hotkeys.take_screenshot, [](void) {g_window_info.has_screenshot_request = true;}},
{&cfg_hotkeys.toggle_fastforward, [](void) {ActiveSettings::SetTimerShiftFactor((ActiveSettings::GetTimerShiftFactor() < 3) ? 3 : 1);}},
};
Copy link
Collaborator

@goeiecool9999 goeiecool9999 Mar 20, 2025

Choose a reason for hiding this comment

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

Since the number of distinct hotkey bindings is known at compile time, wouldn't a fixed size array with enum indices suffice?

Copy link
Author

@Anime37 Anime37 Mar 20, 2025

Choose a reason for hiding this comment

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

hmmm, sounds like a bit of an extra step when adding new hotkeys though. maybe could get away with making this map const?

as for the only other map left - hotkey_to_func_map - i pretty much go from input to func via hash by using it, so i really don't want to change that one especially.

Copy link
Collaborator

@goeiecool9999 goeiecool9999 Mar 20, 2025

Choose a reason for hiding this comment

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

I've thought about it and while it would be possible it would make the code more complex. Still, something about using pointers as keys in a std::map feels off. Though I can't really justify that feeling. I guess it's fine since the pointed to objects have static storage duration anyway.
Otherwise you would have to use the enum as a key everywhere including the button ClientData and map the enum to both uHotkey instances from the config class and also to the functions in separate arrays. (or use std::pair and reduce that to one)
All in all how it currently is is probably cleaner.

Copy link
Author

Choose a reason for hiding this comment

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

ya, pointers as keys are scary, but since it's pointers to static data, i guess it's a edge-case where it stops being scary 😆

Copy link
Author

Choose a reason for hiding this comment

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

made the map const anyways, maybe some of unordered_map data will go to .rodata, who knows.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch 2 times, most recently from 409f626 to 3208cc6 Compare March 21, 2025 14:46
@Anime37
Copy link
Author

Anime37 commented Mar 21, 2025

fixed issue where you pressing another hotkey input button wouldn't restore the previously clicked hotkey input button.
just not sure if there's a better way to go around such an edge-case.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 3208cc6 to b2b4a2a Compare March 21, 2025 15:18
@Anime37
Copy link
Author

Anime37 commented Mar 21, 2025

cleaned up some formatting issues after setting up vscode environment with project's coding style rules in mind.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from b2b4a2a to 69c541e Compare March 22, 2025 07:48
@Anime37 Anime37 requested a review from goeiecool9999 March 24, 2025 16:57
Comment on lines 87 to 190
if (active_hotkey_input_btn)
{
wxKeyEvent revert_event{};
revert_event.SetEventObject(active_hotkey_input_btn);
on_key_up(revert_event);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of invoking the event handler, perhaps it would be better to separate out the part of on_key_up that resets the button into a separate method and call that from both on_key_up and on_hotkey_input_click?

Copy link
Author

Choose a reason for hiding this comment

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

should be better now.

wxPanel* m_panel;
wxFlexGridSizer* m_sizer;
std::vector<HotkeyEntry> m_hotkeys;
wxButton* active_hotkey_input_btn = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Missing m_

Copy link
Author

Choose a reason for hiding this comment

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

added missing prefix

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 69c541e to 16c7b13 Compare March 26, 2025 09:26
@Anime37 Anime37 marked this pull request as draft March 26, 2025 09:27
@Anime37
Copy link
Author

Anime37 commented Mar 26, 2025

will adapt coding style to conform with repo's coding style rules. putting on draft till that is done.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 16c7b13 to 74edd9a Compare March 26, 2025 10:02
@Anime37
Copy link
Author

Anime37 commented Mar 26, 2025

fixed all occurrences (i hope atleast) of code style being broken. was less annoying than i thought it'd be.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 74edd9a to 56fb4e6 Compare March 26, 2025 10:17
@Anime37
Copy link
Author

Anime37 commented Mar 26, 2025

moved globals into class scope (i think looks cleaner)

@Anime37 Anime37 marked this pull request as ready for review March 26, 2025 10:19
@Anime37 Anime37 requested a review from goeiecool9999 March 26, 2025 10:19
@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 56fb4e6 to 1facb8a Compare March 26, 2025 14:55
@Anime37
Copy link
Author

Anime37 commented Mar 26, 2025

disabled default hotkey for the added fast-forward hotkey to not mess up anyone's current setups with unexpected behaviors when pressing SPACE.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 1facb8a to dc2d090 Compare March 27, 2025 14:20
@Anime37
Copy link
Author

Anime37 commented Mar 27, 2025

added an ability to cancel hotkey input and clear configured hotkey with a right mouse button click

@Anime37
Copy link
Author

Anime37 commented Mar 28, 2025

@goeiecool9999 since many structural things changed after adding controller hotkey support in #1523 (mainly due to uHotkey becoming sHotkeyCfg), should i port the new structure to this PR (with all controller hotkey related things omitted)? would make the controller hotkey support PR easier to review.

@goeiecool9999
Copy link
Collaborator

I think there's no need for that. We'll have to review everything anyway. It looks like the sHotkeyCfg struct is specifically intended to support binding both keyboard and controller inputs. I don't think it makes much sense to backport that to a PR that only supports keyboard bindings. But to be honest I don't really even see the point in having a separate PR to add the controller support to the hotkeys. I think we should merge it all at once.

@Anime37
Copy link
Author

Anime37 commented Mar 30, 2025

I think there's no need for that. We'll have to review everything anyway. It looks like the sHotkeyCfg struct is specifically intended to support binding both keyboard and controller inputs. I don't think it makes much sense to backport that to a PR that only supports keyboard bindings. But to be honest I don't really even see the point in having a separate PR to add the controller support to the hotkeys. I think we should merge it all at once.

aight, i'll make it so this PR has both keyboard and controller support then.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from dc2d090 to 0de34da Compare March 30, 2025 12:49
@Anime37
Copy link
Author

Anime37 commented Mar 30, 2025

restructured PR so that it would include the new structure + controller hotkey support.
split keyboard and controller into separate commits.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 0de34da to 2e7f587 Compare March 30, 2025 13:01
@Anime37
Copy link
Author

Anime37 commented Mar 30, 2025

fixed some code styling issues (snake notation stuff)

@Anime37
Copy link
Author

Anime37 commented Apr 3, 2025

Hm, guess i didn't commit something when it comes to the icon. Will check tomorrow.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 2e7f587 to d273a82 Compare April 4, 2025 11:26
@Anime37
Copy link
Author

Anime37 commented Apr 4, 2025

seems like vcpkg issue, synced the fork and rebased to latest main. hopefully this works.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from d273a82 to f666350 Compare April 4, 2025 11:38
@Anime37
Copy link
Author

Anime37 commented Apr 4, 2025

included resources.h for Linux and MacOS (monkey see, monkey do). hopefully linux and macos build pipelines pass now.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from f666350 to d505ca1 Compare April 12, 2025 13:39
@Anime37
Copy link
Author

Anime37 commented Apr 12, 2025

made sure everything builds on my fork repo, so ready to be reviewed.

@Exzap
Copy link
Member

Exzap commented Apr 23, 2025

The UI needs a bit more work. There should be spacing between text labels and the window border. You can look at the code of other windows for common values that we use. The buttons should be centered on the same height as the labels next to them, right now they look misaligned.

{
if (hotkey.alt)
{
ret.append("ALT + ");
Copy link
Member

@Exzap Exzap Apr 23, 2025

Choose a reason for hiding this comment

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

To make these strings localizable wrap it inside _("string"). E.g. the whole line becomes:
ret.append(_("ALT + "));
Also a nitpick: I think only capitalizing the first letter is what is more commonly done and at least to me looks better. E.g. Ctrl instead of CTRL

Copy link
Author

Choose a reason for hiding this comment

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

made strings localizable and made modifier's names like suggested

@@ -67,6 +69,22 @@ const ControllerState& ControllerBase::update_state()

#undef APPLY_AXIS_BUTTON

/* hotkey capturing */
const auto& hotkey_mod = HotkeySettings::s_cfgHotkeys.modifiers.controller;
Copy link
Member

Choose a reason for hiding this comment

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

Working with internal state of HotkeySettings here. Preferably you would encapsulate this into function(s) of HotkeySettings. Right now the logic of Hotkey tracking leaks into other classes and thats something that should be avoided.

Copy link
Author

Choose a reason for hiding this comment

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

encapsulated logic in CaptureInput methods for both keyboard and controller hotkeys

};

HotkeySettings::HotkeySettings(wxWindow* parent)
: wxFrame(parent, wxID_ANY, "Hotkey Settings")
Copy link
Member

Choose a reason for hiding this comment

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

Make the strings in this function localizable. E.g. _("Hotkey Settings"), same for the hotkey row labels

Copy link
Author

Choose a reason for hiding this comment

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

fixed

s_keyboardHotkeyToFuncMap[keyboardHotkey] = func;
}
auto controllerHotkey = cfgHotkey->controller;
if (controllerHotkey > 0)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't this be >= 0 because -1 is the value for disabled controller hotkeys and not 0?

Copy link
Author

Choose a reason for hiding this comment

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

oops, fixed. also defined None values in sHotkeyCfg struct. should look better now

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from d505ca1 to 9855aac Compare May 24, 2025 07:10
@Anime37
Copy link
Author

Anime37 commented May 24, 2025

The UI needs a bit more work. There should be spacing between text labels and the window border. You can look at the code of other windows for common values that we use. The buttons should be centered on the same height as the labels next to them, right now they look misaligned.

will address this later, since it will take more time to analyze the code.

@Anime37 Anime37 force-pushed the feature/configurable_hotkeys branch from 9855aac to d617277 Compare May 25, 2025 04:20
@Anime37
Copy link
Author

Anime37 commented May 25, 2025

reworked the UI (updated pictures in PR description)

had to rework the key/controller hotkey differentiation logic in order to change button colors to be the same as in Input Settings.

took some inspiration from Elden Ring for displaying unbound/edit-mode hotkeys 😄

@Anime37 Anime37 requested a review from Exzap May 25, 2025 04:30
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.

4 participants