summaryrefslogtreecommitdiffstats
path: root/ui/qt/widgets/detachable_tabwidget.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/qt/widgets/detachable_tabwidget.cpp')
-rw-r--r--ui/qt/widgets/detachable_tabwidget.cpp212
1 files changed, 212 insertions, 0 deletions
diff --git a/ui/qt/widgets/detachable_tabwidget.cpp b/ui/qt/widgets/detachable_tabwidget.cpp
new file mode 100644
index 00000000..caffd743
--- /dev/null
+++ b/ui/qt/widgets/detachable_tabwidget.cpp
@@ -0,0 +1,212 @@
+/* @file
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <ui/qt/widgets/detachable_tabwidget.h>
+
+#include <QStackedWidget>
+#include <QBoxLayout>
+#include <QEvent>
+#include <QCloseEvent>
+#include <QMouseEvent>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QMimeData>
+#include <QStringList>
+#include <QApplication>
+#include <QDrag>
+#include <QPixmap>
+#include <QPainter>
+
+DetachableTabWidget::DetachableTabWidget(QWidget *parent) :
+ QTabWidget(parent)
+{
+ DragDropTabBar * tabBar = new DragDropTabBar(this);
+ connect(tabBar, &DragDropTabBar::onDetachTab, this, &DetachableTabWidget::detachTab);
+ connect(tabBar, &DragDropTabBar::onMoveTab, this, &DetachableTabWidget::moveTab);
+
+ setMovable(false);
+
+ setTabBar(tabBar);
+}
+
+void DetachableTabWidget::setTabBasename(QString newName) {
+ _tabBasename = newName;
+}
+
+QString DetachableTabWidget::tabBasename() const {
+ return _tabBasename;
+}
+
+void DetachableTabWidget::moveTab(int from, int to)
+{
+ QWidget * contentWidget = widget(from);
+ QString text = tabText(from);
+
+ removeTab(from);
+ insertTab(to, contentWidget, text);
+ setCurrentIndex(to);
+}
+
+void DetachableTabWidget::detachTab(int tabIdx, QPoint pos)
+{
+ QString name = tabText(tabIdx);
+
+ QWidget * contentWidget = widget(tabIdx);
+
+ /* For the widget to properly show in the dialog, it has to be
+ * removed properly and unhidden. QTabWidget uses a QStackedWidget for
+ * all parents of widgets. So we remove it from it's own parent and then
+ * unhide it to show the widget in the dialog */
+ QStackedWidget * par = qobject_cast<QStackedWidget *>(contentWidget->parent());
+ if (!par)
+ return;
+ QRect contentWidgetRect = par->frameGeometry();
+ par->removeWidget(contentWidget);
+ contentWidget->setHidden(false);
+
+ ToolDialog * detachedTab = new ToolDialog(contentWidget, parentWidget());
+ detachedTab->setWindowModality(Qt::NonModal);
+ detachedTab->setWindowTitle(_tabBasename + ": " + name);
+ detachedTab->setObjectName(name);
+ detachedTab->setGeometry(contentWidgetRect);
+ connect(detachedTab, &ToolDialog::onCloseSignal, this, &DetachableTabWidget::attachTab);
+ detachedTab->move(pos);
+ detachedTab->show();
+}
+
+void DetachableTabWidget::attachTab(QWidget * content, QString name)
+{
+ content->setParent(this);
+
+ int index = addTab(content, name);
+ if (index > -1)
+ setCurrentIndex(index);
+}
+
+ToolDialog::ToolDialog(QWidget *contentWidget, QWidget *parent, Qt::WindowFlags f) :
+ QDialog(parent, f)
+{
+ _contentWidget = contentWidget;
+
+ _contentWidget->setParent(this);
+ QVBoxLayout * layout = new QVBoxLayout(this);
+ layout->addWidget(_contentWidget);
+ this->setLayout(layout);
+}
+
+bool ToolDialog::event(QEvent *event)
+{
+ /**
+ * Capture a double click event on the dialog's window frame
+ */
+ if (event->type() == QEvent::NonClientAreaMouseButtonDblClick) {
+ event->accept();
+ close();
+ }
+
+ return QDialog::event(event);
+}
+
+void ToolDialog::closeEvent(QCloseEvent * /*event*/)
+{
+ emit onCloseSignal(_contentWidget, objectName());
+}
+
+DragDropTabBar::DragDropTabBar(QWidget *parent) :
+ QTabBar(parent)
+{
+ setAcceptDrops(true);
+ setElideMode(Qt::ElideRight);
+ setSelectionBehaviorOnRemove(QTabBar::SelectLeftTab);
+
+ _dragStartPos = QPoint();
+ _dragDropPos = QPoint();
+ _mouseCursor = QCursor();
+ _dragInitiated = false;
+}
+
+void DragDropTabBar::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ event->accept();
+ emit onDetachTab(tabAt(event->pos()), _mouseCursor.pos());
+}
+
+void DragDropTabBar::mousePressEvent(QMouseEvent *event)
+{
+ if (event->button() == Qt::LeftButton)
+ _dragStartPos = event->pos();
+
+ _dragDropPos = QPoint(0, 0);
+ _dragInitiated = false;
+
+ QTabBar::mousePressEvent(event);
+}
+
+void DragDropTabBar::mouseMoveEvent(QMouseEvent *event)
+{
+ if (!_dragStartPos.isNull() &&
+ ((event->pos() - _dragStartPos).manhattanLength() > QApplication::startDragDistance()))
+ _dragInitiated = true;
+
+ if ((event->buttons() & Qt::LeftButton) && _dragInitiated) {
+ QMouseEvent * finishMouseMove = new QMouseEvent(QEvent::MouseMove, event->pos(), QCursor::pos(), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
+ QTabBar::mouseMoveEvent(finishMouseMove);
+
+ QDrag * drag = new QDrag(this);
+ QMimeData * mimeData = new QMimeData();
+ mimeData->setData("action", "application/tab-detach");
+ drag->setMimeData(mimeData);
+
+ QWidget * original = parentWidget();
+ if (qobject_cast<DetachableTabWidget *>(original)) {
+ DetachableTabWidget * tabWidget = qobject_cast<DetachableTabWidget *>(original);
+ original = tabWidget->widget(tabWidget->currentIndex());
+ }
+ QPixmap pixmap = original->grab();
+ QPixmap targetPixmap = QPixmap(pixmap.size());
+ targetPixmap.fill(Qt::transparent);
+
+ QPainter painter(&targetPixmap);
+ painter.setOpacity(0.85);
+ painter.drawPixmap(0, 0, pixmap);
+ painter.end();
+ drag->setPixmap(targetPixmap);
+
+ Qt::DropAction dropAction = drag->exec(Qt::MoveAction | Qt::CopyAction);
+ if (dropAction == Qt::IgnoreAction) {
+ event->accept();
+ emit onDetachTab(tabAt(_dragStartPos), _mouseCursor.pos());
+ } if (dropAction == Qt::MoveAction) {
+ if (! _dragDropPos.isNull()) {
+ event->accept();
+ emit onMoveTab(tabAt(_dragStartPos), tabAt(_dragDropPos));
+ }
+ }
+ } else
+ QTabBar::mouseMoveEvent(event);
+}
+
+void DragDropTabBar::dragEnterEvent(QDragEnterEvent *event)
+{
+ const QMimeData * mimeData = event->mimeData();
+ QStringList formats = mimeData->formats();
+
+ if (formats.contains("action") && mimeData->data("action") == "application/tab-detach")
+ event->acceptProposedAction();
+}
+
+void DragDropTabBar::dropEvent(QDropEvent *event)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ _dragDropPos = event->position().toPoint();
+#else
+ _dragDropPos = event->pos();
+#endif
+ QTabBar::dropEvent(event);
+}