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,91 @@ */ #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) +{ + _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 @@ -23,12 +23,9 @@ #define VIEWCONTAINER_H // Qt -#include -#include #include #include #include -#include // Konsole #include "Profile.h" @@ -38,13 +35,10 @@ class QPoint; class QToolButton; class QMenu; -class QDropEvent; namespace Konsole { -class IncrementalSearchBar; class ViewProperties; class ViewManager; -class TabbedViewContainer; /** * An interface for container widgets which can hold one or more views. @@ -135,7 +129,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 +149,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 +179,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 @@ -57,13 +57,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 +146,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 @@ -25,6 +25,7 @@ // Qt #include #include +#include // KDE #include @@ -649,35 +650,14 @@ 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()); SessionController *controller = qobject_cast(ViewProperties::propertiesById(id)); - if (controller == nullptr) { - return; - } - - // do not move the last tab in a split view. - if (sourceTabbedContainer != nullptr) { - QPointer sourceContainer = qobject_cast(sourceTabbedContainer); - - if (_viewSplitter->containers().contains(sourceContainer)) { - return; - } else { - ViewManager *sourceViewManager = sourceTabbedContainer->connectedViewManager(); - - // do not remove the last tab on the window - if (qobject_cast(sourceViewManager->widget())->containers().size() > 1) { - return; - } - } - } - createView(controller->session(), container, index); controller->session()->refresh(); - success = true; } void ViewManager::setNavigationMethod(NavigationMethod method) diff --git a/src/ViewProperties.h b/src/ViewProperties.h --- a/src/ViewProperties.h +++ b/src/ViewProperties.h @@ -83,36 +83,6 @@ /** Finds a ViewProperties instance given its numeric identifier. */ static ViewProperties *propertiesById(int id); - /** Name of mime format to use in drag-and-drop operations. */ - static QString mimeType() - { - return _mimeType; - } - - /** Returns a new QMimeData instance which represents the view with the - * given @p id (See identifier()). The QMimeData instance returned must - * be deleted by the caller. - */ - static QMimeData *createMimeData(int id) - { - auto mimeData = new QMimeData; - mimeData->setData(mimeType(), QByteArray::number(id)); - return mimeData; - } - - /** Decodes a QMimeData instance created with createMimeData() and - * returns the identifier of the associated view. The associated - * ViewProperties instance can then be retrieved by calling propertiesById() - * - * The QMimeData instance must support the mime format returned by mimeType() - */ - static int decodeMimeData(const QMimeData *mimeData) - { - bool ok; - // we are not checking return value ok; not sure how int could be invalid - return mimeData->data(ViewProperties::mimeType()).toInt(&ok); - } - Q_SIGNALS: /** Emitted when the icon for a view changes */ void iconChanged(ViewProperties *properties); @@ -153,7 +123,6 @@ int _identifier; static QHash _viewProperties; - static QString _mimeType; }; } diff --git a/src/ViewProperties.cpp b/src/ViewProperties.cpp --- a/src/ViewProperties.cpp +++ b/src/ViewProperties.cpp @@ -23,7 +23,6 @@ using Konsole::ViewProperties; QHash ViewProperties::_viewProperties; -QString ViewProperties::_mimeType = QStringLiteral("application/x-konsole-view-id"); ViewProperties::ViewProperties(QObject *parent) : QObject(parent),