diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 0bfc826b3..c20b0b1e2 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -24,6 +24,7 @@ #include "CaptureContext.h" #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include "Windows/MainWindow.h" +#include "QRDUtils.h" CaptureContext::CaptureContext(QString paramFilename, QString remoteHost, uint32_t remoteIdent, bool temp, PersistantConfig &cfg) @@ -377,135 +379,3 @@ void *CaptureContext::FillWindowingData(WId widget) #endif } - -void GUIInvoke::call(const std::function &f) -{ - if(qApp->thread() == QThread::currentThread()) - { - f(); - return; - } - - // TODO: could maybe do away with string compare here via caching - // invoke->metaObject()->indexOfMethod("doInvoke"); ? - - GUIInvoke *invoke = new GUIInvoke(f); - invoke->moveToThread(qApp->thread()); - QMetaObject::invokeMethod(invoke, "doInvoke", Qt::QueuedConnection); -} - -void GUIInvoke::blockcall(const std::function &f) -{ - if(qApp->thread() == QThread::currentThread()) - { - f(); - return; - } - - // TODO: could maybe do away with string compare here via caching - // invoke->metaObject()->indexOfMethod("doInvoke"); ? - - GUIInvoke *invoke = new GUIInvoke(f); - invoke->moveToThread(qApp->thread()); - QMetaObject::invokeMethod(invoke, "doInvoke", Qt::BlockingQueuedConnection); -} - -void RDDialog::show(QMenu *menu, QPoint pos) -{ - menu->setWindowModality(Qt::ApplicationModal); - menu->popup(pos); - QEventLoop loop; - while(menu->isVisible()) - { - loop.processEvents(QEventLoop::WaitForMoreEvents); - QCoreApplication::sendPostedEvents(); - } -} - -int RDDialog::show(QDialog *dialog) -{ - dialog->setWindowModality(Qt::ApplicationModal); - dialog->show(); - QEventLoop loop; - while(dialog->isVisible()) - { - loop.processEvents(QEventLoop::WaitForMoreEvents); - QCoreApplication::sendPostedEvents(); - } - - return dialog->result(); -} - -QMessageBox::StandardButton RDDialog::messageBox(QMessageBox::Icon icon, QWidget *parent, - const QString &title, const QString &text, - QMessageBox::StandardButtons buttons, - QMessageBox::StandardButton defaultButton) -{ - QMessageBox mb(icon, title, text, buttons, parent); - mb.setDefaultButton(defaultButton); - show(&mb); - return mb.standardButton(mb.clickedButton()); -} - -QString RDDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, - QFileDialog::Options options) -{ - QFileDialog fd(parent, caption, dir, QString()); - fd.setAcceptMode(QFileDialog::AcceptOpen); - fd.setFileMode(QFileDialog::DirectoryOnly); - fd.setOptions(options); - show(&fd); - - if(fd.result() == QFileDialog::Accepted) - { - QStringList files = fd.selectedFiles(); - if(!files.isEmpty()) - return files[0]; - } - - return QString(); -} - -QString RDDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, - const QString &filter, QString *selectedFilter, - QFileDialog::Options options) -{ - QFileDialog fd(parent, caption, dir, filter); - fd.setAcceptMode(QFileDialog::AcceptOpen); - fd.setOptions(options); - show(&fd); - - if(fd.result() == QFileDialog::Accepted) - { - if(selectedFilter) - *selectedFilter = fd.selectedNameFilter(); - - QStringList files = fd.selectedFiles(); - if(!files.isEmpty()) - return files[0]; - } - - return QString(); -} - -QString RDDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, - const QString &filter, QString *selectedFilter, - QFileDialog::Options options) -{ - QFileDialog fd(parent, caption, dir, filter); - fd.setAcceptMode(QFileDialog::AcceptSave); - fd.setOptions(options); - show(&fd); - - if(fd.result() == QFileDialog::Accepted) - { - if(selectedFilter) - *selectedFilter = fd.selectedNameFilter(); - - QStringList files = fd.selectedFiles(); - if(!files.isEmpty()) - return files[0]; - } - - return QString(); -} diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 8f1a7f8ea..7a0e01666 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -25,7 +25,6 @@ #pragma once #include -#include #include #include #include @@ -194,163 +193,3 @@ private: QProgressDialog *m_Progress; MainWindow *m_MainWindow; }; - -// implementation of QOverload, to avoid depending on 5.7. -// From: http://stackoverflow.com/a/16795664/4070143 -template -struct OverloadedSlot -{ - template - static constexpr auto of(R (C::*pmf)(Args...)) -> decltype(pmf) - { - return pmf; - } -}; - -// Utility class for invoking a lambda on the GUI thread. -// This is supported by QTimer::singleShot on Qt 5.4 but it's probably -// wise not to require a higher version that necessary. -#include - -class GUIInvoke : public QObject -{ -private: - Q_OBJECT - GUIInvoke(const std::function &f) : func(f) {} - std::function func; - -public: - static void call(const std::function &f); - static void blockcall(const std::function &f); - -protected slots: - void doInvoke() - { - func(); - deleteLater(); - } -}; - -// Utility class for calling a lambda on a new thread. -#include - -class LambdaThread : public QObject -{ -private: - Q_OBJECT - - std::function m_func; - QThread *m_Thread; - QSemaphore completed; - bool m_SelfDelete = false; - -public slots: - void process() - { - m_func(); - m_Thread->quit(); - m_Thread->deleteLater(); - m_Thread = NULL; - if(m_SelfDelete) - deleteLater(); - completed.acquire(); - } - - void selfDelete(bool d) { m_SelfDelete = d; } -public: - explicit LambdaThread(std::function f) - { - completed.release(); - m_Thread = new QThread(); - m_func = f; - moveToThread(m_Thread); - QObject::connect(m_Thread, &QThread::started, this, &LambdaThread::process); - } - - void start(QThread::Priority prio = QThread::InheritPriority) { m_Thread->start(prio); } - bool isRunning() { return completed.available(); } - bool wait(unsigned long time = ULONG_MAX) - { - if(m_Thread) - return m_Thread->wait(time); - return true; - } -}; - -class QMenu; - -// helper for doing a manual blocking invoke of a dialog -struct RDDialog -{ - static void show(QMenu *menu, QPoint pos); - static int show(QDialog *dialog); - static QMessageBox::StandardButton messageBox( - QMessageBox::Icon, QWidget *parent, const QString &title, const QString &text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); - - static QMessageBox::StandardButton information( - QWidget *parent, const QString &title, const QString &text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) - { - return messageBox(QMessageBox::Information, parent, title, text, buttons, defaultButton); - } - - static QMessageBox::StandardButton question( - QWidget *parent, const QString &title, const QString &text, - QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | - QMessageBox::No), - QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) - { - return messageBox(QMessageBox::Question, parent, title, text, buttons, defaultButton); - } - - static QMessageBox::StandardButton warning( - QWidget *parent, const QString &title, const QString &text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) - { - return messageBox(QMessageBox::Warning, parent, title, text, buttons, defaultButton); - } - - static QMessageBox::StandardButton critical( - QWidget *parent, const QString &title, const QString &text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) - { - return messageBox(QMessageBox::Critical, parent, title, text, buttons, defaultButton); - } - - static QString getExistingDirectory(QWidget *parent = NULL, const QString &caption = QString(), - const QString &dir = QString(), - QFileDialog::Options options = QFileDialog::ShowDirsOnly); - - static QString getOpenFileName(QWidget *parent = NULL, const QString &caption = QString(), - const QString &dir = QString(), const QString &filter = QString(), - QString *selectedFilter = NULL, - QFileDialog::Options options = QFileDialog::Options()); - - static QString getSaveFileName(QWidget *parent = NULL, const QString &caption = QString(), - const QString &dir = QString(), const QString &filter = QString(), - QString *selectedFilter = NULL, - QFileDialog::Options options = QFileDialog::Options()); -}; - -// useful delegate for enforcing a given size -#include - -class SizeDelegate : public QItemDelegate -{ -private: - Q_OBJECT - - QSize m_Size; - -public: - SizeDelegate(QSize size) : m_Size(size) {} - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const - { - return m_Size; - } -}; diff --git a/qrenderdoc/Code/PersistantConfig.cpp b/qrenderdoc/Code/PersistantConfig.cpp index 5ba8bde3d..2b342f4dd 100644 --- a/qrenderdoc/Code/PersistantConfig.cpp +++ b/qrenderdoc/Code/PersistantConfig.cpp @@ -25,9 +25,12 @@ #include "PersistantConfig.h" #include #include -#include +#include "QRDUtils.h" #include "renderdoc_replay.h" +#define JSON_ID "rdocConfigData" +#define JSON_VER 1 + // helper templates to convert some more complex types to/from appropriate variants template variantType convertToVariant(const origType &val) @@ -93,29 +96,12 @@ bool PersistantConfig::Deserialize(QString filename) if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { - QByteArray json = f.readAll(); + QVariantMap values; - if(json.isEmpty()) - { - qCritical() << "Read invalid empty JSON data from file " << f.errorString(); + bool success = LoadFromJSON(values, f, JSON_ID, JSON_VER); + + if(!success) return false; - } - - QJsonDocument doc = QJsonDocument::fromJson(json); - - if(doc.isEmpty() || doc.isNull()) - { - qCritical() << "Failed to convert file to JSON document"; - return false; - } - - QVariantMap values = doc.toVariant().toMap(); - - if(values.isEmpty() || !values.contains("renderdocConfigData")) - { - qCritical() << "Converted config data is invalid or unrecognised"; - return false; - } applyValues(values); @@ -131,32 +117,9 @@ bool PersistantConfig::Serialize(QString filename) { QVariantMap values = storeValues(); - // marker that this is indeed a valid config to load from - values["renderdocConfigData"] = 1; - QFile f(filename); if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - { - QJsonDocument doc = QJsonDocument::fromVariant(values); - - if(doc.isEmpty() || doc.isNull()) - { - qCritical() << "Failed to convert config data to JSON document"; - return false; - } - - QByteArray jsontext = doc.toJson(QJsonDocument::Indented); - - qint64 ret = f.write(jsontext); - - if(ret != jsontext.size()) - { - qCritical() << "Failed to write JSON data to file: " << ret << " " << f.errorString(); - return false; - } - - return true; - } + return SaveToJSON(values, f, JSON_ID, JSON_VER); qWarning() << "Couldn't write to " << filename << " " << f.errorString(); diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp new file mode 100644 index 000000000..4c0133e5a --- /dev/null +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -0,0 +1,221 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2016 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "QRDUtils.h" +#include +#include +#include + +bool SaveToJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, uint32_t magicVersion) +{ + // marker that this data is valid + data[magicIdentifier] = magicVersion; + + QJsonDocument doc = QJsonDocument::fromVariant(data); + + if(doc.isEmpty() || doc.isNull()) + { + qCritical() << "Failed to convert data to JSON document"; + return false; + } + + QByteArray jsontext = doc.toJson(QJsonDocument::Indented); + + qint64 ret = f.write(jsontext); + + if(ret != jsontext.size()) + { + qCritical() << "Failed to write JSON data: " << ret << " " << f.errorString(); + return false; + } + + return true; +} + +bool LoadFromJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, uint32_t magicVersion) +{ + QByteArray json = f.readAll(); + + if(json.isEmpty()) + { + qCritical() << "Read invalid empty JSON data from file " << f.errorString(); + return false; + } + + QJsonDocument doc = QJsonDocument::fromJson(json); + + if(doc.isEmpty() || doc.isNull()) + { + qCritical() << "Failed to convert file to JSON document"; + return false; + } + + data = doc.toVariant().toMap(); + + if(data.isEmpty() || !data.contains(magicIdentifier)) + { + qCritical() << "Converted config data is invalid or unrecognised"; + return false; + } + + if(data[magicIdentifier].toUInt() != magicVersion) + { + qCritical() << "Converted config data is not the right version"; + return false; + } + + return true; +} + +void GUIInvoke::call(const std::function &f) +{ + if(qApp->thread() == QThread::currentThread()) + { + f(); + return; + } + + // TODO: could maybe do away with string compare here via caching + // invoke->metaObject()->indexOfMethod("doInvoke"); ? + + GUIInvoke *invoke = new GUIInvoke(f); + invoke->moveToThread(qApp->thread()); + QMetaObject::invokeMethod(invoke, "doInvoke", Qt::QueuedConnection); +} + +void GUIInvoke::blockcall(const std::function &f) +{ + if(qApp->thread() == QThread::currentThread()) + { + f(); + return; + } + + // TODO: could maybe do away with string compare here via caching + // invoke->metaObject()->indexOfMethod("doInvoke"); ? + + GUIInvoke *invoke = new GUIInvoke(f); + invoke->moveToThread(qApp->thread()); + QMetaObject::invokeMethod(invoke, "doInvoke", Qt::BlockingQueuedConnection); +} + +void RDDialog::show(QMenu *menu, QPoint pos) +{ + menu->setWindowModality(Qt::ApplicationModal); + menu->popup(pos); + QEventLoop loop; + while(menu->isVisible()) + { + loop.processEvents(QEventLoop::WaitForMoreEvents); + QCoreApplication::sendPostedEvents(); + } +} + +int RDDialog::show(QDialog *dialog) +{ + dialog->setWindowModality(Qt::ApplicationModal); + dialog->show(); + QEventLoop loop; + while(dialog->isVisible()) + { + loop.processEvents(QEventLoop::WaitForMoreEvents); + QCoreApplication::sendPostedEvents(); + } + + return dialog->result(); +} + +QMessageBox::StandardButton RDDialog::messageBox(QMessageBox::Icon icon, QWidget *parent, + const QString &title, const QString &text, + QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) +{ + QMessageBox mb(icon, title, text, buttons, parent); + mb.setDefaultButton(defaultButton); + show(&mb); + return mb.standardButton(mb.clickedButton()); +} + +QString RDDialog::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, + QFileDialog::Options options) +{ + QFileDialog fd(parent, caption, dir, QString()); + fd.setAcceptMode(QFileDialog::AcceptOpen); + fd.setFileMode(QFileDialog::DirectoryOnly); + fd.setOptions(options); + show(&fd); + + if(fd.result() == QFileDialog::Accepted) + { + QStringList files = fd.selectedFiles(); + if(!files.isEmpty()) + return files[0]; + } + + return QString(); +} + +QString RDDialog::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, QString *selectedFilter, + QFileDialog::Options options) +{ + QFileDialog fd(parent, caption, dir, filter); + fd.setAcceptMode(QFileDialog::AcceptOpen); + fd.setOptions(options); + show(&fd); + + if(fd.result() == QFileDialog::Accepted) + { + if(selectedFilter) + *selectedFilter = fd.selectedNameFilter(); + + QStringList files = fd.selectedFiles(); + if(!files.isEmpty()) + return files[0]; + } + + return QString(); +} + +QString RDDialog::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, + const QString &filter, QString *selectedFilter, + QFileDialog::Options options) +{ + QFileDialog fd(parent, caption, dir, filter); + fd.setAcceptMode(QFileDialog::AcceptSave); + fd.setOptions(options); + show(&fd); + + if(fd.result() == QFileDialog::Accepted) + { + if(selectedFilter) + *selectedFilter = fd.selectedNameFilter(); + + QStringList files = fd.selectedFiles(); + if(!files.isEmpty()) + return files[0]; + } + + return QString(); +} diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h new file mode 100644 index 000000000..cf1507c88 --- /dev/null +++ b/qrenderdoc/Code/QRDUtils.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2016 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +bool SaveToJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, uint32_t magicVersion); +bool LoadFromJSON(QVariantMap &data, QIODevice &f, const char *magicIdentifier, + uint32_t magicVersion); + +// implementation of QOverload, to avoid depending on 5.7. +// From: http://stackoverflow.com/a/16795664/4070143 +template +struct OverloadedSlot +{ + template + static constexpr auto of(R (C::*pmf)(Args...)) -> decltype(pmf) + { + return pmf; + } +}; + +// Utility class for invoking a lambda on the GUI thread. +// This is supported by QTimer::singleShot on Qt 5.4 but it's probably +// wise not to require a higher version that necessary. +#include + +class GUIInvoke : public QObject +{ +private: + Q_OBJECT + GUIInvoke(const std::function &f) : func(f) {} + std::function func; + +public: + static void call(const std::function &f); + static void blockcall(const std::function &f); + +protected slots: + void doInvoke() + { + func(); + deleteLater(); + } +}; + +// Utility class for calling a lambda on a new thread. +#include + +class LambdaThread : public QObject +{ +private: + Q_OBJECT + + std::function m_func; + QThread *m_Thread; + QSemaphore completed; + bool m_SelfDelete = false; + +public slots: + void process() + { + m_func(); + m_Thread->quit(); + m_Thread->deleteLater(); + m_Thread = NULL; + if(m_SelfDelete) + deleteLater(); + completed.acquire(); + } + + void selfDelete(bool d) { m_SelfDelete = d; } +public: + explicit LambdaThread(std::function f) + { + completed.release(); + m_Thread = new QThread(); + m_func = f; + moveToThread(m_Thread); + QObject::connect(m_Thread, &QThread::started, this, &LambdaThread::process); + } + + void start(QThread::Priority prio = QThread::InheritPriority) { m_Thread->start(prio); } + bool isRunning() { return completed.available(); } + bool wait(unsigned long time = ULONG_MAX) + { + if(m_Thread) + return m_Thread->wait(time); + return true; + } +}; + +class QMenu; + +// helper for doing a manual blocking invoke of a dialog +struct RDDialog +{ + static void show(QMenu *menu, QPoint pos); + static int show(QDialog *dialog); + static QMessageBox::StandardButton messageBox( + QMessageBox::Icon, QWidget *parent, const QString &title, const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + static QMessageBox::StandardButton information( + QWidget *parent, const QString &title, const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) + { + return messageBox(QMessageBox::Information, parent, title, text, buttons, defaultButton); + } + + static QMessageBox::StandardButton question( + QWidget *parent, const QString &title, const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | + QMessageBox::No), + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) + { + return messageBox(QMessageBox::Question, parent, title, text, buttons, defaultButton); + } + + static QMessageBox::StandardButton warning( + QWidget *parent, const QString &title, const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) + { + return messageBox(QMessageBox::Warning, parent, title, text, buttons, defaultButton); + } + + static QMessageBox::StandardButton critical( + QWidget *parent, const QString &title, const QString &text, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) + { + return messageBox(QMessageBox::Critical, parent, title, text, buttons, defaultButton); + } + + static QString getExistingDirectory(QWidget *parent = NULL, const QString &caption = QString(), + const QString &dir = QString(), + QFileDialog::Options options = QFileDialog::ShowDirsOnly); + + static QString getOpenFileName(QWidget *parent = NULL, const QString &caption = QString(), + const QString &dir = QString(), const QString &filter = QString(), + QString *selectedFilter = NULL, + QFileDialog::Options options = QFileDialog::Options()); + + static QString getSaveFileName(QWidget *parent = NULL, const QString &caption = QString(), + const QString &dir = QString(), const QString &filter = QString(), + QString *selectedFilter = NULL, + QFileDialog::Options options = QFileDialog::Options()); +}; + +// useful delegate for enforcing a given size +#include + +class SizeDelegate : public QItemDelegate +{ +private: + Q_OBJECT + + QSize m_Size; + +public: + SizeDelegate(QSize size) : m_Size(size) {} + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + return m_Size; + } +}; diff --git a/qrenderdoc/Code/RenderManager.cpp b/qrenderdoc/Code/RenderManager.cpp index 567da3bc4..890237304 100644 --- a/qrenderdoc/Code/RenderManager.cpp +++ b/qrenderdoc/Code/RenderManager.cpp @@ -25,6 +25,7 @@ #include "RenderManager.h" #include #include "CaptureContext.h" +#include "QRDUtils.h" RenderManager::RenderManager() { diff --git a/qrenderdoc/Code/qrenderdoc.cpp b/qrenderdoc/Code/qrenderdoc.cpp index 4e08cc8dc..e71a8145b 100644 --- a/qrenderdoc/Code/qrenderdoc.cpp +++ b/qrenderdoc/Code/qrenderdoc.cpp @@ -28,6 +28,7 @@ #include #include #include "Code/CaptureContext.h" +#include "Code/QRDUtils.h" #include "Windows/MainWindow.h" void sharedLogOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) diff --git a/qrenderdoc/Windows/Dialogs/TextureSaveDialog.cpp b/qrenderdoc/Windows/Dialogs/TextureSaveDialog.cpp index c1f60e7d1..452891134 100644 --- a/qrenderdoc/Windows/Dialogs/TextureSaveDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/TextureSaveDialog.cpp @@ -24,7 +24,9 @@ #include "TextureSaveDialog.h" #include +#include #include "Code/CaptureContext.h" +#include "Code/QRDUtils.h" #include "ui_TextureSaveDialog.h" TextureSaveDialog::TextureSaveDialog(const FetchTexture &t, const TextureSave &s, QWidget *parent) diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index 3e340bdca..33884007e 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -25,6 +25,7 @@ #include "EventBrowser.h" #include #include "Code/CaptureContext.h" +#include "Code/QRDUtils.h" #include "ui_EventBrowser.h" enum diff --git a/qrenderdoc/Windows/EventBrowser.h b/qrenderdoc/Windows/EventBrowser.h index 0ed0345cf..a792c2e7a 100644 --- a/qrenderdoc/Windows/EventBrowser.h +++ b/qrenderdoc/Windows/EventBrowser.h @@ -35,6 +35,7 @@ class EventBrowser; class QTreeWidgetItem; class QTimer; +class SizeDelegate; class EventBrowser : public QFrame, public ILogViewerForm { diff --git a/qrenderdoc/Windows/MainWindow.cpp b/qrenderdoc/Windows/MainWindow.cpp index d73ab6b6f..4eb4cb65b 100644 --- a/qrenderdoc/Windows/MainWindow.cpp +++ b/qrenderdoc/Windows/MainWindow.cpp @@ -25,14 +25,17 @@ #include "MainWindow.h" #include #include -#include #include #include "Code/CaptureContext.h" +#include "Code/QRDUtils.h" #include "Windows/Dialogs/AboutDialog.h" #include "EventBrowser.h" #include "TextureViewer.h" #include "ui_MainWindow.h" +#define JSON_ID "rdocLayoutData" +#define JSON_VER 1 + MainWindow::MainWindow(CaptureContext *ctx) : QMainWindow(NULL), ui(new Ui::MainWindow), m_Ctx(ctx) { ui->setupUi(this); @@ -790,32 +793,9 @@ bool MainWindow::SaveLayout(int layout) QVariantMap state = saveState(); - // marker that this is indeed a valid state to load from - state["renderdocLayoutData"] = 1; - QFile f(path); if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - { - QJsonDocument doc = QJsonDocument::fromVariant(state); - - if(doc.isEmpty() || doc.isNull()) - { - qCritical() << "Failed to convert state data to JSON document"; - return false; - } - - QByteArray jsontext = doc.toJson(QJsonDocument::Indented); - - qint64 ret = f.write(jsontext); - - if(ret != jsontext.size()) - { - qCritical() << "Failed to write JSON data to file: " << ret << " " << f.errorString(); - return false; - } - - return true; - } + return SaveToJSON(state, f, JSON_ID, JSON_VER); qWarning() << "Couldn't write to " << path << " " << f.errorString(); @@ -829,29 +809,12 @@ bool MainWindow::LoadLayout(int layout) QFile f(path); if(f.open(QIODevice::ReadOnly | QIODevice::Text)) { - QByteArray json = f.readAll(); + QVariantMap state; - if(json.isEmpty()) - { - qCritical() << "Read invalid empty JSON data from file " << f.errorString(); + bool success = LoadFromJSON(state, f, JSON_ID, JSON_VER); + + if(!success) return false; - } - - QJsonDocument doc = QJsonDocument::fromJson(json); - - if(doc.isEmpty() || doc.isNull()) - { - qCritical() << "Failed to convert file to JSON document"; - return false; - } - - QVariantMap state = doc.toVariant().toMap(); - - if(state.isEmpty() || !state.contains("renderdocLayoutData")) - { - qCritical() << "Converted state data is invalid or unrecognised"; - return false; - } return restoreState(state); } diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index dba22f56c..e13441a70 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -27,12 +27,14 @@ #include #include #include +#include #include #include #include #include #include "3rdparty/toolwindowmanager/ToolWindowManagerArea.h" #include "Code/CaptureContext.h" +#include "Code/QRDUtils.h" #include "Dialogs/TextureSaveDialog.h" #include "Widgets/ResourcePreview.h" #include "Widgets/TextureGoto.h" diff --git a/qrenderdoc/Windows/TextureViewer.h b/qrenderdoc/Windows/TextureViewer.h index 55cb96869..b44deb4e2 100644 --- a/qrenderdoc/Windows/TextureViewer.h +++ b/qrenderdoc/Windows/TextureViewer.h @@ -25,6 +25,7 @@ #pragma once #include +#include #include #include "Code/CaptureContext.h" diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 0c08bb078..0f1f50148 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -95,7 +95,8 @@ SOURCES += 3rdparty/toolwindowmanager/ToolWindowManager.cpp \ Widgets/ThumbnailStrip.cpp \ Widgets/TextureGoto.cpp \ Widgets/RangeHistogram.cpp \ - Windows/Dialogs/TextureSaveDialog.cpp + Windows/Dialogs/TextureSaveDialog.cpp \ + Code/QRDUtils.cpp HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ 3rdparty/toolwindowmanager/ToolWindowManagerArea.h \ @@ -118,7 +119,8 @@ HEADERS += 3rdparty/toolwindowmanager/ToolWindowManager.h \ Widgets/ThumbnailStrip.h \ Widgets/TextureGoto.h \ Widgets/RangeHistogram.h \ - Windows/Dialogs/TextureSaveDialog.h + Windows/Dialogs/TextureSaveDialog.h \ + Code/QRDUtils.h FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/MainWindow.ui \ diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index aad42e6b6..4f3dcd1a8 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -278,10 +278,12 @@ + + @@ -330,6 +332,7 @@ + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index eab69ddde..d3b8d5cca 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -169,6 +169,12 @@ Generated Files + + Code + + + Generated Files + @@ -246,6 +252,9 @@ Generated Files + + Code +