Last active
August 29, 2015 14:03
-
-
Save kuldeepdhaka/2e1186dc985c130efc1a to your computer and use it in GitHub Desktop.
C++ equivalent of https://github.com/devkid/QRearrangeableLayout (Python)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) | |
PROJECT(QRearrangeableLayout) | |
FIND_PACKAGE(Qt4 REQUIRED) | |
INCLUDE(${QT_USE_FILE}) | |
ADD_DEFINITIONS(${QT_DEFINITIONS}) | |
SET(qrearrangeablelayout_SOURCES QRearrangeableLayout.cpp) | |
SET(qrearrangeablelayout_HEADERS QRearrangeableLayout.h) | |
QT4_WRAP_CPP(qrearrangeablelayout_HEADERS_MOC ${qrearrangeablelayout_HEADERS}) | |
ADD_LIBRARY(qrearrangeablelayout SHARED ${qrearrangeablelayout_HEADERS_MOC} ${qrearrangeablelayout_HEADERS} ${qrearrangeablelayout_SOURCES}) | |
TARGET_LINK_LIBRARIES(qrearrangeablelayout ${QT_LIBRARIES}) | |
ADD_EXECUTABLE(test main.cpp) | |
TARGET_LINK_LIBRARIES(test ${QT_LIBRARIES}) | |
TARGET_LINK_LIBRARIES(test qrearrangeablelayout) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <QApplication> | |
#include <QMainWindow> | |
#include <QListWidget> | |
#include <QListWidgetItem> | |
#include <QHBoxLayout> | |
#include <QSplitter> | |
#include <QLabel> | |
#include <QPushButton> | |
#include "QRearrangeableLayout.h" | |
int main(int argc, char *argv[]) | |
{ | |
QApplication a(argc, argv); | |
QMainWindow w; | |
w.setWindowTitle("QRearrangeableLayout - Test"); | |
w.setObjectName(QString::fromUtf8("QMain-test")); | |
QListWidget *list1 = new QListWidget(); | |
list1->addItem(new QListWidgetItem("List1")); | |
QListWidget *list2 = new QListWidget(); | |
list2->addItem(new QListWidgetItem("List2")); | |
for(int i = 0; i < 10; i++ ) { | |
list1->addItem(new QListWidgetItem("Another item")); | |
list2->addItem(new QListWidgetItem("Another item")); | |
} | |
QRearrangeableLayout *central = new QRearrangeableLayout(&a, &w); | |
central->setRearrangeable(true); | |
w.setCentralWidget(central); | |
QHBoxLayout *mainhbox = new QHBoxLayout(); | |
central->setLayout(mainhbox); | |
QSplitter *hbox = new QSplitter(Qt::Horizontal); | |
hbox->addWidget(list1); | |
hbox->addWidget(list2); | |
hbox->addWidget(new QLabel()); | |
hbox->addWidget(new QPushButton()); | |
hbox->addWidget(new QListWidget()); | |
hbox->addWidget(new QListWidget());; | |
hbox->addWidget(new QListWidget()); | |
for(int i = 0; i < hbox->count(); i++) { | |
hbox->setCollapsible(i, false); | |
} | |
mainhbox->addWidget(hbox); | |
w.show(); | |
return a.exec(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "QRearrangeableLayout.h" | |
#include <QBoxLayout> | |
#include <QMimeData> | |
#include <QDrag> | |
#include <QList> | |
#include <QMouseEvent> | |
#include <iostream> | |
/* blind(mostly) conversion of QRearrangeableLayout.py */ | |
QRearrangeableLayout::QRearrangeableLayout(QApplication *app, QWidget *parent) : QWidget(parent) | |
{ | |
this->app = app; | |
app->installEventFilter(this); | |
dragStarting = false; | |
dragRunning = false; | |
setAcceptDrops(true); | |
_rearrangeable = false; | |
dragSource = 0; | |
} | |
void QRearrangeableLayout::setRearrangeable(bool rearrangeable) | |
{ | |
_rearrangeable = rearrangeable; | |
} | |
bool QRearrangeableLayout::rearrangeable(void) | |
{ | |
return _rearrangeable; | |
} | |
QSplitter* QRearrangeableLayout::removeWidget(QWidget *widget) | |
{ | |
/* | |
* Removes a widget from its parent splitter. If the splitter is left with | |
* one or less widgets in it, it is removed from its parent itself and the | |
*(possibly) remaining widget is moved to the splitters parent. | |
* Returns the splitter if there are more then one widgets left in there, | |
* otherwise returns the parent of the splitter. | |
*/ | |
QSplitter *splitter = static_cast<QSplitter *>(widget->parent()); | |
if(! splitter->inherits("QSplitter")) { | |
//~ raise Exception /* ("Expecting a widget whose parent is a QSplitter") */; | |
printf("Exception(\"Expecting a widget whose parent is a QSplitter\")\n"); | |
return 0; | |
} | |
widget->setParent(0); | |
QSplitter *parent = static_cast<QSplitter *>(splitter->parent()); | |
if(splitter->count() > 2) { | |
return splitter; | |
} | |
if(splitter->count() == 1) { | |
/* | |
* the splitter's parent is also a splitter, we only need to need to | |
* move that one remaining widget to the parent | |
*/ | |
if(parent->inherits("QSplitter")) { | |
QWidget *child = splitter->widget(0); | |
child->setParent(0); | |
parent->insertWidget(parent->indexOf(splitter), child); | |
} | |
/* | |
* if not: check if the remaining child is another splitter and if | |
* so, change the splitters layout to the one of the remaining | |
* splitter, move it's children to our top level splitter and | |
* remove the remaining splitter afterwards | |
*/ | |
else { | |
QSplitter *child = static_cast<QSplitter *>(splitter->widget(0)); | |
if(child->inherits("QSplitter")) { | |
QList<int>sizes = child->sizes(); | |
splitter->setOrientation(child->orientation()); | |
child->setParent(0); | |
QList<QWidget *> widgets; | |
for(int i = child->count() - 1; i >= 0; i--) { | |
QWidget *w = child->widget(i); | |
w->setParent(0); | |
widgets.append(w); | |
} | |
while(! widgets.isEmpty()) { | |
QWidget *w = widgets.takeLast(); | |
splitter->addWidget(w); | |
} | |
splitter->setSizes(sizes); | |
} | |
return splitter; | |
} | |
} | |
if(parent->inherits("QSplitter")) { | |
splitter->setParent(0); | |
return parent; | |
} else { | |
return splitter; | |
} | |
} | |
QWidget* QRearrangeableLayout::findChildOfSplitter(QWidget *widget) | |
{ | |
/* | |
* Walks up the family tree of the given widget until it finds a widget | |
* whose parent is a QSplitter. | |
* Returns that widget-> | |
*/ | |
/* FIX: if we have clicked on a area that is under QRearrangeableLayout but not the underlying widget or click (check if parent is NULL) */ | |
while((widget != 0) and (widget->parent() != 0 and ! widget->parent()->inherits("QSplitter"))) { | |
widget = static_cast<QWidget *>(widget->parent()); | |
} | |
return widget; | |
} | |
bool QRearrangeableLayout::eventFilter(QObject *source, QEvent *event) | |
{ | |
if(_rearrangeable == false) { | |
return QWidget::eventFilter(source, event); | |
//~ return QBoxLayout::eventFilter(source, event); | |
//~ return false; | |
} | |
QMouseEvent *mouseEvent; | |
if( event->type() == QEvent::MouseButtonPress or | |
event->type() == QEvent::MouseButtonRelease or | |
event->type() == QEvent::MouseMove) { | |
mouseEvent = static_cast<QMouseEvent *>(event); | |
/* ignore events that aren't going to our widget */ | |
if(! rect().contains(mouseEvent->pos())) { | |
//~ return QBoxLayout::eventFilter(source, event); | |
return QWidget::eventFilter(source, event); | |
//~ return false; | |
} | |
} | |
if(event->type() == QEvent::MouseButtonPress) { | |
dragSource = QRearrangeableLayout::findChildOfSplitter(static_cast<QWidget *>(source)); | |
QWidget *widget = dragSource; | |
while(widget != 0 and ! widget->inherits("QRearrangeableLayout")) { | |
widget = static_cast<QWidget *>(widget->parent()); | |
} | |
if(! (widget == 0 or widget == this) and widget->inherits("QRearrangeableLayout")) { | |
return widget->eventFilter(source, event); | |
} | |
if(dragSource != 0) { | |
dragStarting = true; | |
dragPos = mouseEvent->pos(); | |
} | |
if(dragSource->inherits("QSplitterHandle")) { | |
dragStarting = false; | |
} | |
if(dragStarting) { | |
emit draggingStarted(dragSource, mouseEvent->pos()); | |
} | |
} | |
else if(event->type() == QEvent::MouseButtonRelease) { | |
dragStarting = false; | |
} | |
else if(event->type() == QEvent::MouseMove and dragStarting and | |
((mouseEvent->pos() - dragPos).manhattanLength() > QApplication::startDragDistance())) { | |
dragStarting = false; | |
dragRunning = true; | |
/* begin dragging procedure */ | |
QMimeData *mimeData = new QMimeData(); | |
QDrag *drag = new QDrag(dragSource); | |
drag->setMimeData(mimeData); | |
drag->setHotSpot(dragPos); | |
//~ QPixmap pixmap = dragSource->pixmap(); | |
//~ drag->setPixmap(pixmap); | |
Qt::DropAction dropAction = drag->exec(Qt::MoveAction); | |
//~ std::cout << drag->supportedActions() << " " << dropAction << std::endl; | |
std::cout << dropAction << " " << dragSource->metaObject()->className() << std::endl; | |
dragRunning = false; | |
} | |
//~ return false; | |
//~ return QBoxLayout::eventFilter(source, event); | |
return QWidget::eventFilter(source, event); | |
} | |
void QRearrangeableLayout::dragEnterEvent(QDragEnterEvent *event) | |
{ | |
event->setAccepted(dragRunning); | |
} | |
void QRearrangeableLayout::dragMoveEvent(QDragMoveEvent *event) | |
{ | |
QWidget *under = app->widgetAt(QCursor::pos()); | |
/* | |
* check if this event should go to another rearrangeable layout more | |
* nested | |
*/ | |
QWidget *widget = under; | |
while(widget != 0 and ! widget->inherits("QRearrangeableLayout")) { | |
widget = static_cast<QWidget *>(widget->parent()); | |
} | |
if(! (widget == 0 or widget == this)) { | |
under = widget; | |
} | |
if(! rect().contains(event->pos())) { | |
return; | |
} | |
/* is the widget underneath a child of a QSplitter? if yes, put it there */ | |
widget = QRearrangeableLayout::findChildOfSplitter(under); | |
if(widget == 0 or widget == dragSource or widget->inherits("QSplitterHandle")) { | |
return; | |
} | |
QSplitter *splitter = static_cast<QSplitter *>(widget->parent()); | |
/* | |
* the direction in which we want to expand: | |
* 0 = up, 1 = right, 2 = left, 3 = down(order for arithmetic reasons) | |
*/ | |
int direction = 0; | |
int i = 0; | |
QPoint pos = event->pos() - widget->mapTo(this, QPoint(0,0)); | |
QSize size = widget->size(); | |
int w = size.width(); | |
int h = size.height(); | |
int d = pos.x() * h / w; /* function value of the diagonal */ | |
QSize cornersize = size / 4; | |
int ww = w * 3/4; | |
int wh = h * 3/4; | |
QRect rects[5] = { | |
/* disable a region in the middle of the widget, | |
* half the size of the widget itself | |
*/ | |
QRect(QPoint(cornersize.width(), cornersize.height()), size / 2), | |
/* | |
* disable corner regions; | |
* these regions touch the middle regions at one point | |
*/ | |
QRect(QPoint(0, 0), cornersize), | |
QRect(QPoint(ww, 0), cornersize), | |
QRect(QPoint(0, wh), cornersize), | |
QRect(QPoint(ww, wh), cornersize) | |
}; | |
for(i = 0; i < 5; i++) { | |
QRect r = rects[i]; | |
/* | |
*(need to ajust a bit, so that we don't collide with the borders, | |
* is a little bit strange) | |
*/ | |
r.adjust(-10, -10, 10, 10); | |
if (r.contains(pos)) { | |
return; | |
} | |
} | |
/* calculate the direction in which to expand */ | |
if(pos.y() > d) { | |
direction += 2; | |
} | |
/* ... ? */ | |
if(pos.y() > (-d + h)) { | |
direction += 1; | |
} | |
/* | |
* break down to the actual direction and if we insert before or after | |
* the widget underneath | |
*/ | |
Qt::Orientation orientation = (direction == 0 or direction == 3) ? Qt::Vertical : Qt::Horizontal; | |
bool before =((direction % 2) == 0); | |
/* do not remove ourselves just to push us back afterwards */ | |
if(dragSource->parent() == splitter) { | |
int indexDrag = splitter->indexOf(dragSource); | |
int indexWidget = splitter->indexOf(widget); | |
int indexTarget = indexWidget + 1 - before -(indexDrag < indexWidget); | |
if(splitter->orientation() == orientation and indexTarget == indexDrag) { | |
return; | |
} | |
} | |
QRearrangeableLayout::removeWidget(dragSource); | |
splitter = static_cast<QSplitter *>(widget->parent()); | |
i = splitter->indexOf(widget); | |
if(splitter->orientation() == orientation) { | |
/* we want to insert the dragging source into the same direction the | |
* underneath splitter already is so we just need to put it in there | |
*/ | |
if(! before) { | |
i += 1; | |
} | |
splitter->insertWidget(i, dragSource); | |
} else { | |
/* here we put the dragging widget and widget underneath the mouse | |
* into a new splitter and put it to the index the widget was located | |
* before | |
*/ | |
widget->setParent(0); | |
QSplitter *newSplitter = new QSplitter(orientation); | |
if(before) { | |
newSplitter->addWidget(dragSource); | |
newSplitter->addWidget(widget); | |
} else { | |
newSplitter->addWidget(widget); | |
newSplitter->addWidget(dragSource); | |
} | |
splitter->insertWidget(i, newSplitter); | |
} | |
} | |
void QRearrangeableLayout::dropEvent(QDropEvent *event) | |
{ | |
dragRunning = false; | |
emit draggingEnded(dragSource, event->pos()); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef QREARRANGABLELAYOUT_H | |
#define QREARRANGABLELAYOUT_H | |
#include <QApplication> | |
#include <QWidget> | |
#include <QPoint> | |
#include <QSplitter> | |
#include <QEvent> | |
#include <QPoint> | |
#include <QDrag> | |
class QRearrangeableLayout : public QWidget | |
{ | |
Q_OBJECT | |
signals: | |
/* Gets emitted, when the user started dragging a widget. */ | |
void draggingStarted(QWidget *widget, QPoint point); | |
/* Gets emitted, when the user ended dragging a widget by releasing the mouse button. */ | |
void draggingEnded(QWidget *widget, QPoint point); | |
public: | |
QRearrangeableLayout(QApplication *app, QWidget *parent); | |
//~ ~QRearrangeableLayout(void); | |
void setRearrangeable(bool rearrangeable); | |
bool rearrangeable(void); | |
void dragEnterEvent(QDragEnterEvent *event); | |
void dragMoveEvent(QDragMoveEvent *event); | |
void dropEvent(QDropEvent *event); | |
bool eventFilter(QObject *source, QEvent *event); | |
private: | |
QApplication *app; | |
bool dragStarting; | |
bool dragRunning; | |
//~ bool setAcceptDrops[]; | |
bool _rearrangeable; | |
QWidget *dragSource; | |
QPoint dragPos; | |
static QSplitter* removeWidget(QWidget *widget); | |
static QWidget* findChildOfSplitter(QWidget *widget); | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment