1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-04 18:29:40 +00:00

Attempts mouse event capture.

This commit is contained in:
Thomas Harte 2020-07-04 00:29:37 -04:00
parent 51f4242e9b
commit cdda3f74ab
4 changed files with 176 additions and 37 deletions

View File

@ -709,30 +709,41 @@ void MainWindow::setUIPhase(UIPhase phase) {
ui->topTipLabel->setVisible(phase == UIPhase::SelectingMachine);
// Consider setting a window title, if it's knowable.
switch(phase) {
case UIPhase::SelectingMachine:
setWindowTitle(tr("Select a machine..."));
break;
case UIPhase::RequestingROMs:
setWindowTitle(tr("Provide ROMs..."));
break;
default:
// Update the window title. TODO: clearly I need a proper functional solution for the window title.
if(openFileName.isEmpty()) {
const auto machineType = targets[0]->machine;
setWindowTitle(QString::fromStdString(Machine::LongNameForTargetMachine(machineType)));
} else {
setWindowTitle(openFileName);
}
break;
}
setWindowTitle();
// Set appropriate focus if necessary; e.g. this ensures that machine-picker
// widgets aren't still selectable after a machine starts.
if(phase != UIPhase::SelectingMachine) {
ui->openGLWidget->setFocus();
}
// Indicate whether to catch mouse input.
ui->openGLWidget->setMouseDelegate(
(phase == UIPhase::RunningMachine && machine && machine->mouse_machine()) ? this : nullptr
);
}
void MainWindow::setWindowTitle() {
QString title;
switch(uiPhase) {
case UIPhase::SelectingMachine: title = tr("Select a machine..."); break;
case UIPhase::RequestingROMs: title = tr("Provide ROMs..."); break;
default:
// Update the window title. TODO: clearly I need a proper functional solution for the window title.
if(openFileName.isEmpty()) {
const auto machineType = targets[0]->machine;
title = QString::fromStdString(Machine::LongNameForTargetMachine(machineType));
} else {
title = openFileName;
}
break;
}
if(mouseIsCaptured) title += " (press control+escape to release mouse)";
QMainWindow::setWindowTitle(title);
}
// MARK: - Event Processing
@ -817,6 +828,27 @@ bool MainWindow::processEvent(QKeyEvent *event) {
return false;
}
void MainWindow::setMouseIsCaptured(bool isCaptured) {
mouseIsCaptured = isCaptured;
setWindowTitle();
}
void MainWindow::moveMouse(QPoint vector) {
std::unique_lock lock(machineMutex);
auto mouseMachine = machine->mouse_machine();
if(!mouseMachine) return;
mouseMachine->get_mouse().move(vector.x(), vector.y());
}
void MainWindow::setButtonPressed(int index, bool isPressed) {
std::unique_lock lock(machineMutex);
auto mouseMachine = machine->mouse_machine();
if(!mouseMachine) return;
mouseMachine->get_mouse().set_button_pressed(index, isPressed);
}
// MARK: - New Machine Creation
#include "../../Analyser/Static/Acorn/Target.hpp"

View File

