From 8fd74e8b74bebf16bdd98fc65598535b95f8dd96 Mon Sep 17 00:00:00 2001 From: Tomaz Canabrava Date: Sun, 16 Sep 2018 12:47:34 -0400 Subject: [PATCH] Fix tab attach/reattach and simplify tab dnd code Summary: This continues the patch series of the rewrite of the Tab handling code to a more modern approach using QTabWidget. This fixes the attach and reattach bug on multiple konsole windows and reworks the drag & drop code to a more simplified version of it. Reviewers: hindenburg, ngraham, sandsmark Reviewed By: hindenburg Subscribers: lbergdoll, konsole-devel Tags: #konsole Differential Revision: https://phabricator.kde.org/D15377 --- src/DetachableTabBar.cpp | 82 ++++++++++++++++++++++++++++++++++------ src/DetachableTabBar.h | 14 ++++++- src/ViewContainer.cpp | 23 ++++++++++- src/ViewContainer.h | 8 ++-- src/ViewManager.cpp | 3 +- src/ViewManager.h | 2 +- 6 files changed, 109 insertions(+), 23 deletions(-) diff --git a/src/DetachableTabBar.cpp b/src/DetachableTabBar.cpp index fd789e439..7f3a06823 100644 --- 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/DetachableTabBar.h b/src/DetachableTabBar.h index 9a751d185..488ac409f 100644 --- 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/ViewContainer.cpp b/src/ViewContainer.cpp index a8f0c4df4..0eee05055 100644 --- a/src/ViewContainer.cpp +++ b/src/ViewContainer.cpp @@ -28,6 +28,9 @@ #include #include #include +#include +#include +#include // KDE #include @@ -57,13 +60,14 @@ TabbedViewContainer::TabbedViewContainer(ViewManager *connectedViewManager, QWid _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 @@ TabbedViewContainer::~TabbedViewContainer() 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/ViewContainer.h b/src/ViewContainer.h index a4f415afc..603e795a2 100644 --- a/src/ViewContainer.h +++ b/src/ViewContainer.h @@ -39,6 +39,7 @@ class QPoint; class QToolButton; class QMenu; class QDropEvent; +class QDragEnterEvent; namespace Konsole { class IncrementalSearchBar; @@ -135,7 +136,7 @@ public: 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 @@ Q_SIGNALS: * 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 @@ protected: // close tabs and unregister void closeTerminalTab(int idx); - private Q_SLOTS: void viewDestroyed(QObject *view); void konsoleConfigChanged(); diff --git a/src/ViewManager.cpp b/src/ViewManager.cpp index 15c34cfb9..8e146a9ce 100644 --- a/src/ViewManager.cpp +++ b/src/ViewManager.cpp @@ -649,7 +649,7 @@ TabbedViewContainer *ViewManager::createContainer() 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 @@ void ViewManager::containerMoveViewRequest(int index, int id, bool &success, createView(controller->session(), container, index); controller->session()->refresh(); - success = true; } void ViewManager::setNavigationMethod(NavigationMethod method) diff --git a/src/ViewManager.h b/src/ViewManager.h index ec556abee..6d09f6a7b 100644 --- a/src/ViewManager.h +++ b/src/ViewManager.h @@ -368,7 +368,7 @@ private Q_SLOTS: // 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);