1
0
Fork 0

GraphicsWindowQt5: improved keyboard + modifiers

This fixes many kinds of input weirdness, caused by ‘stuck’ modifiers
being set internally, and causing input bindings to be excluded.
This commit is contained in:
James Turner 2017-06-10 15:49:32 +01:00
parent b2cc191bc6
commit 9031182583
2 changed files with 91 additions and 66 deletions

View file

@ -42,7 +42,7 @@ public:
QtKeyboardMap() QtKeyboardMap()
{ {
mKeyMap[Qt::Key_Escape ] = osgGA::GUIEventAdapter::KEY_Escape; mKeyMap[Qt::Key_Escape ] = osgGA::GUIEventAdapter::KEY_Escape;
mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete; mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete;
mKeyMap[Qt::Key_Home ] = osgGA::GUIEventAdapter::KEY_Home; mKeyMap[Qt::Key_Home ] = osgGA::GUIEventAdapter::KEY_Home;
mKeyMap[Qt::Key_Enter ] = osgGA::GUIEventAdapter::KEY_KP_Enter; mKeyMap[Qt::Key_Enter ] = osgGA::GUIEventAdapter::KEY_KP_Enter;
mKeyMap[Qt::Key_End ] = osgGA::GUIEventAdapter::KEY_End; mKeyMap[Qt::Key_End ] = osgGA::GUIEventAdapter::KEY_End;
@ -57,10 +57,22 @@ public:
mKeyMap[Qt::Key_Tab ] = osgGA::GUIEventAdapter::KEY_Tab; mKeyMap[Qt::Key_Tab ] = osgGA::GUIEventAdapter::KEY_Tab;
mKeyMap[Qt::Key_Space ] = osgGA::GUIEventAdapter::KEY_Space; mKeyMap[Qt::Key_Space ] = osgGA::GUIEventAdapter::KEY_Space;
mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete; mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_Delete;
mKeyMap[Qt::Key_Alt ] = osgGA::GUIEventAdapter::KEY_Alt_L; mKeyMap[Qt::Key_Alt ] = osgGA::GUIEventAdapter::KEY_Alt_L;
mKeyMap[Qt::Key_Shift ] = osgGA::GUIEventAdapter::KEY_Shift_L; mKeyMap[Qt::Key_Shift ] = osgGA::GUIEventAdapter::KEY_Shift_L;
mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Control_L;
mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Meta_L; #if defined(Q_OS_MACOS)
// undo the Qt-mac remapping
mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Control_L;
mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Meta_L;
#else
mKeyMap[Qt::Key_Control ] = osgGA::GUIEventAdapter::KEY_Control_L;
mKeyMap[Qt::Key_Meta ] = osgGA::GUIEventAdapter::KEY_Meta_L;
#endif
mKeyMap[Qt::Key_Super_L ] = osgGA::GUIEventAdapter::KEY_Super_L;
mKeyMap[Qt::Key_Super_R ] = osgGA::GUIEventAdapter::KEY_Super_R;
mKeyMap[Qt::Key_Hyper_L ] = osgGA::GUIEventAdapter::KEY_Hyper_L;
mKeyMap[Qt::Key_Hyper_R ] = osgGA::GUIEventAdapter::KEY_Hyper_R;
mKeyMap[Qt::Key_F1 ] = osgGA::GUIEventAdapter::KEY_F1; mKeyMap[Qt::Key_F1 ] = osgGA::GUIEventAdapter::KEY_F1;
mKeyMap[Qt::Key_F2 ] = osgGA::GUIEventAdapter::KEY_F2; mKeyMap[Qt::Key_F2 ] = osgGA::GUIEventAdapter::KEY_F2;
@ -83,22 +95,7 @@ public:
mKeyMap[Qt::Key_F19 ] = osgGA::GUIEventAdapter::KEY_F19; mKeyMap[Qt::Key_F19 ] = osgGA::GUIEventAdapter::KEY_F19;
mKeyMap[Qt::Key_F20 ] = osgGA::GUIEventAdapter::KEY_F20; mKeyMap[Qt::Key_F20 ] = osgGA::GUIEventAdapter::KEY_F20;
mKeyMap[Qt::Key_hyphen ] = '-';
mKeyMap[Qt::Key_Equal ] = '=';
mKeyMap[Qt::Key_division ] = osgGA::GUIEventAdapter::KEY_KP_Divide;
mKeyMap[Qt::Key_multiply ] = osgGA::GUIEventAdapter::KEY_KP_Multiply;
mKeyMap[Qt::Key_Minus ] = '-';
mKeyMap[Qt::Key_Plus ] = '+';
//mKeyMap[Qt::Key_H ] = osgGA::GUIEventAdapter::KEY_KP_Home;
//mKeyMap[Qt::Key_ ] = osgGA::GUIEventAdapter::KEY_KP_Up;
//mKeyMap[92 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Up;
//mKeyMap[86 ] = osgGA::GUIEventAdapter::KEY_KP_Left;
//mKeyMap[87 ] = osgGA::GUIEventAdapter::KEY_KP_Begin;
//mKeyMap[88 ] = osgGA::GUIEventAdapter::KEY_KP_Right;
//mKeyMap[83 ] = osgGA::GUIEventAdapter::KEY_KP_End;
//mKeyMap[84 ] = osgGA::GUIEventAdapter::KEY_KP_Down;
//mKeyMap[85 ] = osgGA::GUIEventAdapter::KEY_KP_Page_Down;
mKeyMap[Qt::Key_Insert ] = osgGA::GUIEventAdapter::KEY_KP_Insert; mKeyMap[Qt::Key_Insert ] = osgGA::GUIEventAdapter::KEY_KP_Insert;
//mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_KP_Delete; //mKeyMap[Qt::Key_Delete ] = osgGA::GUIEventAdapter::KEY_KP_Delete;
} }
@ -109,16 +106,32 @@ public:
int remapKey(QKeyEvent* event) int remapKey(QKeyEvent* event)
{ {
const QChar unicodePoint = event->text().isEmpty() ? QChar() : event->text().at(0);
KeyMap::iterator itr = mKeyMap.find(event->key()); KeyMap::iterator itr = mKeyMap.find(event->key());
if (itr == mKeyMap.end()) if (itr == mKeyMap.end()) {
{ if (unicodePoint.isNull()) {
return int(*(event->text().toLatin1().data())); // this happens for Ctrl modifiers on A-Z (at least). Keyboard.xml relies on the
// old ASCII mappings of these values, so we need to synthesise those here
// since Qt won't do it.
const auto k = event->key();
if ((k >= Qt::Key_A) && (k <= Qt::Key_Z)) {
return 1 + (k - Qt::Key_A); // offset into the ASCII control code range
} else {
qWarning() << Q_FUNC_INFO << "misssing mapping for key";
}
}
#if 0
qDebug() << "key" << event->key() << ", mods" << event->modifiers() << "Unicode:" << unicodePoint << ", ASCII:"
<< unicodePoint.toLatin1() << "(" << (int)unicodePoint.toLatin1() << "), raw text:" << event->text();
#endif
return int(unicodePoint.toLatin1());
} }
else
return itr->second; return itr->second;
} }
private: private:
typedef std::map<unsigned int, int> KeyMap; typedef std::map<unsigned int, int> KeyMap;
KeyMap mKeyMap; KeyMap mKeyMap;
}; };
@ -143,12 +156,12 @@ void GLWindow::onScreenChanged()
#if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 2, 0))
_devicePixelRatio = screen()->devicePixelRatio(); _devicePixelRatio = screen()->devicePixelRatio();
#endif #endif
if (_isPrimaryWindow) { if (_isPrimaryWindow) {
// allow PUI and Canvas to be scaled // allow PUI and Canvas to be scaled
fgSetDouble("/sim/rendering/gui-pixel-ratio", _devicePixelRatio); fgSetDouble("/sim/rendering/gui-pixel-ratio", _devicePixelRatio);
} }
syncGeometryWithOSG(); syncGeometryWithOSG();
} }
@ -156,10 +169,10 @@ void GLWindow::syncGeometryWithOSG()
{ {
const int w = width(); const int w = width();
const int h = height(); const int h = height();
int scaled_width = static_cast<int>(w *_devicePixelRatio); int scaled_width = static_cast<int>(w *_devicePixelRatio);
int scaled_height = static_cast<int>(h*_devicePixelRatio); int scaled_height = static_cast<int>(h*_devicePixelRatio);
if (_gw) { if (_gw) {
_gw->resized( x(), y(), scaled_width, scaled_height); _gw->resized( x(), y(), scaled_width, scaled_height);
_gw->getEventQueue()->windowResize( x(), y(), scaled_width, scaled_height ); _gw->getEventQueue()->windowResize( x(), y(), scaled_width, scaled_height );
@ -189,7 +202,7 @@ bool GLWindow::event( QEvent* event )
if (event->type() == QEvent::WindowStateChange) { if (event->type() == QEvent::WindowStateChange) {
// keep full-screen state in sync // keep full-screen state in sync
const bool isFullscreen = (windowState() == Qt::WindowFullScreen); const bool isFullscreen = (windowState() == Qt::WindowFullScreen);
if (_isPrimaryWindow) { if (_isPrimaryWindow) {
fgSetBool("/sim/startup/fullscreen", isFullscreen); fgSetBool("/sim/startup/fullscreen", isFullscreen);
} }
@ -214,7 +227,7 @@ void GLWindow::processUpdateEvent()
if (_gw->_viewer.lock(v)) { if (_gw->_viewer.lock(v)) {
v->frame(); v->frame();
} }
// see discussion of QWindow::requestUpdate to see // see discussion of QWindow::requestUpdate to see
// why this is good behaviour // why this is good behaviour
if (_gw->_continousUpdate) { if (_gw->_continousUpdate) {
@ -223,14 +236,29 @@ void GLWindow::processUpdateEvent()
} }
void GLWindow::setKeyboardModifiers( QInputEvent* event ) static void setOSGModifier(int& modifiers, unsigned int bits, bool set)
{ {
int modkey = event->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier); if (set) {
unsigned int mask = 0; modifiers |= bits;
if ( modkey & Qt::ShiftModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_SHIFT; } else {
if ( modkey & Qt::ControlModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_CTRL; modifiers &= ~bits;
if ( modkey & Qt::AltModifier ) mask |= osgGA::GUIEventAdapter::MODKEY_ALT; }
_gw->getEventQueue()->getCurrentEventState()->setModKeyMask( mask ); }
void GLWindow::setKeyboardModifiers(const Qt::KeyboardModifiers qtMods)
{
auto es = _gw->getEventQueue()->getCurrentEventState();
auto modifiers = es->getModKeyMask();
#if defined(Q_OS_MACOS)
setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_CTRL, qtMods & Qt::MetaModifier);
#else
setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_CTRL, qtMods & Qt::ControlModifier);
#endif
setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_ALT, qtMods & Qt::AltModifier);
setOSGModifier(modifiers, osgGA::GUIEventAdapter::MODKEY_SHIFT, qtMods & Qt::ShiftModifier);
_gw->getEventQueue()->getCurrentEventState()->setModKeyMask(modifiers);
} }
void GLWindow::resizeEvent( QResizeEvent* event ) void GLWindow::resizeEvent( QResizeEvent* event )
@ -247,7 +275,7 @@ void GLWindow::moveEvent( QMoveEvent* event )
void GLWindow::keyPressEvent( QKeyEvent* event ) void GLWindow::keyPressEvent( QKeyEvent* event )
{ {
setKeyboardModifiers( event ); setKeyboardModifiers( event->modifiers() );
int value = s_QtKeyboardMap.remapKey( event ); int value = s_QtKeyboardMap.remapKey( event );
_gw->getEventQueue()->keyPress( value ); _gw->getEventQueue()->keyPress( value );
@ -259,13 +287,10 @@ void GLWindow::keyPressEvent( QKeyEvent* event )
void GLWindow::keyReleaseEvent( QKeyEvent* event ) void GLWindow::keyReleaseEvent( QKeyEvent* event )
{ {
if( event->isAutoRepeat() ) if (event->isAutoRepeat()) {
{
event->ignore(); event->ignore();
} } else {
else setKeyboardModifiers(event->modifiers() );
{
setKeyboardModifiers( event );
int value = s_QtKeyboardMap.remapKey( event ); int value = s_QtKeyboardMap.remapKey( event );
_gw->getEventQueue()->keyRelease( value ); _gw->getEventQueue()->keyRelease( value );
} }
@ -287,7 +312,7 @@ void GLWindow::mousePressEvent( QMouseEvent* event )
case Qt::NoButton: button = 0; break; case Qt::NoButton: button = 0; break;
default: button = 0; break; default: button = 0; break;
} }
setKeyboardModifiers( event ); setKeyboardModifiers(event->modifiers());
_gw->getEventQueue()->mouseButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); _gw->getEventQueue()->mouseButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
} }
@ -302,7 +327,7 @@ void GLWindow::mouseReleaseEvent( QMouseEvent* event )
case Qt::NoButton: button = 0; break; case Qt::NoButton: button = 0; break;
default: button = 0; break; default: button = 0; break;
} }
setKeyboardModifiers( event ); setKeyboardModifiers(event->modifiers());
_gw->getEventQueue()->mouseButtonRelease( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); _gw->getEventQueue()->mouseButtonRelease( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
} }
@ -317,19 +342,19 @@ void GLWindow::mouseDoubleClickEvent( QMouseEvent* event )
case Qt::NoButton: button = 0; break; case Qt::NoButton: button = 0; break;
default: button = 0; break; default: button = 0; break;
} }
setKeyboardModifiers( event ); setKeyboardModifiers(event->modifiers());
_gw->getEventQueue()->mouseDoubleButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button ); _gw->getEventQueue()->mouseDoubleButtonPress( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio, button );
} }
void GLWindow::mouseMoveEvent( QMouseEvent* event ) void GLWindow::mouseMoveEvent( QMouseEvent* event )
{ {
setKeyboardModifiers( event ); setKeyboardModifiers(event->modifiers());
_gw->getEventQueue()->mouseMotion( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio ); _gw->getEventQueue()->mouseMotion( event->x()*_devicePixelRatio, event->y()*_devicePixelRatio );
} }
void GLWindow::wheelEvent( QWheelEvent* event ) void GLWindow::wheelEvent( QWheelEvent* event )
{ {
setKeyboardModifiers( event ); setKeyboardModifiers(event->modifiers());
_gw->getEventQueue()->mouseScroll( _gw->getEventQueue()->mouseScroll(
event->orientation() == Qt::Vertical ? event->orientation() == Qt::Vertical ?
(event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) : (event->delta()>0 ? osgGA::GUIEventAdapter::SCROLL_UP : osgGA::GUIEventAdapter::SCROLL_DOWN) :
@ -354,17 +379,17 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f )
WindowData* windowData = _traits.get() ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : 0; WindowData* windowData = _traits.get() ? dynamic_cast<WindowData*>(_traits->inheritedWindowData.get()) : 0;
assert(!_window); assert(!_window);
_ownsWidget = true; _ownsWidget = true;
// WindowFlags // WindowFlags
Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint; Qt::WindowFlags flags = f | Qt::Window | Qt::CustomizeWindowHint;
if ( _traits->windowDecoration ) { if ( _traits->windowDecoration ) {
flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint; flags |= Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
flags |= Qt::WindowFullscreenButtonHint; flags |= Qt::WindowFullscreenButtonHint;
// TODO - check if this is desirable or not on Windows+Linux // TODO - check if this is desirable or not on Windows+Linux
//flags |= Qt::MaximizeUsingFullscreenGeometryHint; //flags |= Qt::MaximizeUsingFullscreenGeometryHint;
} }
// create window // create window
_window.reset(new GLWindow); _window.reset(new GLWindow);
_window->setFlags(flags); _window->setFlags(flags);
@ -372,7 +397,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f )
_window->setFormat(traits2qSurfaceFormat(_traits.get())); _window->setFormat(traits2qSurfaceFormat(_traits.get()));
_window->create(); _window->create();
_window->setTitle( _traits->windowName.c_str() ); _window->setTitle( _traits->windowName.c_str() );
// to get OS-dependant default positioning of the window (which is desirable), // to get OS-dependant default positioning of the window (which is desirable),
// we must take care to only set the position if explicitly requested. // we must take care to only set the position if explicitly requested.
// hence we set X & Y to these marker values by default. // hence we set X & Y to these marker values by default.
@ -380,7 +405,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f )
if ((_traits->x != std::numeric_limits<int>::max()) && (_traits->y != std::numeric_limits<int>::max())) { if ((_traits->x != std::numeric_limits<int>::max()) && (_traits->y != std::numeric_limits<int>::max())) {
_window->setPosition( _traits->x, _traits->y ); _window->setPosition( _traits->x, _traits->y );
} }
QSize sz(_traits->width, _traits->height); QSize sz(_traits->width, _traits->height);
if ( !_traits->supportsResize ) { if ( !_traits->supportsResize ) {
_window->setMinimumSize( sz ); _window->setMinimumSize( sz );
@ -388,7 +413,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f )
} else { } else {
_window->resize( sz ); _window->resize( sz );
} }
if (windowData->createFullscreen) { if (windowData->createFullscreen) {
// this doesn't seem to actually work, so we // this doesn't seem to actually work, so we
// check the flag again in realizeImplementation() // check the flag again in realizeImplementation()
@ -399,7 +424,7 @@ bool GraphicsWindowQt5::init( Qt::WindowFlags f )
if (_window->_isPrimaryWindow) { if (_window->_isPrimaryWindow) {
fgSetDouble("/sim/rendering/gui-pixel-ratio", _window->_devicePixelRatio); fgSetDouble("/sim/rendering/gui-pixel-ratio", _window->_devicePixelRatio);
} }
_window->setGraphicsWindow( this ); _window->setGraphicsWindow( this );
useCursor( _traits->useCursor ); useCursor( _traits->useCursor );
@ -619,12 +644,12 @@ bool GraphicsWindowQt5::realizeImplementation()
} else { } else {
_window->show(); _window->show();
} }
#if (OPENSCENEGRAPH_MAJOR_VERSION == 3) && (OPENSCENEGRAPH_MINOR_VERSION >= 4) #if (OPENSCENEGRAPH_MAJOR_VERSION == 3) && (OPENSCENEGRAPH_MINOR_VERSION >= 4)
// make sure the event queue has the correct window rectangle size and input range // make sure the event queue has the correct window rectangle size and input range
getEventQueue()->syncWindowRectangleWithGraphicsContext(); getEventQueue()->syncWindowRectangleWithGraphicsContext();
#endif #endif
_realized = true; _realized = true;
return true; return true;
} }
@ -640,7 +665,7 @@ void GraphicsWindowQt5::closeImplementation()
_window->close(); _window->close();
_window.reset(); _window.reset();
} }
_context.reset(); _context.reset();
_realized = false; _realized = false;
} }
@ -714,7 +739,7 @@ bool GraphicsWindowQt5::checkEvents()
_sendResizeOnEventCheck = false; _sendResizeOnEventCheck = false;
_window->syncGeometryWithOSG(); _window->syncGeometryWithOSG();
} }
// todo - only if not running inside QApplication::exec; can we check this? // todo - only if not running inside QApplication::exec; can we check this?
QCoreApplication::processEvents(QEventLoop::AllEvents); QCoreApplication::processEvents(QEventLoop::AllEvents);
@ -764,8 +789,6 @@ public:
Qt5WindowingSystem() Qt5WindowingSystem()
{ {
OSG_INFO << "QtWindowingSystemInterface()" << std::endl;
} }
~Qt5WindowingSystem() ~Qt5WindowingSystem()

View file

@ -62,7 +62,7 @@ public:
inline bool getForwardKeyEvents() const { return _forwardKeyEvents; } inline bool getForwardKeyEvents() const { return _forwardKeyEvents; }
virtual void setForwardKeyEvents( bool f ) { _forwardKeyEvents = f; } virtual void setForwardKeyEvents( bool f ) { _forwardKeyEvents = f; }
void setKeyboardModifiers( QInputEvent* event ); void setKeyboardModifiers(const Qt::KeyboardModifiers qtMods);
virtual void keyPressEvent( QKeyEvent* event ); virtual void keyPressEvent( QKeyEvent* event );
virtual void keyReleaseEvent( QKeyEvent* event ); virtual void keyReleaseEvent( QKeyEvent* event );
@ -87,6 +87,8 @@ private slots:
protected: protected:
void syncGeometryWithOSG(); void syncGeometryWithOSG();
void updateEventQueueModifiers(QKeyEvent* event);
friend class GraphicsWindowQt5; friend class GraphicsWindowQt5;
GraphicsWindowQt5* _gw = nullptr; // back-pointer GraphicsWindowQt5* _gw = nullptr; // back-pointer