[UserInput] Filter key events globally
This changes key events to be filtered globally by the application, preventing any widget from accessing them. Widgets should instead use the new VBAM_USER_INPUT_EVENT event. In addition, this explicitly removes accelerator handling from wxWidgets main menu, allowing us to populate joystick options in the menu without firing wxWidgets assertions.
This commit is contained in:
parent
902c6c8e4b
commit
d543784a3d
|
@ -73,6 +73,7 @@ set(VBAM_WX_COMMON
|
||||||
widgets/checkedlistctrl.h
|
widgets/checkedlistctrl.h
|
||||||
widgets/client-data.h
|
widgets/client-data.h
|
||||||
widgets/dpi-support.h
|
widgets/dpi-support.h
|
||||||
|
widgets/event-handler-provider.h
|
||||||
widgets/group-check-box.cpp
|
widgets/group-check-box.cpp
|
||||||
widgets/group-check-box.h
|
widgets/group-check-box.h
|
||||||
widgets/keep-on-top-styler.cpp
|
widgets/keep-on-top-styler.cpp
|
||||||
|
@ -87,6 +88,8 @@ set(VBAM_WX_COMMON
|
||||||
widgets/user-input-event.h
|
widgets/user-input-event.h
|
||||||
widgets/sdl-poller.cpp
|
widgets/sdl-poller.cpp
|
||||||
widgets/sdl-poller.h
|
widgets/sdl-poller.h
|
||||||
|
widgets/shortcut-menu-bar.cpp
|
||||||
|
widgets/shortcut-menu-bar.h
|
||||||
widgets/utils.cpp
|
widgets/utils.cpp
|
||||||
widgets/utils.h
|
widgets/utils.h
|
||||||
widgets/webupdatedef.h
|
widgets/webupdatedef.h
|
||||||
|
|
|
@ -230,12 +230,16 @@ JoyId JoyId::Invalid() {
|
||||||
return JoyId(kInvalidSdlIndex);
|
return JoyId(kInvalidSdlIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString JoyId::ToString() const {
|
wxString JoyId::ToConfigString() const {
|
||||||
return wxString::Format("Joy%d", sdl_index_ + 1);
|
return wxString::Format("Joy%d", sdl_index_ + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString JoyInput::ToString() const {
|
wxString JoyId::ToLocalizedString() const {
|
||||||
const wxString joy_string = joy_.ToString();
|
return wxString::Format(_("Joystick %d"), sdl_index_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString JoyInput::ToConfigString() const {
|
||||||
|
const wxString joy_string = joy_.ToConfigString();
|
||||||
switch (control_) {
|
switch (control_) {
|
||||||
case JoyControl::AxisPlus:
|
case JoyControl::AxisPlus:
|
||||||
return wxString::Format("%s-Axis%d+", joy_string, control_index_);
|
return wxString::Format("%s-Axis%d+", joy_string, control_index_);
|
||||||
|
@ -258,6 +262,30 @@ wxString JoyInput::ToString() const {
|
||||||
return wxEmptyString;
|
return wxEmptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxString JoyInput::ToLocalizedString() const {
|
||||||
|
const wxString joy_string = joy_.ToLocalizedString();
|
||||||
|
switch (control_) {
|
||||||
|
case JoyControl::AxisPlus:
|
||||||
|
return wxString::Format(_("%s: Axis %d+"), joy_string, control_index_);
|
||||||
|
case JoyControl::AxisMinus:
|
||||||
|
return wxString::Format(_("%s: Axis %d-"), joy_string, control_index_);
|
||||||
|
case JoyControl::Button:
|
||||||
|
return wxString::Format(_("%s: Button %d"), joy_string, control_index_);
|
||||||
|
case JoyControl::HatNorth:
|
||||||
|
return wxString::Format(_("%s: Hat %d North"), joy_string, control_index_);
|
||||||
|
case JoyControl::HatSouth:
|
||||||
|
return wxString::Format(_("%s: Hat %d South"), joy_string, control_index_);
|
||||||
|
case JoyControl::HatWest:
|
||||||
|
return wxString::Format(_("%s: Hat %d West"), joy_string, control_index_);
|
||||||
|
case JoyControl::HatEast:
|
||||||
|
return wxString::Format(_("%s: Hat %d East"), joy_string, control_index_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unreachable.
|
||||||
|
assert(false);
|
||||||
|
return wxEmptyString;
|
||||||
|
}
|
||||||
|
|
||||||
wxString KeyboardInput::ToConfigString() const {
|
wxString KeyboardInput::ToConfigString() const {
|
||||||
// Handle the modifier case separately.
|
// Handle the modifier case separately.
|
||||||
if (KeyIsModifier(key_)) {
|
if (KeyIsModifier(key_)) {
|
||||||
|
@ -291,10 +319,6 @@ wxString KeyboardInput::ToLocalizedString() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxString accel_string = wxAcceleratorEntry(mod_, key_).ToRawString().MakeUpper();
|
const wxString accel_string = wxAcceleratorEntry(mod_, key_).ToRawString().MakeUpper();
|
||||||
if (!accel_string.IsAscii()) {
|
|
||||||
// Unicode handling.
|
|
||||||
return wxString::Format("%d:%d", key_, mod_);
|
|
||||||
}
|
|
||||||
return accel_string;
|
return accel_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +360,7 @@ wxString UserInput::ToConfigString() const {
|
||||||
case Device::Keyboard:
|
case Device::Keyboard:
|
||||||
return keyboard_input().ToConfigString();
|
return keyboard_input().ToConfigString();
|
||||||
case Device::Joystick:
|
case Device::Joystick:
|
||||||
return joy_input().ToString();
|
return joy_input().ToConfigString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unreachable.
|
// Unreachable.
|
||||||
|
@ -351,7 +375,7 @@ wxString UserInput::ToLocalizedString() const {
|
||||||
case Device::Keyboard:
|
case Device::Keyboard:
|
||||||
return keyboard_input().ToLocalizedString();
|
return keyboard_input().ToLocalizedString();
|
||||||
case Device::Joystick:
|
case Device::Joystick:
|
||||||
return joy_input().ToString();
|
return joy_input().ToLocalizedString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unreachable.
|
// Unreachable.
|
||||||
|
|
|
@ -70,7 +70,8 @@ public:
|
||||||
constexpr explicit JoyId(int sdl_index) : sdl_index_(sdl_index){};
|
constexpr explicit JoyId(int sdl_index) : sdl_index_(sdl_index){};
|
||||||
~JoyId() = default;
|
~JoyId() = default;
|
||||||
|
|
||||||
wxString ToString() const;
|
wxString ToConfigString() const;
|
||||||
|
wxString ToLocalizedString() const;
|
||||||
|
|
||||||
constexpr bool operator==(const JoyId& other) const { return sdl_index_ == other.sdl_index_; }
|
constexpr bool operator==(const JoyId& other) const { return sdl_index_ == other.sdl_index_; }
|
||||||
constexpr bool operator!=(const JoyId& other) const { return sdl_index_ != other.sdl_index_; }
|
constexpr bool operator!=(const JoyId& other) const { return sdl_index_ != other.sdl_index_; }
|
||||||
|
@ -100,7 +101,8 @@ public:
|
||||||
constexpr JoyControl control() const { return control_; }
|
constexpr JoyControl control() const { return control_; }
|
||||||
constexpr uint8_t control_index() const { return control_index_; }
|
constexpr uint8_t control_index() const { return control_index_; }
|
||||||
|
|
||||||
wxString ToString() const;
|
wxString ToConfigString() const;
|
||||||
|
wxString ToLocalizedString() const;
|
||||||
|
|
||||||
constexpr bool operator==(const JoyInput& other) const {
|
constexpr bool operator==(const JoyInput& other) const {
|
||||||
return joy_ == other.joy_ && control_ == other.control_ &&
|
return joy_ == other.joy_ && control_ == other.control_ &&
|
||||||
|
|
|
@ -165,7 +165,6 @@ GameArea::GameArea()
|
||||||
config::OptionID::kSoundBuffers, config::OptionID::kSoundDSoundHWAccel,
|
config::OptionID::kSoundBuffers, config::OptionID::kSoundDSoundHWAccel,
|
||||||
config::OptionID::kSoundUpmix},
|
config::OptionID::kSoundUpmix},
|
||||||
[&](config::Option*) { schedule_audio_restart_ = true; }) {
|
[&](config::Option*) { schedule_audio_restart_ = true; }) {
|
||||||
this->SetClientObject(new widgets::UserInputEventSender(this));
|
|
||||||
SetSizer(new wxBoxSizer(wxVERTICAL));
|
SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||||
// all renderers prefer 32-bit
|
// all renderers prefer 32-bit
|
||||||
// well, "simple" prefers 24-bit, but that's not available for filters
|
// well, "simple" prefers 24-bit, but that's not available for filters
|
||||||
|
@ -1461,7 +1460,6 @@ DrawingPanel::DrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
, wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize(),
|
, wxPanel(parent, wxID_ANY, wxPoint(0, 0), parent->GetClientSize(),
|
||||||
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
||||||
{
|
{
|
||||||
this->SetClientObject(new widgets::UserInputEventSender(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawingPanelBase::DrawingPanelInit()
|
void DrawingPanelBase::DrawingPanelInit()
|
||||||
|
@ -2193,7 +2191,6 @@ GLDrawingPanel::GLDrawingPanel(wxWindow* parent, int _width, int _height)
|
||||||
, wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(),
|
, wxglc(parent, wxID_ANY, glopts, wxPoint(0, 0), parent->GetClientSize(),
|
||||||
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
wxFULL_REPAINT_ON_RESIZE | wxWANTS_CHARS)
|
||||||
{
|
{
|
||||||
this->SetClientObject(new widgets::UserInputEventSender(this));
|
|
||||||
widgets::RequestHighResolutionOpenGlSurfaceForWindow(this);
|
widgets::RequestHighResolutionOpenGlSurfaceForWindow(this);
|
||||||
SetContext();
|
SetContext();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,11 @@
|
||||||
#include "wx/viewsupt.h"
|
#include "wx/viewsupt.h"
|
||||||
|
|
||||||
#include "wx/config/option-proxy.h"
|
#include "wx/config/option-proxy.h"
|
||||||
|
#include "wx/config/user-input.h"
|
||||||
#include "wx/wxvbam.h"
|
#include "wx/wxvbam.h"
|
||||||
|
|
||||||
namespace Viewers {
|
namespace Viewers {
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
int getKeyboardKeyCode(const wxKeyEvent& event) {
|
|
||||||
const int key_code = event.GetKeyCode();
|
|
||||||
if (key_code > WXK_START) {
|
|
||||||
return key_code;
|
|
||||||
}
|
|
||||||
int uc = event.GetUnicodeKey();
|
|
||||||
if (uc != WXK_NONE) {
|
|
||||||
if (uc < 32) { // not all control chars
|
|
||||||
switch (uc) {
|
|
||||||
case WXK_BACK:
|
|
||||||
case WXK_TAB:
|
|
||||||
case WXK_RETURN:
|
|
||||||
case WXK_ESCAPE:
|
|
||||||
return uc;
|
|
||||||
default:
|
|
||||||
return WXK_NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return uc;
|
|
||||||
} else {
|
|
||||||
return event.GetKeyCode();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void Viewer::CloseDlg(wxCloseEvent& ev)
|
void Viewer::CloseDlg(wxCloseEvent& ev)
|
||||||
{
|
{
|
||||||
(void)ev; // unused params
|
(void)ev; // unused params
|
||||||
|
@ -339,12 +312,11 @@ void MemView::Refit()
|
||||||
NULL, this);
|
NULL, this);
|
||||||
disp.Connect(wxEVT_LEFT_UP, wxMouseEventHandler(MemView::MouseEvent),
|
disp.Connect(wxEVT_LEFT_UP, wxMouseEventHandler(MemView::MouseEvent),
|
||||||
NULL, this);
|
NULL, this);
|
||||||
disp.Connect(wxEVT_CHAR, wxKeyEventHandler(MemView::KeyEvent),
|
|
||||||
NULL, this);
|
|
||||||
disp.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX));
|
disp.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_LISTBOX));
|
||||||
sb.Create(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
sb.Create(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
|
||||||
wxSB_VERTICAL);
|
wxSB_VERTICAL);
|
||||||
sb.SetScrollbar(0, 15, 500, 15);
|
sb.SetScrollbar(0, 15, 500, 15);
|
||||||
|
disp.Bind(VBAM_EVT_USER_INPUT, &MemView::KeyEvent, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
wxClientDC dc(&disp);
|
wxClientDC dc(&disp);
|
||||||
|
@ -443,17 +415,28 @@ void MemView::ShowCaret()
|
||||||
disp.SetFocus();
|
disp.SetFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemView::KeyEvent(wxKeyEvent& ev)
|
void MemView::KeyEvent(widgets::UserInputEvent& ev)
|
||||||
{
|
{
|
||||||
uint32_t k = getKeyboardKeyCode(ev);
|
const nonstd::optional<config::UserInput> opt_user_input = ev.FirstReleasedInput();
|
||||||
int nnib = 2 << fmt;
|
if (opt_user_input == nonstd::nullopt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
switch (k) {
|
const config::UserInput& user_input = opt_user_input.value();
|
||||||
|
if (!user_input.is_keyboard()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxKeyCode key = user_input.keyboard_input().key();
|
||||||
|
const wxKeyModifier mod = user_input.keyboard_input().mod();
|
||||||
|
|
||||||
|
int nnib = 2 << fmt;
|
||||||
|
switch (key) {
|
||||||
case WXK_RIGHT:
|
case WXK_RIGHT:
|
||||||
case WXK_NUMPAD_RIGHT:
|
case WXK_NUMPAD_RIGHT:
|
||||||
if (isasc)
|
if (isasc)
|
||||||
selnib += 2;
|
selnib += 2;
|
||||||
else if (ev.GetModifiers() == wxMOD_SHIFT)
|
else if (mod == wxMOD_SHIFT)
|
||||||
selnib += 2 << fmt;
|
selnib += 2 << fmt;
|
||||||
else if (!(selnib % nnib))
|
else if (!(selnib % nnib))
|
||||||
selnib += nnib + nnib - 1;
|
selnib += nnib + nnib - 1;
|
||||||
|
@ -475,7 +458,7 @@ void MemView::KeyEvent(wxKeyEvent& ev)
|
||||||
case WXK_NUMPAD_LEFT:
|
case WXK_NUMPAD_LEFT:
|
||||||
if (isasc)
|
if (isasc)
|
||||||
selnib -= 2;
|
selnib -= 2;
|
||||||
else if (ev.GetModifiers() == wxMOD_SHIFT)
|
else if (mod == wxMOD_SHIFT)
|
||||||
selnib -= 2 << fmt;
|
selnib -= 2 << fmt;
|
||||||
else if (!(++selnib % nnib))
|
else if (!(++selnib % nnib))
|
||||||
selnib -= nnib * 2;
|
selnib -= nnib * 2;
|
||||||
|
@ -506,7 +489,7 @@ void MemView::KeyEvent(wxKeyEvent& ev)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (k > 0x7f || (isasc && !isprint(k)) || (!isasc && !isxdigit(k))) {
|
if (key > 0x7f || (isasc && !isprint(key)) || (!isasc && !isxdigit(key))) {
|
||||||
ev.Skip();
|
ev.Skip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -537,10 +520,10 @@ void MemView::KeyEvent(wxKeyEvent& ev)
|
||||||
|
|
||||||
if (isasc) {
|
if (isasc) {
|
||||||
mask = 0xff << bno * 8;
|
mask = 0xff << bno * 8;
|
||||||
val = k << bno * 8;
|
val = key << bno * 8;
|
||||||
} else {
|
} else {
|
||||||
mask = 8 * (0xf << bno) + 4 * nibno;
|
mask = 8 * (0xf << bno) + 4 * nibno;
|
||||||
val = isdigit(k) ? k - '0' : tolower(k) + 10 - 'a';
|
val = isdigit(key) ? key - '0' : tolower(key) + 10 - 'a';
|
||||||
val <<= bno * 8 + nibno * 4;
|
val <<= bno * 8 + nibno * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef VBAM_WX_VIEWSUPT_H_
|
#ifndef VBAM_WX_VIEWSUPT_H_
|
||||||
#define VBAM_WX_VIEWSUPT_H_
|
#define VBAM_WX_VIEWSUPT_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
#include <wx/wx.h>
|
#include <wx/wx.h>
|
||||||
#include <wx/window.h>
|
#include <wx/window.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
|
@ -14,7 +16,7 @@
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
#include <wx/checkbox.h>
|
#include <wx/checkbox.h>
|
||||||
|
|
||||||
#include <stdint.h> // for uint32_t
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
|
||||||
// avoid exporting too much stuff
|
// avoid exporting too much stuff
|
||||||
namespace Viewers {
|
namespace Viewers {
|
||||||
|
@ -218,7 +220,7 @@ protected:
|
||||||
int addrlen;
|
int addrlen;
|
||||||
|
|
||||||
void MouseEvent(wxMouseEvent& ev);
|
void MouseEvent(wxMouseEvent& ev);
|
||||||
void KeyEvent(wxKeyEvent& ev);
|
void KeyEvent(widgets::UserInputEvent& ev);
|
||||||
// the subwidgets
|
// the subwidgets
|
||||||
wxPanel disp;
|
wxPanel disp;
|
||||||
wxScrollBar sb;
|
wxScrollBar sb;
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef VBAM_WX_WIDGETS_EVENT_HANDLER_PROVIDER_H_
|
||||||
|
#define VBAM_WX_WIDGETS_EVENT_HANDLER_PROVIDER_H_
|
||||||
|
|
||||||
|
// Forward declaration.
|
||||||
|
class wxEvtHandler;
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
// Abstract interface to provide the currently active event handler to use for
|
||||||
|
// user input events.
|
||||||
|
class EventHandlerProvider {
|
||||||
|
public:
|
||||||
|
virtual ~EventHandlerProvider() = default;
|
||||||
|
|
||||||
|
// Returns the currently active event handler to use for user input events.
|
||||||
|
virtual wxEvtHandler* event_handler() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widgets
|
||||||
|
|
||||||
|
#endif // VBAM_WX_WIDGETS_EVENT_HANDLER_PROVIDER_H_
|
|
@ -300,12 +300,14 @@ void JoyState::Notify() {
|
||||||
SetRumble(rumbling_);
|
SetRumble(rumbling_);
|
||||||
}
|
}
|
||||||
|
|
||||||
SdlPoller::SdlPoller(const EventHandlerProvider handler_provider)
|
SdlPoller::SdlPoller(EventHandlerProvider* const handler_provider)
|
||||||
: enable_game_controller_(OPTION(kSDLGameControllerMode)),
|
: enable_game_controller_(OPTION(kSDLGameControllerMode)),
|
||||||
handler_provider_(handler_provider),
|
handler_provider_(handler_provider),
|
||||||
game_controller_enabled_observer_(
|
game_controller_enabled_observer_(
|
||||||
config::OptionID::kSDLGameControllerMode,
|
config::OptionID::kSDLGameControllerMode,
|
||||||
[this](config::Option* option) { ReconnectControllers(option->GetBool()); }) {
|
[this](config::Option* option) { ReconnectControllers(option->GetBool()); }) {
|
||||||
|
assert(handler_provider);
|
||||||
|
|
||||||
wxTimer::Start(50);
|
wxTimer::Start(50);
|
||||||
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS);
|
SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS);
|
||||||
SDL_GameControllerEventState(SDL_ENABLE);
|
SDL_GameControllerEventState(SDL_ENABLE);
|
||||||
|
@ -406,7 +408,7 @@ void SdlPoller::Notify() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event_data.empty()) {
|
if (!event_data.empty()) {
|
||||||
wxEvtHandler* handler = handler_provider_();
|
wxEvtHandler* handler = handler_provider_->event_handler();
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler->QueueEvent(new UserInputEvent(std::move(event_data)));
|
handler->QueueEvent(new UserInputEvent(std::move(event_data)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#ifndef WX_WIDGETS_SDL_POLLER_H_
|
#ifndef WX_WIDGETS_SDL_POLLER_H_
|
||||||
#define WX_WIDGETS_SDL_POLLER_H_
|
#define WX_WIDGETS_SDL_POLLER_H_
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
@ -9,6 +8,7 @@
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
#include "wx/config/option-observer.h"
|
#include "wx/config/option-observer.h"
|
||||||
|
#include "wx/widgets/event-handler-provider.h"
|
||||||
|
|
||||||
// Forward declarations.
|
// Forward declarations.
|
||||||
class wxEvtHandler;
|
class wxEvtHandler;
|
||||||
|
@ -18,16 +18,13 @@ namespace widgets {
|
||||||
// Forward declaration.
|
// Forward declaration.
|
||||||
class JoyState;
|
class JoyState;
|
||||||
|
|
||||||
// Provider for the event handler to send the events to.
|
|
||||||
using EventHandlerProvider = std::function<wxEvtHandler*()>;
|
|
||||||
|
|
||||||
// The SDL worker is responsible for handling SDL events and firing the
|
// The SDL worker is responsible for handling SDL events and firing the
|
||||||
// appropriate `UserInputEvent` for joysticks.
|
// appropriate `UserInputEvent` for joysticks.
|
||||||
// It is used to fire `UserInputEvent` for joysticks. This class should be kept
|
// It is used to fire `UserInputEvent` for joysticks. This class should be kept
|
||||||
// as a singleton owned by the application object.
|
// as a singleton owned by the application object.
|
||||||
class SdlPoller final : public wxTimer {
|
class SdlPoller final : public wxTimer {
|
||||||
public:
|
public:
|
||||||
explicit SdlPoller(const EventHandlerProvider handler_provider);
|
explicit SdlPoller(EventHandlerProvider* const handler_provider);
|
||||||
~SdlPoller() final;
|
~SdlPoller() final;
|
||||||
|
|
||||||
// Disable copy and copy assignment.
|
// Disable copy and copy assignment.
|
||||||
|
@ -59,7 +56,7 @@ private:
|
||||||
bool enable_game_controller_ = false;
|
bool enable_game_controller_ = false;
|
||||||
|
|
||||||
// The provider of event handlers to send the events to.
|
// The provider of event handlers to send the events to.
|
||||||
const EventHandlerProvider handler_provider_;
|
EventHandlerProvider* const handler_provider_;
|
||||||
|
|
||||||
// Observer for the game controller enabled option.
|
// Observer for the game controller enabled option.
|
||||||
const config::OptionsObserver game_controller_enabled_observer_;
|
const config::OptionsObserver game_controller_enabled_observer_;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
#include "wx/widgets/shortcut-menu-bar.h"
|
||||||
|
|
||||||
|
#include <wx/accel.h>
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
void ShortcutMenuBar::SetAcceleratorTable(const wxAcceleratorTable& /*accel*/) {
|
||||||
|
// Do nothing. We don't want to set up accelerators on the menu bar.
|
||||||
|
wxMenuBar::SetAcceleratorTable(wxNullAcceleratorTable);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxIMPLEMENT_DYNAMIC_CLASS(ShortcutMenuBar, wxMenuBar);
|
||||||
|
|
||||||
|
} // namespace widgets
|
|
@ -0,0 +1,23 @@
|
||||||
|
#ifndef VBAM_WIDGETS_SHORTCUT_MENU_BAR_H_
|
||||||
|
#define VBAM_WIDGETS_SHORTCUT_MENU_BAR_H_
|
||||||
|
|
||||||
|
#include <wx/menu.h>
|
||||||
|
|
||||||
|
namespace widgets {
|
||||||
|
|
||||||
|
// A menu bar with no accelerator table. This is used to prevent the menu bar
|
||||||
|
// from stealing keyboard shortcuts from the main window.
|
||||||
|
class ShortcutMenuBar : public wxMenuBar {
|
||||||
|
public:
|
||||||
|
ShortcutMenuBar() = default;
|
||||||
|
~ShortcutMenuBar() override = default;
|
||||||
|
|
||||||
|
// wxMenuBar implementation.
|
||||||
|
void SetAcceleratorTable(const wxAcceleratorTable& accel) override;
|
||||||
|
|
||||||
|
wxDECLARE_DYNAMIC_CLASS(ShortcutMenuBar);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace widgets
|
||||||
|
|
||||||
|
#endif // VBAM_WIDGETS_SHORTCUT_MENU_BAR_H_
|
|
@ -30,7 +30,6 @@ bool UserInputCtrl::Create(wxWindow* parent,
|
||||||
const wxSize& size,
|
const wxSize& size,
|
||||||
long style,
|
long style,
|
||||||
const wxString& name) {
|
const wxString& name) {
|
||||||
this->SetClientObject(new UserInputEventSender(this));
|
|
||||||
this->Bind(VBAM_EVT_USER_INPUT, &UserInputCtrl::OnUserInput, this);
|
this->Bind(VBAM_EVT_USER_INPUT, &UserInputCtrl::OnUserInput, this);
|
||||||
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
this->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent& event) {
|
||||||
last_focus_time_ = wxGetUTCTimeMillis();
|
last_focus_time_ = wxGetUTCTimeMillis();
|
||||||
|
|
|
@ -151,20 +151,27 @@ wxEvent* UserInputEvent::Clone() const {
|
||||||
return new UserInputEvent(this->data_);
|
return new UserInputEvent(this->data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInputEventSender::UserInputEventSender(wxWindow* const window)
|
KeyboardInputSender::KeyboardInputSender(EventHandlerProvider* const handler_provider)
|
||||||
: window_(window) {
|
: handler_provider_(handler_provider) {
|
||||||
assert(window);
|
assert(handler_provider_);
|
||||||
|
|
||||||
// Attach the event handlers.
|
|
||||||
window_->Bind(wxEVT_KEY_DOWN, &UserInputEventSender::OnKeyDown, this);
|
|
||||||
window_->Bind(wxEVT_KEY_UP, &UserInputEventSender::OnKeyUp, this);
|
|
||||||
window_->Bind(wxEVT_SET_FOCUS, &UserInputEventSender::Reset, this, window_->GetId());
|
|
||||||
window_->Bind(wxEVT_KILL_FOCUS, &UserInputEventSender::Reset, this, window_->GetId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UserInputEventSender::~UserInputEventSender() = default;
|
KeyboardInputSender::~KeyboardInputSender() = default;
|
||||||
|
|
||||||
void UserInputEventSender::OnKeyDown(wxKeyEvent& event) {
|
void KeyboardInputSender::ProcessKeyEvent(wxKeyEvent& event) {
|
||||||
|
if (!handler_provider_->event_handler()) {
|
||||||
|
// No event handler to send the event to.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.GetEventType() == wxEVT_KEY_DOWN) {
|
||||||
|
OnKeyDown(event);
|
||||||
|
} else if (event.GetEventType() == wxEVT_KEY_UP) {
|
||||||
|
OnKeyUp(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void KeyboardInputSender::OnKeyDown(wxKeyEvent& event) {
|
||||||
// Stop propagation of the event.
|
// Stop propagation of the event.
|
||||||
event.Skip(false);
|
event.Skip(false);
|
||||||
|
|
||||||
|
@ -214,10 +221,10 @@ void UserInputEventSender::OnKeyDown(wxKeyEvent& event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
void KeyboardInputSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
// Stop propagation of the event.
|
// Stop propagation of the event.
|
||||||
event.Skip(false);
|
event.Skip(false);
|
||||||
|
|
||||||
|
@ -297,17 +304,8 @@ void UserInputEventSender::OnKeyUp(wxKeyEvent& event) {
|
||||||
for (const auto& data : event_data) {
|
for (const auto& data : event_data) {
|
||||||
active_mod_inputs_.erase(data.input.keyboard_input());
|
active_mod_inputs_.erase(data.input.keyboard_input());
|
||||||
}
|
}
|
||||||
wxQueueEvent(window_, new UserInputEvent(std::move(event_data)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void UserInputEventSender::Reset(wxFocusEvent& event) {
|
wxQueueEvent(handler_provider_->event_handler(), new UserInputEvent(std::move(event_data)));
|
||||||
// Reset the internal state.
|
|
||||||
active_keys_.clear();
|
|
||||||
active_mods_.clear();
|
|
||||||
active_mod_inputs_.clear();
|
|
||||||
|
|
||||||
// Let the event propagate.
|
|
||||||
event.Skip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
|
||||||
#include "wx/config/user-input.h"
|
#include "wx/config/user-input.h"
|
||||||
|
#include "wx/widgets/event-handler-provider.h"
|
||||||
|
|
||||||
namespace widgets {
|
namespace widgets {
|
||||||
|
|
||||||
|
@ -53,34 +54,32 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
// Object that is used to fire user input events when a key is pressed or
|
// Object that is used to fire user input events when a key is pressed or
|
||||||
// released. To use this object, attach it to a wxWindow and listen for
|
// released. This class should be kept as a singleton owned by the application
|
||||||
// the VBAM_EVT_USER_INPUT_DOWN and VBAM_EVT_USER_INPUT_UP events.
|
// object. It is meant to be used in the FilterEvent() method of the app.
|
||||||
class UserInputEventSender final : public wxClientData {
|
class KeyboardInputSender final : public wxClientData {
|
||||||
public:
|
public:
|
||||||
// `window` must not be nullptr. Will assert otherwise.
|
explicit KeyboardInputSender(EventHandlerProvider* const handler_provider);
|
||||||
// `window` must outlive this object.
|
~KeyboardInputSender() override;
|
||||||
explicit UserInputEventSender(wxWindow* const window);
|
|
||||||
~UserInputEventSender() override;
|
|
||||||
|
|
||||||
// Disable copy and copy assignment.
|
// Disable copy and copy assignment.
|
||||||
UserInputEventSender(const UserInputEventSender&) = delete;
|
KeyboardInputSender(const KeyboardInputSender&) = delete;
|
||||||
UserInputEventSender& operator=(const UserInputEventSender&) = delete;
|
KeyboardInputSender& operator=(const KeyboardInputSender&) = delete;
|
||||||
|
|
||||||
|
// Processes the provided key event and sends the appropriate user input
|
||||||
|
// event to the current event handler.
|
||||||
|
void ProcessKeyEvent(wxKeyEvent& event);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Keyboard event handlers.
|
// Keyboard event handlers.
|
||||||
void OnKeyDown(wxKeyEvent& event);
|
void OnKeyDown(wxKeyEvent& event);
|
||||||
void OnKeyUp(wxKeyEvent& event);
|
void OnKeyUp(wxKeyEvent& event);
|
||||||
|
|
||||||
// Resets the internal state. Called on focus in/out.
|
|
||||||
void Reset(wxFocusEvent& event);
|
|
||||||
|
|
||||||
std::unordered_set<wxKeyCode> active_keys_;
|
std::unordered_set<wxKeyCode> active_keys_;
|
||||||
std::unordered_set<wxKeyModifier> active_mods_;
|
std::unordered_set<wxKeyModifier> active_mods_;
|
||||||
std::unordered_set<config::KeyboardInput> active_mod_inputs_;
|
std::unordered_set<config::KeyboardInput> active_mod_inputs_;
|
||||||
|
|
||||||
// The wxWindow this object is attached to.
|
// The provider of event handlers to send the events to.
|
||||||
// Must outlive this object.
|
EventHandlerProvider* const handler_provider_;
|
||||||
wxWindow* const window_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace widgets
|
} // namespace widgets
|
||||||
|
|
|
@ -64,11 +64,6 @@ void ResetMenuItemAccelerator(wxMenuItem* menu_item) {
|
||||||
wxGetApp().bindings()->InputsForCommand(
|
wxGetApp().bindings()->InputsForCommand(
|
||||||
config::ShortcutCommand(menu_item->GetId()));
|
config::ShortcutCommand(menu_item->GetId()));
|
||||||
for (const config::UserInput& user_input : user_inputs) {
|
for (const config::UserInput& user_input : user_inputs) {
|
||||||
if (user_input.device() != config::UserInput::Device::Keyboard) {
|
|
||||||
// Cannot use joystick keybinding as text without wx assertion error.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_label.append('\t');
|
new_label.append('\t');
|
||||||
new_label.append(user_input.ToLocalizedString());
|
new_label.append(user_input.ToLocalizedString());
|
||||||
break;
|
break;
|
||||||
|
@ -252,7 +247,8 @@ wxvbamApp::wxvbamApp()
|
||||||
frame(nullptr),
|
frame(nullptr),
|
||||||
using_wayland(false),
|
using_wayland(false),
|
||||||
emulated_gamepad_(std::bind(&wxvbamApp::bindings, this)),
|
emulated_gamepad_(std::bind(&wxvbamApp::bindings, this)),
|
||||||
sdl_poller_(std::bind(&wxvbamApp::GetJoyEventHandler, this)) {}
|
sdl_poller_(this),
|
||||||
|
keyboard_input_sender_(this) {}
|
||||||
|
|
||||||
const wxString wxvbamApp::GetPluginsDir()
|
const wxString wxvbamApp::GetPluginsDir()
|
||||||
{
|
{
|
||||||
|
@ -826,7 +822,7 @@ wxvbamApp::~wxvbamApp() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
wxEvtHandler* wxvbamApp::GetJoyEventHandler() {
|
wxEvtHandler* wxvbamApp::event_handler() {
|
||||||
// Use the active window, if any.
|
// Use the active window, if any.
|
||||||
wxWindow* focused_window = wxWindow::FindFocus();
|
wxWindow* focused_window = wxWindow::FindFocus();
|
||||||
if (focused_window) {
|
if (focused_window) {
|
||||||
|
@ -842,7 +838,7 @@ wxEvtHandler* wxvbamApp::GetJoyEventHandler() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OPTION(kUIAllowJoystickBackgroundInput)) {
|
if (OPTION(kUIAllowJoystickBackgroundInput) || OPTION(kUIAllowKeyboardBackgroundInput)) {
|
||||||
// Use the game panel, if the background polling option is enabled.
|
// Use the game panel, if the background polling option is enabled.
|
||||||
return panel->panel->GetWindow()->GetEventHandler();
|
return panel->panel->GetWindow()->GetEventHandler();
|
||||||
}
|
}
|
||||||
|
@ -990,53 +986,6 @@ void MainFrame::OnSize(wxSizeEvent& event)
|
||||||
OPTION(kGeomFullScreen) = IsFullScreen();
|
OPTION(kGeomFullScreen) = IsFullScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
int MainFrame::FilterEvent(wxEvent& event) {
|
|
||||||
if (menus_opened || dialog_opened) {
|
|
||||||
return wxEventFilter::Event_Skip;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.GetEventType() != VBAM_EVT_USER_INPUT) {
|
|
||||||
// We only treat "VBAM_EVT_USER_INPUT" events here.
|
|
||||||
return wxEventFilter::Event_Skip;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute the associated shortcut command.
|
|
||||||
wxCommandEvent command_event(wxEVT_COMMAND_MENU_SELECTED, command_id);
|
|
||||||
command_event.SetEventObject(this);
|
|
||||||
this->GetEventHandler()->ProcessEvent(command_event);
|
|
||||||
|
|
||||||
// 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)
|
wxString MainFrame::GetGamePath(wxString path)
|
||||||
{
|
{
|
||||||
wxString game_path = path;
|
wxString game_path = path;
|
||||||
|
@ -1376,15 +1325,57 @@ void MainFrame::IdentifyRom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// global event filter
|
|
||||||
// apparently required for win32; just setting accel table still misses
|
|
||||||
// a few keys (e.g. only ctrl-x works for exit, but not esc & ctrl-q;
|
|
||||||
// ctrl-w does not work for close). It's possible another entity is
|
|
||||||
// grabbing those keys, but I can't track it down.
|
|
||||||
int wxvbamApp::FilterEvent(wxEvent& event)
|
int wxvbamApp::FilterEvent(wxEvent& event)
|
||||||
{
|
{
|
||||||
if (frame) {
|
if (!frame) {
|
||||||
return frame->FilterEvent(event);
|
// Ignore early events.
|
||||||
|
return wxEventFilter::Event_Skip;
|
||||||
}
|
}
|
||||||
return wxApp::FilterEvent(event);
|
|
||||||
|
if (event.GetEventType() == wxEVT_KEY_DOWN || event.GetEventType() == wxEVT_KEY_UP) {
|
||||||
|
// Handle keyboard input events here. No control will receive them.
|
||||||
|
keyboard_input_sender_.ProcessKeyEvent(static_cast<wxKeyEvent&>(event));
|
||||||
|
return wxEventFilter::Event_Processed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frame->CanProcessShortcuts()) {
|
||||||
|
return wxEventFilter::Event_Skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.GetEventType() != VBAM_EVT_USER_INPUT) {
|
||||||
|
// We only treat "VBAM_EVT_USER_INPUT" events here.
|
||||||
|
return wxEventFilter::Event_Skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
widgets::UserInputEvent& user_input_event = static_cast<widgets::UserInputEvent&>(event);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue the associated shortcut command.
|
||||||
|
wxCommandEvent* command_event = new wxCommandEvent(wxEVT_COMMAND_MENU_SELECTED, command_id);
|
||||||
|
command_event->SetEventObject(this);
|
||||||
|
frame->GetEventHandler()->QueueEvent(command_event);
|
||||||
|
|
||||||
|
return user_input_event.FilterProcessedInput(user_input.value());
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "wx/config/option.h"
|
#include "wx/config/option.h"
|
||||||
#include "wx/dialogs/base-dialog.h"
|
#include "wx/dialogs/base-dialog.h"
|
||||||
#include "wx/widgets/dpi-support.h"
|
#include "wx/widgets/dpi-support.h"
|
||||||
|
#include "wx/widgets/event-handler-provider.h"
|
||||||
#include "wx/widgets/keep-on-top-styler.h"
|
#include "wx/widgets/keep-on-top-styler.h"
|
||||||
#include "wx/widgets/sdl-poller.h"
|
#include "wx/widgets/sdl-poller.h"
|
||||||
#include "wx/widgets/user-input-event.h"
|
#include "wx/widgets/user-input-event.h"
|
||||||
|
@ -59,7 +60,7 @@ inline std::string ToString(const wxChar* aString)
|
||||||
|
|
||||||
class MainFrame;
|
class MainFrame;
|
||||||
|
|
||||||
class wxvbamApp final : public wxApp {
|
class wxvbamApp final : public wxApp, public widgets::EventHandlerProvider {
|
||||||
public:
|
public:
|
||||||
wxvbamApp();
|
wxvbamApp();
|
||||||
|
|
||||||
|
@ -133,8 +134,8 @@ protected:
|
||||||
int console_status = 0;
|
int console_status = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Returns the currently active event handler to use for user input events.
|
// EventHandlerProvider implementation.
|
||||||
wxEvtHandler* GetJoyEventHandler();
|
wxEvtHandler* event_handler() override;
|
||||||
|
|
||||||
config::Bindings bindings_;
|
config::Bindings bindings_;
|
||||||
config::EmulatedGamepad emulated_gamepad_;
|
config::EmulatedGamepad emulated_gamepad_;
|
||||||
|
@ -143,6 +144,7 @@ private:
|
||||||
char* home = nullptr;
|
char* home = nullptr;
|
||||||
|
|
||||||
widgets::SdlPoller sdl_poller_;
|
widgets::SdlPoller sdl_poller_;
|
||||||
|
widgets::KeyboardInputSender keyboard_input_sender_;
|
||||||
|
|
||||||
// Main configuration file.
|
// Main configuration file.
|
||||||
wxFileName config_file_;
|
wxFileName config_file_;
|
||||||
|
@ -200,8 +202,6 @@ public:
|
||||||
void GetMenuOptionBool(const wxString& menuName, bool* field);
|
void GetMenuOptionBool(const wxString& menuName, bool* field);
|
||||||
void SetMenuOption(const wxString& menuName, bool value);
|
void SetMenuOption(const wxString& menuName, bool value);
|
||||||
|
|
||||||
int FilterEvent(wxEvent& event);
|
|
||||||
|
|
||||||
GameArea* GetPanel()
|
GameArea* GetPanel()
|
||||||
{
|
{
|
||||||
return panel;
|
return panel;
|
||||||
|
@ -209,6 +209,8 @@ public:
|
||||||
|
|
||||||
wxString GetGamePath(wxString path);
|
wxString GetGamePath(wxString path);
|
||||||
|
|
||||||
|
bool CanProcessShortcuts() const { return !menus_opened && !dialog_opened; }
|
||||||
|
|
||||||
// wxMSW pauses the game for menu popups and modal dialogs, but wxGTK
|
// wxMSW pauses the game for menu popups and modal dialogs, but wxGTK
|
||||||
// does not. It's probably desirable to pause the game. To do this for
|
// does not. It's probably desirable to pause the game. To do this for
|
||||||
// dialogs, use this function instead of dlg->ShowModal()
|
// dialogs, use this function instead of dlg->ShowModal()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.5.3.0">
|
<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.5.3.0">
|
||||||
<object class="wxMenuBar" name="MainMenu">
|
<object class="wxMenuBar" name="MainMenu" subclass="ShortcutMenuBar">
|
||||||
<object class="wxMenu">
|
<object class="wxMenu">
|
||||||
<label>_File</label>
|
<label>_File</label>
|
||||||
<object class="wxMenuItem" name="wxID_OPEN">
|
<object class="wxMenuItem" name="wxID_OPEN">
|
||||||
|
|
Loading…
Reference in New Issue