diff --git a/src/DetachableTabBar.cpp b/src/DetachableTabBar.cpp index f10bf385..5d30318b 100644 --- a/src/DetachableTabBar.cpp +++ b/src/DetachableTabBar.cpp @@ -1,144 +1,217 @@ /* Copyright 2018 by Tomaz Canabrava This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "DetachableTabBar.h" #include "KonsoleSettings.h" #include "ViewContainer.h" #include #include #include #include - +#include +#include +#include +#include +#include #include #include +#include namespace { QString splitDragId(QStringLiteral("konsole/terminal_display")); QString tabDragMime(QStringLiteral("konsole/tab")); } namespace Konsole { DetachableTabBar::DetachableTabBar(QWidget *parent) : QTabBar(parent), dragType(DragType::NONE), _originalCursor(cursor()), - tabId(-1) + tabId(-1), + _draggerHandlerPosition(-1) { setAcceptDrops(true); setElideMode(Qt::TextElideMode::ElideMiddle); KAcceleratorManager::setNoAccel(this); } void DetachableTabBar::middleMouseButtonClickAt(const QPoint& pos) { tabId = tabAt(pos); if (tabId != -1) { emit closeTab(tabId); } } void DetachableTabBar::mousePressEvent(QMouseEvent *event) { QTabBar::mousePressEvent(event); _containers = window()->findChildren(); } void DetachableTabBar::mouseMoveEvent(QMouseEvent *event) { - QTabBar::mouseMoveEvent(event); - - if (!contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { - - QDrag *drag = new QDrag(this); - QMimeData *mimeData = new QMimeData(); - quintptr data = (quintptr) topLevelWidget(); - - mimeData->setData(tabDragMime, QByteArray::number(data)); - drag->setMimeData(mimeData); - - // this is blocking, so the code below will execute only after mouse release. - drag->exec(); - - QWidget *targetWidget = qobject_cast(drag->target()); - if (drag->target() == nullptr) { - if (count() != 1) { - emit detachTab(currentIndex()); - setCursor(_originalCursor); - } - } else if (window() != targetWidget->window()) { - if (_containers.size() == 1 || count() > 1) { - emit moveTabToWindow(currentIndex(), targetWidget); - } + QDrag *drag = new QDrag(this); + QMimeData *mimeData = new QMimeData(); + quintptr data = (quintptr) topLevelWidget(); + + mimeData->setData(tabDragMime, QByteArray::number(data)); + mimeData->setProperty("tabId", tabAt(event->pos())); + + drag->setMimeData(mimeData); + + // this is blocking, so the code below will execute only after mouse release. + drag->exec(); + + QWidget *targetWidget = qobject_cast(drag->target()); + if (drag->target() == nullptr) { + if (count() != 1) { + emit detachTab(currentIndex()); + setCursor(_originalCursor); + } + } else if (window() != targetWidget->window()) { + if (_containers.size() == 1 || count() > 1) { + emit moveTabToWindow(currentIndex(), targetWidget); } } } + void DetachableTabBar::mouseReleaseEvent(QMouseEvent *event) { QTabBar::mouseReleaseEvent(event); switch(event->button()) { case Qt::MiddleButton : if (KonsoleSettings::closeTabOnMiddleMouseButton()) { middleMouseButtonClickAt(event->pos()); } tabId = tabAt(event->pos()); if (tabId == -1) { emit newTabRequest(); } break; case Qt::LeftButton: _containers = window()->findChildren(); break; default: break; } setCursor(_originalCursor); if (contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { return; } } void DetachableTabBar::dragEnterEvent(QDragEnterEvent* event) { if (event->mimeData()->hasFormat(splitDragId)) { auto other_pid = event->mimeData()->data(splitDragId).toInt(); // don't accept the drop if it's another instance of konsole if (qApp->applicationPid() != other_pid) { return; } event->accept(); + } else if (event->mimeData()->hasFormat(tabDragMime)) { + event->accept(); } } void DetachableTabBar::dragMoveEvent(QDragMoveEvent* event) { if (event->mimeData()->hasFormat(splitDragId)) { int tabIdx = tabAt(event->pos()); if (tabIdx != -1) { setCurrentIndex(tabIdx); } + } else if (event->mimeData()->hasFormat(tabDragMime)) { + const int tabIdx = tabAt(event->pos()); + const QRect tabSize = tabRect(tabIdx); + const int newHandlerPosition = event->pos().x() - tabSize.x() < tabSize.width() / 2 + ? tabSize.x() + : tabSize.x() + tabSize.width(); + + if (_draggerHandlerPosition != newHandlerPosition) { + _draggerHandlerPosition = newHandlerPosition; + update(); + } + } +} + +void DetachableTabBar::dragLeaveEvent(QDragLeaveEvent* event) +{ + _draggerHandlerPosition = -1; +} + +void DetachableTabBar::dropEvent(QDropEvent *event) +{ + event->acceptProposedAction(); + const int originalTab = event->mimeData()->property("tabId").toInt(); + const int thisTab = tabAt(event->pos()); + + const QRect tabSize = tabRect(thisTab); + const int dropPosition = event->pos().x() - tabSize.x() < tabSize.width() / 2 + ? Qt::LeftEdge : Qt::RightEdge; + + _draggerHandlerPosition = -1; + + qDebug() << "dropping" << originalTab << "at" << thisTab << "on the" + << (dropPosition == Qt::LeftEdge ? "Left" : "Right"); + + if (dropPosition == Qt::LeftEdge && thisTab -1 == originalTab) { + qDebug() << "Dropping at the same position on the left"; + update(); + return; + } + + if (dropPosition == Qt::RightEdge && thisTab +1 == originalTab) { + qDebug() << "Dropping at the same potision on the right"; + update(); + return; + } + + if (originalTab == thisTab) { + qDebug() << "Dropping at the same time"; + update(); + return; + } + + // There's something wrong with the math here, but already works much better. + moveTab(originalTab, thisTab); + _draggerHandlerPosition = -1; +} + +void DetachableTabBar::paintEvent(QPaintEvent* event) +{ + QTabBar::paintEvent(event); + if (_draggerHandlerPosition != -1) { + QPainter p(this); + p.drawPolygon(QPolygon({ + {_draggerHandlerPosition - 10, 0}, + {_draggerHandlerPosition + 10, 0}, + {_draggerHandlerPosition, height()} + })); } } } diff --git a/src/DetachableTabBar.h b/src/DetachableTabBar.h index 2d490795..6bdabc0e 100644 --- a/src/DetachableTabBar.h +++ b/src/DetachableTabBar.h @@ -1,55 +1,63 @@ /* Copyright 2018 by Tomaz Canabrava This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef DETACHABLETABBAR_H #define DETACHABLETABBAR_H #include #include +class QPaintEvent; +class QDropEvent; +class QDragLeaveEvent; + namespace Konsole { class TabbedViewContainer; class DetachableTabBar : public QTabBar { Q_OBJECT public: enum class DragType : unsigned char {NONE, OUTSIDE, WINDOW}; explicit DetachableTabBar(QWidget *parent = nullptr); Q_SIGNALS: void detachTab(int index); void moveTabToWindow(int tabIndex, QWidget *otherWindow); void closeTab(int index); void newTabRequest(); protected: void middleMouseButtonClickAt(const QPoint& pos); void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent*event) override; void mouseReleaseEvent(QMouseEvent *event) override; void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent * event) override; + void dragMoveEvent(QDragMoveEvent *event) override; + void dragLeaveEvent(QDragLeaveEvent *event) override; + void paintEvent(QPaintEvent *event) override; + void dropEvent(QDropEvent *event) override; private: DragType dragType; QCursor _originalCursor; QList _containers; int tabId; + int _draggerHandlerPosition; }; } #endif