summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTobias Markmann <tm@ayena.de>2014-11-25 14:07:29 (GMT)
committerKevin Smith <git@kismith.co.uk>2014-12-14 14:44:00 (GMT)
commitb4a54583c4d575fe152122c21da616c3c942bbfd (patch)
tree7e558fd9cb75291f4d5eec90da9d8dd5650ceea2 /Swift/QtUI/Trellis/QtDNDTabBar.cpp
parent92672e17a52d0c86e693183627c8c6b8fa44fb86 (diff)
downloadswift-b4a54583c4d575fe152122c21da616c3c942bbfd.zip
swift-b4a54583c4d575fe152122c21da616c3c942bbfd.tar.bz2
Add support for new trellis layout for chat windows.
This includes dynamic customizable grid layouting of chat tabs including: - arrengement of tabs via menu, keyboard shortcuts or drag'n'drop - change of grid size via mouse or keyboard - save/restore of grid size and positions of chats inside the grid Test-Information: Tested on OS X 10.9.8. Change-Id: I43f94293a1c672971640c3000abfc6530f2ea7a8
Diffstat (limited to 'Swift/QtUI/Trellis/QtDNDTabBar.cpp')
-rw-r--r--Swift/QtUI/Trellis/QtDNDTabBar.cpp151
1 files changed, 151 insertions, 0 deletions
diff --git a/Swift/QtUI/Trellis/QtDNDTabBar.cpp b/Swift/QtUI/Trellis/QtDNDTabBar.cpp
new file mode 100644
index 0000000..af9c0f5
--- /dev/null
+++ b/Swift/QtUI/Trellis/QtDNDTabBar.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2014 Kevin Smith and Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <Swift/QtUI/Trellis/QtDNDTabBar.h>
+
+#include <cassert>
+
+#include <QMouseEvent>
+#include <QDropEvent>
+#include <QDrag>
+#include <QMimeData>
+#include <QPainter>
+#include <QTabWidget>
+
+namespace Swift {
+
+QtDNDTabBar::QtDNDTabBar(QWidget* parent) : QTabBar(parent) {
+ setAcceptDrops(true);
+
+ // detect the default tab bar height;
+ insertTab(0, "");
+ defaultTabHeight = QTabBar::sizeHint().height();
+ removeTab(0);
+}
+
+QtDNDTabBar::~QtDNDTabBar() {
+
+}
+
+int QtDNDTabBar::getDragIndex() const {
+ return dragIndex;
+}
+
+QString QtDNDTabBar::getDragText() const {
+ return dragText;
+}
+
+QWidget* QtDNDTabBar::getDragWidget() const {
+ return dragWidget;
+}
+
+QSize QtDNDTabBar::sizeHint() const {
+ QSize hint = QTabBar::sizeHint();
+ if (hint.isEmpty()) {
+ hint = QSize(parentWidget()->width(), defaultTabHeight);
+ }
+ return hint;
+}
+
+void QtDNDTabBar::dragEnterEvent(QDragEnterEvent* dragEnterEvent) {
+ QtDNDTabBar* sourceTabBar = dynamic_cast<QtDNDTabBar*>(dragEnterEvent->source());
+ if (sourceTabBar) {
+ dragEnterEvent->acceptProposedAction();
+ }
+}
+
+void QtDNDTabBar::dropEvent(QDropEvent* dropEvent) {
+ QtDNDTabBar* sourceTabBar = dynamic_cast<QtDNDTabBar*>(dropEvent->source());
+ if (sourceTabBar && dropEvent->mimeData() && dropEvent->mimeData()->data("action") == QByteArray("application/tab-detach")) {
+ QtDNDTabBar* source = dynamic_cast<QtDNDTabBar*>(dropEvent->source());
+
+ int targetTabIndex = tabAt(dropEvent->pos());
+ QRect rect = tabRect(targetTabIndex);
+ if (targetTabIndex >= 0 && (dropEvent->pos().x() - rect.left() - rect.width()/2 > 0)) {
+ targetTabIndex++;
+ }
+
+ QWidget* tab = source->getDragWidget();
+ assert(tab);
+ QTabWidget* targetTabWidget = dynamic_cast<QTabWidget*>(parentWidget());
+
+ QString tabText = source->getDragText();
+
+ /*
+ * When you add a widget to an empty QTabWidget, it's automatically made the current widget.
+ * Making the widget the current widget, widget->show() is called for the widget. Directly reacting
+ * to that event, and adding the widget again to the QTabWidget results in undefined behavior. For
+ * example the tab label is shown but the widget is neither has the old nor in the new QTabWidget as
+ * parent. Blocking signals on the QWidget to be added to a QTabWidget prevents this behavior.
+ */
+ targetTabWidget->setUpdatesEnabled(false);
+ tab->blockSignals(true);
+ targetTabWidget->insertTab(targetTabIndex, tab, tabText);
+ dropEvent->acceptProposedAction();
+ tab->blockSignals(false);
+ targetTabWidget->setUpdatesEnabled(true);
+ onDropSucceeded();
+ }
+}
+
+bool QtDNDTabBar::event(QEvent* event) {
+ QMouseEvent* mouseEvent = dynamic_cast<QMouseEvent*>(event);
+ if (mouseEvent) {
+ QWidget* childAtPoint = window()->childAt(mapTo(window(), mouseEvent->pos()));
+ QtDNDTabBar* underMouse = dynamic_cast<QtDNDTabBar*>(childAtPoint);
+ if (!underMouse && childAtPoint) {
+ underMouse = dynamic_cast<QtDNDTabBar*>(childAtPoint->parent());
+ }
+ if (!underMouse && currentIndex() >= 0) {
+ // detach and drag
+
+ // stop move event
+ QMouseEvent* finishMoveEvent = new QMouseEvent (QEvent::MouseMove, mouseEvent->pos (), Qt::NoButton, Qt::NoButton, Qt::NoModifier);
+ QTabBar::event(finishMoveEvent);
+ delete finishMoveEvent;
+ finishMoveEvent = NULL;
+
+ // start drag
+ QDrag* drag = new QDrag(this);
+ QMimeData* mimeData = new QMimeData;
+
+ // distinguish tab-reordering drops from other ones
+ mimeData->setData("action", "application/tab-detach") ;
+ drag->setMimeData(mimeData);
+
+ // set drag image
+ QRect rect = tabRect( currentIndex() );
+#if QT_VERSION >= 0x050000
+ QPixmap pixmap = grab(rect);
+#else
+ QPixmap pixmap = QPixmap::grabWidget(this, rect);
+#endif
+ QPixmap targetPixmap (pixmap.size ());
+ QPainter painter (&targetPixmap);
+ painter.setOpacity(0.9);
+ painter.drawPixmap(0,0, pixmap);
+ painter.end ();
+ drag->setPixmap (targetPixmap);
+
+ drag->setHotSpot(QPoint(drag->pixmap().width()/2, drag->pixmap().height()));
+
+ dragIndex = currentIndex();
+ dragText = tabText(dragIndex);
+ dragWidget = dynamic_cast<QTabWidget*>(parent())->widget(dragIndex);
+ assert(dragWidget);
+ dynamic_cast<QTabWidget*>(parent())->removeTab(currentIndex());
+ Qt::DropAction dropAction = drag->exec();
+ if (dropAction == Qt::IgnoreAction) {
+ // aborted drag, put tab back in place
+ dynamic_cast<QTabWidget*>(parent())->insertTab(dragIndex, dragWidget, dragText);
+ }
+ return true;
+ }
+ }
+ return QTabBar::event(event);
+}
+
+}