Как подружить 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
Ну и как и прежде, коментарии, вопросы, здоровая критика — приветствуются.

Продолжение следует…

ссылка на оригинал статьи http://habrahabr.ru/post/249383/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *