Как подружить QML с чужим OpenGL контекстом. Часть III: Обработка пользовательского ввода
В данной статье я попытаюсь рассказать о том как передавать события мыши и клавиатуры в QQuickWindow, в случае его использования в связке с QQuickRenderControl. Причиной того, что этому необходимо уделять специальное внимание, является то, что в случае использования QQuickRenderControl, никакого окна на самом деле не создается, соответственно у QQuickWindow нет абсолютно никакой возможности получать какие либо события, и их приходится эмулировать. То же самое касается изменения размеров — об этой операции так же необходимо оповещать в явном виде.Для тех кто пропустил предыдущие части: Как инициируютя события в QtОтправка события в Qt осуществляется с помощью метода bool QCoreApplication: sendEvent (QObject* receiver, QEvent* event) где, receiver — получатель сообщения, в нашем случае это экземпляр QQuickWindow (или его потомка); event — это экземпляр конкретного типа события; Передача событий мыши Для корректного функционирования достаточно реализовать 3 события мыши: QEvent: MouseButtonPress:
QPointF mousePoint (150, 201); Qt: MouseButton button = Qt: LeftButton; Qt: MouseButton buttons = Qt: LeftButton | Qt: RightButton; Qt: KeyboardModifiers modifiers = Qt: AltModifier; QMouseEvent mouseEvent (QEvent: MouseButtonPress, mousePoint, mousePoint, button, buttons, modifiers); QCoreApplication: sendEvent (quickWindow, &mouseEvent); где, mousePoint — текущая позиция мыши в координатах QQuickWindow; button — кнопка мыши вызвавшая данное событие; buttons — все кнопки мыши нажатые в момент генерации события; modifiers — нажатые на клавиатуре клавиши-модификаторы (Ctrl, Alt, Shift и т.д.); mousePoint используется дважды, поскольку в первый раз передается в координатах QQuckWindow, второй раз в screen координатах. Но поскольку окно на самом не создается, трактуется всегда как окно верхнего уровня, и его позицией мы управляем самостоятельно, то передаем одно и то же значение (как будто окно находится всегда в верхнем левом углу экрана), а при установке позиции окна, просто будем этот факт учитывать.QEvent: MouseMove: QPointF mousePoint (170, 198); Qt: MouseButton button = Qt: NoButton; Qt: MouseButton buttons = Qt: LeftButton | Qt: RightButton; Qt: KeyboardModifiers modifiers = Qt: AltModifier; QMouseEvent mouseEvent (QEvent: MouseMove, mousePoint, mousePoint, button, buttons, modifiers); QCoreApplication: sendEvent (quickWindow, &mouseEvent); Поскольку причиной события передвижения мыши является сам факт передвижения мыши, а не какая-либо из кнопок, переменной button присваивается значение Qt: NoButton.QEvent: MouseButtonRelease: QPointF mousePoint (160, 251); Qt: MouseButton button = Qt: LeftButton; Qt: MouseButton buttons = Qt: LeftButton; Qt: KeyboardModifiers modifiers = Qt: AltModifier; QMouseEvent mouseEvent (QEvent: MouseButtonRelease, mousePoint, mousePoint, button, buttons, modifiers); QCoreApplication: sendEvent (quickWindow, &mouseEvent); button в данном случае означает кнопку являвшуюся причиной данного события, но в данном случае кнопка была отпущена, соответственно buttons она присутствовать уже не может (иначе Qt начинает обрабатывать это событие неверно).Передача событий клавиатуры Аналогично, для корректной обработки событий клавиатуры, достаточно реализовать 2 события: QEvent: KeyPress:
Qt: Key qtKey = Qt: Key_Space; QKeyEvent keyEvent (QEvent: KeyPress, qtKey, Qt: NoModifier); QCoreApplication: sendEvent (quickWindow, &keyEvent); QEvent: KeyRelease: Qt: Key qtKey = Qt: Key_Space; QKeyEvent keyEvent (QEvent: KeyRelease, qtKey, Qt: NoModifier); QCoreApplication: sendEvent (quickWindow, &keyEvent); Изменение размеров Как уже упомяналось выше, offscreen окна в Qt трактутся как окна верхнего уровня, поэтому используем соответствующий метод: QSize newSize (320, 240); quickWindow→setGeometry (0, 0, newSize.width (), newSize.height ()); Помимо изменения размеров собственно окна, желательно так же изменить размеры FBO, т.к. в противном случае либо получим пикселизацию (при увеличении размера), либо бессмысленное расходование ресурсов (т.к. размер FBO будет больше чем требуется): if (context→makeCurrent (offscreenSurface)) { destroyFbo (); createFbo (); context→doneCurrent (); } Поскольку изменить размер FBO невозможно, просто удаляем текущий и создаем новый (см. детали в Первой части).На этом все.
Примеры реализации, как обычно, доступены на GitHubНу и как и прежде, коментарии, вопросы, здоровая критика — приветствуются.
Продолжение следует…