#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; }