1 //=========================================== 2 // Lumina-DE source code 3 // Copyright (c) 2017, Ken Moore 4 // Available under the 3-clause BSD license 5 // See the LICENSE file for full details 6 //=========================================== 7 // This is a drag and drop capable version of the QTabBar 8 //=========================================== 9 #ifndef _LUMINA_TEXT_EDITOR_DND_TABWIDGET_H 10 #define _LUMINA_TEXT_EDITOR_DND_TABWIDGET_H 11 12 #include <QTabBar> 13 #include <QTabWidget> 14 #include <QMouseEvent> 15 #include <QDebug> 16 #include <QDrag> 17 #include <QMimeData> 18 #include <QMenu> 19 #include <QAction> 20 21 class DnDTabBar : public QTabBar{ 22 Q_OBJECT 23 signals: 24 void DetachTab(int); // 25 void DroppedIn(QStringList); //The full path of some file(s) 26 void DraggedOut(int, Qt::DropAction); //The tab number dragged/accepted by another app, and which action was accepted 27 28 private: 29 QMenu *tabMenu; 30 QString selTab; 31 32 private slots: slotDetachTab()33 void slotDetachTab(){ 34 //qDebug() << "Detach Tab:" << selTab; 35 int tab = tabMenu->whatsThis().toInt(); 36 if(tab>=0){ emit DetachTab(tab); } 37 } 38 public: DnDTabBar(QWidget * parent)39 DnDTabBar(QWidget *parent) : QTabBar(parent){ 40 this->setAcceptDrops(true); 41 this->setMouseTracking(true); 42 tabMenu = new QMenu(this); 43 tabMenu->addAction(tr("Detach Tab"), this, SLOT(slotDetachTab()) ); 44 } ~DnDTabBar()45 ~DnDTabBar(){ 46 47 } 48 49 50 51 protected: mousePressEvent(QMouseEvent * ev)52 virtual void mousePressEvent(QMouseEvent *ev){ 53 int tab = this->tabAt(ev->pos()); 54 if(ev->button() == Qt::LeftButton && tab>=0){ 55 selTab = this->tabWhatsThis(tab); 56 } 57 QTabBar::mousePressEvent(ev); 58 } 59 mouseMoveEvent(QMouseEvent * ev)60 virtual void mouseMoveEvent(QMouseEvent *ev){ 61 //qDebug() << "Got Move Event:" << this->geometry() << ev->pos(); 62 QWidget *parent = this->parentWidget(); //top-level parent widget 63 while(parent->parentWidget()!=0 && !parent->isWindow()){ parent = parent->parentWidget(); } 64 if(selTab>=0 && !QRect(QPoint(0,0), parent->size()).contains( this->mapTo(parent, ev->pos())) ){ 65 //qDebug() << "Got Mouse outside of parent:" << parent->geometry() << this->geometry() << this->mapTo(parent, ev->pos()); 66 //if(ev->button()!=Qt::LeftButton){ QTabBar::mouseMoveEvent(ev); return; } 67 qDebug() << "Starting Drag:" << this->geometry() << ev->pos(); 68 QString tab = selTab; 69 this->mouseReleaseEvent(new QMouseEvent(QEvent::MouseButtonRelease, ev->pos(), ev->button(), ev->buttons(), ev->modifiers()) ); //will reset selTab 70 //this->update(); 71 QDrag *drag = new QDrag(this); 72 QMimeData *mimeData = new QMimeData; 73 mimeData->setUrls(QList<QUrl>() << QUrl::fromLocalFile(tab)); 74 //qDebug() << "Start Drag:" << this->tabWhatsThis(tab); 75 drag->setMimeData(mimeData); 76 Qt::DropAction dropAction = drag->exec(Qt::MoveAction); 77 //Convert the tab->number and emit 78 for(int i=0; i<this->count(); i++){ 79 if(this->tabWhatsThis(i) == tab){ this->emit DraggedOut(i, dropAction); this->setCurrentIndex(i); break; } 80 } 81 return; 82 } 83 QTabBar::mouseMoveEvent(ev); 84 } 85 mouseReleaseEvent(QMouseEvent * ev)86 virtual void mouseReleaseEvent(QMouseEvent *ev){ 87 int tab = this->tabAt(ev->pos()); 88 if(ev->button() == Qt::RightButton && tab>=0){ 89 tabMenu->setWhatsThis(QString::number(tab)); 90 tabMenu->popup(ev->globalPos()); 91 } 92 selTab.clear(); //reset this flag - not in a drag right now 93 QTabBar::mouseReleaseEvent(ev); 94 } 95 dragEnterEvent(QDragEnterEvent * ev)96 virtual void dragEnterEvent(QDragEnterEvent *ev){ 97 //qDebug() << "Got Drag Enter Event:" << ev->mimeData()->hasUrls(); 98 if(ev->mimeData()->hasUrls() && ev->source()!=this){ 99 ev->acceptProposedAction(); 100 } 101 } 102 dragMoveEvent(QDragMoveEvent * ev)103 virtual void dragMoveEvent(QDragMoveEvent *ev){ 104 //qDebug() << "Got Drag Move Event:" << ev->mimeData()->hasUrls(); 105 if(ev->mimeData()->hasUrls() && ev->source()!=this){ 106 ev->accept(); 107 } 108 } 109 dropEvent(QDropEvent * ev)110 virtual void dropEvent(QDropEvent *ev){ 111 //qDebug() << "Got Drop Event:" << ev->mimeData()->hasUrls(); 112 if(ev->mimeData()->hasUrls() && ev->source()!=this){ 113 QStringList files; 114 for(int i=0; i<ev->mimeData()->urls().length(); i++){ 115 QString path = ev->mimeData()->urls().at(i).toLocalFile(); 116 if(QFile::exists(path)){ files << path; } 117 } 118 //qDebug() << "[DROP] URLS:" << ev->mimeData()->urls() << "Files:" << files; 119 if(!files.isEmpty() && (ev->proposedAction()==Qt::CopyAction || ev->proposedAction()==Qt::MoveAction)){ 120 ev->setDropAction(Qt::MoveAction); 121 ev->accept(); 122 this->emit DroppedIn(files); 123 } 124 } 125 } 126 }; 127 128 class DnDTabWidget : public QTabWidget{ 129 Q_OBJECT 130 private: 131 DnDTabBar *TB; 132 public: DnDTabWidget(QWidget * parent)133 DnDTabWidget(QWidget *parent) : QTabWidget(parent){ 134 TB = new DnDTabBar(this); 135 this->setTabBar(TB); 136 this->setTabsClosable(true); 137 this->setMovable(true); 138 } ~DnDTabWidget()139 ~DnDTabWidget(){} 140 dndTabBar()141 DnDTabBar* dndTabBar(){ return TB; } 142 }; 143 144 #endif 145