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

далі буде...

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.