diff --git a/gui/include/DragDropHelper.h b/gui/include/DragAndDrop/DragDropHelper.h similarity index 83% rename from gui/include/DragDropHelper.h rename to gui/include/DragAndDrop/DragDropHelper.h index f33fdba..c557cb5 100644 --- a/gui/include/DragDropHelper.h +++ b/gui/include/DragAndDrop/DragDropHelper.h @@ -7,6 +7,7 @@ class QVBoxLayout; class QScrollArea; +class QTabBar; class VisualizationDragWidget; class VisualizationDragDropContainer; class QMimeData; @@ -54,6 +55,9 @@ public: void addDragDropScrollArea(QScrollArea *scrollArea); void removeDragDropScrollArea(QScrollArea *scrollArea); + void addDragDropTabBar(QTabBar *tabBar); + void removeDragDropTabBar(QTabBar *tabBar); + QUrl imageTemporaryUrl(const QImage &image) const; void setHightlightedDragWidget(VisualizationDragWidget *dragWidget); @@ -64,28 +68,4 @@ private: spimpl::unique_impl_ptr impl; }; -/** - * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. - * @note A QScrollArea inside an other QScrollArea is not fully supported. - */ -class DragDropScroller : public QObject { - Q_OBJECT - -public: - DragDropScroller(QObject *parent = nullptr); - - void addScrollArea(QScrollArea *scrollArea); - void removeScrollArea(QScrollArea *scrollArea); - -protected: - bool eventFilter(QObject *obj, QEvent *event); - -private: - class DragDropScrollerPrivate; - spimpl::unique_impl_ptr impl; - -private slots: - void onTimer(); -}; - #endif // SCIQLOP_DRAGDROPHELPER_H diff --git a/gui/include/DragAndDrop/DragDropScroller.h b/gui/include/DragAndDrop/DragDropScroller.h new file mode 100644 index 0000000..eb84baf --- /dev/null +++ b/gui/include/DragAndDrop/DragDropScroller.h @@ -0,0 +1,32 @@ +#ifndef SCIQLOP_DRAGDROPSCROLLER_H +#define SCIQLOP_DRAGDROPSCROLLER_H + +#include +#include + +/** + * @brief Event filter class which manage the scroll of QScrollArea during a drag&drop operation. + * @note A QScrollArea inside an other QScrollArea is not fully supported. + */ +class DragDropScroller : public QObject { + Q_OBJECT + +public: + DragDropScroller(QObject *parent = nullptr); + + void addScrollArea(QScrollArea *scrollArea); + void removeScrollArea(QScrollArea *scrollArea); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + class DragDropScrollerPrivate; + spimpl::unique_impl_ptr impl; + +private slots: + void onTimer(); +}; + + +#endif // SCIQLOP_DRAGDROPSCROLLER_H diff --git a/gui/include/DragAndDrop/DragDropTabSwitcher.h b/gui/include/DragAndDrop/DragDropTabSwitcher.h new file mode 100644 index 0000000..1665d46 --- /dev/null +++ b/gui/include/DragAndDrop/DragDropTabSwitcher.h @@ -0,0 +1,29 @@ +#ifndef SCIQLOP_DRAGDROPTABSWITCHER_H +#define SCIQLOP_DRAGDROPTABSWITCHER_H + +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(LOG_DragDropTabSwitcher) + +class DragDropTabSwitcher : public QObject { + Q_OBJECT + +public: + DragDropTabSwitcher(QObject *parent = nullptr); + + void addTabBar(QTabBar *tabBar); + void removeTabBar(QTabBar *tabBar); + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +private: + class DragDropTabSwitcherPrivate; + spimpl::unique_impl_ptr impl; +}; + + +#endif // SCIQLOP_DRAGDROPTABSWITCHER_H diff --git a/gui/include/Visualization/VisualizationDragDropContainer.h b/gui/include/Visualization/VisualizationDragDropContainer.h index 65e630b..d100bff 100644 --- a/gui/include/Visualization/VisualizationDragDropContainer.h +++ b/gui/include/Visualization/VisualizationDragDropContainer.h @@ -9,7 +9,7 @@ #include -#include +#include Q_DECLARE_LOGGING_CATEGORY(LOG_VisualizationDragDropContainer) diff --git a/gui/meson.build b/gui/meson.build index 9da44ab..6520ba3 100644 --- a/gui/meson.build +++ b/gui/meson.build @@ -6,7 +6,9 @@ gui_moc_headers = [ 'include/Settings/SqpSettingsGeneralWidget.h', 'include/SidePane/SqpSidePane.h', 'include/SqpApplication.h', - 'include/DragDropHelper.h', + 'include/DragAndDrop/DragDropHelper.h', + 'include/DragAndDrop/DragDropScroller.h', + 'include/DragAndDrop/DragDropTabSwitcher.h', 'include/TimeWidget/TimeWidget.h', 'include/Variable/VariableInspectorWidget.h', 'include/Variable/VariableInspectorTableView.h', @@ -43,7 +45,9 @@ gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers, gui_sources = [ 'src/SqpApplication.cpp', - 'src/DragDropHelper.cpp', + 'src/DragAndDrop/DragDropHelper.cpp', + 'src/DragAndDrop/DragDropScroller.cpp', + 'src/DragAndDrop/DragDropTabSwitcher.cpp', 'src/Common/ColorUtils.cpp', 'src/Common/VisualizationDef.cpp', 'src/DataSource/DataSourceTreeWidgetItem.cpp', diff --git a/gui/src/DataSource/DataSourceTreeWidget.cpp b/gui/src/DataSource/DataSourceTreeWidget.cpp index 97e62b1..24f7f00 100644 --- a/gui/src/DataSource/DataSourceTreeWidget.cpp +++ b/gui/src/DataSource/DataSourceTreeWidget.cpp @@ -4,7 +4,7 @@ #include "DataSource/DataSourceItem.h" #include "DataSource/DataSourceTreeWidgetItem.h" -#include "DragDropHelper.h" +#include "DragAndDrop/DragDropHelper.h" #include "SqpApplication.h" #include diff --git a/gui/src/DragAndDrop/DragDropHelper.cpp b/gui/src/DragAndDrop/DragDropHelper.cpp new file mode 100644 index 0000000..dd2b000 --- /dev/null +++ b/gui/src/DragAndDrop/DragDropHelper.cpp @@ -0,0 +1,275 @@ +#include "DragAndDrop/DragDropHelper.h" +#include "DragAndDrop/DragDropScroller.h" +#include "DragAndDrop/DragDropTabSwitcher.h" +#include "SqpApplication.h" +#include "Visualization/VisualizationDragDropContainer.h" +#include "Visualization/VisualizationDragWidget.h" +#include "Visualization/VisualizationWidget.h" +#include "Visualization/operations/FindVariableOperation.h" + +#include "Variable/Variable.h" +#include "Variable/VariableController.h" + +#include "Common/MimeTypesDef.h" +#include "Common/VisualizationDef.h" + +#include +#include +#include +#include + + +Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDropHelper") + + +struct DragDropHelper::DragDropHelperPrivate { + + VisualizationDragWidget *m_CurrentDragWidget = nullptr; + std::unique_ptr m_PlaceHolder = nullptr; + QLabel *m_PlaceHolderLabel; + QWidget *m_PlaceBackground; + std::unique_ptr m_DragDropScroller = nullptr; + std::unique_ptr m_DragDropTabSwitcher = nullptr; + QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using + // QTemporaryFile to have a name which is not generated. + + VisualizationDragWidget *m_HighlightedDragWidget = nullptr; + + QMetaObject::Connection m_DragWidgetDestroyedConnection; + QMetaObject::Connection m_HighlightedWidgetDestroyedConnection; + + explicit DragDropHelperPrivate() + : m_PlaceHolder{std::make_unique()}, + m_DragDropScroller{std::make_unique()}, + m_DragDropTabSwitcher{std::make_unique()} + { + + auto layout = new QVBoxLayout{m_PlaceHolder.get()}; + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()}; + m_PlaceHolderLabel->setMinimumHeight(25); + layout->addWidget(m_PlaceHolderLabel); + + m_PlaceBackground = new QWidget{m_PlaceHolder.get()}; + m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + layout->addWidget(m_PlaceBackground); + + sqpApp->installEventFilter(m_DragDropScroller.get()); + sqpApp->installEventFilter(m_DragDropTabSwitcher.get()); + + m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png"); + } + + void preparePlaceHolder(DragDropHelper::PlaceHolderType type, const QString &topLabelText) const + { + if (m_CurrentDragWidget) { + m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size()); + m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy()); + } + else { + // Configuration of the placeHolder when there is no dragWidget + // (for instance with a drag from a variable) + + m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT); + m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + } + + switch (type) { + case DragDropHelper::PlaceHolderType::Graph: + m_PlaceBackground->setStyleSheet( + "background-color: #BBD5EE; border: 1px solid #2A7FD4"); + break; + case DragDropHelper::PlaceHolderType::Zone: + case DragDropHelper::PlaceHolderType::Default: + m_PlaceBackground->setStyleSheet( + "background-color: #BBD5EE; border: 2px solid #2A7FD4"); + m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4"); + break; + } + + m_PlaceHolderLabel->setText(topLabelText); + m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty()); + } +}; + + +DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl()} {} + +DragDropHelper::~DragDropHelper() +{ + QFile::remove(impl->m_ImageTempUrl); +} + +void DragDropHelper::resetDragAndDrop() +{ + setCurrentDragWidget(nullptr); + impl->m_HighlightedDragWidget = nullptr; +} + +void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget) +{ + if (impl->m_CurrentDragWidget) { + + QObject::disconnect(impl->m_DragWidgetDestroyedConnection); + } + + if (dragWidget) { + // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed + impl->m_DragWidgetDestroyedConnection + = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, + [this]() { impl->m_CurrentDragWidget = nullptr; }); + } + + impl->m_CurrentDragWidget = dragWidget; +} + +VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const +{ + return impl->m_CurrentDragWidget; +} + +QWidget &DragDropHelper::placeHolder() const +{ + return *impl->m_PlaceHolder; +} + +void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type, + const QString &topLabelText) +{ + removePlaceHolder(); + impl->preparePlaceHolder(type, topLabelText); + layout->insertWidget(index, impl->m_PlaceHolder.get()); + impl->m_PlaceHolder->show(); +} + +void DragDropHelper::removePlaceHolder() +{ + auto parentWidget = impl->m_PlaceHolder->parentWidget(); + if (parentWidget) { + parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get()); + impl->m_PlaceHolder->setParent(nullptr); + impl->m_PlaceHolder->hide(); + } +} + +bool DragDropHelper::isPlaceHolderSet() const +{ + return impl->m_PlaceHolder->parentWidget(); +} + +void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea) +{ + impl->m_DragDropScroller->addScrollArea(scrollArea); +} + +void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea) +{ + impl->m_DragDropScroller->removeScrollArea(scrollArea); +} + +void DragDropHelper::addDragDropTabBar(QTabBar *tabBar) +{ + impl->m_DragDropTabSwitcher->addTabBar(tabBar); +} + +void DragDropHelper::removeDragDropTabBar(QTabBar *tabBar) +{ + impl->m_DragDropTabSwitcher->removeTabBar(tabBar); +} + +QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const +{ + image.save(impl->m_ImageTempUrl); + return QUrl::fromLocalFile(impl->m_ImageTempUrl); +} + +void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget) +{ + if (impl->m_HighlightedDragWidget) { + impl->m_HighlightedDragWidget->highlightForMerge(false); + QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection); + } + + if (dragWidget) { + dragWidget->highlightForMerge(true); + + // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed + impl->m_DragWidgetDestroyedConnection + = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, + [this]() { impl->m_HighlightedDragWidget = nullptr; }); + } + + impl->m_HighlightedDragWidget = dragWidget; +} + +VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const +{ + return impl->m_HighlightedDragWidget; +} + +bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData, + VisualizationDragDropContainer *dropContainer) +{ + if (!mimeData || !dropContainer) { + qCWarning(LOG_DragDropHelper()) << QObject::tr( + "DragDropHelper::checkMimeDataForVisualization, invalid input parameters."); + Q_ASSERT(false); + return false; + } + + auto result = false; + + if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { + auto variables = sqpApp->variableController().variablesForMimeData( + mimeData->data(MIME_TYPE_VARIABLE_LIST)); + + if (variables.count() == 1) { + + auto variable = variables.first(); + if (variable->dataSeries() != nullptr) { + + // Check that the variable is not already in a graph + + auto parent = dropContainer->parentWidget(); + while (parent && qobject_cast(parent) == nullptr) { + parent = parent->parentWidget(); // Search for the top level VisualizationWidget + } + + if (parent) { + auto visualizationWidget = static_cast(parent); + + FindVariableOperation findVariableOperation{variable}; + visualizationWidget->accept(&findVariableOperation); + auto variableContainers = findVariableOperation.result(); + if (variableContainers.empty()) { + result = true; + } + else { + // result = false: the variable already exist in the visualisation + } + } + else { + qCWarning(LOG_DragDropHelper()) << QObject::tr( + "DragDropHelper::checkMimeDataForVisualization, the parent " + "VisualizationWidget cannot be found. Cannot check if the variable is " + "already used or not."); + } + } + else { + // result = false: the variable is not fully loaded + } + } + else { + // result = false: cannot drop multiple variables in the visualisation + } + } + else { + // Other MIME data + // no special rules, accepted by default + result = true; + } + + return result; +} diff --git a/gui/src/DragDropHelper.cpp b/gui/src/DragAndDrop/DragDropScroller.cpp similarity index 55% rename from gui/src/DragDropHelper.cpp rename to gui/src/DragAndDrop/DragDropScroller.cpp index 046d142..0e8ac8e 100644 --- a/gui/src/DragDropHelper.cpp +++ b/gui/src/DragAndDrop/DragDropScroller.cpp @@ -1,30 +1,13 @@ -#include "DragDropHelper.h" -#include "SqpApplication.h" -#include "Visualization/VisualizationDragDropContainer.h" -#include "Visualization/VisualizationDragWidget.h" -#include "Visualization/VisualizationWidget.h" -#include "Visualization/operations/FindVariableOperation.h" +#include "DragAndDrop/DragDropScroller.h" -#include "Variable/Variable.h" -#include "Variable/VariableController.h" - -#include "Common/MimeTypesDef.h" -#include "Common/VisualizationDef.h" - -#include #include #include -#include -#include #include #include -#include const int SCROLL_SPEED = 5; const int SCROLL_ZONE_SIZE = 50; -Q_LOGGING_CATEGORY(LOG_DragDropHelper, "DragDrophelper") - struct DragDropScroller::DragDropScrollerPrivate { QList m_ScrollAreas; @@ -140,244 +123,3 @@ void DragDropScroller::onTimer() impl->m_CurrentScrollArea->verticalScrollBar()->value() + mvt); } } - -struct DragDropHelper::DragDropHelperPrivate { - - VisualizationDragWidget *m_CurrentDragWidget = nullptr; - std::unique_ptr m_PlaceHolder = nullptr; - QLabel *m_PlaceHolderLabel; - QWidget *m_PlaceBackground; - std::unique_ptr m_DragDropScroller = nullptr; - QString m_ImageTempUrl; // Temporary file for image url generated by the drag & drop. Not using - // QTemporaryFile to have a name which is not generated. - - VisualizationDragWidget *m_HighlightedDragWidget = nullptr; - - QMetaObject::Connection m_DragWidgetDestroyedConnection; - QMetaObject::Connection m_HighlightedWidgetDestroyedConnection; - - explicit DragDropHelperPrivate() - : m_PlaceHolder{std::make_unique()}, - m_DragDropScroller{std::make_unique()} - { - - auto layout = new QVBoxLayout{m_PlaceHolder.get()}; - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - - m_PlaceHolderLabel = new QLabel{"", m_PlaceHolder.get()}; - m_PlaceHolderLabel->setMinimumHeight(25); - layout->addWidget(m_PlaceHolderLabel); - - m_PlaceBackground = new QWidget{m_PlaceHolder.get()}; - m_PlaceBackground->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - layout->addWidget(m_PlaceBackground); - - sqpApp->installEventFilter(m_DragDropScroller.get()); - - m_ImageTempUrl = QDir::temp().absoluteFilePath("Sciqlop_graph.png"); - } - - void preparePlaceHolder(DragDropHelper::PlaceHolderType type, const QString &topLabelText) const - { - if (m_CurrentDragWidget) { - m_PlaceHolder->setMinimumSize(m_CurrentDragWidget->size()); - m_PlaceHolder->setSizePolicy(m_CurrentDragWidget->sizePolicy()); - } - else { - // Configuration of the placeHolder when there is no dragWidget - // (for instance with a drag from a variable) - - m_PlaceHolder->setMinimumSize(0, GRAPH_MINIMUM_HEIGHT); - m_PlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - } - - switch (type) { - case DragDropHelper::PlaceHolderType::Graph: - m_PlaceBackground->setStyleSheet( - "background-color: #BBD5EE; border: 1px solid #2A7FD4"); - break; - case DragDropHelper::PlaceHolderType::Zone: - case DragDropHelper::PlaceHolderType::Default: - m_PlaceBackground->setStyleSheet( - "background-color: #BBD5EE; border: 2px solid #2A7FD4"); - m_PlaceHolderLabel->setStyleSheet("color: #2A7FD4"); - break; - } - - m_PlaceHolderLabel->setText(topLabelText); - m_PlaceHolderLabel->setVisible(!topLabelText.isEmpty()); - } -}; - - -DragDropHelper::DragDropHelper() : impl{spimpl::make_unique_impl()} -{ -} - -DragDropHelper::~DragDropHelper() -{ - QFile::remove(impl->m_ImageTempUrl); -} - -void DragDropHelper::resetDragAndDrop() -{ - setCurrentDragWidget(nullptr); - impl->m_HighlightedDragWidget = nullptr; -} - -void DragDropHelper::setCurrentDragWidget(VisualizationDragWidget *dragWidget) -{ - if (impl->m_CurrentDragWidget) { - - QObject::disconnect(impl->m_DragWidgetDestroyedConnection); - } - - if (dragWidget) { - // ensures the impl->m_CurrentDragWidget is reset when the widget is destroyed - impl->m_DragWidgetDestroyedConnection - = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, - [this]() { impl->m_CurrentDragWidget = nullptr; }); - } - - impl->m_CurrentDragWidget = dragWidget; -} - -VisualizationDragWidget *DragDropHelper::getCurrentDragWidget() const -{ - return impl->m_CurrentDragWidget; -} - -QWidget &DragDropHelper::placeHolder() const -{ - return *impl->m_PlaceHolder; -} - -void DragDropHelper::insertPlaceHolder(QVBoxLayout *layout, int index, PlaceHolderType type, - const QString &topLabelText) -{ - removePlaceHolder(); - impl->preparePlaceHolder(type, topLabelText); - layout->insertWidget(index, impl->m_PlaceHolder.get()); - impl->m_PlaceHolder->show(); -} - -void DragDropHelper::removePlaceHolder() -{ - auto parentWidget = impl->m_PlaceHolder->parentWidget(); - if (parentWidget) { - parentWidget->layout()->removeWidget(impl->m_PlaceHolder.get()); - impl->m_PlaceHolder->setParent(nullptr); - impl->m_PlaceHolder->hide(); - } -} - -bool DragDropHelper::isPlaceHolderSet() const -{ - return impl->m_PlaceHolder->parentWidget(); -} - -void DragDropHelper::addDragDropScrollArea(QScrollArea *scrollArea) -{ - impl->m_DragDropScroller->addScrollArea(scrollArea); -} - -void DragDropHelper::removeDragDropScrollArea(QScrollArea *scrollArea) -{ - impl->m_DragDropScroller->removeScrollArea(scrollArea); -} - -QUrl DragDropHelper::imageTemporaryUrl(const QImage &image) const -{ - image.save(impl->m_ImageTempUrl); - return QUrl::fromLocalFile(impl->m_ImageTempUrl); -} - -void DragDropHelper::setHightlightedDragWidget(VisualizationDragWidget *dragWidget) -{ - if (impl->m_HighlightedDragWidget) { - impl->m_HighlightedDragWidget->highlightForMerge(false); - QObject::disconnect(impl->m_HighlightedWidgetDestroyedConnection); - } - - if (dragWidget) { - dragWidget->highlightForMerge(true); - - // ensures the impl->m_HighlightedDragWidget is reset when the widget is destroyed - impl->m_DragWidgetDestroyedConnection - = QObject::connect(dragWidget, &VisualizationDragWidget::destroyed, - [this]() { impl->m_HighlightedDragWidget = nullptr; }); - } - - impl->m_HighlightedDragWidget = dragWidget; -} - -VisualizationDragWidget *DragDropHelper::getHightlightedDragWidget() const -{ - return impl->m_HighlightedDragWidget; -} - -bool DragDropHelper::checkMimeDataForVisualization(const QMimeData *mimeData, - VisualizationDragDropContainer *dropContainer) -{ - if (!mimeData || !dropContainer) { - qCWarning(LOG_DragDropHelper()) << QObject::tr( - "DragDropHelper::checkMimeDataForVisualization, invalid input parameters."); - Q_ASSERT(false); - return false; - } - - auto result = false; - - if (mimeData->hasFormat(MIME_TYPE_VARIABLE_LIST)) { - auto variables = sqpApp->variableController().variablesForMimeData( - mimeData->data(MIME_TYPE_VARIABLE_LIST)); - - if (variables.count() == 1) { - - auto variable = variables.first(); - if (variable->dataSeries() != nullptr) { - - // Check that the variable is not already in a graph - - auto parent = dropContainer->parentWidget(); - while (parent && qobject_cast(parent) == nullptr) { - parent = parent->parentWidget(); // Search for the top level VisualizationWidget - } - - if (parent) { - auto visualizationWidget = static_cast(parent); - - FindVariableOperation findVariableOperation{variable}; - visualizationWidget->accept(&findVariableOperation); - auto variableContainers = findVariableOperation.result(); - if (variableContainers.empty()) { - result = true; - } - else { - // result = false: the variable already exist in the visualisation - } - } - else { - qCWarning(LOG_DragDropHelper()) << QObject::tr( - "DragDropHelper::checkMimeDataForVisualization, the parent " - "VisualizationWidget cannot be found. Cannot check if the variable is " - "already used or not."); - } - } - else { - // result = false: the variable is not fully loaded - } - } - else { - // result = false: cannot drop multiple variables in the visualisation - } - } - else { - // Other MIME data - // no special rules, accepted by default - result = true; - } - - return result; -} diff --git a/gui/src/DragAndDrop/DragDropTabSwitcher.cpp b/gui/src/DragAndDrop/DragDropTabSwitcher.cpp new file mode 100644 index 0000000..ce42b3a --- /dev/null +++ b/gui/src/DragAndDrop/DragDropTabSwitcher.cpp @@ -0,0 +1,180 @@ +#include "DragAndDrop/DragDropTabSwitcher.h" + +#include +#include +#include +#include + +#include "SqpApplication.h" + +Q_LOGGING_CATEGORY(LOG_DragDropTabSwitcher, "DragDropTabSwitcher") + +const int CHANGE_TAB_INTERVAL = 400; // time necessary over a tab to accept the switch +const int SCROLL_BUTTON_AUTO_CLICK_INTERVAL + = 500; // time between 2 auto clicks on a scroll button of the tab bar + +struct DragDropTabSwitcher::DragDropTabSwitcherPrivate { + + QList m_TabBarList; + QTabBar *m_CurrentTabBar = nullptr; + + int m_HoveredTabIndex = -1; + std::unique_ptr m_TabSwitchTimer = nullptr; + + QAbstractButton *m_HoveredScrollButton = nullptr; + std::unique_ptr m_ScrollButtonsTimer = nullptr; + + explicit DragDropTabSwitcherPrivate() + : m_TabSwitchTimer{std::make_unique()}, + m_ScrollButtonsTimer{std::make_unique()} + { + m_TabSwitchTimer->setSingleShot(true); + m_TabSwitchTimer->setInterval(CHANGE_TAB_INTERVAL); + QObject::connect(m_TabSwitchTimer.get(), &QTimer::timeout, [this]() { + if (m_CurrentTabBar) { + m_CurrentTabBar->setCurrentIndex(m_HoveredTabIndex); + } + else { + qCWarning(LOG_DragDropTabSwitcher()) << "DragDropTabSwitcherPrivate::timeout: " + "Cannot select a new tab: unknown current " + "tab bar."; + } + }); + + m_ScrollButtonsTimer->setInterval(SCROLL_BUTTON_AUTO_CLICK_INTERVAL); + QObject::connect(m_ScrollButtonsTimer.get(), &QTimer::timeout, [this]() { + if (m_HoveredScrollButton) { + m_HoveredScrollButton->animateClick(); + } + else { + qCWarning(LOG_DragDropTabSwitcher()) + << "DragDropTabSwitcherPrivate::timeoutScroll: " + "Unknown scroll button"; + } + }); + } + + bool isScrollTabButton(QAbstractButton *button, QTabBar *tabBar) + { + auto isNextOrPreviousTabButton = true; + + if (tabBar->isAncestorOf(button)) { + for (auto i = 0; i < tabBar->count(); ++i) { + if (tabBar->tabButton(i, QTabBar::RightSide) == button + || tabBar->tabButton(i, QTabBar::LeftSide) == button) { + isNextOrPreviousTabButton = false; + break; + } + } + } + else { + isNextOrPreviousTabButton = false; + } + + return isNextOrPreviousTabButton; + } + + QAbstractButton *tabScrollButtonAt(const QPoint &pos, QTabBar *tabBar) + { + + auto globalPos = tabBar->mapToGlobal(pos); + + auto widgetUnderMouse = sqpApp->widgetAt(globalPos); + if (auto btn = qobject_cast(widgetUnderMouse)) { + + if (isScrollTabButton(btn, tabBar)) { + return btn; + } + } + + return nullptr; + } +}; + +DragDropTabSwitcher::DragDropTabSwitcher(QObject *parent) + : QObject(parent), impl{spimpl::make_unique_impl()} +{ +} + +void DragDropTabSwitcher::addTabBar(QTabBar *tabBar) +{ + impl->m_TabBarList << tabBar; + tabBar->setAcceptDrops(true); +} + +void DragDropTabSwitcher::removeTabBar(QTabBar *tabBar) +{ + impl->m_TabBarList.removeAll(tabBar); + tabBar->setAcceptDrops(false); +} + +bool DragDropTabSwitcher::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::DragMove) { + + if (impl->m_CurrentTabBar) { + + QWidget *w = static_cast(obj); + if (!impl->m_CurrentTabBar->isAncestorOf(w)) { + return false; + } + + auto moveEvent = static_cast(event); + + auto scrollButton = impl->tabScrollButtonAt(moveEvent->pos(), impl->m_CurrentTabBar); + + if (!scrollButton) { + + auto tabIndex = impl->m_CurrentTabBar->tabAt(moveEvent->pos()); + if (tabIndex >= 0 && tabIndex != impl->m_CurrentTabBar->currentIndex()) { + // The mouse is over an unselected tab + if (!impl->m_TabSwitchTimer->isActive() + || tabIndex != impl->m_HoveredTabIndex) { + impl->m_HoveredTabIndex = tabIndex; + impl->m_TabSwitchTimer->start(); + } + else { + // do nothing, timer already running + } + } + else { + impl->m_TabSwitchTimer->stop(); + } + + impl->m_ScrollButtonsTimer->stop(); + } + else { + // The mouse is over a scroll button + // click it in a loop with a timer + if (!impl->m_ScrollButtonsTimer->isActive() + || impl->m_HoveredScrollButton != scrollButton) { + impl->m_HoveredScrollButton = scrollButton; + impl->m_ScrollButtonsTimer->start(); + } + } + } + } + else if (event->type() == QEvent::DragEnter) { + QWidget *w = static_cast(obj); + + for (auto tabBar : impl->m_TabBarList) { + if (w == tabBar) { + auto enterEvent = static_cast(event); + enterEvent->acceptProposedAction(); + enterEvent->setDropAction(Qt::IgnoreAction); + impl->m_CurrentTabBar = tabBar; + break; + } + } + } + else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) { + if (impl->m_CurrentTabBar) { + impl->m_HoveredTabIndex = -1; + impl->m_TabSwitchTimer->stop(); + impl->m_CurrentTabBar = nullptr; + impl->m_ScrollButtonsTimer->stop(); + } + } + + return false; +} diff --git a/gui/src/SqpApplication.cpp b/gui/src/SqpApplication.cpp index 7b200c1..aa0e813 100644 --- a/gui/src/SqpApplication.cpp +++ b/gui/src/SqpApplication.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include #include