diff options
| author | Tobias Markmann <tm@ayena.de> | 2014-11-25 14:07:29 (GMT) | 
|---|---|---|
| committer | Kevin Smith <git@kismith.co.uk> | 2014-12-14 14:44:00 (GMT) | 
| commit | b4a54583c4d575fe152122c21da616c3c942bbfd (patch) | |
| tree | 7e558fd9cb75291f4d5eec90da9d8dd5650ceea2 | |
| parent | 92672e17a52d0c86e693183627c8c6b8fa44fb86 (diff) | |
| download | swift-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
30 files changed, 1164 insertions, 92 deletions
| diff --git a/Swift/Controllers/SettingConstants.cpp b/Swift/Controllers/SettingConstants.cpp index a5328a4..a6756f5 100644 --- a/Swift/Controllers/SettingConstants.cpp +++ b/Swift/Controllers/SettingConstants.cpp @@ -25,4 +25,6 @@ const SettingsProvider::Setting<std::string> SettingConstants::DICT_PATH("dictPa  const SettingsProvider::Setting<std::string> SettingConstants::PERSONAL_DICT_PATH("personaldictPath", "/home/");  const SettingsProvider::Setting<std::string> SettingConstants::DICT_FILE("dictFile", "en_US.dic");  const SettingsProvider::Setting<std::string> SettingConstants::INVITE_AUTO_ACCEPT_MODE("inviteAutoAcceptMode", "presence"); +const SettingsProvider::Setting<std::string> SettingConstants::TRELLIS_GRID_SIZE("trellisGridSize", ""); +const SettingsProvider::Setting<std::string> SettingConstants::TRELLIS_GRID_POSITIONS("trellisGridPositions", "");  } diff --git a/Swift/Controllers/SettingConstants.h b/Swift/Controllers/SettingConstants.h index 4d25bde..90e1db6 100644 --- a/Swift/Controllers/SettingConstants.h +++ b/Swift/Controllers/SettingConstants.h @@ -28,5 +28,7 @@ namespace Swift {  			static const SettingsProvider::Setting<std::string> PERSONAL_DICT_PATH;  			static const SettingsProvider::Setting<std::string> DICT_FILE;  			static const SettingsProvider::Setting<std::string> INVITE_AUTO_ACCEPT_MODE; +			static const SettingsProvider::Setting<std::string> TRELLIS_GRID_SIZE; +			static const SettingsProvider::Setting<std::string> TRELLIS_GRID_POSITIONS;  	};  } diff --git a/Swift/QtUI/CocoaApplicationActivateHelper.h b/Swift/QtUI/CocoaApplicationActivateHelper.h index 8bc2af5..8ea86e0 100644 --- a/Swift/QtUI/CocoaApplicationActivateHelper.h +++ b/Swift/QtUI/CocoaApplicationActivateHelper.h @@ -16,7 +16,7 @@ namespace Swift {  	class CocoaApplicationActivateHelper : public QObject {  		public:  			CocoaApplicationActivateHelper(); -			~CocoaApplicationActivateHelper(); +			virtual ~CocoaApplicationActivateHelper();  		private:  			bool eventFilter(QObject* o, QEvent* e); diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index cf335e1..d04977e 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -1,72 +1,141 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ -#include "QtChatTabs.h" +#include <Swift/QtUI/QtChatTabs.h>  #include <algorithm>  #include <vector> +#include <Swiften/Base/Log.h> +  #include <Swift/Controllers/ChatMessageSummarizer.h> +#include <Swift/Controllers/SettingConstants.h> +#include <Swift/Controllers/Settings/SettingsProvider.h>  #include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtTabWidget.h> +#include <Swift/QtUI/QtTabbable.h> +#include <Swift/QtUI/Trellis/QtDynamicGridLayout.h> +#include <Swift/QtUI/Trellis/QtGridSelectionDialog.h> +#include <QAction> +#include <QApplication>  #include <QCloseEvent> +#include <QCursor>  #include <QDesktopWidget> -#include <QtGlobal> -#include <QTabWidget>  #include <QLayout> +#include <QMenu>  #include <QTabBar> -#include <QApplication> -#include <qdebug.h> +#include <QTabWidget> +#include <QtGlobal>  namespace Swift { -QtChatTabs::QtChatTabs(bool singleWindow) : QWidget(), singleWindow_(singleWindow) { +QtChatTabs::QtChatTabs(bool singleWindow, SettingsProvider* settingsProvider, bool trellisMode) : QWidget(), singleWindow_(singleWindow), settingsProvider_(settingsProvider), trellisMode_(trellisMode), dynamicGrid_(NULL), gridSelectionDialog_(NULL) {  #ifndef Q_OS_MAC  	setWindowIcon(QIcon(":/logo-chat-16.png"));  #else  	setAttribute(Qt::WA_ShowWithoutActivating);  #endif +	dynamicGrid_ = new QtDynamicGridLayout(this, trellisMode); +	connect(dynamicGrid_, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int))); -	tabs_ = new QtTabWidget(this); -	tabs_->setUsesScrollButtons(true); -	tabs_->setElideMode(Qt::ElideRight); -#if QT_VERSION >= 0x040500 -	/*For Macs, change the tab rendering.*/ -	tabs_->setDocumentMode(true); -	/*Closable tabs are only in Qt4.5 and later*/ -	tabs_->setTabsClosable(true); -	tabs_->setMovable(true); -	connect(tabs_, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int))); -#else -#warning Qt 4.5 or later is needed. Trying anyway, some things will be disabled. -#endif  	QVBoxLayout *layout = new QVBoxLayout;  	layout->setSpacing(0); -	layout->setContentsMargins(0, 3, 0, 0); -	layout->addWidget(tabs_); +	layout->setContentsMargins(0, 0, 0, 0); +	layout->addWidget(dynamicGrid_);  	setLayout(layout); + +	if (trellisMode) { +		// restore size +		std::string gridSizeString = settingsProvider->getSetting(SettingConstants::TRELLIS_GRID_SIZE); +		if (!gridSizeString.empty()) { +			QByteArray gridSizeData = QByteArray::fromBase64(P2QSTRING(gridSizeString).toUtf8()); +			QDataStream dataStreamGridSize(&gridSizeData, QIODevice::ReadWrite); +			QSize gridSize(1,1); +			dataStreamGridSize >> gridSize; +			dynamicGrid_->setDimensions(gridSize); +		} + +		// restore positions +		std::string tabPositionsString = settingsProvider->getSetting(SettingConstants::TRELLIS_GRID_POSITIONS); +		if (!tabPositionsString.empty()) { +			QByteArray tabPositionsData = QByteArray::fromBase64(P2QSTRING(tabPositionsString).toUtf8()); +			QDataStream inTabPositions(&tabPositionsData, QIODevice::ReadWrite); +			QHash<QString, QPoint> tabPositions; +			inTabPositions >> tabPositions; +			dynamicGrid_->setTabPositions(tabPositions); +		} +	} + +	gridSelectionDialog_ = new QtGridSelectionDialog(); +} + +QtChatTabs::~QtChatTabs() { +	if (trellisMode_) { +		storeTabPositions(); +	} +	delete gridSelectionDialog_;  }  void QtChatTabs::closeEvent(QCloseEvent* event) {  	//Hide first to prevent flickering as each tab is removed.  	hide(); -	for (int i = tabs_->count() - 1; i >= 0; i--) { -		tabs_->widget(i)->close(); +	if (trellisMode_) { +		storeTabPositions(); +	} + +	for (int i = dynamicGrid_->count() - 1; i >= 0; i--) { +		dynamicGrid_->widget(i)->close();  	}  	event->accept();  }  QtTabbable* QtChatTabs::getCurrentTab() { -	return qobject_cast<QtTabbable*>(tabs_->currentWidget()); +	return qobject_cast<QtTabbable*>(dynamicGrid_->currentWidget()); +} + +void QtChatTabs::setViewMenu(QMenu* viewMenu) { +	if (trellisMode_) { +		viewMenu->addSeparator(); +		QAction* action = new QAction(tr("Change &layout"), this); +		action->setShortcutContext(Qt::ApplicationShortcut); +		action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_L)); +		connect(action, SIGNAL(triggered()), this, SLOT(handleOpenLayoutChangeDialog())); +		viewMenu->addAction(action); + +		action = new QAction(tr("Move Tab right"), this); +		action->setShortcutContext(Qt::ApplicationShortcut); +		action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Right)); +		connect(action, SIGNAL(triggered()), dynamicGrid_, SLOT(moveCurrentTabRight())); +		viewMenu->addAction(action); + +		action = new QAction(tr("Move Tab left"), this); +		action->setShortcutContext(Qt::ApplicationShortcut); +		action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Left)); +		connect(action, SIGNAL(triggered()), dynamicGrid_, SLOT(moveCurrentTabLeft())); +		viewMenu->addAction(action); + +		action = new QAction(tr("Move Tab to next group"), this); +		action->setShortcutContext(Qt::ApplicationShortcut); +		action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Right)); +		connect(action, SIGNAL(triggered()), dynamicGrid_, SLOT(moveCurrentTabToNextGroup())); +		viewMenu->addAction(action); + +		action = new QAction(tr("Move Tab to previous group"), this); +		action->setShortcutContext(Qt::ApplicationShortcut); +		action->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Left)); +		connect(action, SIGNAL(triggered()), dynamicGrid_, SLOT(moveCurrentTabToPreviousGroup())); +		viewMenu->addAction(action); +	}  }  void QtChatTabs::addTab(QtTabbable* tab) {  	QSizePolicy policy = sizePolicy();  	/* Chat windows like to grow - don't let them */  	setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); -	tabs_->addTab(tab, tab->windowTitle()); +	dynamicGrid_->addTab(tab, tab->windowTitle());  	connect(tab, SIGNAL(titleUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection);  	connect(tab, SIGNAL(countUpdated()), this, SLOT(handleTabTitleUpdated()), Qt::UniqueConnection);  	connect(tab, SIGNAL(windowClosing()), this, SLOT(handleTabClosing()), Qt::UniqueConnection); @@ -85,7 +154,7 @@ void QtChatTabs::handleWidgetShown() {  		return;  	}  	checkForFirstShow(); -	if (tabs_->indexOf(widget) >= 0) { +	if (dynamicGrid_->indexOf(widget) >= 0) {  		handleTabTitleUpdated(widget);  		return;  	} @@ -101,7 +170,7 @@ void QtChatTabs::handleWantsToActivate() {  	setWindowState(windowState() | Qt::WindowActive);  	show();  	widget->show(); -	tabs_->setCurrentWidget(widget); +	dynamicGrid_->setCurrentWidget(widget);  	handleTabTitleUpdated(widget);  	widget->setFocus();  	raise(); @@ -111,9 +180,9 @@ void QtChatTabs::handleWantsToActivate() {  void QtChatTabs::handleTabClosing() {  	QWidget* widget = qobject_cast<QWidget*>(sender());  	int index; -	if (widget && ((index = tabs_->indexOf(widget)) >= 0)) { -		tabs_->removeTab(index); -		if (tabs_->count() == 0) { +	if (widget && ((index = dynamicGrid_->indexOf(widget)) >= 0)) { +		dynamicGrid_->removeTab(index); +		if (dynamicGrid_->count() == 0) {  			if (!singleWindow_) {  				hide();  			} @@ -123,19 +192,19 @@ void QtChatTabs::handleTabClosing() {  			}  		}  		else { -			handleTabTitleUpdated(tabs_->currentWidget()); +			handleTabTitleUpdated(dynamicGrid_->currentWidget());  		}  	}  }  void QtChatTabs::handleRequestedPreviousTab() { -	int newIndex = tabs_->currentIndex() - 1; -	tabs_->setCurrentIndex(newIndex >= 0 ? newIndex : tabs_->count() - 1); +	int newIndex = dynamicGrid_->currentIndex() - 1; +	dynamicGrid_->setCurrentIndex(newIndex >= 0 ? newIndex : dynamicGrid_->count() - 1);  }  void QtChatTabs::handleRequestedNextTab() { -	int newIndex = tabs_->currentIndex() + 1; -	tabs_->setCurrentIndex(newIndex < tabs_->count() ? newIndex : 0); +	int newIndex = dynamicGrid_->currentIndex() + 1; +	dynamicGrid_->setCurrentIndex(newIndex < dynamicGrid_->count() ? newIndex : 0);  }  void QtChatTabs::handleRequestedActiveTab() { @@ -143,16 +212,16 @@ void QtChatTabs::handleRequestedActiveTab() {  	bool finished = false;  	for (int j = 0; j < 2; j++) {  		bool looped = false; -		for (int i = tabs_->currentIndex() + 1; !finished && i != tabs_->currentIndex(); i++) { -			if (i >= tabs_->count()) { +		for (int i = dynamicGrid_->currentIndex() + 1; !finished && i != dynamicGrid_->currentIndex(); i++) { +			if (i >= dynamicGrid_->count()) {  				if (looped) {  					break;  				}  				looped = true;  				i = 0;  			} -			if (qobject_cast<QtTabbable*>(tabs_->widget(i))->getWidgetAlertState() == types[j]) { -				tabs_->setCurrentIndex(i); +			if (qobject_cast<QtTabbable*>(dynamicGrid_->widget(i))->getWidgetAlertState() == types[j]) { +				dynamicGrid_->setCurrentIndex(i);  				finished = true;  				break;  			} @@ -162,7 +231,13 @@ void QtChatTabs::handleRequestedActiveTab() {  void QtChatTabs::handleTabCloseRequested(int index) { -	QWidget* widget = tabs_->widget(index); +	if (trellisMode_) { +		storeTabPositions(); +	} + +	assert(index < dynamicGrid_->count()); +	QWidget* widget = dynamicGrid_->widget(index); +	assert(widget);  	widget->close();  } @@ -176,7 +251,7 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  		return;  	}  	QtTabbable* tabbable = qobject_cast<QtTabbable*>(widget); -	int index = tabs_->indexOf(widget); +	int index = dynamicGrid_->indexOf(widget);  	if (index < 0) {  		return;  	} @@ -202,11 +277,14 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  		accelsTaken[i] = (i == 0); //A is used for 'switch to active tab'  		i++;  	} -	int other = tabs_->tabBar()->count(); + +	int other = dynamicGrid_->count();  	while (other >= 0) {  		other--;  		if (other != index) { -			QString t = tabs_->tabBar()->tabText(other).toLower(); +			int tabIndex = -1; +			QtTabWidget* tabWidget = dynamicGrid_->indexToTabWidget(other, tabIndex); +			QString t = tabWidget->tabBar()->tabText(tabIndex).toLower();  			int r = t.indexOf('&');  			if (r >= 0 && t[r+1] >= 'a' && t[r+1] <= 'z') {  				accelsTaken[t[r+1].unicode()-'a'] = true; @@ -235,24 +313,26 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) {  	// doesn't work on Arabic/Indic keyboards (where Latin letters  	// aren't available), but I don't care to deal with those. -	tabs_->setTabText(index, tabbable->getCount() > 0 ? QString("(%1) %2").arg(tabbable->getCount()).arg(tabText) : tabText); +	int tabIndex = -1; +	QtTabWidget* tabWidget = dynamicGrid_->indexToTabWidget(index, tabIndex); +	tabWidget->setTabText(tabIndex, tabbable->getCount() > 0 ? QString("(%1) %2").arg(tabbable->getCount()).arg(tabText) : tabText);  	QColor tabTextColor;  	switch (tabbable->getWidgetAlertState()) {  	case QtTabbable::WaitingActivity : tabTextColor = QColor(217, 20, 43); break;  	case QtTabbable::ImpendingActivity : tabTextColor = QColor(27, 171, 32); break;  	case QtTabbable::NoActivity : tabTextColor = QColor(); break;  	} -	tabs_->tabBar()->setTabTextColor(index, tabTextColor); +	tabWidget->tabBar()->setTabTextColor(tabIndex, tabTextColor);  	std::vector<std::pair<std::string, int> > unreads; -	for (int i = 0; i < tabs_->count(); i++) { -		QtTabbable* tab = qobject_cast<QtTabbable*>(tabs_->widget(i)); +	for (int i = 0; i < dynamicGrid_->count(); i++) { +		QtTabbable* tab = qobject_cast<QtTabbable*>(dynamicGrid_->widget(i));  		if (tab) {  			unreads.push_back(std::pair<std::string, int>(Q2PSTRING(tab->windowTitle()), tab->getCount()));  		}  	} -	std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(tabs_->currentWidget())->windowTitle())); +	std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(dynamicGrid_->currentWidget())->windowTitle()));  	ChatMessageSummarizer summary;  	QString title = summary.getSummary(current, unreads).c_str();  	setWindowTitle(title); @@ -265,6 +345,29 @@ void QtChatTabs::flash() {  #endif  } +void QtChatTabs::handleOpenLayoutChangeDialog() { +	disconnect(gridSelectionDialog_, SIGNAL(currentGridSizeChanged(QSize)), dynamicGrid_, SLOT(setDimensions(QSize))); +	gridSelectionDialog_->setCurrentGridSize(dynamicGrid_->getDimension()); +	gridSelectionDialog_->move(QCursor::pos()); +	connect(gridSelectionDialog_, SIGNAL(currentGridSizeChanged(QSize)), dynamicGrid_, SLOT(setDimensions(QSize))); +	gridSelectionDialog_->show(); +} + +void QtChatTabs::storeTabPositions() { +	// save size +	QByteArray gridSizeData; +	QDataStream dataStreamGridSize(&gridSizeData, QIODevice::ReadWrite); +	dataStreamGridSize << dynamicGrid_->getDimension(); +	settingsProvider_->storeSetting(SettingConstants::TRELLIS_GRID_SIZE, Q2PSTRING(QString(gridSizeData.toBase64()))); + +	// save positions +	QByteArray tabPositionsData; +	QDataStream dataStreamTabPositions(&tabPositionsData, QIODevice::ReadWrite); +	dynamicGrid_->updateTabPositions(); +	dataStreamTabPositions << dynamicGrid_->getTabPositions(); +	settingsProvider_->storeSetting(SettingConstants::TRELLIS_GRID_POSITIONS, Q2PSTRING(QString(tabPositionsData.toBase64()))); +} +  void QtChatTabs::resizeEvent(QResizeEvent*) {  	emit geometryChanged();  } diff --git a/Swift/QtUI/QtChatTabs.h b/Swift/QtUI/QtChatTabs.h index f9cd685..99ab74a 100644 --- a/Swift/QtUI/QtChatTabs.h +++ b/Swift/QtUI/QtChatTabs.h @@ -1,26 +1,36 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once -#include "QtTabbable.h" -#include "QtTabWidget.h"  #include <QWidget>  #include <QRect>  class QTabWidget; +class QMenu;  namespace Swift { +	class SettingsProvider; + +	class QtTabbable; +	class QtTabWidget; +	class QtDynamicGridLayout; +	class QtGridSelectionDialog; +  	class QtChatTabs : public QWidget {  		Q_OBJECT  		public: -			QtChatTabs(bool singleWindow); +			QtChatTabs(bool singleWindow, SettingsProvider* settingsProvider, bool trellisMode); +			virtual ~QtChatTabs(); +  			void addTab(QtTabbable* tab);  			void minimise();  			QtTabbable* getCurrentTab(); +			void setViewMenu(QMenu* viewMenu); +  		signals:  			void geometryChanged();  			void onTitleChanged(const QString& title); @@ -42,10 +52,18 @@ namespace Swift {  			void handleRequestedActiveTab();  			void flash(); +			void handleOpenLayoutChangeDialog(); +  		private: +			void storeTabPositions();  			void checkForFirstShow(); -			QtTabWidget* tabs_; + +		private:  			bool singleWindow_; +			SettingsProvider* settingsProvider_; +			bool trellisMode_; +			QtDynamicGridLayout* dynamicGrid_; +			QtGridSelectionDialog* gridSelectionDialog_;  	};  } diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index a54bf8d..964f67d 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -57,7 +57,7 @@  namespace Swift { -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) { +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), id_(Q2PSTRING(contact)), contact_(contact), nextAlertId_(0), eventStream_(eventStream), blockingState_(BlockingUnsupported), isMUC_(false), supportsImpromptuChat_(false) {  	settings_ = settings;  	unreadCount_ = 0;  	isOnline_ = true; @@ -170,7 +170,7 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt  	settings_->onSettingChanged.connect(boost::bind(&QtChatWindow::handleSettingChanged, this, _1));  	messageLog_->showEmoticons(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS)); - +	setMinimumSize(100, 100);  }  QtChatWindow::~QtChatWindow() { @@ -775,6 +775,10 @@ void QtChatWindow::showBookmarkWindow(const MUCBookmark& bookmark) {  	window->show();  } +std::string QtChatWindow::getID() const { +	return id_; +} +  void QtChatWindow::showRoomConfigurationForm(Form::ref form) {  	if (mucConfigurationWindow_) {  		delete mucConfigurationWindow_.data(); diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index a73782a..b3d8c3e 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -78,7 +78,7 @@ namespace Swift {  		public:  			QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings); -			~QtChatWindow(); +			virtual ~QtChatWindow();  			std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight);  			std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); @@ -130,6 +130,7 @@ namespace Swift {  			void setBlockingState(BlockingState state);  			virtual void setCanInitiateImpromptuChats(bool supportsImpromptu);  			virtual void showBookmarkWindow(const MUCBookmark& bookmark); +			virtual std::string getID() const;  		public slots:  			void handleChangeSplitterState(QByteArray state); @@ -178,10 +179,11 @@ namespace Swift {  			void handleOccupantSelectionChanged(RosterItem* item);  			void handleAppendedToLog(); - +		private:  			int unreadCount_;  			bool contactIsTyping_;  			LastLineTracker lastLineTracker_; +			std::string id_;  			QString contact_;  			QString lastSentMessage_;  			QTextCursor tabCompleteCursor_; diff --git a/Swift/QtUI/QtFileTransferListWidget.cpp b/Swift/QtUI/QtFileTransferListWidget.cpp index 01c632f..fb2b4de 100644 --- a/Swift/QtUI/QtFileTransferListWidget.cpp +++ b/Swift/QtUI/QtFileTransferListWidget.cpp @@ -69,6 +69,10 @@ void QtFileTransferListWidget::setFileTransferOverview(FileTransferOverview *ove  	itemModel->setFileTransferOverview(overview);  } +std::string QtFileTransferListWidget::getID() const { +	return "QtFileTransferListWidget"; +} +  void QtFileTransferListWidget::closeEvent(QCloseEvent* event) {  	emit windowClosing();  	event->accept(); diff --git a/Swift/QtUI/QtFileTransferListWidget.h b/Swift/QtUI/QtFileTransferListWidget.h index c828d4e..8adc009 100644 --- a/Swift/QtUI/QtFileTransferListWidget.h +++ b/Swift/QtUI/QtFileTransferListWidget.h @@ -31,6 +31,8 @@ public:  	void setFileTransferOverview(FileTransferOverview *); +	virtual std::string getID() const; +  private:  	virtual void closeEvent(QCloseEvent* event);  	virtual void showEvent(QShowEvent* event); diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp index 75eeaad..ae03d7e 100644 --- a/Swift/QtUI/QtHistoryWindow.cpp +++ b/Swift/QtUI/QtHistoryWindow.cpp @@ -261,4 +261,8 @@ boost::gregorian::date QtHistoryWindow::getLastVisibleDate() {  	return boost::gregorian::date(boost::gregorian::not_a_date_time);  } +std::string QtHistoryWindow::getID() const { +	return "QtHistoryWindow"; +} +  } diff --git a/Swift/QtUI/QtHistoryWindow.h b/Swift/QtUI/QtHistoryWindow.h index fcbfd7e..04ea61d 100644 --- a/Swift/QtUI/QtHistoryWindow.h +++ b/Swift/QtUI/QtHistoryWindow.h @@ -49,6 +49,8 @@ namespace Swift {  			std::string getSearchBoxText();  			boost::gregorian::date getLastVisibleDate(); +			virtual std::string getID() const; +  		signals:  			void fontResized(int); diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index eeb4317..21095f6 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -490,7 +490,9 @@ void QtLoginWindow::morphInto(MainWindow *mainWindow) {  	stack_->setCurrentWidget(qtMainWindow);  	setEnabled(true);  	setInitialMenus(); -	foreach (QMenu* menu, qtMainWindow->getMenus()) { +	std::vector<QMenu*> mainWindowMenus = qtMainWindow->getMenus(); +	viewMenu_ = mainWindowMenus[0]; +	foreach (QMenu* menu, mainWindowMenus) {  		menuBar_->addMenu(menu);  	}  	setFocus(); diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index 7415fbf..55412bf 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -97,6 +97,7 @@ namespace Swift {  			QMenuBar* menuBar_;  			QMenu* swiftMenu_;  			QMenu* generalMenu_; +			QMenu* viewMenu_;  			QAction* toggleSoundsAction_;  			QAction* toggleNotificationsAction_;  			UIEventStream* uiEventStream_; diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 52b6bcc..55c2727 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -6,41 +6,41 @@  #include <Swift/QtUI/QtMainWindow.h> -#include <boost/optional.hpp>  #include <boost/bind.hpp> +#include <boost/optional.hpp>  #include <boost/smart_ptr/make_shared.hpp> +#include <QAction>  #include <QBoxLayout>  #include <QComboBox>  #include <QLineEdit>  #include <QListWidget>  #include <QListWidgetItem> -#include <QPushButton>  #include <QMenuBar> -#include <QToolBar> -#include <QAction> +#include <QPushButton>  #include <QTabWidget> +#include <QToolBar>  #include <Swiften/Base/Platform.h> -#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> -#include <Swift/Controllers/UIEvents/RequestHistoryUIEvent.h> -#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h> -#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> -#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> +#include <Swift/Controllers/SettingConstants.h>  #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestAddUserDialogUIEvent.h>  #include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h> -#include <Swift/Controllers/SettingConstants.h> +#include <Swift/Controllers/UIEvents/RequestChatWithUserDialogUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestHistoryUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestJoinMUCUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> -#include <Swift/QtUI/Roster/QtFilterWidget.h> +#include <Swift/QtUI/QtAdHocCommandWithJIDWindow.h> +#include <Swift/QtUI/QtLoginWindow.h> +#include <Swift/QtUI/QtSettingsProvider.h>  #include <Swift/QtUI/QtSwiftUtil.h>  #include <Swift/QtUI/QtTabWidget.h> -#include <Swift/QtUI/QtSettingsProvider.h> -#include <Swift/QtUI/QtLoginWindow.h> -#include <Swift/QtUI/Roster/QtRosterWidget.h>  #include <Swift/QtUI/QtUISettingConstants.h> -#include <Swift/QtUI/QtAdHocCommandWithJIDWindow.h> +#include <Swift/QtUI/Roster/QtFilterWidget.h> +#include <Swift/QtUI/Roster/QtRosterWidget.h>  #if defined(SWIFTEN_PLATFORM_MACOSX)  #include <Swift/QtUI/CocoaUIHelpers.h>  #elif defined(SWIFTEN_PLATFORM_WINDOWS) @@ -139,12 +139,6 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr  		handleShowEmoticonsToggled(settings_->getSetting(QtUISettingConstants::SHOW_EMOTICONS));  	} -	//QAction* compactRosterAction_ = new QAction(tr("&Compact Roster"), this); -	//compactRosterAction_->setCheckable(true); -	//compactRosterAction_->setChecked(false); -	//connect(compactRosterAction_, SIGNAL(toggled(bool)), uiPreferences_, SLOT(setCompactRosters(bool))); -	//viewMenu->addAction(compactRosterAction_); -  	QMenu* actionsMenu = new QMenu(tr("&Actions"), this);  	menus_.push_back(actionsMenu);  	QAction* editProfileAction = new QAction(tr("Edit &Profile…"), this); diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index d2224ba..5e798cf 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2013 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ @@ -90,6 +90,7 @@ po::options_description QtSwift::getOptionsDescription() {  		("multi-account", po::value<int>()->default_value(1), "Number of accounts to open windows for (unsupported)")  		("start-minimized", "Don't show the login/roster window at startup")  		("enable-jid-adhocs", "Enable AdHoc commands to custom JID's.") +		("trellis", "Enable support for trellis layout")  #if QT_VERSION >= 0x040800  		("language", po::value<std::string>(), "Use a specific language, instead of the system-wide one")  #endif @@ -164,7 +165,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa  	}  	bool enableAdHocCommandOnJID = options.count("enable-jid-adhocs") > 0; -	tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(splitter_ != NULL); +	tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(splitter_ != NULL, settingsHierachy_, options.count("trellis"));  	bool startMinimized = options.count("start-minimized") > 0;  	applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME);  	storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider()); diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 1ea8886..fb8224c 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -1,5 +1,5 @@  /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */ diff --git a/Swift/QtUI/QtTabWidget.cpp b/Swift/QtUI/QtTabWidget.cpp index 614439a..a5e6cdc 100644 --- a/Swift/QtUI/QtTabWidget.cpp +++ b/Swift/QtUI/QtTabWidget.cpp @@ -20,4 +20,7 @@ QTabBar* QtTabWidget::tabBar() {  	return QTabWidget::tabBar();  } +void QtTabWidget::setTabBar(QTabBar* tabBar) { +	QTabWidget::setTabBar(tabBar); +}  } diff --git a/Swift/QtUI/QtTabWidget.h b/Swift/QtUI/QtTabWidget.h index e4a44ce..7a73b75 100644 --- a/Swift/QtUI/QtTabWidget.h +++ b/Swift/QtUI/QtTabWidget.h @@ -5,14 +5,17 @@   */  #pragma once +  #include <QTabWidget>  namespace Swift {  	class QtTabWidget : public QTabWidget { -		Q_OBJECT +	Q_OBJECT  		public:  			QtTabWidget(QWidget* parent); -			~QtTabWidget(); +			virtual ~QtTabWidget(); +  			QTabBar* tabBar(); +			void setTabBar(QTabBar* tabBar);  	};  } diff --git a/Swift/QtUI/QtTabbable.cpp b/Swift/QtUI/QtTabbable.cpp index 124d1b4..cc901c7 100644 --- a/Swift/QtUI/QtTabbable.cpp +++ b/Swift/QtUI/QtTabbable.cpp @@ -8,6 +8,7 @@  #include <QApplication>  #include <QKeyEvent> +#include <QShortcut>  #include <Swiften/Base/foreach.h>  #include <Swiften/Base/Platform.h> @@ -45,13 +46,17 @@ bool QtTabbable::event(QEvent* event) {  	if (keyEvent) {  		// According to Qt's focus documentation, one can only override CTRL+TAB via reimplementing QWidget::event().  #ifdef SWIFTEN_PLATFORM_LINUX -		if (keyEvent->modifiers() == QtUtilities::ctrlHardwareKeyModifier && keyEvent->key() == Qt::Key_Tab && event->type() != QEvent::KeyRelease) { +		if (keyEvent->modifiers().testFlag(QtUtilities::ctrlHardwareKeyModifier) && keyEvent->key() == Qt::Key_Tab && event->type() != QEvent::KeyRelease) {  #else -		if (keyEvent->modifiers() == QtUtilities::ctrlHardwareKeyModifier && keyEvent->key() == Qt::Key_Tab) { +		if (keyEvent->modifiers().testFlag(QtUtilities::ctrlHardwareKeyModifier) && keyEvent->key() == Qt::Key_Tab) {  #endif  			emit requestNextTab();  			return true;  		} +		else if (keyEvent->modifiers().testFlag(QtUtilities::ctrlHardwareKeyModifier) && keyEvent->key() == Qt::Key_Backtab) { +			emit requestPreviousTab(); +			return true; +		}  	}  	return QWidget::event(event);  } diff --git a/Swift/QtUI/QtTabbable.h b/Swift/QtUI/QtTabbable.h index fc131ca..7201efe 100644 --- a/Swift/QtUI/QtTabbable.h +++ b/Swift/QtUI/QtTabbable.h @@ -1,15 +1,17 @@  /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2014 Kevin Smith   * Licensed under the GNU General Public License v3.   * See Documentation/Licenses/GPLv3.txt for more information.   */  #pragma once -#include <QWidget> -#include <QShortcut> +#include <string> +  #include <QList> +#include <QWidget> +class QShortcut;  namespace Swift {  	class QtTabbable : public QWidget { @@ -20,6 +22,8 @@ namespace Swift {  			bool isWidgetSelected();  			virtual AlertType getWidgetAlertState() {return NoActivity;}  			virtual int getCount() {return 0;} +			virtual std::string getID() const = 0; +  		protected:  			QtTabbable();  			bool event(QEvent* event); diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index b0c1492..e99b305 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -82,6 +82,7 @@ FileTransferListWidget* QtUIFactory::createFileTransferListWidget() {  MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) {  	lastMainWindow  = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), statusCache, emoticonsExist_, enableAdHocCommandOnJID_); +	tabs->setViewMenu(lastMainWindow->getMenus()[0]);  	return lastMainWindow;  } diff --git a/Swift/QtUI/QtXMLConsoleWidget.cpp b/Swift/QtUI/QtXMLConsoleWidget.cpp index f8ab28e..f125fc0 100644 --- a/Swift/QtUI/QtXMLConsoleWidget.cpp +++ b/Swift/QtUI/QtXMLConsoleWidget.cpp @@ -89,6 +89,10 @@ void QtXMLConsoleWidget::handleDataWritten(const SafeByteArray& data) {  	appendTextIfEnabled(tag + "\n" + safeByteArrayToString(data) + "\n", QColor(155,1,0));  } +std::string QtXMLConsoleWidget::getID() const { +	return "QtXMLConsoleWidget"; +} +  void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QColor& color) {  	if (enabled->isChecked()) {  		QScrollBar* scrollBar = textEdit->verticalScrollBar(); diff --git a/Swift/QtUI/QtXMLConsoleWidget.h b/Swift/QtUI/QtXMLConsoleWidget.h index 912ad90..3cf27cf 100644 --- a/Swift/QtUI/QtXMLConsoleWidget.h +++ b/Swift/QtUI/QtXMLConsoleWidget.h @@ -27,6 +27,8 @@ namespace Swift {  			virtual void handleDataRead(const SafeByteArray& data);  			virtual void handleDataWritten(const SafeByteArray& data); +			virtual std::string getID() const; +  		private:  			virtual void closeEvent(QCloseEvent* event);  			virtual void showEvent(QShowEvent* event); diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript index 80daaea..6e90cd4 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -193,7 +193,10 @@ sources = [      "QtUISettingConstants.cpp",      "QtURLValidator.cpp",      "QtResourceHelper.cpp", -    "QtSpellCheckHighlighter.cpp" +    "QtSpellCheckHighlighter.cpp", +    "Trellis/QtDynamicGridLayout.cpp", +    "Trellis/QtDNDTabBar.cpp", +    "Trellis/QtGridSelectionDialog.cpp"    ]  # QtVCardWidget 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); +} + +} diff --git a/Swift/QtUI/Trellis/QtDNDTabBar.h b/Swift/QtUI/Trellis/QtDNDTabBar.h new file mode 100644 index 0000000..4f26bc1 --- /dev/null +++ b/Swift/QtUI/Trellis/QtDNDTabBar.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#pragma once + +#include <QTabBar> + +namespace Swift { + +class QtDNDTabBar : public QTabBar { +	Q_OBJECT +	public: +		explicit QtDNDTabBar(QWidget* parent = 0); +		virtual ~QtDNDTabBar(); + +		int getDragIndex() const; +		QString getDragText() const; +		QWidget* getDragWidget() const; + +		virtual QSize sizeHint() const; + +	signals: +		void onDropSucceeded(); + +	protected: +		void dragEnterEvent(QDragEnterEvent* dragEnterEvent); +		void dropEvent(QDropEvent* dropEvent); +		bool event(QEvent* event); + +	private: +		int defaultTabHeight; +		int dragIndex; +		QString dragText; +		QWidget* dragWidget; +}; + +} diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp new file mode 100644 index 0000000..3b33dd3 --- /dev/null +++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.cpp @@ -0,0 +1,431 @@ +/* + * 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/QtDynamicGridLayout.h> + +#include <cassert> + +#include <QLayoutItem> +#include <QGridLayout> +#include <QtDebug> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtTabbable.h> +#include <Swift/QtUI/QtTabWidget.h> +#include <Swift/QtUI/Trellis/QtDNDTabBar.h> + +namespace Swift { + +QtDynamicGridLayout::QtDynamicGridLayout(QWidget* parent, bool enableDND) : QWidget(parent), dndEnabled_(enableDND) { +	gridLayout_ = new QGridLayout(this); +	setContentsMargins(0,0,0,0); +	setDimensions(QSize(1,1)); +} + +QtDynamicGridLayout::~QtDynamicGridLayout() { +} + +int QtDynamicGridLayout::addTab(QtTabbable* tab, const QString& title) { +	assert(gridLayout_->rowCount() > 0 && gridLayout_->columnCount() > 0); +	int index = -1; + +	QPoint lastPos(0,0); +	if (tabPositions_.contains(P2QSTRING(tab->getID()))) { +		lastPos = tabPositions_[P2QSTRING(tab->getID())]; +	} + +	lastPos = QPoint(qMin(lastPos.x(), gridLayout_->columnCount() - 1), qMin(lastPos.y(), gridLayout_->rowCount() - 1)); + +	QLayoutItem* item = gridLayout_->itemAtPosition(lastPos.y(), lastPos.x()); +	QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(item ? item->widget() : 0); +	if (tabWidget) { +		index = tabWidget->addTab(tab, title); +	} +	return tabWidget ? indexOf(tab) : -1; +} + +int QtDynamicGridLayout::count() const { +	int count = 0; +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			if (tabWidget) { +				count += tabWidget->count(); +			} +		} +	} +	return count; +} + +QWidget* QtDynamicGridLayout::widget(int index) const { +	QWidget* widgetAtIndex = NULL; +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			if (tabWidget) { +				if (index < tabWidget->count()) { +					widgetAtIndex = tabWidget->widget(index); +					return widgetAtIndex; +				} +				else { +					index -= tabWidget->count(); +				} +			} +		} +	} +	return widgetAtIndex; +} + +int QtDynamicGridLayout::indexOf(const QWidget* widget) const { +	int index = 0; +	if (widget) { +		for (int y = 0; y < gridLayout_->rowCount(); y++) { +			for (int x = 0; x < gridLayout_->columnCount(); x++) { +				QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +				QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +				if (tabWidget) { +					for (int n = 0; n < tabWidget->count(); n++) { +						QWidget* nthWidget = tabWidget->widget(n); +						if (nthWidget == widget) { +							return index; +						} +						index++; +					} +				} +			} +		} +	} +	return -1; +} + +int QtDynamicGridLayout::currentIndex() const { +	return indexOf(currentWidget()); +} + +void QtDynamicGridLayout::setCurrentIndex(int index) { +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); +	if (tabIndex >= 0) { +		tabWidget->setCurrentIndex(tabIndex); +		if (!tabWidget->hasFocus()) { +			tabWidget->widget(tabIndex)->setFocus(Qt::TabFocusReason); +		} +	} else { +		assert(false); +	} +} + +void QtDynamicGridLayout::removeTab(int index) { +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); +	if (tabWidget) { +		tabWidget->removeTab(tabIndex); +	} +} + +QWidget* QtDynamicGridLayout::currentWidget() const { +	QWidget* current = NULL; +	current = focusWidget(); +	while (current && !dynamic_cast<QtTabbable*>(current)) { +		if (current->parentWidget()) { +			current = current->parentWidget(); +		} else { +			current = NULL; +			break; +		} +	} +	if (!current) { +		current = widget(0); +	} +	return current; +} + +void QtDynamicGridLayout::setCurrentWidget(QWidget* widget) { +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			if (tabWidget) { +				for (int n = 0; n < tabWidget->count(); n++) { +					if (tabWidget->widget(n) == widget) { +						tabWidget->setCurrentWidget(widget); +					} +				} +			} +		} +	} +} + +QtTabWidget* QtDynamicGridLayout::indexToTabWidget(int index, int& tabIndex) { +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			if (tabWidget) { +				if (index < tabWidget->count()) { +					tabIndex = index; +					return tabWidget; +				} +				else { +					index -= tabWidget->count(); +					if (index < 0) { +						qWarning() << "Called QtDynamicGridLayout::setCurrentIndex with index out of bounds: index = " << index; +						tabIndex = -1; +						return NULL; +					} +				} +			} +		} +	} +	tabIndex = -1; +	return NULL; +} + +bool QtDynamicGridLayout::isDNDEnabled() const { +	return dndEnabled_; +} + +QHash<QString, QPoint> QtDynamicGridLayout::getTabPositions() const { +	return tabPositions_; +} + +void QtDynamicGridLayout::setTabPositions(const QHash<QString, QPoint> positions) { +	tabPositions_ = positions; +} + +QSize QtDynamicGridLayout::getDimension() const { +	return QSize(gridLayout_->columnCount(), gridLayout_->rowCount()); +} + +void QtDynamicGridLayout::setDimensions(const QSize& dim) { +	assert(dim.width() > 0 && dim.height() > 0); +	setUpdatesEnabled(false); + +	QGridLayout* oldLayout = dynamic_cast<QGridLayout*>(layout()); +	QGridLayout* newLayout = new QGridLayout; +	newLayout->setSpacing(4); +	newLayout->setContentsMargins(0,0,0,0); + +	int oldWidth = oldLayout->columnCount(); +	int oldHeight = oldLayout->rowCount(); +	int maxCol = qMax(oldWidth, dim.width()); +	int minCol = qMin(oldWidth, dim.width()); +	int maxRow = qMax(oldHeight, dim.height()); +	int minRow = qMin(oldHeight, dim.height()); + +	for (int row = 0; row < maxRow; row++) { +		for (int col = 0; col < maxCol; col++) { +			QLayoutItem* oldItem = oldLayout->itemAtPosition(row, col); +			QLayoutItem* newItem = newLayout->itemAtPosition(row, col); +			bool removeRow = !(row < dim.height()); +			bool removeCol = !(col < dim.width()); + +			if (removeCol || removeRow) { +				if (oldItem) { +					int squeezeRow = removeRow ? (minRow - 1) : row; +					int squeezeCol = removeCol ? (minCol - 1) : col; +					newItem = newLayout->itemAtPosition(squeezeRow, squeezeCol); +					if (!newItem) { +						newLayout->addWidget(createDNDTabWidget(this), squeezeRow, squeezeCol); +						newItem = newLayout->itemAtPosition(squeezeRow, squeezeCol); +					} +					QtTabWidget* oldTabWidget = dynamic_cast<QtTabWidget*>(oldItem->widget()); +					QtTabWidget* newTabWidget = dynamic_cast<QtTabWidget*>(newItem->widget()); +					assert(oldTabWidget && newTabWidget); + +					oldTabWidget->hide(); +					while(oldTabWidget->count()) { +						QIcon icon = oldTabWidget->tabIcon(0); +						QString text = oldTabWidget->tabText(0); +						newTabWidget->addTab(oldTabWidget->widget(0), icon, text); +					} +					delete oldTabWidget; +				} +			} else { +				if (oldItem) { +					newLayout->addWidget(oldItem->widget(), row, col); +					newItem = newLayout->itemAtPosition(row, col); +				} else { +					newLayout->addWidget(createDNDTabWidget(this), row, col); +				} +			} +		} +	} + +	for (int col = 0; col < dim.width(); col++) { +		newLayout->setColumnStretch(col, 1); +	} +	for (int row = 0; row < dim.height(); row++) { +		newLayout->setRowStretch(row, 1); +	} + +	setUpdatesEnabled(true); +	delete layout(); +	setLayout(newLayout); +	gridLayout_ = newLayout; +} + +void QtDynamicGridLayout::moveCurrentTabRight() { +	int index = currentIndex(); +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); +	assert(tabWidget); +	int newTabIndex = (tabIndex + 1) % tabWidget->count(); +	moveTab(tabWidget, tabIndex, newTabIndex); +} + +void QtDynamicGridLayout::moveCurrentTabLeft() { +	int index = currentIndex(); +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); +	assert(tabWidget); +	int newTabIndex = (tabWidget->count() + tabIndex - 1) % tabWidget->count(); +	moveTab(tabWidget, tabIndex, newTabIndex); +} + +void QtDynamicGridLayout::moveCurrentTabToNextGroup() { +	int index = currentIndex(); +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); + +	int row = -1; +	int col = -1; +	int tmp; +	gridLayout_->getItemPosition(gridLayout_->indexOf(tabWidget), &row, &col, &tmp, &tmp); + +	// calculate next cell +	col++; +	if (!(col < gridLayout_->columnCount())) { +		col = 0; +		row++; +		if (!(row < gridLayout_->rowCount())) { +			row = 0; +		} +	} + +	QtTabWidget* targetTabWidget = dynamic_cast<QtTabWidget*>(gridLayout_->itemAtPosition(row, col)->widget()); +	assert(tabWidget); +	assert(targetTabWidget); + +	// fetch tab information +	QWidget* tab = tabWidget->widget(tabIndex); +	QString tabText = tabWidget->tabText(tabIndex); + +	// move tab +	tab->blockSignals(true); +	targetTabWidget->addTab(tab, tabText); +	tab->blockSignals(false); +	tab->setFocus(Qt::TabFocusReason); + +	updateTabPositions(); +} + +void QtDynamicGridLayout::moveCurrentTabToPreviousGroup() { +	int index = currentIndex(); +	int tabIndex = -1; +	QtTabWidget* tabWidget = indexToTabWidget(index, tabIndex); + +	int row = -1; +	int col = -1; +	int tmp; +	gridLayout_->getItemPosition(gridLayout_->indexOf(tabWidget), &row, &col, &tmp, &tmp); + +	// calculate next cell +	col--; +	if (col < 0) { +		col = gridLayout_->columnCount() - 1; +		row--; +		if (row < 0) { +			row = gridLayout_->rowCount() - 1; +		} +	} + +	QtTabWidget* targetTabWidget = dynamic_cast<QtTabWidget*>(gridLayout_->itemAtPosition(row, col)->widget()); +	assert(tabWidget); +	assert(targetTabWidget); + +	// fetch tab information +	QWidget* tab = tabWidget->widget(tabIndex); +	QString tabText = tabWidget->tabText(tabIndex); + +	// move tab +	tab->blockSignals(true); +	targetTabWidget->addTab(tab, tabText); +	tab->blockSignals(false); +	tab->setFocus(Qt::TabFocusReason); + +	updateTabPositions(); +} + +void QtDynamicGridLayout::handleTabCloseRequested(int index) { +	updateTabPositions(); +	QtTabWidget* tabWidgetSender = dynamic_cast<QtTabWidget*>(sender()); +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			if (tabWidget == tabWidgetSender) { +				tabCloseRequested(index); +			} +			else { +				index += tabWidget->count(); +			} +		} +	} +} + +void QtDynamicGridLayout::updateTabPositions() { +	for (int y = 0; y < gridLayout_->rowCount(); y++) { +		for (int x = 0; x < gridLayout_->columnCount(); x++) { +			QLayoutItem* layoutItem = gridLayout_->itemAtPosition(y, x); +			QtTabWidget* tabWidget = dynamic_cast<QtTabWidget*>(layoutItem->widget()); +			assert(tabWidget); +			for (int index = 0; index < tabWidget->count(); index++) { +				QtTabbable* tab = dynamic_cast<QtTabbable*>(tabWidget->widget(index)); +				assert(tab); +				tabPositions_.insert(P2QSTRING(tab->getID()), QPoint(x, y)); +			} +		} +	} +} + +void QtDynamicGridLayout::moveTab(QtTabWidget* tabWidget, int oldIndex, int newIndex) { +	tabWidget->widget(oldIndex)->blockSignals(true); +	tabWidget->widget(newIndex)->blockSignals(true); +#if QT_VERSION >= 0x040500 +	tabWidget->tabBar()->moveTab(oldIndex, newIndex); +#else +#warning Qt 4.5 or later is needed. Trying anyway, some things will be disabled. +	tabWidget->widget(oldIndex)->blockSignals(false); +	tabWidget->widget(newIndex)->blockSignals(false); +#endif +} + +QtTabWidget* QtDynamicGridLayout::createDNDTabWidget(QWidget* parent) { +	QtTabWidget* tab = new QtTabWidget(parent); +	if (dndEnabled_) { +		QtDNDTabBar* tabBar = new QtDNDTabBar(tab); +		connect(tabBar, SIGNAL(onDropSucceeded()), this, SLOT(updateTabPositions())); +		tab->setTabBar(tabBar); +	} +	tab->setUsesScrollButtons(true); +	tab->setElideMode(Qt::ElideRight); +#if QT_VERSION >= 0x040500 +	/*For Macs, change the tab rendering.*/ +	tab->setDocumentMode(true); +	/*Closable tabs are only in Qt4.5 and later*/ +	tab->setTabsClosable(true); +	tab->setMovable(true); +	connect(tab, SIGNAL(tabCloseRequested(int)), this, SLOT(handleTabCloseRequested(int))); +#else +#warning Qt 4.5 or later is needed. Trying anyway, some things will be disabled. +#endif +	return tab; +} + +} diff --git a/Swift/QtUI/Trellis/QtDynamicGridLayout.h b/Swift/QtUI/Trellis/QtDynamicGridLayout.h new file mode 100644 index 0000000..c87dc21 --- /dev/null +++ b/Swift/QtUI/Trellis/QtDynamicGridLayout.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#pragma once + +#include <QGridLayout> +#include <QHash> +#include <QPoint> +#include <QSize> +#include <QString> +#include <QWidget> + +namespace Swift { +	class QtTabbable; +	class QtTabWidget; + +	class QtDynamicGridLayout : public QWidget { +		Q_OBJECT +	public: +		explicit QtDynamicGridLayout(QWidget* parent = 0, bool enableDND = false); +		virtual ~QtDynamicGridLayout(); + +		QSize getDimension() const; + +		// emulate QtTabWidget API +		int addTab(QtTabbable* tab, const QString& title); +		void removeTab(int index); +		int count() const; + +		QWidget* widget(int index) const; +		QWidget* currentWidget() const; +		void setCurrentWidget(QWidget* widget); + +		QtTabWidget* indexToTabWidget(int index, int& tabIndex); + +		int indexOf(const QWidget* widget) const; +		int currentIndex() const; +		void setCurrentIndex(int index); + +		bool isDNDEnabled() const; + +		QHash<QString, QPoint> getTabPositions() const; +		void setTabPositions(const QHash<QString, QPoint> positions); + +	signals: +		void tabCloseRequested(int index); + +	public slots: +		void setDimensions(const QSize& dim); + +		// Tab Management +		void moveCurrentTabRight(); +		void moveCurrentTabLeft(); +		void moveCurrentTabToNextGroup(); +		void moveCurrentTabToPreviousGroup(); + +		void updateTabPositions(); + +	private slots: +		void handleTabCloseRequested(int index); + +	private: +		void moveTab(QtTabWidget* tabWidget, int oldIndex, int newIndex); +		QtTabWidget* createDNDTabWidget(QWidget* parent); + +	private: +		QGridLayout *gridLayout_; +		bool dndEnabled_; +		QHash<QString, QPoint> tabPositions_; +	}; +} diff --git a/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp b/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp new file mode 100644 index 0000000..a7d3672 --- /dev/null +++ b/Swift/QtUI/Trellis/QtGridSelectionDialog.cpp @@ -0,0 +1,153 @@ +/* + * 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/QtGridSelectionDialog.h> + +#include <QStyle> +#include <QPaintEvent> +#include <QPainter> +#include <QStyleOptionFrame> +#include <QStyleOptionMenuItem> + +namespace Swift { + +QtGridSelectionDialog::QtGridSelectionDialog(QWidget* parent) : QWidget(parent) +{ +	frameSize = QSize(23,23) * 2; +	maxGridSize = QSize(7,7); +	minGridSize = QSize(1,1); +	currentGridSize = QSize(1,1); +	padding = 4; +	setWindowFlags(Qt::Popup); +	horizontalMargin = style()->pixelMetric(QStyle::PM_MenuVMargin); +	verticalMargin = style()->pixelMetric(QStyle::PM_MenuVMargin); + +} + +QSize QtGridSelectionDialog::sizeHint() const +{ +	// PM_MenuVMargin | frameSize | ( padding | frameSize ) * | PM_MenuVMargin +	int width = horizontalMargin + frameSize.width() + (padding + frameSize.width()) * (currentGridSize.width() - 1) + horizontalMargin; +	int height = verticalMargin + frameSize.height() + (padding + frameSize.height()) * (currentGridSize.height() - 1) + verticalMargin; +	return QSize(width, height); +} + +void QtGridSelectionDialog::setCurrentGridSize(const QSize& size) { +	currentGridSize = size; +	emit currentGridSizeChanged(size); +} + +QSize QtGridSelectionDialog::getCurrentGridSize() const { +	return currentGridSize; +} + +void QtGridSelectionDialog::setMinGridSize(const QSize& size) { +	minGridSize = size; +	emit minGridSizeChanged(size); +} + +QSize QtGridSelectionDialog::getMinGridSize() const { +	return minGridSize; +} + +void QtGridSelectionDialog::setMaxGridSize(const QSize& size) { +	maxGridSize = size; +	emit maxGridSizeChanged(size); +} + +QSize QtGridSelectionDialog::getMaxGridSize() const { +	return maxGridSize; +} + +void QtGridSelectionDialog::keyReleaseEvent(QKeyEvent* event) { +	if (event) { +		QSize newGridSize = currentGridSize; +		if (event->key() == Qt::Key_Up) { +			newGridSize += QSize(0, -1); +		} +		else if (event->key() == Qt::Key_Down) { +			newGridSize += QSize(0, 1); +		} +		else if (event->key() == Qt::Key_Left) { +			newGridSize += QSize(-1, 0); +		} +		else if (event->key() == Qt::Key_Right) { +			newGridSize += QSize(1, 0); +		} +		else if (event->key() == Qt::Key_Return) { +			hide(); +			setCurrentGridSize(currentGridSize); +		} +		if (minGridSize.expandedTo(newGridSize).boundedTo(maxGridSize) != currentGridSize) { +			currentGridSize = minGridSize.expandedTo(newGridSize).boundedTo(maxGridSize); +			resize(sizeHint()); +		} +	} +} + +void QtGridSelectionDialog::mousePressEvent(QMouseEvent*) { +	hide(); +	setCurrentGridSize(currentGridSize); +} + +void QtGridSelectionDialog::paintEvent(QPaintEvent*) { +	QPainter painter(this); +	QStyleOptionMenuItem option; +	option.state = QStyle::State_Enabled | QStyle::State_Selected; +	option.menuRect = QRect(QPoint(0,0), frameSize); +	for (int x = 0; x < currentGridSize.width(); x++) { +		for (int y = 0; y < currentGridSize.height(); y++) { +			int xPos = horizontalMargin + (x * (frameSize.width() + padding)); +			int yPos = verticalMargin + (y * (frameSize.height() + padding)); +			option.menuRect.moveTo(QPoint(xPos, yPos)); +			option.rect = option.menuRect; +			style()->drawControl(QStyle::CE_MenuItem, &option, &painter, 0); +		} +	} +} + +void QtGridSelectionDialog::showEvent(QShowEvent*) { +	int xPos = horizontalMargin + frameSize.width() + (padding + frameSize.width()) * (currentGridSize.width() - 1) - frameSize.width()/2; +	int yPos = verticalMargin + frameSize.height() + (padding + frameSize.height()) * (currentGridSize.height() - 1) - frameSize.height()/2; +	QCursor::setPos(mapToGlobal(QPoint(xPos, yPos))); +	setMouseTracking(true); +} + +void QtGridSelectionDialog::hideEvent(QHideEvent*) { +	setMouseTracking(false); +} + +void QtGridSelectionDialog::mouseMoveEvent(QMouseEvent*) { +	QPoint diff = (frameGeometry().bottomRight() - QCursor::pos()); +	QSize newDimensions = currentGridSize; +	if (diff.x() > frameSize.width() * 1.5) { +		newDimensions -= QSize(diff.x() / (frameSize.width() * 1.5), 0); +	} +	if (diff.y() > frameSize.height() * 1.5) { +		newDimensions -= QSize(0, diff.y() / (frameSize.height() * 1.5)); +	} +	if (minGridSize.expandedTo(newDimensions).boundedTo(maxGridSize) != currentGridSize) { +		currentGridSize = minGridSize.expandedTo(newDimensions).boundedTo(maxGridSize); +		resize(sizeHint()); +	} +} + +void QtGridSelectionDialog::leaveEvent(QEvent *) { +	QPoint diff = (frameGeometry().bottomRight() - QCursor::pos()); +	QSize newGridSize = currentGridSize; +	if (diff.x() < 0) { +		newGridSize += QSize(1,0); +	} +	if (diff.y() < 0) { +		newGridSize += QSize(0,1); +	} +	if (minGridSize.expandedTo(newGridSize).boundedTo(maxGridSize) != currentGridSize) { +		currentGridSize = minGridSize.expandedTo(newGridSize).boundedTo(maxGridSize); +		resize(sizeHint()); +	} +} + +} diff --git a/Swift/QtUI/Trellis/QtGridSelectionDialog.h b/Swift/QtUI/Trellis/QtGridSelectionDialog.h new file mode 100644 index 0000000..430189a --- /dev/null +++ b/Swift/QtUI/Trellis/QtGridSelectionDialog.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#pragma once + +#include <QSize> +#include <QWidget> + +namespace Swift { + +	class QtGridSelectionDialog : public QWidget { +			Q_OBJECT + +			Q_PROPERTY(QSize currentGridSize READ getCurrentGridSize WRITE setCurrentGridSize NOTIFY currentGridSizeChanged) +			Q_PROPERTY(QSize minGridSize READ getMinGridSize WRITE setMinGridSize NOTIFY minGridSizeChanged) +			Q_PROPERTY(QSize maxGridSize READ getMaxGridSize WRITE setMaxGridSize NOTIFY maxGridSizeChanged) +		public: +			explicit QtGridSelectionDialog(QWidget* parent = 0); + +			virtual QSize sizeHint() const; + +			void setCurrentGridSize(const QSize& size); +			QSize getCurrentGridSize() const; +			void setMinGridSize(const QSize& size); +			QSize getMinGridSize() const; +			void setMaxGridSize(const QSize& size); +			QSize getMaxGridSize() const; + +		signals: +			void currentGridSizeChanged(QSize); +			void minGridSizeChanged(QSize); +			void maxGridSizeChanged(QSize); + +		protected: +			void keyReleaseEvent(QKeyEvent* event); +			void mousePressEvent(QMouseEvent* event); +			void mouseMoveEvent(QMouseEvent* event); +			void paintEvent(QPaintEvent* event); +			void showEvent(QShowEvent* event); +			void hideEvent(QHideEvent* event); +			void leaveEvent(QEvent *event); + +		private: +			int padding; +			int horizontalMargin; +			int verticalMargin; + +			QSize frameSize; + +			QSize currentGridSize; +			QSize minGridSize; +			QSize maxGridSize; +	}; +} | 
 Swift
 Swift