diff --git a/src/DetachableTabBar.h b/src/DetachableTabBar.h --- a/src/DetachableTabBar.h +++ b/src/DetachableTabBar.h @@ -23,18 +23,28 @@ #include #include +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 idx); + void detachTab(int index); + void moveTabToWindow(int tabIndex, QWidget *otherWindow); protected: + void mousePressEvent(QMouseEvent *event) override; void mouseMoveEvent(QMouseEvent*event) override; void mouseReleaseEvent(QMouseEvent *event) override; + bool droppedContainerIsNotThis(const QPoint& currentPos) const; + private: - bool _draggingOutside; + DragType dragType; QCursor _originalCursor; + QList _containers; }; +} #endif diff --git a/src/DetachableTabBar.cpp b/src/DetachableTabBar.cpp --- a/src/DetachableTabBar.cpp +++ b/src/DetachableTabBar.cpp @@ -18,32 +18,92 @@ */ #include "DetachableTabBar.h" +#include "ViewContainer.h" + #include +#include + +namespace Konsole { DetachableTabBar::DetachableTabBar(QWidget *parent) : QTabBar(parent), - _draggingOutside(false) {} + dragType(DragType::NONE), + _originalCursor(cursor()) +{} + +bool DetachableTabBar::droppedContainerIsNotThis(const QPoint& currentPos) const +{ + for(const auto dropWidget : _containers) { + if (dropWidget->rect().contains(dropWidget->mapFromGlobal(currentPos))) { + if (dropWidget != parent()) { + return true; + } + } + } + return false; +} + +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())) { - if (!_draggingOutside) { - _draggingOutside = true; - _originalCursor = cursor(); + auto widgetAtPos = qApp->topLevelAt(event->globalPos()); + if (widgetAtPos){ + if (window() == widgetAtPos->window()) { + if (droppedContainerIsNotThis(event->globalPos())) { + if (dragType != DragType::WINDOW) { + dragType = DragType::WINDOW; + setCursor(QCursor(Qt::DragMoveCursor)); + } + } else { + if (dragType != DragType::NONE) { + dragType = DragType::NONE; + setCursor(_originalCursor); + } + } + } else { + if (dragType != DragType::WINDOW) { + dragType = DragType::WINDOW; + setCursor(QCursor(Qt::DragMoveCursor)); + } + } + } else if (!contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { + // Don't let it detach the last tab. + if (count() == 1) + return; + if (dragType != DragType::OUTSIDE) { + dragType = DragType::OUTSIDE; setCursor(QCursor(Qt::DragCopyCursor)); } - } else if (_draggingOutside) { - _draggingOutside = false; - setCursor(_originalCursor); } } void DetachableTabBar::mouseReleaseEvent(QMouseEvent *event) { QTabBar::mouseReleaseEvent(event); - if (!contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { - setCursor(_originalCursor); - emit detachTab(currentIndex()); + setCursor(_originalCursor); + + if (contentsRect().adjusted(-30,-30,30,30).contains(event->pos())) { + return; } + + auto widgetAtPos = qApp->topLevelAt(event->globalPos()); + if (!widgetAtPos) { + if (count() != 1) + emit detachTab(currentIndex()); + } else if (window() != widgetAtPos->window()) { + // splits have a tendency to break, forbid to detach if splitted and it's the last tab. + if (_containers.size() == 1 || count() > 1) + emit moveTabToWindow(currentIndex(), widgetAtPos); + } else if (droppedContainerIsNotThis(event->globalPos())){ + if (count() != 1) + emit moveTabToWindow(currentIndex(), widgetAtPos); + } +} + } diff --git a/src/ViewContainer.h b/src/ViewContainer.h --- a/src/ViewContainer.h +++ b/src/ViewContainer.h @@ -39,6 +39,7 @@ class QToolButton; class QMenu; class QDropEvent; +class QDragEnterEvent; namespace Konsole { class IncrementalSearchBar; @@ -135,7 +136,7 @@ void tabDoubleClicked(int index); void openTabContextMenu(const QPoint &point); void setNavigationVisibility(ViewManager::NavigationVisibility navigationVisibility); - + void moveTabToWindow(int index, QWidget *window); Q_SIGNALS: /** Emitted when the container has no more children */ void empty(TabbedViewContainer *container); @@ -155,11 +156,9 @@ * to append it. This index should be passed to addView() when the new view * has been created. * @param id The identifier of the view. - * @param success The slot handling this signal should set this to true if the - * new view was successfully created. * @param sourceContainer Initial move event Tabbed view container. */ - void moveViewRequest(int index, int id, bool &success, TabbedViewContainer *sourceContainer); + void moveViewRequest(int index, int id, TabbedViewContainer *sourceContainer); /** Emitted when the active view changes */ void activeViewChanged(QWidget *view); @@ -187,7 +186,6 @@ // close tabs and unregister void closeTerminalTab(int idx); - private Q_SLOTS: void viewDestroyed(QObject *view); void konsoleConfigChanged(); diff --git a/src/ViewContainer.cpp b/src/ViewContainer.cpp --- a/src/ViewContainer.cpp +++ b/src/ViewContainer.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include // KDE #include @@ -57,13 +60,14 @@ _contextMenuTabIndex(-1), _navigationVisibility(ViewManager::NavigationVisibility::NavigationNotSet) { + setAcceptDrops(true); + auto tabBarWidget = new DetachableTabBar(); setTabBar(tabBarWidget); setDocumentMode(true); setMovable(true); - + connect(tabBarWidget, &DetachableTabBar::moveTabToWindow, this, &TabbedViewContainer::moveTabToWindow); tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); - _newTabButton->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); _newTabButton->setAutoRaise(true); connect(_newTabButton, &QToolButton::clicked, this, [this]{ @@ -145,6 +149,21 @@ emit destroyed(this); } +void TabbedViewContainer::moveTabToWindow(int index, QWidget *window) +{ + const int id = viewProperties(widget(index))->identifier(); + // This one line here will be removed as soon as I finish my new split handling. + // it's hacky but it works. + const auto widgets = window->findChildren(); + const auto currentPos = QCursor::pos(); + for(const auto dropWidget : widgets) { + if (dropWidget->rect().contains(dropWidget->mapFromGlobal(currentPos))) { + dropWidget->moveViewRequest(-1, id, this); + removeView(widget(index)); + } + } +} + void TabbedViewContainer::konsoleConfigChanged() { // if we start with --show-tabbar or --hide-tabbar we ignore the preferences. diff --git a/src/ViewManager.h b/src/ViewManager.h --- a/src/ViewManager.h +++ b/src/ViewManager.h @@ -368,7 +368,7 @@ // called when a ViewContainer requests a view be // moved - void containerMoveViewRequest(int index, int id, bool &success, + void containerMoveViewRequest(int index, int id, TabbedViewContainer *sourceTabbedContainer); void detachView(TabbedViewContainer *container, QWidget *view); diff --git a/src/ViewManager.cpp b/src/ViewManager.cpp --- a/src/ViewManager.cpp +++ b/src/ViewManager.cpp @@ -649,7 +649,7 @@ return container; } -void ViewManager::containerMoveViewRequest(int index, int id, bool &success, +void ViewManager::containerMoveViewRequest(int index, int id, TabbedViewContainer *sourceTabbedContainer) { TabbedViewContainer *container = qobject_cast(sender()); @@ -677,7 +677,6 @@ createView(controller->session(), container, index); controller->session()->refresh(); - success = true; } void ViewManager::setNavigationMethod(NavigationMethod method)