wireshark/ui/qt/widgets/detachable_tabwidget.cpp

213 lines
6.1 KiB
C++

/* @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);
}