Propagate key events (#1323)
Previously, `wxEVT_KEY_DOWN` and `wxEVT_KEY_UP` were transformed into `VBAM_EVT_USER_INPUT` and marked as processed early. This broke a number of controls that rely on the traditional widgets events. To fix this, we now filter the `wxEVT_KEY_*` and `wxEVT_CHAR` events in the only widget we use that cares about them, `UserInputCtrl` instead of filtering them at the application level.
This commit is contained in:
parent
cf5cb40cb9
commit
e4ef4aa625
|
@ -389,11 +389,6 @@ endif()
|
|||
if(APPLE)
|
||||
target_sources(visualboyadvance-m PRIVATE
|
||||
macsupport.mm
|
||||
widgets/dpi-support-mac.mm
|
||||
)
|
||||
else()
|
||||
target_sources(visualboyadvance-m PRIVATE
|
||||
widgets/dpi-support.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ public:
|
|||
bool operator++(int) { return *this += 1; }
|
||||
bool operator--(int) { return *this -= 1; }
|
||||
bool operator+=(T value) {
|
||||
const int new_value = Get() + value;
|
||||
const T new_value = Get() + value;
|
||||
if (new_value > Max()) {
|
||||
return Set(Max());
|
||||
} else {
|
||||
|
@ -196,7 +196,7 @@ public:
|
|||
}
|
||||
}
|
||||
bool operator-=(T value) {
|
||||
const int new_value = Get() - value;
|
||||
const T new_value = Get() - value;
|
||||
if (new_value < Min()) {
|
||||
return Set(Min());
|
||||
} else {
|
||||
|
|
|
@ -8,6 +8,7 @@ target_sources(vbam-wx-widgets
|
|||
PRIVATE
|
||||
# from external source with minor modifications
|
||||
checkedlistctrl.cpp
|
||||
$<IF:$<BOOL:${APPLE}>,dpi-support-mac.mm,dpi-support.cpp>
|
||||
group-check-box.cpp
|
||||
keep-on-top-styler.cpp
|
||||
option-validator.cpp
|
||||
|
@ -46,6 +47,7 @@ if(BUILD_TESTING)
|
|||
group-check-box-test.cpp
|
||||
keep-on-top-styler-test.cpp
|
||||
option-validator-test.cpp
|
||||
user-input-ctrl-test.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(vbam-wx-widgets-tests
|
||||
|
|
|
@ -14,7 +14,7 @@ WidgetsTest::~WidgetsTest() = default;
|
|||
void WidgetsTest::SetUp() {
|
||||
// Give the wxFrame a unique name and initialize it.
|
||||
const char* test_name(testing::UnitTest::GetInstance()->current_test_info()->name());
|
||||
frame_ = std::make_unique<wxFrame>(nullptr, wxXmlResource ::DoGetXRCID(test_name), test_name);
|
||||
frame_ = std::make_unique<wxFrame>(nullptr, wxXmlResource::DoGetXRCID(test_name), test_name);
|
||||
}
|
||||
|
||||
void WidgetsTest::TearDown() {
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
#include "wx/widgets/user-input-ctrl.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "wx/config/user-input.h"
|
||||
#include "wx/widgets/test/widgets-test.h"
|
||||
|
||||
namespace widgets {
|
||||
TEST_F(WidgetsTest, UserInputCtrlTest) {
|
||||
// Add a UserInputCtrl to the frame, `frame()` takes ownership here.
|
||||
UserInputCtrl* user_input_ctrl = new UserInputCtrl(frame(), XRCID("UserInputCtrl"));
|
||||
|
||||
// Check the UserInputCtrl is empty.
|
||||
EXPECT_TRUE(user_input_ctrl->IsEmpty());
|
||||
|
||||
// Send a EVT_CHAR event to the UserInputCtrl.
|
||||
wxKeyEvent key_event(wxEVT_CHAR);
|
||||
key_event.m_keyCode = 'a';
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(key_event);
|
||||
|
||||
// Check the UserInputCtrl is empty.
|
||||
EXPECT_TRUE(user_input_ctrl->IsEmpty());
|
||||
|
||||
// Send a EVT_KEY_DOWN event to the UserInputCtrl.
|
||||
wxKeyEvent key_down_event(wxEVT_KEY_DOWN);
|
||||
key_down_event.m_keyCode = 'a';
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(key_down_event);
|
||||
|
||||
// Check the UserInputCtrl is empty.
|
||||
EXPECT_TRUE(user_input_ctrl->IsEmpty());
|
||||
|
||||
// Send a EVT_USER_INPUT event to the UserInputCtrl.
|
||||
UserInputEvent user_input_event1({UserInputEvent::Data(config::KeyboardInput('A'), false)});
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(user_input_event1);
|
||||
|
||||
// Check the UserInputCtrl is not empty.
|
||||
EXPECT_FALSE(user_input_ctrl->IsEmpty());
|
||||
EXPECT_EQ(user_input_ctrl->SingleInput(), config::KeyboardInput('A'));
|
||||
|
||||
// Send another EVT_USER_INPUT event to the UserInputCtrl.
|
||||
UserInputEvent user_input_event2({UserInputEvent::Data(config::KeyboardInput('B'), false)});
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(user_input_event2);
|
||||
|
||||
// Check the UserInputCtrl is not empty and contains a single input.
|
||||
EXPECT_FALSE(user_input_ctrl->IsEmpty());
|
||||
EXPECT_EQ(user_input_ctrl->SingleInput(), config::KeyboardInput('B'));
|
||||
EXPECT_EQ(user_input_ctrl->inputs().size(), 1);
|
||||
}
|
||||
|
||||
TEST_F(WidgetsTest, UserInputCtrlMultiKeyTest) {
|
||||
// Add a UserInputCtrl to the frame, `frame()` takes ownership here.
|
||||
UserInputCtrl* user_input_ctrl = new UserInputCtrl(frame(), XRCID("UserInputCtrl"));
|
||||
user_input_ctrl->SetMultiKey(true);
|
||||
|
||||
// Check the UserInputCtrl is empty.
|
||||
EXPECT_TRUE(user_input_ctrl->IsEmpty());
|
||||
|
||||
// Send a EVT_USER_INPUT event to the UserInputCtrl.
|
||||
UserInputEvent user_input_event1({UserInputEvent::Data(config::KeyboardInput('A'), false)});
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(user_input_event1);
|
||||
|
||||
// Check the UserInputCtrl is not empty.
|
||||
EXPECT_FALSE(user_input_ctrl->IsEmpty());
|
||||
EXPECT_EQ(user_input_ctrl->inputs(),
|
||||
std::unordered_set<config::UserInput>({config::KeyboardInput('A')}));
|
||||
|
||||
// Send another EVT_USER_INPUT event to the UserInputCtrl.
|
||||
UserInputEvent user_input_event2({UserInputEvent::Data(config::KeyboardInput('B'), false)});
|
||||
user_input_ctrl->GetEventHandler()->ProcessEvent(user_input_event2);
|
||||
|
||||
// Check the UserInputCtrl is not empty and contains two inputs.
|
||||
EXPECT_FALSE(user_input_ctrl->IsEmpty());
|
||||
EXPECT_EQ(user_input_ctrl->inputs(),
|
||||
std::unordered_set<config::UserInput>(
|
||||
{config::KeyboardInput('A'), config::KeyboardInput('B')}));
|
||||
}
|
||||
|
||||
} // namespace widgets
|
|
@ -8,25 +8,33 @@
|
|||
|
||||
namespace widgets {
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper callback to disable an event. This is bound dynmically to prevent
|
||||
// the event from being processed by the base class.
|
||||
void DisableEvent(wxEvent& event) {
|
||||
event.Skip(false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern const char UserInputCtrlNameStr[] = "userinputctrl";
|
||||
|
||||
UserInputCtrl::UserInputCtrl() : wxTextCtrl() {}
|
||||
|
||||
UserInputCtrl::UserInputCtrl(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxString& value,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
long style,
|
||||
const wxString& name) {
|
||||
Create(parent, id, value, pos, size, style, name);
|
||||
Create(parent, id, pos, size, style, name);
|
||||
}
|
||||
|
||||
UserInputCtrl::~UserInputCtrl() = default;
|
||||
|
||||
bool UserInputCtrl::Create(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxString& value,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
long style,
|
||||
|
@ -36,7 +44,12 @@ bool UserInputCtrl::Create(wxWindow* parent,
|
|||
last_focus_time_ = wxGetUTCTimeMillis();
|
||||
event.Skip();
|
||||
});
|
||||
return wxTextCtrl::Create(parent, id, value, pos, size, style, wxValidator(), name);
|
||||
|
||||
// Diable key events.
|
||||
this->Bind(wxEVT_CHAR, &DisableEvent);
|
||||
this->Bind(wxEVT_KEY_DOWN, &DisableEvent);
|
||||
|
||||
return wxTextCtrl::Create(parent, id, wxEmptyString, pos, size, style, wxValidator(), name);
|
||||
}
|
||||
|
||||
void UserInputCtrl::SetMultiKey(bool multikey) {
|
||||
|
@ -115,7 +128,7 @@ UserInputCtrlXmlHandler::UserInputCtrlXmlHandler() : wxXmlResourceHandler() {
|
|||
wxObject* UserInputCtrlXmlHandler::DoCreateResource() {
|
||||
XRC_MAKE_INSTANCE(control, UserInputCtrl)
|
||||
|
||||
control->Create(m_parentAsWindow, GetID(), GetText("value"), GetPosition(), GetSize(),
|
||||
control->Create(m_parentAsWindow, GetID(), GetPosition(), GetSize(),
|
||||
GetStyle() | wxTE_PROCESS_ENTER | wxTE_PROCESS_TAB, GetName());
|
||||
SetupWindow(control);
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
UserInputCtrl();
|
||||
UserInputCtrl(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxString& value = wxEmptyString,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = 0,
|
||||
|
@ -34,7 +33,6 @@ public:
|
|||
|
||||
bool Create(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxString& value = wxEmptyString,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = 0,
|
||||
|
|
|
@ -1333,9 +1333,9 @@ int wxvbamApp::FilterEvent(wxEvent& event)
|
|||
}
|
||||
|
||||
if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_KEY_UP) {
|
||||
// Handle keyboard input events here. No control will receive them.
|
||||
// Handle keyboard input events here to generate user input events.
|
||||
keyboard_input_sender_.ProcessKeyEvent(static_cast<wxKeyEvent&>(event));
|
||||
return wxEventFilter::Event_Processed;
|
||||
return wxEventFilter::Event_Skip;
|
||||
}
|
||||
|
||||
if (!frame->CanProcessShortcuts()) {
|
||||
|
|
Loading…
Reference in New Issue