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 @@ -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]{ @@ -86,6 +87,8 @@ connect(this, &TabbedViewContainer::currentChanged, this, [this](int index) { if (index != -1) { widget(index)->setFocus(); + } else { + deleteLater(); } }); @@ -141,8 +144,21 @@ auto view = widget(i); disconnect(view, &QWidget::destroyed, this, &Konsole::TabbedViewContainer::viewDestroyed); } +} - 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() 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 @@ -45,6 +46,7 @@ #include "ViewSplitter.h" #include "Enumeration.h" #include "ViewContainer.h" +#include "KonsoleSettings.h" using namespace Konsole; @@ -602,8 +604,8 @@ emit splitViewToggle(false); } - // new tab will be put at the end by default. - int index = -1; + const int index = KonsoleSettings::newTabBehavior() == KonsoleSettings::PutNewTabAtTheEnd ? + -1 : _viewSplitter->activeContainer()->currentIndex() + 1; // iterate over the view containers owned by this view manager // and create a new terminal display for the session in each of them, along with @@ -649,35 +651,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) @@ -878,8 +859,10 @@ QList list; TabbedViewContainer *container = _viewSplitter->activeContainer(); + if (container == nullptr) { + return {}; + } - Q_ASSERT(container); list.reserve(container->count()); for(int i = 0, end = container->count(); i < end; i++) { 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), diff --git a/src/ViewSplitter.h b/src/ViewSplitter.h --- a/src/ViewSplitter.h +++ b/src/ViewSplitter.h @@ -170,9 +170,6 @@ void updateSizes(); private Q_SLOTS: - // Called to indicate that a child ViewContainer has been deleted - void containerDestroyed(TabbedViewContainer *container); - // Called to indicate that a child ViewContainer is empty void containerEmpty(TabbedViewContainer *container); diff --git a/src/ViewSplitter.cpp b/src/ViewSplitter.cpp --- a/src/ViewSplitter.cpp +++ b/src/ViewSplitter.cpp @@ -23,6 +23,7 @@ #include "ViewSplitter.h" // Qt +#include // Konsole #include "ViewContainer.h" @@ -88,13 +89,6 @@ void ViewSplitter::registerContainer(TabbedViewContainer *container) { _containers << container; - // Connecting to container::destroyed() using the new-style connection - // syntax causes a crash at exit. I don't know why. Using the old-style - // syntax works. - //connect(container , static_cast(&Konsole::ViewContainer::destroyed) , this , &Konsole::ViewSplitter::containerDestroyed); - //connect(container , &Konsole::ViewContainer::empty , this , &Konsole::ViewSplitter::containerEmpty); - connect(container, SIGNAL(destroyed(TabbedViewContainer*)), this, - SLOT(containerDestroyed(TabbedViewContainer*))); connect(container, SIGNAL(empty(TabbedViewContainer*)), this, SLOT(containerEmpty(TabbedViewContainer*))); } @@ -179,29 +173,23 @@ } } -void ViewSplitter::containerEmpty(TabbedViewContainer * /*container*/) +void ViewSplitter::containerEmpty(TabbedViewContainer * myContainer) { + _containers.removeAll(myContainer); + if (count() == 0) { + emit empty(this); + } + int children = 0; - foreach (TabbedViewContainer *container, _containers) { + foreach (auto container, _containers) { children += container->count(); } if (children == 0) { emit allContainersEmpty(); } } -void ViewSplitter::containerDestroyed(TabbedViewContainer *container) -{ - Q_ASSERT(_containers.contains(container)); - - _containers.removeAll(container); - - if (count() == 0) { - emit empty(this); - } -} - void ViewSplitter::activateNextContainer() { TabbedViewContainer *active = activeContainer();