1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-16 18:30:32 +00:00

Tidies up and makes a failing attempt at SDI improvements.

This commit is contained in:
Thomas Harte 2020-06-21 23:50:18 -04:00
parent 782a62585e
commit b17cceaeaf
3 changed files with 186 additions and 160 deletions

View File

@ -1,4 +1,4 @@
QT += core gui multimedia
QT += core gui multimedia gamepad
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

View File

@ -8,155 +8,6 @@
#include "../../Numeric/CRC.hpp"
/*
General Qt implementation notes:
* it seems like Qt doesn't offer a way to constrain the aspect ratio of a view by constraining
the size of the window (i.e. you can use a custom layout to constrain a view, but that won't
affect the window, so isn't useful for this project). Therefore the emulation window resizes freely.
*/
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
init();
setVisibleWidgetSet(WidgetSet::MachinePicker);
}
MainWindow::MainWindow(const QString &fileName) {
init();
launchFile(fileName);
}
void MainWindow::init() {
qApp->installEventFilter(this);
ui = std::make_unique<Ui::MainWindow>();
ui->setupUi(this);
romRequestBaseText = ui->missingROMsBox->toPlainText();
createActions();
restoreSelections();
timer = std::make_unique<Timer>(this);
}
void MainWindow::createActions() {
// Create a file menu.
QMenu *const fileMenu = menuBar()->addMenu(tr("&File"));
// Add file option: 'New'
QAction *const newAct = new QAction(tr("&New"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
connect(newAct, &QAction::triggered, this, &MainWindow::newFile);
fileMenu->addAction(newAct);
// Add file option: 'Open...'
QAction *const openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
openAct->setStatusTip(tr("Open an existing file"));
connect(openAct, &QAction::triggered, this, &MainWindow::open);
fileMenu->addAction(openAct);
// Add a separator and then an 'Insert...'.
fileMenu->addSeparator();
QAction *const insertAct = new QAction(tr("&Insert..."), this);
insertAct->setStatusTip(tr("Open an existing file"));
connect(insertAct, &QAction::triggered, this, &MainWindow::insert);
fileMenu->addAction(insertAct);
// Add Help menu, with an 'About...' option.
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
QAction *aboutAct = helpMenu->addAction(tr("&About"), this, &MainWindow::about);
aboutAct->setStatusTip(tr("Show the application's About box"));
// Link up the start machine button.
connect(ui->startMachineButton, &QPushButton::clicked, this, &MainWindow::startMachine);
}
void MainWindow::open() {
Settings settings;
// Use the Settings to get a default open path; write it back afterwards.
QString fileName = QFileDialog::getOpenFileName(this, tr("Open..."), settings.value("openPath").toString());
if(!fileName.isEmpty()) {
settings.setValue("openPath", QFileInfo(fileName).absoluteDir().path());
// My understanding of SDI: if a file was opened for a 'vacant' window, launch it directly there;
// otherwise create a new window for it.
if(machine) {
MainWindow *const other = new MainWindow(fileName);
other->tile(this);
other->show();
} else {
launchFile(fileName);
}
}
}
void MainWindow::insert() {
}
void MainWindow::launchFile(const QString &fileName) {
targets = Analyser::Static::GetTargets(fileName.toStdString());
if(!targets.empty()) {
launchMachine();
}
}
void MainWindow::newFile() {
MainWindow *other = new MainWindow;
other->tile(this);
other->show();
}
void MainWindow::tile(const QMainWindow *previous) {
// This entire function is essentially verbatim from the Qt SDI example.
if (!previous)
return;
int topFrameWidth = previous->geometry().top() - previous->pos().y();
if (!topFrameWidth)
topFrameWidth = 40;
const QPoint pos = previous->pos() + 2 * QPoint(topFrameWidth, topFrameWidth);
if (screen()->availableGeometry().contains(rect().bottomRight() + pos))
move(pos);
}
void MainWindow::about() {
QMessageBox::about(this, tr("About Clock Signal"),
tr( "<p>Clock Signal is an emulator of various platforms.</p>"
"<p>This emulator is offered under the MIT licence; its source code "
"is available from <a href=\"https://github.com/tomharte/CLK\">GitHub</a>.</p>"
"<p>This port is experimental, especially with regard to latency; "
"please don't hesitate to provide feedback, "
"<a href=\"mailto:thomas.harte@gmail.com\">by email</a> or via the "
"<a href=\"https://github.com/tomharte/CLK/issues\">GitHub issue tracker</a>.</p>"
));
}
MainWindow::~MainWindow() {
// Stop the timer; stopping this first ensures the machine won't attempt
// to write to the audioOutput while it is being shut down.
timer.reset();
// Stop the audio output, and its thread.
if(audioOutput) {
audioThread.performAsync([this] {
audioOutput->stop();
});
audioThread.stop();
}
// Store the current user selections.
storeSelections();
}
// MARK: Machine launch.
namespace {
std::unique_ptr<std::vector<uint8_t>> fileContentsAndClose(FILE *file) {
@ -177,6 +28,172 @@ std::unique_ptr<std::vector<uint8_t>> fileContentsAndClose(FILE *file) {
}
/*
General Qt implementation notes:
* it seems like Qt doesn't offer a way to constrain the aspect ratio of a view by constraining
the size of the window (i.e. you can use a custom layout to constrain a view, but that won't
affect the window, so isn't useful for this project). Therefore the emulation window resizes freely.
*/
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
init();
setVisibleWidgetSet(WidgetSet::MachinePicker);
}
MainWindow::MainWindow(const QString &fileName) {
init();
launchFile(fileName);
}
MainWindow::~MainWindow() {
// Stop the timer; stopping this first ensures the machine won't attempt
// to write to the audioOutput while it is being shut down.
timer.reset();
// Stop the audio output, and its thread.
if(audioOutput) {
audioThread.performAsync([this] {
audioOutput->stop();
});
audioThread.stop();
}
// Store the current user selections.
storeSelections();
// SDI behaviour, which may or may not be normal (?): if the user is closing a
// final window, and it contains a machine, send them back to the machine picker.
// i.e. assume they were closing that document, not the application.
--mainWindowCount;
if(machine && !mainWindowCount) {
MainWindow *const other = new MainWindow;
other->show();
}
}
void MainWindow::closeEvent(QCloseEvent *event) {
if(mainWindowCount == 1 && machine) {
qDebug() << "close, yah";
}
QMainWindow::closeEvent(event);
}
void MainWindow::init() {
++mainWindowCount;
qApp->installEventFilter(this);
ui = std::make_unique<Ui::MainWindow>();
ui->setupUi(this);
romRequestBaseText = ui->missingROMsBox->toPlainText();
createActions();
restoreSelections();
timer = std::make_unique<Timer>(this);
}
void MainWindow::createActions() {
// Create a file menu.
QMenu *const fileMenu = menuBar()->addMenu(tr("&File"));
// Add file option: 'New'
QAction *const newAct = new QAction(tr("&New"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setStatusTip(tr("Create a new file"));
connect(newAct, &QAction::triggered, this, [this] {
storeSelections();
MainWindow *other = new MainWindow;
other->tile(this);
other->show();
});
fileMenu->addAction(newAct);
// Add file option: 'Open...'
QAction *const openAct = new QAction(tr("&Open..."), this);
openAct->setShortcuts(QKeySequence::Open);
openAct->setStatusTip(tr("Open an existing file"));
connect(openAct, &QAction::triggered, this, [this] {
const QString fileName = getFilename("Open...");
if(!fileName.isEmpty()) {
// My understanding of SDI: if a file was opened for a 'vacant' window, launch it directly there;
// otherwise create a new window for it.
if(machine) {
MainWindow *const other = new MainWindow(fileName);
other->tile(this);
other->show();
} else {
launchFile(fileName);
}
}
});
fileMenu->addAction(openAct);
// Add a separator and then an 'Insert...'.
fileMenu->addSeparator();
QAction *const insertAct = new QAction(tr("&Insert..."), this);
insertAct->setStatusTip(tr("Open an existing file"));
connect(insertAct, &QAction::triggered, this, [] {
// TODO.
});
fileMenu->addAction(insertAct);
// Add Help menu, with an 'About...' option.
QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
QAction *aboutAct = helpMenu->addAction(tr("&About"), this, [this] {
QMessageBox::about(this, tr("About Clock Signal"),
tr( "<p>Clock Signal is an emulator of various platforms.</p>"
"<p>This emulator is offered under the MIT licence; its source code "
"is available from <a href=\"https://github.com/tomharte/CLK\">GitHub</a>.</p>"
"<p>This port is experimental, especially with regard to latency; "
"please don't hesitate to provide feedback, "
"<a href=\"mailto:thomas.harte@gmail.com\">by email</a> or via the "
"<a href=\"https://github.com/tomharte/CLK/issues\">GitHub issue tracker</a>.</p>"
));
});
aboutAct->setStatusTip(tr("Show the application's About box"));
// Link up the start machine button.
connect(ui->startMachineButton, &QPushButton::clicked, this, &MainWindow::startMachine);
}
QString MainWindow::getFilename(const char *title) {
Settings settings;
// Use the Settings to get a default open path; write it back afterwards.
QString fileName = QFileDialog::getOpenFileName(this, tr(title), settings.value("openPath").toString());
if(!fileName.isEmpty()) {
settings.setValue("openPath", QFileInfo(fileName).absoluteDir().path());
}
return fileName;
}
void MainWindow::launchFile(const QString &fileName) {
targets = Analyser::Static::GetTargets(fileName.toStdString());
if(!targets.empty()) {
launchMachine();
}
}
void MainWindow::tile(const QMainWindow *previous) {
// This entire function is essentially verbatim from the Qt SDI example.
if (!previous)
return;
int topFrameWidth = previous->geometry().top() - previous->pos().y();
if (!topFrameWidth)
topFrameWidth = 40;
const QPoint pos = previous->pos() + 2 * QPoint(topFrameWidth, topFrameWidth);
if (screen()->availableGeometry().contains(rect().bottomRight() + pos))
move(pos);
}
// MARK: Machine launch.
void MainWindow::launchMachine() {
const QStringList appDataLocations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
missingRoms.clear();
@ -309,9 +326,16 @@ void MainWindow::dropEvent(QDropEvent* event) {
event->accept();
switch(uiPhase) {
case UIPhase::NoFileSelected:
case UIPhase::NoFileSelected: {
// Treat exactly as a File -> Open... .
break;
// TODO: permit multiple files dropped at once.
const auto fileName = event->mimeData()->urls()[0].toLocalFile();
launchFile(fileName);
} break;
case UIPhase::RunningMachine: {
// Attempt to insert into the running machine.
} break;
case UIPhase::RequestingROMs: {
// Attempt to match up the dragged files to the requested ROM list;
@ -351,10 +375,6 @@ void MainWindow::dropEvent(QDropEvent* event) {
if(foundROM) launchMachine();
} break;
case UIPhase::RunningMachine:
// Attempt to insert into the running machine.
break;
}
}
@ -370,6 +390,9 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
}
} break;
case QEvent::Close:
break;
default:
break;
}
@ -670,4 +693,7 @@ void MainWindow::storeSelections() {
}
void MainWindow::restoreSelections() {
Settings settings;
ui->machineSelectionTabs->setCurrentIndex(settings.value("machineSelection").toInt());
}

View File

@ -68,10 +68,6 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void setVisibleWidgetSet(WidgetSet);
private slots:
void open();
void newFile();
void about();
void insert();
void startMachine();
private:
@ -94,6 +90,10 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
void init();
void tile(const QMainWindow *previous);
QString getFilename(const char *title);
void closeEvent(QCloseEvent *event) override;
static inline int mainWindowCount = 0;
};
#endif // MAINWINDOW_H