From e0c5d2365f151212bb552cd745cf5cae8af5d72f Mon Sep 17 00:00:00 2001 From: Eike Ziller <eike.ziller@digia.com> Date: Thu, 4 Sep 2014 20:43:09 +0200 Subject: [PATCH] Support drag and drop between splits Change-Id: Ia1e43cb44639e332ee4f9100c7ce3029e9485198 Reviewed-by: Alessandro Portale <alessandro.portale@digia.com> Reviewed-by: Daniel Teske <daniel.teske@digia.com> --- src/libs/utils/fileutils.cpp | 17 +++- src/libs/utils/fileutils.h | 20 +++-- .../coreplugin/editormanager/editorview.cpp | 5 +- src/plugins/coreplugin/editortoolbar.cpp | 83 ++++++++++++++++--- src/plugins/coreplugin/editortoolbar.h | 6 +- src/plugins/coreplugin/mainwindow.cpp | 4 +- 6 files changed, 109 insertions(+), 26 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 4ea20d86762..31ab9fd653d 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -711,8 +711,9 @@ static bool isDesktopFileManagerDrop(const QMimeData *d, QStringList *files = 0) return hasFiles; } -FileDropSupport::FileDropSupport(QWidget *parentWidget) - : QObject(parentWidget) +FileDropSupport::FileDropSupport(QWidget *parentWidget, const DropFilterFunction &filterFunction) + : QObject(parentWidget), + m_filterFunction(filterFunction) { QTC_ASSERT(parentWidget, return); parentWidget->setAcceptDrops(true); @@ -744,15 +745,22 @@ bool FileDropSupport::eventFilter(QObject *obj, QEvent *event) Q_UNUSED(obj) if (event->type() == QEvent::DragEnter) { auto dee = static_cast<QDragEnterEvent *>(event); - if (isDesktopFileManagerDrop(dee->mimeData())) + if (isDesktopFileManagerDrop(dee->mimeData()) + && (!m_filterFunction || m_filterFunction(dee))) event->accept(); else event->ignore(); + return true; + } else if (event->type() == QEvent::DragMove) { + event->accept(); + return true; } else if (event->type() == QEvent::Drop) { auto de = static_cast<QDropEvent *>(event); QStringList tempFiles; - if (isDesktopFileManagerDrop(de->mimeData(), &tempFiles)) { + if (isDesktopFileManagerDrop(de->mimeData(), &tempFiles) + && (!m_filterFunction || m_filterFunction(de))) { event->accept(); + de->acceptProposedAction(); bool needToScheduleEmit = m_files.isEmpty(); m_files.append(tempFiles); if (needToScheduleEmit) // otherwise we already have a timer pending @@ -760,6 +768,7 @@ bool FileDropSupport::eventFilter(QObject *obj, QEvent *event) } else { event->ignore(); } + return true; } return false; } diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index 69457b32bdc..9215603cf36 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -37,18 +37,21 @@ #include <QMetaType> #include <QStringList> +#include <functional> + namespace Utils {class FileName; } QT_BEGIN_NAMESPACE +class QDataStream; +class QDateTime; +class QDir; +class QDropEvent; class QFile; +class QFileInfo; class QMimeData; class QTemporaryFile; -class QWidget; class QTextStream; -class QDataStream; -class QDateTime; -class QFileInfo; -class QDir; +class QWidget; QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug dbg, const Utils::FileName &c); @@ -198,7 +201,11 @@ class QTCREATOR_UTILS_EXPORT FileDropSupport : public QObject { Q_OBJECT public: - FileDropSupport(QWidget *parentWidget); + // returns true if the event should be accepted + typedef std::function<bool(QDropEvent*)> DropFilterFunction; + + FileDropSupport(QWidget *parentWidget, const DropFilterFunction &filterFunction + = DropFilterFunction()); static QStringList mimeTypesForFilePaths(); static QMimeData *mimeDataForFilePaths(const QStringList &filePaths); @@ -214,6 +221,7 @@ private slots: void emitFilesDropped(); private: + DropFilterFunction m_filterFunction; QStringList m_files; }; diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index 585baa0aa6f..e36ec54dca1 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -78,6 +78,7 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) : connect(m_toolBar, SIGNAL(goForwardClicked()), this, SLOT(goForwardInNavigationHistory())); connect(m_toolBar, SIGNAL(closeClicked()), this, SLOT(closeCurrentEditor())); connect(m_toolBar, SIGNAL(listSelectionActivated(int)), this, SLOT(listSelectionActivated(int))); + connect(m_toolBar, &EditorToolBar::currentDocumentMoved, this, &EditorView::closeCurrentEditor); connect(m_toolBar, SIGNAL(horizontalSplitClicked()), this, SLOT(splitHorizontally())); connect(m_toolBar, SIGNAL(verticalSplitClicked()), this, SLOT(splitVertically())); connect(m_toolBar, SIGNAL(splitNewWindowClicked()), this, SLOT(splitNewWindow())); @@ -122,7 +123,9 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) : m_container->addWidget(empty); m_widgetEditorMap.insert(empty, 0); - auto dropSupport = new Utils::FileDropSupport(this); + auto dropSupport = new Utils::FileDropSupport(this, [this](QDropEvent *event) { + return event->source() != m_toolBar; // do not accept drops on ourselves + }); connect(dropSupport, SIGNAL(filesDropped(QStringList)), this, SLOT(openDroppedFiles(QStringList))); diff --git a/src/plugins/coreplugin/editortoolbar.cpp b/src/plugins/coreplugin/editortoolbar.cpp index 7a3240e3d25..1eab4846295 100644 --- a/src/plugins/coreplugin/editortoolbar.cpp +++ b/src/plugins/coreplugin/editortoolbar.cpp @@ -29,24 +29,32 @@ #include "editortoolbar.h" +#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/coreconstants.h> -#include <coreplugin/editormanager/ieditor.h> -#include <coreplugin/icore.h> - +#include <coreplugin/editormanager/documentmodel.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager_p.h> -#include <coreplugin/editormanager/documentmodel.h> -#include <coreplugin/actionmanager/actionmanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/fileiconprovider.h> +#include <coreplugin/icore.h> +#include <utils/fileutils.h> #include <utils/hostosinfo.h> #include <utils/qtcassert.h> -#include <QDir> #include <QApplication> #include <QComboBox> -#include <QVBoxLayout> -#include <QToolButton> +#include <QDir> +#include <QDrag> +#include <QLabel> #include <QMenu> +#include <QMimeData> +#include <QMouseEvent> +#include <QTimer> +#include <QToolButton> +#include <QVBoxLayout> + +#include <QDebug> enum { debug = false @@ -61,6 +69,7 @@ struct EditorToolBarPrivate QComboBox *m_editorList; QToolButton *m_closeEditorButton; QToolButton *m_lockButton; + QToolButton *m_dragHandle; QAction *m_goBackAction; QAction *m_goForwardAction; QToolButton *m_backButton; @@ -75,22 +84,25 @@ struct EditorToolBarPrivate QWidget *m_toolBarPlaceholder; QWidget *m_defaultToolBar; + QPoint m_dragStartPosition; + bool m_isStandalone; }; EditorToolBarPrivate::EditorToolBarPrivate(QWidget *parent, EditorToolBar *q) : m_editorList(new QComboBox(q)), - m_closeEditorButton(new QToolButton), - m_lockButton(new QToolButton), + m_closeEditorButton(new QToolButton(q)), + m_lockButton(new QToolButton(q)), + m_dragHandle(new QToolButton(q)), m_goBackAction(new QAction(QIcon(QLatin1String(Constants::ICON_PREV)), EditorManager::tr("Go Back"), parent)), m_goForwardAction(new QAction(QIcon(QLatin1String(Constants::ICON_NEXT)), EditorManager::tr("Go Forward"), parent)), - m_splitButton(new QToolButton), + m_splitButton(new QToolButton(q)), m_horizontalSplitAction(new QAction(QIcon(QLatin1String(Constants::ICON_SPLIT_HORIZONTAL)), EditorManager::tr("Split"), parent)), m_verticalSplitAction(new QAction(QIcon(QLatin1String(Constants::ICON_SPLIT_VERTICAL)), EditorManager::tr("Split Side by Side"), parent)), m_splitNewWindowAction(new QAction(EditorManager::tr("Open in New Window"), parent)), - m_closeSplitButton(new QToolButton), + m_closeSplitButton(new QToolButton(q)), m_activeToolBar(0), - m_toolBarPlaceholder(new QWidget), + m_toolBarPlaceholder(new QWidget(q)), m_defaultToolBar(new QWidget(q)), m_isStandalone(false) { @@ -115,6 +127,11 @@ EditorToolBar::EditorToolBar(QWidget *parent) : d->m_lockButton->setAutoRaise(true); d->m_lockButton->setEnabled(false); + d->m_dragHandle->setCheckable(false); + d->m_dragHandle->setChecked(false); + d->m_dragHandle->setToolTip(tr("Drag to drag documents between splits")); + d->m_dragHandle->installEventFilter(this); + connect(d->m_goBackAction, SIGNAL(triggered()), this, SIGNAL(goBackClicked())); connect(d->m_goForwardAction, SIGNAL(triggered()), this, SIGNAL(goForwardClicked())); @@ -164,6 +181,7 @@ EditorToolBar::EditorToolBar(QWidget *parent) : toplayout->addWidget(d->m_backButton); toplayout->addWidget(d->m_forwardButton); toplayout->addWidget(d->m_lockButton); + toplayout->addWidget(d->m_dragHandle); toplayout->addWidget(d->m_editorList); toplayout->addWidget(d->m_closeEditorButton); toplayout->addWidget(d->m_toolBarPlaceholder, 1); // Custom toolbar stretches @@ -367,6 +385,7 @@ void EditorToolBar::updateDocumentStatus(IDocument *document) d->m_lockButton->setIcon(QIcon()); d->m_lockButton->setEnabled(false); d->m_lockButton->setToolTip(QString()); + d->m_dragHandle->setIcon(QIcon()); d->m_editorList->setToolTip(QString()); return; } @@ -386,12 +405,50 @@ void EditorToolBar::updateDocumentStatus(IDocument *document) d->m_lockButton->setEnabled(false); d->m_lockButton->setToolTip(tr("File is writable")); } + + if (document->filePath().isEmpty()) + d->m_dragHandle->setIcon(QIcon()); + else + d->m_dragHandle->setIcon(FileIconProvider::icon(QFileInfo(document->filePath()))); + d->m_editorList->setToolTip( document->filePath().isEmpty() ? document->displayName() : QDir::toNativeSeparators(document->filePath())); } +bool EditorToolBar::eventFilter(QObject *obj, QEvent *event) +{ + if (obj == d->m_dragHandle) { + if (event->type() == QEvent::MouseButtonPress) { + auto me = static_cast<QMouseEvent *>(event); + if (me->buttons() == Qt::LeftButton) { + d->m_dragStartPosition = me->pos(); + return true; + } + return Utils::StyledBar::eventFilter(obj, event); + } else if (event->type() == QEvent::MouseMove) { + auto me = static_cast<QMouseEvent *>(event); + if (me->buttons() != Qt::LeftButton) + return Utils::StyledBar::eventFilter(obj, event); + if ((me->pos() - d->m_dragStartPosition).manhattanLength() + < QApplication::startDragDistance()) + return Utils::StyledBar::eventFilter(obj, event); + DocumentModel::Entry *entry = DocumentModel::entryAtRow( + d->m_editorList->currentIndex()); + if (!entry) // no document + return Utils::StyledBar::eventFilter(obj, event); + auto *drag = new QDrag(this); + drag->setMimeData(Utils::FileDropSupport::mimeDataForFilePath(entry->fileName())); + Qt::DropAction action = drag->exec(Qt::MoveAction | Qt::CopyAction, Qt::MoveAction); + if (action == Qt::MoveAction) + emit currentDocumentMoved(); + return true; + } + } + return Utils::StyledBar::eventFilter(obj, event); +} + void EditorToolBar::setNavigationVisible(bool isVisible) { d->m_goBackAction->setVisible(isVisible); diff --git a/src/plugins/coreplugin/editortoolbar.h b/src/plugins/coreplugin/editortoolbar.h index 4ad4b7496d8..7c6c26ea90b 100644 --- a/src/plugins/coreplugin/editortoolbar.h +++ b/src/plugins/coreplugin/editortoolbar.h @@ -94,11 +94,15 @@ signals: void closeSplitClicked(); void listSelectionActivated(int row); void listContextMenuRequested(QPoint globalpos); + void currentDocumentMoved(); + +protected: + bool eventFilter(QObject *obj, QEvent *event); private slots: void updateEditorListSelection(Core::IEditor *newSelection); void changeActiveEditor(int row); - void listContextMenu(QPoint); + void listContextMenu(QPoint pos); void makeEditorWritable(); void checkDocumentStatus(); diff --git a/src/plugins/coreplugin/mainwindow.cpp b/src/plugins/coreplugin/mainwindow.cpp index 561bc2b04af..f16b10e3515 100644 --- a/src/plugins/coreplugin/mainwindow.cpp +++ b/src/plugins/coreplugin/mainwindow.cpp @@ -207,7 +207,9 @@ MainWindow::MainWindow() : statusBar()->setProperty("p_styled", true); - auto dropSupport = new Utils::FileDropSupport(this); + auto dropSupport = new Utils::FileDropSupport(this, [](QDropEvent *event) { + return event->source() == 0; // only accept drops from the "outside" (e.g. file manager) + }); connect(dropSupport, SIGNAL(filesDropped(QStringList)), this, SLOT(openDroppedFiles(QStringList))); } -- GitLab