From 6e665a6a41134fa7081209f56c761604a58b247f Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 21 May 2021 11:36:17 -0400 Subject: [PATCH] feat: arbitrary view splitting and full view persistence for #100 --- src/hobbits-gui/mainwindow.cpp | 47 ++++------ src/hobbits-gui/mainwindow.h | 4 - src/hobbits-widgets/displayhandle.cpp | 6 ++ src/hobbits-widgets/displayhandle.h | 2 + src/hobbits-widgets/displaysplitter.cpp | 58 +++++++++--- src/hobbits-widgets/displaysplitter.h | 11 ++- src/hobbits-widgets/multidisplaywidget.cpp | 103 ++++++++++++++++++++- src/hobbits-widgets/multidisplaywidget.h | 13 ++- 8 files changed, 188 insertions(+), 56 deletions(-) diff --git a/src/hobbits-gui/mainwindow.cpp b/src/hobbits-gui/mainwindow.cpp index 93d67ae1..d59013a0 100644 --- a/src/hobbits-gui/mainwindow.cpp +++ b/src/hobbits-gui/mainwindow.cpp @@ -39,8 +39,7 @@ MainWindow::MainWindow(QString extraPluginPath, QString configFilePath, QWidget m_bitContainerManager(QSharedPointer(new BitContainerManagerUi())), m_pluginManager(QSharedPointer(new HobbitsPluginManager())), m_pluginActionManager(new PluginActionManager(m_pluginManager)), - m_previewScroll(new PreviewScrollBar()), - m_splitViewMenu(new QMenu("Split View")) + m_previewScroll(new PreviewScrollBar()) { m_pluginActionManager->setContainerManager(m_bitContainerManager); ui->setupUi(this); @@ -56,8 +55,6 @@ MainWindow::MainWindow(QString extraPluginPath, QString configFilePath, QWidget ui->menu_View->addAction(ui->dock_bitContainerSelect->toggleViewAction()); ui->menu_View->addAction(ui->dock_operatorPlugins->toggleViewAction()); ui->menu_View->addAction(ui->dock_findBits->toggleViewAction()); - ui->menu_View->addSeparator(); - ui->menu_View->addMenu(m_splitViewMenu); ui->dock_bitContainerSelect->setContentsMargins(0, 0, 0, 0); ui->dock_operatorPlugins->setContentsMargins(0, 0, 0, 0); @@ -215,6 +212,9 @@ MainWindow::MainWindow(QString extraPluginPath, QString configFilePath, QWidget MainWindow::~MainWindow() { + // release the offset controls from DisplayHandle before deleting UI + m_displayHandle->deactivate(); + delete ui; } @@ -228,7 +228,7 @@ void MainWindow::closeEvent(QCloseEvent *event) SettingsManager::setPrivateSetting(BATCH_EDITOR_SIZE_KEY, m_batchEditor->size()); SettingsManager::setPrivateSetting(BATCH_EDITOR_POSITION_KEY, m_batchEditor->pos()); - QByteArray splitDisplayConfig = m_rootDisplay->getConfig(); + QByteArray splitDisplayConfig = m_rootDisplay->saveState(); SettingsManager::setPrivateSetting(SPLIT_DISPLAY_CONFIG_KEY, splitDisplayConfig); event->accept(); @@ -243,34 +243,19 @@ void MainWindow::initializeDisplays() // Add display splits if they were saved in the config QVariant savedConfig = SettingsManager::getPrivateSetting(SPLIT_DISPLAY_CONFIG_KEY); if (!savedConfig.isNull() && savedConfig.canConvert()) { - m_rootDisplay->applyConfig(savedConfig.toByteArray()); + m_rootDisplay->restoreState(savedConfig.toByteArray()); } - setupSplitViewMenu(); -} - -void MainWindow::setupSplitViewMenu() -{ - m_splitViewMenu->clear(); - - // if (m_displayWidgets.size() < 5) { - // m_splitViewMenu->addAction( - // "Add split view", - // [this]() { - // this->addDisplayGroup(); - // })->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V)); - // } - - // for (int i = 1; i < m_displayWidgets.size(); i++) { - // QAction *remove = m_splitViewMenu->addAction( - // QString("Remove split view %1").arg(i + 1), - // [this, i]() { - // this->removeDisplayGroup(i); - // }); - // if (i == m_displayWidgets.size() - 1) { - // remove->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_X)); - // } - // } + ui->menu_View->addSeparator(); + auto tabShowToggle = ui->menu_View->addAction("Display Tabs", [this](bool show) { + m_rootDisplay->setShowViewSelect(show); + }, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V)); + tabShowToggle->setCheckable(true); + tabShowToggle->setChecked(true); + + ui->menu_View->addAction("Collapse All Displays", [this]() { + m_rootDisplay->unSplit(); + }, QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_X)); } void MainWindow::initializeImporterExporters() diff --git a/src/hobbits-gui/mainwindow.h b/src/hobbits-gui/mainwindow.h index 8abd5195..73fb8956 100644 --- a/src/hobbits-gui/mainwindow.h +++ b/src/hobbits-gui/mainwindow.h @@ -99,8 +99,6 @@ private slots: QPair removal = QPair()); void populateRecentBatchesMenu(QString addition = QString(), QString removal = QString()); - void setupSplitViewMenu(); - void sendBitContainerPreview(); static void processBitPreview(QSharedPointer preview, AbstractParameterEditor* editor); @@ -132,8 +130,6 @@ private slots: PreviewScrollBar *m_previewScroll; BatchEditor *m_batchEditor; - - QMenu *m_splitViewMenu; }; #endif // MAINWINDOW_H diff --git a/src/hobbits-widgets/displayhandle.cpp b/src/hobbits-widgets/displayhandle.cpp index 12f3adc9..f467dea4 100644 --- a/src/hobbits-widgets/displayhandle.cpp +++ b/src/hobbits-widgets/displayhandle.cpp @@ -293,6 +293,12 @@ Range DisplayHandle::renderedRange(DisplayInterface *display) const return m_renderedRangeMap.value(display); } +void DisplayHandle::deactivate() +{ + m_bitOffsetControl = nullptr; + m_frameOffsetControl = nullptr; +} + qint64 DisplayHandle::bitOffsetHover() const { return m_bitOffsetHover; diff --git a/src/hobbits-widgets/displayhandle.h b/src/hobbits-widgets/displayhandle.h index 006e6825..ad84615d 100644 --- a/src/hobbits-widgets/displayhandle.h +++ b/src/hobbits-widgets/displayhandle.h @@ -38,6 +38,8 @@ class HOBBITSWIDGETSSHARED_EXPORT DisplayHandle : public QObject Range renderedRange(DisplayInterface* display) const; + void deactivate(); + Q_SIGNALS: void newBitOffset(qint64 bitOffset); void newFrameOffset(qint64 frameOffset); diff --git a/src/hobbits-widgets/displaysplitter.cpp b/src/hobbits-widgets/displaysplitter.cpp index fd83456b..cd43ff83 100644 --- a/src/hobbits-widgets/displaysplitter.cpp +++ b/src/hobbits-widgets/displaysplitter.cpp @@ -51,8 +51,8 @@ QPair DisplaySplitter::takeSplitWidgets() m_vBox->takeAt(0); QPair pair; - pair.first = qobject_cast(m_splitter->widget(0)); - pair.second = qobject_cast(m_splitter->widget(1)); + pair.first = split1(); + pair.second = split2(); m_splitter->hide(); pair.first->setParent(nullptr); @@ -119,7 +119,7 @@ void DisplaySplitter::unSplit(bool keepSecond) pair.second->deleteLater(); } -QByteArray DisplaySplitter::getConfig() const +QByteArray DisplaySplitter::saveState() const { QByteArray config; QDataStream stream(&config, QIODevice::WriteOnly); @@ -128,21 +128,21 @@ QByteArray DisplaySplitter::getConfig() const QByteArray splitterState = m_splitter->saveState(); stream.writeBytes(splitterState.data(), splitterState.size()); - QByteArray side1 = qobject_cast(m_splitter->widget(0))->getConfig(); + QByteArray side1 = split1()->saveState(); stream.writeBytes(side1.data(), side1.size()); - QByteArray side2 = qobject_cast(m_splitter->widget(1))->getConfig(); + QByteArray side2 = split2()->saveState(); stream.writeBytes(side2.data(), side2.size()); } else { - QByteArray name = m_nonSplitWidget->activeDisplay()->name().toLatin1(); - stream.writeBytes(name.data(), name.size()); + QByteArray displayState = m_nonSplitWidget->saveState(); + stream.writeBytes(displayState.data(), displayState.size()); } return config; } -QByteArray readStreamBytes(QDataStream &stream) { +QByteArray DisplaySplitter::readStreamBytes(QDataStream &stream) { char *readBuf; uint len; stream.readBytes(readBuf, len); @@ -155,7 +155,23 @@ QByteArray readStreamBytes(QDataStream &stream) { return bytes; } -bool DisplaySplitter::applyConfig(QByteArray config) +DisplaySplitter* DisplaySplitter::split1() const +{ + if (!isSplit()) { + return nullptr; + } + return qobject_cast(m_splitter->widget(0)); +} + +DisplaySplitter* DisplaySplitter::split2() const +{ + if (!isSplit()) { + return nullptr; + } + return qobject_cast(m_splitter->widget(1)); +} + +bool DisplaySplitter::restoreState(QByteArray config) { QDataStream stream(config); bool configSplit; @@ -175,7 +191,7 @@ bool DisplaySplitter::applyConfig(QByteArray config) if (side1.isEmpty()) { return false; } - if (!qobject_cast(m_splitter->widget(0))->applyConfig(side1)) { + if (!split1()->restoreState(side1)) { return false; } @@ -183,23 +199,35 @@ bool DisplaySplitter::applyConfig(QByteArray config) if (side2.isEmpty()) { return false; } - if (!qobject_cast(m_splitter->widget(1))->applyConfig(side2)) { + if (!split2()->restoreState(side2)) { return false; } } else { unSplit(); - QByteArray nameBytes = readStreamBytes(stream); - if (nameBytes.isEmpty()) { + QByteArray displayConfig = readStreamBytes(stream); + if (displayConfig.isEmpty()) { + return false; + } + if (!m_nonSplitWidget->restoreState(displayConfig)) { return false; } - QString name = QString::fromLatin1(nameBytes); - m_nonSplitWidget->setActiveDisplay(name); } return true; } +void DisplaySplitter::setShowViewSelect(bool show) +{ + if (isSplit()) { + split1()->setShowViewSelect(show); + split2()->setShowViewSelect(show); + } + else { + m_nonSplitWidget->setShowViewSelect(show); + } +} + void DisplaySplitter::leaveEvent(QEvent *event) { if (m_mousePressing) { diff --git a/src/hobbits-widgets/displaysplitter.h b/src/hobbits-widgets/displaysplitter.h index 71240f1d..562e39f8 100644 --- a/src/hobbits-widgets/displaysplitter.h +++ b/src/hobbits-widgets/displaysplitter.h @@ -25,8 +25,10 @@ class HOBBITSWIDGETSSHARED_EXPORT DisplaySplitter : public QWidget void split(Qt::Orientation orientation); void unSplit(bool keepSecond = false); - QByteArray getConfig() const; - bool applyConfig(QByteArray config); + QByteArray saveState() const; + bool restoreState(QByteArray config); + + void setShowViewSelect(bool show); private: void leaveEvent(QEvent *event) override; @@ -35,6 +37,11 @@ class HOBBITSWIDGETSSHARED_EXPORT DisplaySplitter : public QWidget void mousePressEvent(QMouseEvent *event) override; void mouseReleaseEvent(QMouseEvent *event) override; + static QByteArray readStreamBytes(QDataStream &stream); + + DisplaySplitter* split1() const; + DisplaySplitter* split2() const; + bool m_mousePressing; MultiDisplayWidget* takeNonSplitWidget(); diff --git a/src/hobbits-widgets/multidisplaywidget.cpp b/src/hobbits-widgets/multidisplaywidget.cpp index 8a8477fd..4f60890d 100644 --- a/src/hobbits-widgets/multidisplaywidget.cpp +++ b/src/hobbits-widgets/multidisplaywidget.cpp @@ -1,6 +1,7 @@ #include "multidisplaywidget.h" #include "settingsmanager.h" #include +#include MultiDisplayWidget::MultiDisplayWidget(QSharedPointer pluginManager, QSharedPointer handle, @@ -53,6 +54,15 @@ MultiDisplayWidget::MultiDisplayWidget(QSharedPointer plug }); } +MultiDisplayWidget::~MultiDisplayWidget() +{ + auto activeDisplays = m_handle->activeDisplays(); + for (auto p : m_displayMap.values()) { + activeDisplays.remove(p->display); + } + m_handle->setActiveDisplays(activeDisplays); +} + bool MultiDisplayWidget::setActiveDisplay(QString name) { for (int idx : m_displayMap.keys()) { @@ -65,7 +75,7 @@ bool MultiDisplayWidget::setActiveDisplay(QString name) return false; } -QSharedPointer MultiDisplayWidget::activeDisplay() +QSharedPointer MultiDisplayWidget::activeDisplay() const { auto parts = m_displayMap.value(m_tabs->currentIndex()); if (parts.isNull()) { @@ -74,7 +84,7 @@ QSharedPointer MultiDisplayWidget::activeDisplay() return parts->interface; } -DisplayWidget* MultiDisplayWidget::activeDisplayWidget() +DisplayWidget* MultiDisplayWidget::activeDisplayWidget() const { auto parts = m_displayMap.value(m_tabs->currentIndex()); if (parts.isNull()) { @@ -83,6 +93,94 @@ DisplayWidget* MultiDisplayWidget::activeDisplayWidget() return parts->display; } +QByteArray MultiDisplayWidget::saveState() const +{ + QByteArray config; + QDataStream stream(&config, QIODevice::WriteOnly); + + QByteArray activeName = activeDisplay()->name().toLatin1(); + stream.writeBytes(activeName.data(), activeName.size()); + + QByteArray splitterState = m_splitter->saveState(); + stream.writeBytes(splitterState.data(), splitterState.size()); + + auto params = activeDisplayWidget()->displayParameters(); + bool hasParams = !params.isNull(); + stream << hasParams; + if (hasParams) { + QJsonDocument jsonParams(params.values()); + stream << jsonParams.toJson(QJsonDocument::Compact); + } + + return config; +} + +QByteArray MultiDisplayWidget::readStreamBytes(QDataStream &stream) { + char *readBuf; + uint len; + stream.readBytes(readBuf, len); + if (len < 1) { + stream.setStatus(QDataStream::Status::ReadCorruptData); + return QByteArray(); + } + QByteArray bytes(readBuf, len); + delete [] readBuf; + return bytes; +} + +bool MultiDisplayWidget::restoreState(QByteArray config) +{ + QDataStream stream(config); + + QByteArray activeDisplayBytes = readStreamBytes(stream); + if (activeDisplayBytes.isEmpty()) { + return false; + } + if (!setActiveDisplay(QString::fromLatin1(activeDisplayBytes))) { + // failed to load this display, but stream is still ok + return true; + } + + QByteArray splitterState = readStreamBytes(stream); + if (splitterState.isEmpty()) { + return false; + } + if (!m_splitter->restoreState(splitterState)) { + return false; + } + + bool hasParams; + stream >> hasParams; + if (hasParams) { + QByteArray paramBytes = readStreamBytes(stream); + if (paramBytes.isEmpty()) { + return false; + } + auto jsonParams = QJsonDocument::fromJson(paramBytes); + Parameters params(jsonParams.object()); + + if (m_splitter->count() > 1) { + auto editor = qobject_cast(m_splitter->widget(1)); + editor->setParameters(params); + } + else { + activeDisplayWidget()->setDisplayParameters(params); + } + } + + return true; +} + +void MultiDisplayWidget::setShowViewSelect(bool show) +{ + if (show) { + m_tabs->tabBar()->show(); + } + else { + m_tabs->tabBar()->hide(); + } +} + void MultiDisplayWidget::activateCurrentDisplay() { auto parts = m_displayMap.value(m_tabs->currentIndex()); @@ -135,6 +233,7 @@ AbstractParameterEditor* MultiDisplayWidget::DisplayParts::createEditor() if (editor == nullptr) { return nullptr; } + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); if (this->display->displayParameters().isNull()) { this->display->setDisplayParameters(editor->parameters()); diff --git a/src/hobbits-widgets/multidisplaywidget.h b/src/hobbits-widgets/multidisplaywidget.h index 2c691282..d1f58792 100644 --- a/src/hobbits-widgets/multidisplaywidget.h +++ b/src/hobbits-widgets/multidisplaywidget.h @@ -14,15 +14,24 @@ class HOBBITSWIDGETSSHARED_EXPORT MultiDisplayWidget : public QWidget explicit MultiDisplayWidget(QSharedPointer pluginManager, QSharedPointer handle, QWidget *parent = nullptr); + + ~MultiDisplayWidget(); bool setActiveDisplay(QString name); - QSharedPointer activeDisplay(); - DisplayWidget* activeDisplayWidget(); + QSharedPointer activeDisplay() const; + DisplayWidget* activeDisplayWidget() const; + + QByteArray saveState() const; + bool restoreState(QByteArray config); + + void setShowViewSelect(bool show); public slots: void activateCurrentDisplay(); private: + static QByteArray readStreamBytes(QDataStream &stream); + class DisplayParts { public: static QSharedPointer create(QSharedPointer interface,