mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-23 20:29:42 +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);
|
||||
|
||||
// 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"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user