[UserInput] Only process shortcut commands once
This modifies the UserInputEvent class to fire a vector of events at once, rather than individual down/up events for each UserInput. This simplifies handling of the global event filter and prevents the firing of spurious events. This also fixes a bug when pressing "Ctrl+1" would trigger the command for both the command assigned to "Ctrl+1" and to "1". Now, only the "Ctrl+1" command will fire.
This commit is contained in:
parent
d32be9ddbe
commit
902c6c8e4b
|
@ -1177,8 +1177,7 @@ void GameArea::OnIdle(wxIdleEvent& event)
|
|||
wxWindow* w = panel->GetWindow();
|
||||
|
||||
// set up event handlers
|
||||
w->Bind(VBAM_EVT_USER_INPUT_DOWN, &GameArea::OnUserInputDown, this);
|
||||
w->Bind(VBAM_EVT_USER_INPUT_UP, &GameArea::OnUserInputUp, this);
|
||||
w->Bind(VBAM_EVT_USER_INPUT, &GameArea::OnUserInput, this);
|
||||
w->Bind(wxEVT_PAINT, &GameArea::PaintEv, this);
|
||||
w->Bind(wxEVT_ERASE_BACKGROUND, &GameArea::EraseBackground, this);
|
||||
|
||||
|
@ -1330,26 +1329,33 @@ static Display* GetX11Display() {
|
|||
}
|
||||
#endif // __WXGTK__
|
||||
|
||||
void GameArea::OnUserInputDown(widgets::UserInputEvent& event) {
|
||||
if (wxGetApp().emulated_gamepad()->OnInputPressed(event.input())) {
|
||||
wxWakeUpIdle();
|
||||
}
|
||||
}
|
||||
|
||||
void GameArea::OnUserInputUp(widgets::UserInputEvent& event) {
|
||||
if (wxGetApp().emulated_gamepad()->OnInputReleased(event.input())) {
|
||||
wxWakeUpIdle();
|
||||
void GameArea::OnUserInput(widgets::UserInputEvent& event) {
|
||||
bool emulated_key_pressed = false;
|
||||
for (const auto& event_data : event.data()) {
|
||||
if (event_data.pressed) {
|
||||
if (wxGetApp().emulated_gamepad()->OnInputPressed(event_data.input)) {
|
||||
emulated_key_pressed = true;
|
||||
}
|
||||
} else {
|
||||
if (wxGetApp().emulated_gamepad()->OnInputReleased(event_data.input)) {
|
||||
emulated_key_pressed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tell Linux to turn off the screensaver/screen-blank if joystick button was pressed
|
||||
// this shouldn't be necessary of course
|
||||
if (emulated_key_pressed) {
|
||||
wxWakeUpIdle();
|
||||
|
||||
#if defined(__WXGTK__) && defined(HAVE_X11) && !defined(HAVE_XSS)
|
||||
if (event.input().is_joystick() && !wxGetApp().UsingWayland()) {
|
||||
auto display = GetX11Display();
|
||||
XResetScreenSaver(display);
|
||||
XFlush(display);
|
||||
}
|
||||
// Tell X11 to turn off the screensaver/screen-blank if a button was
|
||||
// was pressed. This shouldn't be necessary.
|
||||
if (!wxGetApp().UsingWayland()) {
|
||||
auto display = GetX11Display();
|
||||
XResetScreenSaver(display);
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// these three are forwarded to the DrawingPanel instance
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <cassert>
|
||||
#include <map>
|
||||
|
||||
#include <SDL_events.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/toplevel.h>
|
||||
|
||||
|
@ -86,9 +85,10 @@ public:
|
|||
bool is_game_controller() const { return !!game_controller_; }
|
||||
|
||||
// Processes the corresponding events.
|
||||
std::vector<UserInputEvent> ProcessAxisEvent(const uint8_t index, const JoyAxisStatus status);
|
||||
std::vector<UserInputEvent> ProcessButtonEvent(const uint8_t index, const bool pressed);
|
||||
std::vector<UserInputEvent> ProcessHatEvent(const uint8_t index, const uint8_t status);
|
||||
std::vector<UserInputEvent::Data> ProcessAxisEvent(const uint8_t index,
|
||||
const JoyAxisStatus status);
|
||||
std::vector<UserInputEvent::Data> ProcessButtonEvent(const uint8_t index, const bool pressed);
|
||||
std::vector<UserInputEvent::Data> ProcessHatEvent(const uint8_t index, const uint8_t status);
|
||||
|
||||
// Activates or deactivates rumble.
|
||||
void SetRumble(bool activate_rumble);
|
||||
|
@ -192,14 +192,14 @@ bool JoyState::IsValid() const {
|
|||
return sdl_joystick_;
|
||||
}
|
||||
|
||||
std::vector<UserInputEvent> JoyState::ProcessAxisEvent(const uint8_t index,
|
||||
const JoyAxisStatus status) {
|
||||
std::vector<UserInputEvent::Data> JoyState::ProcessAxisEvent(const uint8_t index,
|
||||
const JoyAxisStatus status) {
|
||||
const JoyAxisStatus previous_status = axis_[index];
|
||||
std::vector<UserInputEvent> events;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
|
||||
// Nothing to do if no-op.
|
||||
if (status == previous_status) {
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
// Update the value.
|
||||
|
@ -207,48 +207,50 @@ std::vector<UserInputEvent> JoyState::ProcessAxisEvent(const uint8_t index,
|
|||
|
||||
if (previous_status != JoyAxisStatus::Neutral) {
|
||||
// Send the "unpressed" event.
|
||||
events.push_back(UserInputEvent(
|
||||
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(previous_status), index), false));
|
||||
event_data.emplace_back(
|
||||
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(previous_status), index), false);
|
||||
}
|
||||
|
||||
// We already sent the "unpressed" event so nothing more to do.
|
||||
if (status == JoyAxisStatus::Neutral) {
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
// Send the "pressed" event.
|
||||
events.push_back(UserInputEvent(
|
||||
config::JoyInput(wx_joystick_, AxisStatusToJoyControl(status), index), true));
|
||||
event_data.emplace_back(config::JoyInput(wx_joystick_, AxisStatusToJoyControl(status), index),
|
||||
true);
|
||||
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
std::vector<UserInputEvent> JoyState::ProcessButtonEvent(const uint8_t index, const bool status) {
|
||||
std::vector<UserInputEvent::Data> JoyState::ProcessButtonEvent(const uint8_t index,
|
||||
const bool status) {
|
||||
const bool previous_status = buttons_[index];
|
||||
std::vector<UserInputEvent> events;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
|
||||
// Nothing to do if no-op.
|
||||
if (status == previous_status) {
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
// Update the value.
|
||||
buttons_[index] = status;
|
||||
|
||||
// Send the event.
|
||||
events.push_back(
|
||||
UserInputEvent(config::JoyInput(wx_joystick_, config::JoyControl::Button, index), status));
|
||||
event_data.emplace_back(config::JoyInput(wx_joystick_, config::JoyControl::Button, index),
|
||||
status);
|
||||
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
std::vector<UserInputEvent> JoyState::ProcessHatEvent(const uint8_t index, const uint8_t status) {
|
||||
std::vector<UserInputEvent::Data> JoyState::ProcessHatEvent(const uint8_t index,
|
||||
const uint8_t status) {
|
||||
const uint16_t previous_status = hats_[index];
|
||||
std::vector<UserInputEvent> events;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
|
||||
// Nothing to do if no-op.
|
||||
if (status == previous_status) {
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
// Update the value.
|
||||
|
@ -262,17 +264,17 @@ std::vector<UserInputEvent> JoyState::ProcessHatEvent(const uint8_t index, const
|
|||
const bool new_control_pressed = (status & bit) != 0;
|
||||
if (old_control_pressed && !new_control_pressed) {
|
||||
// Send the "unpressed" event.
|
||||
events.push_back(UserInputEvent(
|
||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), false));
|
||||
event_data.emplace_back(
|
||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), false);
|
||||
}
|
||||
if (!old_control_pressed && new_control_pressed) {
|
||||
// Send the "pressed" event.
|
||||
events.push_back(UserInputEvent(
|
||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), true));
|
||||
event_data.emplace_back(
|
||||
config::JoyInput(wx_joystick_, HatStatusToJoyControl(bit), index), true);
|
||||
}
|
||||
}
|
||||
|
||||
return events;
|
||||
return event_data;
|
||||
}
|
||||
|
||||
void JoyState::SetRumble(bool activate_rumble) {
|
||||
|
@ -341,23 +343,23 @@ void SdlPoller::Notify() {
|
|||
SDL_Event sdl_event;
|
||||
|
||||
while (SDL_PollEvent(&sdl_event)) {
|
||||
std::vector<UserInputEvent> events;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
JoyState* joy_state = nullptr;
|
||||
switch (sdl_event.type) {
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
joy_state = FindJoyState(sdl_event.cbutton.which);
|
||||
if (joy_state) {
|
||||
events = joy_state->ProcessButtonEvent(sdl_event.cbutton.button,
|
||||
sdl_event.cbutton.state);
|
||||
event_data = joy_state->ProcessButtonEvent(sdl_event.cbutton.button,
|
||||
sdl_event.cbutton.state);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
joy_state = FindJoyState(sdl_event.caxis.which);
|
||||
if (joy_state) {
|
||||
events = joy_state->ProcessAxisEvent(sdl_event.caxis.axis,
|
||||
AxisValueToStatus(sdl_event.caxis.value));
|
||||
event_data = joy_state->ProcessAxisEvent(
|
||||
sdl_event.caxis.axis, AxisValueToStatus(sdl_event.caxis.value));
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -372,23 +374,24 @@ void SdlPoller::Notify() {
|
|||
case SDL_JOYBUTTONUP:
|
||||
joy_state = FindJoyState(sdl_event.jbutton.which);
|
||||
if (joy_state && !joy_state->is_game_controller()) {
|
||||
events = joy_state->ProcessButtonEvent(sdl_event.jbutton.button,
|
||||
sdl_event.jbutton.state);
|
||||
event_data = joy_state->ProcessButtonEvent(sdl_event.jbutton.button,
|
||||
sdl_event.jbutton.state);
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYAXISMOTION:
|
||||
joy_state = FindJoyState(sdl_event.jaxis.which);
|
||||
if (joy_state && !joy_state->is_game_controller()) {
|
||||
events = joy_state->ProcessAxisEvent(sdl_event.jaxis.axis,
|
||||
AxisValueToStatus(sdl_event.jaxis.value));
|
||||
event_data = joy_state->ProcessAxisEvent(
|
||||
sdl_event.jaxis.axis, AxisValueToStatus(sdl_event.jaxis.value));
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_JOYHATMOTION:
|
||||
joy_state = FindJoyState(sdl_event.jhat.which);
|
||||
if (joy_state && !joy_state->is_game_controller()) {
|
||||
events = joy_state->ProcessHatEvent(sdl_event.jhat.hat, sdl_event.jhat.value);
|
||||
event_data =
|
||||
joy_state->ProcessHatEvent(sdl_event.jhat.hat, sdl_event.jhat.value);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -402,12 +405,10 @@ void SdlPoller::Notify() {
|
|||
break;
|
||||
}
|
||||
|
||||
if (!events.empty()) {
|
||||
if (!event_data.empty()) {
|
||||
wxEvtHandler* handler = handler_provider_();
|
||||
if (handler) {
|
||||
for (const auto& user_input_event : events) {
|
||||
handler->QueueEvent(user_input_event.Clone());
|
||||
}
|
||||
handler->QueueEvent(new UserInputEvent(std::move(event_data)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,9 +31,8 @@ bool UserInputCtrl::Create(wxWindow* parent,
|
|||
long style,
|
||||
const wxString& name) {
|
||||
this->SetClientObject(new UserInputEventSender(this));
|
||||
this->Bind(VBAM_EVT_USER_INPUT_UP, &UserInputCtrl::OnUserInputUp, this);
|
||||
this->Bind(VBAM_EVT_USER_INPUT, &UserInputCtrl::OnUserInput, this);
|
||||
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
||||
is_navigating_away_ = false;
|
||||
last_focus_time_ = wxGetUTCTimeMillis();
|
||||
event.Skip();
|
||||
});
|
||||
|
@ -66,7 +65,15 @@ void UserInputCtrl::Clear() {
|
|||
|
||||
wxIMPLEMENT_DYNAMIC_CLASS(UserInputCtrl, wxTextCtrl);
|
||||
|
||||
void UserInputCtrl::OnUserInputUp(UserInputEvent& event) {
|
||||
void UserInputCtrl::OnUserInput(UserInputEvent& event) {
|
||||
// Find the first pressed input.
|
||||
nonstd::optional<config::UserInput> input = event.FirstReleasedInput();
|
||||
|
||||
if (input == nonstd::nullopt) {
|
||||
// No pressed inputs.
|
||||
return;
|
||||
}
|
||||
|
||||
static const wxLongLong kInterval = 100;
|
||||
if (wxGetUTCTimeMillis() - last_focus_time_ < kInterval) {
|
||||
// Ignore events sent very shortly after focus. This is used to ignore
|
||||
|
@ -75,20 +82,13 @@ void UserInputCtrl::OnUserInputUp(UserInputEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (is_navigating_away_) {
|
||||
// Ignore events sent after the control has been navigated away from.
|
||||
event.Skip();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_multikey_) {
|
||||
inputs_.clear();
|
||||
}
|
||||
|
||||
inputs_.insert(event.input());
|
||||
inputs_.insert(std::move(input.value()));
|
||||
UpdateText();
|
||||
Navigate();
|
||||
is_navigating_away_ = true;
|
||||
}
|
||||
|
||||
void UserInputCtrl::UpdateText() {
|
||||
|
|
|
@ -61,7 +61,7 @@ public:
|
|||
|
||||
private:
|
||||
// Event handler.
|
||||
void OnUserInputUp(widgets::UserInputEvent& event);
|
||||
void OnUserInput(widgets::UserInputEvent& event);
|
||||
|
||||
// Updates the text in the control to reflect the current inputs.
|
||||
void UpdateText();
|
||||
|
@ -72,10 +72,6 @@ private:
|
|||
// very shortly after activation.
|
||||
wxLongLong last_focus_time_ = 0;
|
||||
|
||||
// Set to true after one input has been received. This is used to ignore
|
||||
// subsequent events until the control is focused again.
|
||||
bool is_navigating_away_ = false;
|
||||
|
||||
std::unordered_set<config::UserInput> inputs_;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#include "wx/widgets/user-input-event.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <wx/event.h>
|
||||
#include <wx/eventfilter.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
#include "wx/config/user-input.h"
|
||||
|
@ -109,11 +111,44 @@ wxKeyCode KeyFromModifier(const wxKeyModifier mod) {
|
|||
|
||||
} // namespace
|
||||
|
||||
UserInputEvent::UserInputEvent(const config::UserInput& input, bool pressed)
|
||||
: wxEvent(0, pressed ? VBAM_EVT_USER_INPUT_DOWN : VBAM_EVT_USER_INPUT_UP), input_(input) {}
|
||||
UserInputEvent::UserInputEvent(std::vector<Data> event_data)
|
||||
: wxEvent(0, VBAM_EVT_USER_INPUT), data_(std::move(event_data)) {}
|
||||
|
||||
nonstd::optional<config::UserInput> UserInputEvent::FirstReleasedInput() const {
|
||||
const auto iter =
|
||||
std::find_if(data_.begin(), data_.end(), [](const auto& data) { return !data.pressed; });
|
||||
|
||||
if (iter == data_.end()) {
|
||||
// No pressed inputs.
|
||||
return nonstd::nullopt;
|
||||
}
|
||||
|
||||
return iter->input;
|
||||
}
|
||||
|
||||
int UserInputEvent::FilterProcessedInput(const config::UserInput& user_input) {
|
||||
// Keep all data not using `user_input`.
|
||||
std::vector<Data> new_data;
|
||||
for (const auto& data : data_) {
|
||||
if (data.input != user_input) {
|
||||
new_data.push_back(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the internal data.
|
||||
data_ = std::move(new_data);
|
||||
|
||||
if (data_.empty()) {
|
||||
// All data was removed, the event was fully processed.
|
||||
return wxEventFilter::Event_Processed;
|
||||
} else {
|
||||
// Some data remains, let the event propagate.
|
||||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
}
|
||||
|
||||
wxEvent* UserInputEvent::Clone() const {
|
||||
return new UserInputEvent(*this);
|
||||
return new UserInputEvent(this->data_);
|
||||
}
|
||||
|
||||
UserInputEventSender::UserInputEventSender(wxWindow* const window)
|
||||
|
@ -161,26 +196,25 @@ void UserInputEventSender::OnKeyDown(wxKeyEvent& event) {
|
|||
}
|
||||
|
||||
const wxKeyModifier active_mods = GetModifiersFromSet(active_mods_);
|
||||
std::vector<config::KeyboardInput> new_inputs;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
if (key_pressed == WXK_NONE) {
|
||||
// A new standalone modifier was pressed, send the event.
|
||||
new_inputs.emplace_back(KeyFromModifier(mod_pressed), mod_pressed);
|
||||
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_pressed), mod_pressed),
|
||||
true);
|
||||
} else {
|
||||
// A new key was pressed, send the event with modifiers, first.
|
||||
new_inputs.emplace_back(key, active_mods);
|
||||
event_data.emplace_back(config::KeyboardInput(key, active_mods), true);
|
||||
|
||||
if (active_mods != wxMOD_NONE) {
|
||||
// Keep track of the key pressed with the active modifiers.
|
||||
active_mod_inputs_.emplace(key, active_mods);
|
||||
|
||||
// Also send the key press event without modifiers.
|
||||
new_inputs.emplace_back(key, wxMOD_NONE);
|
||||
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), true);
|
||||
}
|
||||
}
|
||||
|
||||
for (const config::KeyboardInput& input : new_inputs) {
|
||||
wxQueueEvent(window_, new UserInputEvent(input, true));
|
||||
}
|
||||
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
||||
}
|
||||
|
||||
void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||
|
@ -220,31 +254,32 @@ void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
|||
return;
|
||||
}
|
||||
|
||||
std::vector<config::KeyboardInput> released_inputs;
|
||||
std::vector<UserInputEvent::Data> event_data;
|
||||
if (key_released == WXK_NONE) {
|
||||
// A standalone modifier was released, send it.
|
||||
released_inputs.emplace_back(KeyFromModifier(mod_released), mod_released);
|
||||
event_data.emplace_back(config::KeyboardInput(KeyFromModifier(mod_released), mod_released),
|
||||
false);
|
||||
} else {
|
||||
// A key was released.
|
||||
if (previous_mods == wxMOD_NONE) {
|
||||
// The key was pressed without modifiers, just send the key release event.
|
||||
released_inputs.emplace_back(key, wxMOD_NONE);
|
||||
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
|
||||
} else {
|
||||
// Check if the key was pressed with the active modifiers.
|
||||
const config::KeyboardInput input_with_modifiers(key, previous_mods);
|
||||
auto iter = active_mod_inputs_.find(input_with_modifiers);
|
||||
if (iter == active_mod_inputs_.end()) {
|
||||
// The key press event was never sent, so do it now.
|
||||
wxQueueEvent(window_, new UserInputEvent(input_with_modifiers, true));
|
||||
event_data.emplace_back(input_with_modifiers, true);
|
||||
} else {
|
||||
active_mod_inputs_.erase(iter);
|
||||
}
|
||||
|
||||
// Send the key release event with the active modifiers.
|
||||
released_inputs.emplace_back(key, previous_mods);
|
||||
event_data.emplace_back(config::KeyboardInput(key, previous_mods), false);
|
||||
|
||||
// Also send the key release event without modifiers.
|
||||
released_inputs.emplace_back(key, wxMOD_NONE);
|
||||
event_data.emplace_back(config::KeyboardInput(key, wxMOD_NONE), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -255,15 +290,14 @@ void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
|||
auto iter = active_mod_inputs_.find(input);
|
||||
if (iter != active_mod_inputs_.end()) {
|
||||
active_mod_inputs_.erase(iter);
|
||||
released_inputs.push_back(std::move(input));
|
||||
event_data.emplace_back(std::move(input), false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (const config::KeyboardInput& input : released_inputs) {
|
||||
active_mod_inputs_.erase(input);
|
||||
wxQueueEvent(window_, new UserInputEvent(input, false));
|
||||
for (const auto& data : event_data) {
|
||||
active_mod_inputs_.erase(data.input.keyboard_input());
|
||||
}
|
||||
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
||||
}
|
||||
|
||||
void UserInputEventSender::Reset(wxFocusEvent& event) {
|
||||
|
@ -278,5 +312,4 @@ void UserInputEventSender::Reset(wxFocusEvent& event) {
|
|||
|
||||
} // namespace widgets
|
||||
|
||||
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
||||
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT_UP, widgets::UserInputEvent);
|
||||
wxDEFINE_EVENT(VBAM_EVT_USER_INPUT, widgets::UserInputEvent);
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#define WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include <optional.hpp>
|
||||
|
||||
#include <wx/clntdata.h>
|
||||
#include <wx/event.h>
|
||||
|
@ -10,20 +13,43 @@
|
|||
|
||||
namespace widgets {
|
||||
|
||||
// Event fired when a user input is pressed or released. The event contains the
|
||||
// user input that was pressed or released.
|
||||
// Event fired when a set of user input are pressed or released. The event
|
||||
// contains the set of user input that were pressed or released. The order
|
||||
// in the vector matters, this is the order in which the inputs were pressed or
|
||||
// released.
|
||||
class UserInputEvent final : public wxEvent {
|
||||
public:
|
||||
UserInputEvent(const config::UserInput& input, bool pressed);
|
||||
// Data for the event. Contains the user input and whether it was pressed or
|
||||
// released.
|
||||
struct Data {
|
||||
const config::UserInput input;
|
||||
const bool pressed;
|
||||
|
||||
Data(config::UserInput input, bool pressed) : input(input), pressed(pressed){};
|
||||
};
|
||||
|
||||
UserInputEvent(std::vector<Data> event_data);
|
||||
virtual ~UserInputEvent() override = default;
|
||||
|
||||
// Disable copy and copy assignment.
|
||||
UserInputEvent(const UserInputEvent&) = delete;
|
||||
UserInputEvent& operator=(const UserInputEvent&) = delete;
|
||||
|
||||
// Returns the first pressed input, if any.
|
||||
nonstd::optional<config::UserInput> FirstReleasedInput() const;
|
||||
|
||||
// Mark `event_data` as processed and returns the new event filter. This is
|
||||
// meant to be used with FilterEvent() to process global shortcuts before
|
||||
// sending the event to the next handler.
|
||||
int FilterProcessedInput(const config::UserInput& user_input);
|
||||
|
||||
// wxEvent implementation.
|
||||
wxEvent* Clone() const override;
|
||||
|
||||
const config::UserInput& input() const { return input_; }
|
||||
const std::vector<Data>& data() const { return data_; }
|
||||
|
||||
private:
|
||||
const config::UserInput input_;
|
||||
std::vector<Data> data_;
|
||||
};
|
||||
|
||||
// Object that is used to fire user input events when a key is pressed or
|
||||
|
@ -59,9 +85,7 @@ private:
|
|||
|
||||
} // namespace widgets
|
||||
|
||||
// Fired when a user input is pressed.
|
||||
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT_DOWN, widgets::UserInputEvent);
|
||||
// Fired when a user input is released.
|
||||
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT_UP, widgets::UserInputEvent);
|
||||
// Fired when a set of user inputs are pressed or released.
|
||||
wxDECLARE_EVENT(VBAM_EVT_USER_INPUT, widgets::UserInputEvent);
|
||||
|
||||
#endif // WX_WIDGETS_USER_INPUT_EVENT_H_
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "wx/wxvbam.h"
|
||||
#include "wx/config/command.h"
|
||||
|
||||
#ifdef __WXMSW__
|
||||
#include <windows.h>
|
||||
|
@ -35,6 +34,7 @@
|
|||
#include "wx/builtin-over.h"
|
||||
#include "wx/builtin-xrc.h"
|
||||
#include "wx/config/cmdtab.h"
|
||||
#include "wx/config/command.h"
|
||||
#include "wx/config/emulated-gamepad.h"
|
||||
#include "wx/config/option-proxy.h"
|
||||
#include "wx/config/option.h"
|
||||
|
@ -995,27 +995,46 @@ int MainFrame::FilterEvent(wxEvent& event) {
|
|||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
|
||||
if (event.GetEventType() != VBAM_EVT_USER_INPUT_DOWN) {
|
||||
// We only treat "VBAM_EVT_USER_INPUT_DOWN" events here.
|
||||
if (event.GetEventType() != VBAM_EVT_USER_INPUT) {
|
||||
// We only treat "VBAM_EVT_USER_INPUT" events here.
|
||||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
|
||||
const widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
||||
nonstd::optional<config::Command> command =
|
||||
wxGetApp().bindings()->CommandForInput(user_input_event.input());
|
||||
if (command == nonstd::nullopt) {
|
||||
widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
||||
const config::Bindings* bindings = wxGetApp().bindings();
|
||||
int command_id = wxID_NONE;
|
||||
nonstd::optional<config::UserInput> user_input;
|
||||
|
||||
for (const auto& event_data : user_input_event.data()) {
|
||||
if (!event_data.pressed) {
|
||||
// We only treat key press events here.
|
||||
continue;
|
||||
}
|
||||
|
||||
const nonstd::optional<config::Command> command =
|
||||
bindings->CommandForInput(event_data.input);
|
||||
if (command != nonstd::nullopt && command->is_shortcut()) {
|
||||
// Associated shortcut command found.
|
||||
command_id = command->shortcut().id();
|
||||
user_input.emplace(event_data.input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (command_id == wxID_NONE) {
|
||||
// No associated command found.
|
||||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
|
||||
if (!command->is_shortcut()) {
|
||||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
|
||||
wxCommandEvent command_event(wxEVT_COMMAND_MENU_SELECTED, command->shortcut().id());
|
||||
// Execute the associated shortcut command.
|
||||
wxCommandEvent command_event(wxEVT_COMMAND_MENU_SELECTED, command_id);
|
||||
command_event.SetEventObject(this);
|
||||
this->GetEventHandler()->ProcessEvent(command_event);
|
||||
return wxEventFilter::Event_Processed;
|
||||
|
||||
// Filter out the processed input so it is not processed again. This also
|
||||
// prevents us from firing 2 commands if the user presses "Ctrl+1" and has
|
||||
// a shortcut for both "Ctrl+1" and "1". Only one will be handled here.
|
||||
return user_input_event.FilterProcessedInput(user_input.value());
|
||||
}
|
||||
|
||||
wxString MainFrame::GetGamePath(wxString path)
|
||||
|
|
|
@ -530,8 +530,7 @@ protected:
|
|||
|
||||
bool paused;
|
||||
void OnIdle(wxIdleEvent&);
|
||||
void OnUserInputDown(widgets::UserInputEvent& event);
|
||||
void OnUserInputUp(widgets::UserInputEvent& event);
|
||||
void OnUserInput(widgets::UserInputEvent& event);
|
||||
void PaintEv(wxPaintEvent& ev);
|
||||
void EraseBackground(wxEraseEvent& ev);
|
||||
void OnSize(wxSizeEvent& ev);
|
||||
|
|
Loading…
Reference in New Issue