@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegate {
class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegate, public ScanTargetWidget::MouseDelegate {
Q_OBJECT
void createActions();
@ -35,6 +35,10 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
void setMouseIsCaptured(bool) override;
void moveMouse(QPoint) override;
void setButtonPressed(int index, bool isPressed) override;
private:
std::unique_ptr<Ui::MainWindow> ui;
std::unique_ptr<Timer> timer;
@ -117,6 +121,9 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void addAtari2600Menu();
void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
void setWindowTitle();
bool mouseIsCaptured = false;
};
#endif // MAINWINDOW_H

View File

@ -1,9 +1,12 @@
#include "scantargetwidget.h"
#include <QApplication>
#include <QCursor>
#include <QDebug>
#include <QDesktopWidget>
#include <QGuiApplication>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QOpenGLContext>
#include <QScreen>
#include <QTimer>
@ -21,10 +24,10 @@ void ScanTargetWidget::initializeGL() {
}
void ScanTargetWidget::paintGL() {
if(requested_redraw_time_) {
if(requestedRedrawTime) {
const auto now = Time::nanos_now();
vsyncPredictor.add_timer_jitter(now - requested_redraw_time_);
requested_redraw_time_ = 0;
vsyncPredictor.add_timer_jitter(now - requestedRedrawTime);
requestedRedrawTime = 0;
}
// TODO: if Qt 5.14 can be guaranteed, just use window()->screen().
@ -81,27 +84,27 @@ void ScanTargetWidget::vsync() {
vsyncPredictor.announce_vsync();
const auto time_now = Time::nanos_now();
requested_redraw_time_ = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requested_redraw_time_ - time_now) / 1'000'000;
requestedRedrawTime = vsyncPredictor.suggested_draw_time();
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
if(delay_time > 0) {
QTimer::singleShot(delay_time, this, SLOT(repaint()));
} else {
requested_redraw_time_ = 0;
requestedRedrawTime = 0;
repaint();
}
}
void ScanTargetWidget::resizeGL(int w, int h) {
if(width != w || height != h) {
width = w;
height = h;
if(rawWidth != w || rawHeight != h) {
rawWidth = w;
rawHeight = h;
resize();
}
}
void ScanTargetWidget::resize() {
const int newScaledWidth = int(float(width) * outputScale);
const int newScaledHeight = int(float(height) * outputScale);
const int newScaledWidth = int(float(rawWidth) * outputScale);
const int newScaledHeight = int(float(rawHeight) * outputScale);
if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) {
scaledWidth = newScaledWidth;
@ -121,7 +124,7 @@ void ScanTargetWidget::stop() {
isConnected = false;
setDefaultClearColour();
vsyncPredictor.pause();
requested_redraw_time_ = 0;
requestedRedrawTime = 0;
repaint();
}
@ -130,3 +133,76 @@ void ScanTargetWidget::setDefaultClearColour() {
const QColor backgroundColour = palette().color(QWidget::backgroundRole());
glClearColor(backgroundColour.redF(), backgroundColour.greenF(), backgroundColour.blueF(), 1.0);
}
void ScanTargetWidget::setMouseDelegate(MouseDelegate *delegate) {
if(!delegate && mouseIsCaptured) {
releaseMouse();
}
mouseDelegate = delegate;
setMouseTracking(delegate);
}
void ScanTargetWidget::keyPressEvent(QKeyEvent *event) {
if(mouseIsCaptured && event->key() == Qt::Key_Escape && event->modifiers()&Qt::ControlModifier) {
releaseMouse();
QCursor cursor;
cursor.setShape(Qt::ArrowCursor);
setCursor(cursor);
}
}
void ScanTargetWidget::releaseMouse() {
QOpenGLWidget::releaseMouse();
mouseIsCaptured = false;
mouseDelegate->setMouseIsCaptured(false);
}
void ScanTargetWidget::mousePressEvent(QMouseEvent *event) {
if(mouseDelegate) {
if(!mouseIsCaptured) {
mouseIsCaptured = true;
grabMouse();
QCursor cursor;
cursor.setPos(mapToGlobal(QPoint(width() / 2, height() / 2)));
cursor.setShape(Qt::BlankCursor);
setCursor(cursor);
mouseDelegate->setMouseIsCaptured(true);
} else {
setMouseButtonPressed(event->button(), true);
}
}
}
void ScanTargetWidget::mouseReleaseEvent(QMouseEvent *event) {
if(mouseDelegate && !mouseIsCaptured) {
setMouseButtonPressed(event->button(), false);
}
}
void ScanTargetWidget::setMouseButtonPressed(Qt::MouseButton button, bool isPressed) {
switch(button) {
default: break;
case Qt::LeftButton: mouseDelegate->setButtonPressed(0, isPressed); break;
case Qt::RightButton: mouseDelegate->setButtonPressed(1, isPressed); break;
case Qt::MiddleButton: mouseDelegate->setButtonPressed(2, isPressed); break;
}
}
void ScanTargetWidget::mouseMoveEvent(QMouseEvent *event) {
// Recentre the mouse cursor upon every move if it is currently captured.
if(mouseDelegate && mouseIsCaptured) {
const QPoint centre = QPoint(width() / 2, height() / 2);
const QPoint vector = event->pos() - centre;
mouseDelegate->moveMouse(vector);
QCursor::setPos(mapToGlobal(centre));
}
}
bool ScanTargetWidget::isMouseCaptured() {
return mouseIsCaptured;
}

View File

@ -8,23 +8,44 @@
#include "../../ClockReceiver/VSyncPredictor.hpp"
class ScanTargetWidget : public QOpenGLWidget
{
class ScanTargetWidget : public QOpenGLWidget {
public:
ScanTargetWidget(QWidget *parent = nullptr);
~ScanTargetWidget();
// Sets the current scan producer; this scan producer will be
// handed a suitable scan target as soon as one exists.
/// Sets the current scan producer; this scan producer will be
/// handed a suitable scan target as soon as one exists.
void setScanProducer(MachineTypes::ScanProducer *);
/// Destructs the current scan target
void stop();
struct MouseDelegate {
virtual void setMouseIsCaptured(bool) = 0;
virtual void moveMouse(QPoint) = 0;
virtual void setButtonPressed(int index, bool isPressed) = 0;
};
/// If a delegate is assigned then this widget will respond to clicks by capturing
/// the mouse, unless and until either ::stop() is called or ctrl+escape is pressed.
/// Mouse events can be tracked by the main window while the mouse is captured.
void setMouseDelegate(MouseDelegate *);
/// @returns @c true if the mouse is currently captured; @c false otherwise.
bool isMouseCaptured();
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void mousePressEvent(QMouseEvent *) override;
void mouseReleaseEvent(QMouseEvent *) override;
void mouseMoveEvent(QMouseEvent *) override;
void keyPressEvent(QKeyEvent *) override;
void releaseMouse();
void setMouseButtonPressed(Qt::MouseButton, bool);
private:
// This should be created only once there's an OpenGL context. So it
// can't be done at creation time.
@ -34,15 +55,18 @@ class ScanTargetWidget : public QOpenGLWidget
GLuint framebuffer = 0;
MachineTypes::ScanProducer *producer = nullptr;
Time::Nanos requested_redraw_time_ = 0;
Time::Nanos requestedRedrawTime = 0;
void setDefaultClearColour();
int width = 0, height = 0;
int rawWidth = 0, rawHeight = 0;
int scaledWidth = 0, scaledHeight = 0;
float outputScale = 1.0f;
void resize();
MouseDelegate *mouseDelegate = nullptr;
bool mouseIsCaptured = false;
private slots:
void vsync();
};