mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-16 16:28:59 +00:00
Attempts mouse event capture.
This commit is contained in:
parent
51f4242e9b
commit
cdda3f74ab
@ -709,30 +709,41 @@ void MainWindow::setUIPhase(UIPhase phase) {
|
|||||||
ui->topTipLabel->setVisible(phase == UIPhase::SelectingMachine);
|
ui->topTipLabel->setVisible(phase == UIPhase::SelectingMachine);
|
||||||
|
|
||||||
// Consider setting a window title, if it's knowable.
|
// Consider setting a window title, if it's knowable.
|
||||||
switch(phase) {
|
setWindowTitle();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set appropriate focus if necessary; e.g. this ensures that machine-picker
|
// Set appropriate focus if necessary; e.g. this ensures that machine-picker
|
||||||
// widgets aren't still selectable after a machine starts.
|
// widgets aren't still selectable after a machine starts.
|
||||||
if(phase != UIPhase::SelectingMachine) {
|
if(phase != UIPhase::SelectingMachine) {
|
||||||
ui->openGLWidget->setFocus();
|
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
|
// MARK: - Event Processing
|
||||||
@ -817,6 +828,27 @@ bool MainWindow::processEvent(QKeyEvent *event) {
|
|||||||
return false;
|
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
|
// MARK: - New Machine Creation
|
||||||
|
|
||||||
#include "../../Analyser/Static/Acorn/Target.hpp"
|
#include "../../Analyser/Static/Acorn/Target.hpp"
|
||||||
|
@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
namespace Ui { class MainWindow; }
|
namespace Ui { class MainWindow; }
|
||||||
QT_END_NAMESPACE
|
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
|
Q_OBJECT
|
||||||
|
|
||||||
void createActions();
|
void createActions();
|
||||||
@ -35,6 +35,10 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void keyReleaseEvent(QKeyEvent *event) override;
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
|
void setMouseIsCaptured(bool) override;
|
||||||
|
void moveMouse(QPoint) override;
|
||||||
|
void setButtonPressed(int index, bool isPressed) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Ui::MainWindow> ui;
|
std::unique_ptr<Ui::MainWindow> ui;
|
||||||
std::unique_ptr<Timer> timer;
|
std::unique_ptr<Timer> timer;
|
||||||
@ -117,6 +121,9 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
|||||||
|
|
||||||
void addAtari2600Menu();
|
void addAtari2600Menu();
|
||||||
void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
|
void toggleAtari2600Switch(Atari2600Switch toggleSwitch);
|
||||||
|
|
||||||
|
void setWindowTitle();
|
||||||
|
bool mouseIsCaptured = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MAINWINDOW_H
|
#endif // MAINWINDOW_H
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
#include "scantargetwidget.h"
|
#include "scantargetwidget.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QCursor>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDesktopWidget>
|
#include <QDesktopWidget>
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QMouseEvent>
|
||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -21,10 +24,10 @@ void ScanTargetWidget::initializeGL() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ScanTargetWidget::paintGL() {
|
void ScanTargetWidget::paintGL() {
|
||||||
if(requested_redraw_time_) {
|
if(requestedRedrawTime) {
|
||||||
const auto now = Time::nanos_now();
|
const auto now = Time::nanos_now();
|
||||||
vsyncPredictor.add_timer_jitter(now - requested_redraw_time_);
|
vsyncPredictor.add_timer_jitter(now - requestedRedrawTime);
|
||||||
requested_redraw_time_ = 0;
|
requestedRedrawTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: if Qt 5.14 can be guaranteed, just use window()->screen().
|
// TODO: if Qt 5.14 can be guaranteed, just use window()->screen().
|
||||||
@ -81,27 +84,27 @@ void ScanTargetWidget::vsync() {
|
|||||||
vsyncPredictor.announce_vsync();
|
vsyncPredictor.announce_vsync();
|
||||||
|
|
||||||
const auto time_now = Time::nanos_now();
|
const auto time_now = Time::nanos_now();
|
||||||
requested_redraw_time_ = vsyncPredictor.suggested_draw_time();
|
requestedRedrawTime = vsyncPredictor.suggested_draw_time();
|
||||||
const auto delay_time = (requested_redraw_time_ - time_now) / 1'000'000;
|
const auto delay_time = (requestedRedrawTime - time_now) / 1'000'000;
|
||||||
if(delay_time > 0) {
|
if(delay_time > 0) {
|
||||||
QTimer::singleShot(delay_time, this, SLOT(repaint()));
|
QTimer::singleShot(delay_time, this, SLOT(repaint()));
|
||||||
} else {
|
} else {
|
||||||
requested_redraw_time_ = 0;
|
requestedRedrawTime = 0;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanTargetWidget::resizeGL(int w, int h) {
|
void ScanTargetWidget::resizeGL(int w, int h) {
|
||||||
if(width != w || height != h) {
|
if(rawWidth != w || rawHeight != h) {
|
||||||
width = w;
|
rawWidth = w;
|
||||||
height = h;
|
rawHeight = h;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScanTargetWidget::resize() {
|
void ScanTargetWidget::resize() {
|
||||||
const int newScaledWidth = int(float(width) * outputScale);
|
const int newScaledWidth = int(float(rawWidth) * outputScale);
|
||||||
const int newScaledHeight = int(float(height) * outputScale);
|
const int newScaledHeight = int(float(rawHeight) * outputScale);
|
||||||
|
|
||||||
if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) {
|
if(newScaledWidth != scaledWidth || newScaledHeight != scaledHeight) {
|
||||||
scaledWidth = newScaledWidth;
|
scaledWidth = newScaledWidth;
|
||||||
@ -121,7 +124,7 @@ void ScanTargetWidget::stop() {
|
|||||||
isConnected = false;
|
isConnected = false;
|
||||||
setDefaultClearColour();
|
setDefaultClearColour();
|
||||||
vsyncPredictor.pause();
|
vsyncPredictor.pause();
|
||||||
requested_redraw_time_ = 0;
|
requestedRedrawTime = 0;
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,3 +133,76 @@ void ScanTargetWidget::setDefaultClearColour() {
|
|||||||
const QColor backgroundColour = palette().color(QWidget::backgroundRole());
|
const QColor backgroundColour = palette().color(QWidget::backgroundRole());
|
||||||
glClearColor(backgroundColour.redF(), backgroundColour.greenF(), backgroundColour.blueF(), 1.0);
|
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;
|
||||||
|
}
|
||||||
|
@ -8,23 +8,44 @@
|
|||||||
|
|
||||||
#include "../../ClockReceiver/VSyncPredictor.hpp"
|
#include "../../ClockReceiver/VSyncPredictor.hpp"
|
||||||
|
|
||||||
class ScanTargetWidget : public QOpenGLWidget
|
class ScanTargetWidget : public QOpenGLWidget {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
ScanTargetWidget(QWidget *parent = nullptr);
|
ScanTargetWidget(QWidget *parent = nullptr);
|
||||||
~ScanTargetWidget();
|
~ScanTargetWidget();
|
||||||
|
|
||||||
// Sets the current scan producer; this scan producer will be
|
/// Sets the current scan producer; this scan producer will be
|
||||||
// handed a suitable scan target as soon as one exists.
|
/// handed a suitable scan target as soon as one exists.
|
||||||
void setScanProducer(MachineTypes::ScanProducer *);
|
void setScanProducer(MachineTypes::ScanProducer *);
|
||||||
|
|
||||||
|
/// Destructs the current scan target
|
||||||
void stop();
|
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:
|
protected:
|
||||||
void initializeGL() override;
|
void initializeGL() override;
|
||||||
void resizeGL(int w, int h) override;
|
void resizeGL(int w, int h) override;
|
||||||
void paintGL() 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:
|
private:
|
||||||
// This should be created only once there's an OpenGL context. So it
|
// This should be created only once there's an OpenGL context. So it
|
||||||
// can't be done at creation time.
|
// can't be done at creation time.
|
||||||
@ -34,15 +55,18 @@ class ScanTargetWidget : public QOpenGLWidget
|
|||||||
GLuint framebuffer = 0;
|
GLuint framebuffer = 0;
|
||||||
MachineTypes::ScanProducer *producer = nullptr;
|
MachineTypes::ScanProducer *producer = nullptr;
|
||||||
|
|
||||||
Time::Nanos requested_redraw_time_ = 0;
|
Time::Nanos requestedRedrawTime = 0;
|
||||||
|
|
||||||
void setDefaultClearColour();
|
void setDefaultClearColour();
|
||||||
|
|
||||||
int width = 0, height = 0;
|
int rawWidth = 0, rawHeight = 0;
|
||||||
int scaledWidth = 0, scaledHeight = 0;
|
int scaledWidth = 0, scaledHeight = 0;
|
||||||
float outputScale = 1.0f;
|
float outputScale = 1.0f;
|
||||||
void resize();
|
void resize();
|
||||||
|
|
||||||
|
MouseDelegate *mouseDelegate = nullptr;
|
||||||
|
bool mouseIsCaptured = false;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void vsync();
|
void vsync();
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user