Group view gets keyboard navigation back.
And a bunch of fixes.
This commit is contained in:
		| @@ -366,14 +366,14 @@ SET(MULTIMC_SOURCES | ||||
|  | ||||
|  | ||||
| 	# GUI - instance group view | ||||
| 	gui/groupview/Group.cpp | ||||
| 	gui/groupview/Group.h | ||||
| 	gui/groupview/GroupedProxyModel.cpp | ||||
| 	gui/groupview/GroupedProxyModel.h | ||||
| 	gui/groupview/GroupView.cpp | ||||
| 	gui/groupview/GroupView.h | ||||
| 	gui/groupview/InstanceDelegate.cpp | ||||
| 	gui/groupview/InstanceDelegate.h | ||||
| 	gui/groupview/VisualGroup.cpp | ||||
| 	gui/groupview/VisualGroup.h | ||||
|  | ||||
| 	# LOGIC - Base classes and infrastructure | ||||
| 	logic/BaseVersion.h | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
| #include <QCache> | ||||
| #include <QScrollBar> | ||||
|  | ||||
| #include "Group.h" | ||||
| #include "VisualGroup.h" | ||||
| #include "logger/QsLog.h" | ||||
|  | ||||
| template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2) | ||||
| @@ -27,17 +27,12 @@ template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2) | ||||
| } | ||||
|  | ||||
| GroupView::GroupView(QWidget *parent) | ||||
| 	: QAbstractItemView(parent), m_leftMargin(5), m_rightMargin(5), m_bottomMargin(5), | ||||
| 	  m_categoryMargin(5) //, m_updatesDisabled(false), m_categoryEditor(0), m_editedCategory(0) | ||||
| 	: QAbstractItemView(parent) | ||||
| { | ||||
| 	// setViewMode(IconMode); | ||||
| 	// setMovement(Snap); | ||||
| 	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); | ||||
| 	setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); | ||||
| 	// setWordWrap(true); | ||||
| 	// setDragDropMode(QListView::InternalMove); | ||||
| 	setAcceptDrops(true); | ||||
| 	m_spacing = 5; | ||||
| 	setAutoScroll(true); | ||||
| } | ||||
|  | ||||
| GroupView::~GroupView() | ||||
| @@ -72,7 +67,7 @@ void GroupView::updateGeometries() | ||||
| 	geometryCache.clear(); | ||||
| 	int previousScroll = verticalScrollBar()->value(); | ||||
|  | ||||
| 	QMap<QString, Group *> cats; | ||||
| 	QMap<QString, VisualGroup *> cats; | ||||
|  | ||||
| 	for (int i = 0; i < model()->rowCount(); ++i) | ||||
| 	{ | ||||
| @@ -80,14 +75,14 @@ void GroupView::updateGeometries() | ||||
| 			model()->index(i, 0).data(GroupViewRoles::GroupRole).toString(); | ||||
| 		if (!cats.contains(groupName)) | ||||
| 		{ | ||||
| 			Group *old = this->category(groupName); | ||||
| 			VisualGroup *old = this->category(groupName); | ||||
| 			if (old) | ||||
| 			{ | ||||
| 				cats.insert(groupName, new Group(old)); | ||||
| 				cats.insert(groupName, new VisualGroup(old)); | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				cats.insert(groupName, new Group(groupName, this)); | ||||
| 				cats.insert(groupName, new VisualGroup(groupName, this)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| @@ -149,7 +144,7 @@ void GroupView::modelReset() | ||||
|  | ||||
| bool GroupView::isIndexHidden(const QModelIndex &index) const | ||||
| { | ||||
| 	Group *cat = category(index); | ||||
| 	VisualGroup *cat = category(index); | ||||
| 	if (cat) | ||||
| 	{ | ||||
| 		return cat->collapsed; | ||||
| @@ -160,12 +155,12 @@ bool GroupView::isIndexHidden(const QModelIndex &index) const | ||||
| 	} | ||||
| } | ||||
|  | ||||
| Group *GroupView::category(const QModelIndex &index) const | ||||
| VisualGroup *GroupView::category(const QModelIndex &index) const | ||||
| { | ||||
| 	return category(index.data(GroupViewRoles::GroupRole).toString()); | ||||
| } | ||||
|  | ||||
| Group *GroupView::category(const QString &cat) const | ||||
| VisualGroup *GroupView::category(const QString &cat) const | ||||
| { | ||||
| 	for (auto group : m_groups) | ||||
| 	{ | ||||
| @@ -177,11 +172,11 @@ Group *GroupView::category(const QString &cat) const | ||||
| 	return nullptr; | ||||
| } | ||||
|  | ||||
| Group *GroupView::categoryAt(const QPoint &pos) const | ||||
| VisualGroup *GroupView::categoryAt(const QPoint &pos) const | ||||
| { | ||||
| 	for (auto group : m_groups) | ||||
| 	{ | ||||
| 		if(group->hitScan(pos) & Group::CheckboxHit) | ||||
| 		if(group->hitScan(pos) & VisualGroup::CheckboxHit) | ||||
| 		{ | ||||
| 			return group; | ||||
| 		} | ||||
| @@ -189,7 +184,7 @@ Group *GroupView::categoryAt(const QPoint &pos) const | ||||
| 	return nullptr; | ||||
| } | ||||
|  | ||||
| int GroupView::itemsPerRow() const | ||||
| int GroupView::calculateItemsPerRow() const | ||||
| { | ||||
| 	return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); | ||||
| } | ||||
| @@ -201,77 +196,7 @@ int GroupView::contentWidth() const | ||||
|  | ||||
| int GroupView::itemWidth() const | ||||
| { | ||||
| 	return itemDelegate() | ||||
| 		->sizeHint(viewOptions(), model()->index(model()->rowCount() - 1, 0)) | ||||
| 		.width(); | ||||
| } | ||||
|  | ||||
| int GroupView::categoryRowHeight(const QModelIndex &index) const | ||||
| { | ||||
| 	QModelIndexList indices; | ||||
| 	int internalRow = categoryInternalPosition(index).second; | ||||
| 	for (auto &i : category(index)->items()) | ||||
| 	{ | ||||
| 		if (categoryInternalPosition(i).second == internalRow) | ||||
| 		{ | ||||
| 			indices.append(i); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	int largestHeight = 0; | ||||
| 	for (auto &i : indices) | ||||
| 	{ | ||||
| 		largestHeight = | ||||
| 			qMax(largestHeight, itemDelegate()->sizeHint(viewOptions(), i).height()); | ||||
| 	} | ||||
| 	return largestHeight + m_spacing; | ||||
| } | ||||
|  | ||||
| QPair<int, int> GroupView::categoryInternalPosition(const QModelIndex &index) const | ||||
| { | ||||
| 	QList<QModelIndex> indices = category(index)->items(); | ||||
| 	int x = 0; | ||||
| 	int y = 0; | ||||
| 	const int perRow = itemsPerRow(); | ||||
| 	for (int i = 0; i < indices.size(); ++i) | ||||
| 	{ | ||||
| 		if (indices.at(i) == index) | ||||
| 		{ | ||||
| 			break; | ||||
| 		} | ||||
| 		++x; | ||||
| 		if (x == perRow) | ||||
| 		{ | ||||
| 			x = 0; | ||||
| 			++y; | ||||
| 		} | ||||
| 	} | ||||
| 	return qMakePair(x, y); | ||||
| } | ||||
|  | ||||
| int GroupView::categoryInternalRowTop(const QModelIndex &index) const | ||||
| { | ||||
| 	Group *cat = category(index); | ||||
| 	int categoryInternalRow = categoryInternalPosition(index).second; | ||||
| 	int result = 0; | ||||
| 	for (int i = 0; i < categoryInternalRow; ++i) | ||||
| 	{ | ||||
| 		result += cat->rowHeights.at(i); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
|  | ||||
| int GroupView::itemHeightForCategoryRow(const Group *category, const int internalRow) const | ||||
| { | ||||
| 	for (auto &i : category->items()) | ||||
| 	{ | ||||
| 		QPair<int, int> pos = categoryInternalPosition(i); | ||||
| 		if (pos.second == internalRow) | ||||
| 		{ | ||||
| 			return categoryRowHeight(i); | ||||
| 		} | ||||
| 	} | ||||
| 	return -1; | ||||
| 	return m_itemWidth; | ||||
| } | ||||
|  | ||||
| void GroupView::mousePressEvent(QMouseEvent *event) | ||||
| @@ -297,13 +222,19 @@ void GroupView::mousePressEvent(QMouseEvent *event) | ||||
|  | ||||
| 	if (index.isValid() && (index.flags() & Qt::ItemIsEnabled)) | ||||
| 	{ | ||||
| 		if(index != currentIndex()) | ||||
| 		{ | ||||
| 			// FIXME: better! | ||||
| 			m_currentCursorColumn = -1; | ||||
| 		} | ||||
| 		// we disable scrollTo for mouse press so the item doesn't change position | ||||
| 		// when the user is interacting with it (ie. clicking on it) | ||||
| 		bool autoScroll = hasAutoScroll(); | ||||
| 		setAutoScroll(false); | ||||
| 		selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); | ||||
|  | ||||
| 		setAutoScroll(autoScroll); | ||||
| 		QRect rect(geometryPos, geometryPos); | ||||
| 		QRect rect(visualPos, visualPos); | ||||
| 		setSelection(rect, QItemSelectionModel::ClearAndSelect); | ||||
|  | ||||
| 		// signal handlers may change the model | ||||
| @@ -360,7 +291,7 @@ void GroupView::mouseMoveEvent(QMouseEvent *event) | ||||
| 	{ | ||||
| 		setState(DragSelectingState); | ||||
|  | ||||
| 		setSelection(QRect(geometryPos, geometryPos), QItemSelectionModel::ClearAndSelect); | ||||
| 		setSelection(QRect(visualPos, visualPos), QItemSelectionModel::ClearAndSelect); | ||||
| 		QModelIndex index = indexAt(visualPos); | ||||
|  | ||||
| 		// set at the end because it might scroll the view | ||||
| @@ -453,7 +384,7 @@ void GroupView::paintEvent(QPaintEvent *event) | ||||
| 	option.rect.setWidth(wpWidth); | ||||
| 	for (int i = 0; i < m_groups.size(); ++i) | ||||
| 	{ | ||||
| 		Group *category = m_groups.at(i); | ||||
| 		VisualGroup *category = m_groups.at(i); | ||||
| 		int y = category->verticalPosition(); | ||||
| 		y -= verticalOffset(); | ||||
| 		QRect backup = option.rect; | ||||
| @@ -529,16 +460,12 @@ void GroupView::paintEvent(QPaintEvent *event) | ||||
|  | ||||
| void GroupView::resizeEvent(QResizeEvent *event) | ||||
| { | ||||
| 	// QListView::resizeEvent(event); | ||||
|  | ||||
| 	//	if (m_categoryEditor) | ||||
| 	//	{ | ||||
| 	//		m_categoryEditor->resize(qMax(contentWidth() / 2, | ||||
| 	// m_editedCategory->textRect.width()), | ||||
| 	// m_categoryEditor->height()); | ||||
| 	//	} | ||||
|  | ||||
| 	updateGeometries(); | ||||
| 	int newItemsPerRow = calculateItemsPerRow(); | ||||
| 	if(newItemsPerRow != m_currentItemsPerRow) | ||||
| 	{ | ||||
| 		m_currentItemsPerRow = newItemsPerRow; | ||||
| 		updateGeometries(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void GroupView::dragEnterEvent(QDragEnterEvent *event) | ||||
| @@ -581,8 +508,8 @@ void GroupView::dropEvent(QDropEvent *event) | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	QPair<Group *, int> dropPos = rowDropPos(event->pos() + offset()); | ||||
| 	const Group *category = dropPos.first; | ||||
| 	QPair<VisualGroup *, int> dropPos = rowDropPos(event->pos() + offset()); | ||||
| 	const VisualGroup *category = dropPos.first; | ||||
| 	const int row = dropPos.second; | ||||
|  | ||||
| 	if (row == -1) | ||||
| @@ -606,44 +533,44 @@ void GroupView::dropEvent(QDropEvent *event) | ||||
| void GroupView::startDrag(Qt::DropActions supportedActions) | ||||
| { | ||||
| 	QModelIndexList indexes = selectionModel()->selectedIndexes(); | ||||
| 	if (indexes.count() > 0) | ||||
| 	{ | ||||
| 		QMimeData *data = model()->mimeData(indexes); | ||||
| 		if (!data) | ||||
| 		{ | ||||
| 			return; | ||||
| 		} | ||||
| 		QRect rect; | ||||
| 		QPixmap pixmap = renderToPixmap(indexes, &rect); | ||||
| 		//rect.translate(offset()); | ||||
| 		// rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); | ||||
| 		QDrag *drag = new QDrag(this); | ||||
| 		drag->setPixmap(pixmap); | ||||
| 		drag->setMimeData(data); | ||||
| 		Qt::DropAction defaultDropAction = Qt::IgnoreAction; | ||||
| 		if (this->defaultDropAction() != Qt::IgnoreAction && | ||||
| 			(supportedActions & this->defaultDropAction())) | ||||
| 		{ | ||||
| 			defaultDropAction = this->defaultDropAction(); | ||||
| 		} | ||||
| 		if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) | ||||
| 		{ | ||||
| 			const QItemSelection selection = selectionModel()->selection(); | ||||
| 	if(indexes.count() == 0) | ||||
| 		return; | ||||
|  | ||||
| 			for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) | ||||
| 	QMimeData *data = model()->mimeData(indexes); | ||||
| 	if (!data) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	QRect rect; | ||||
| 	QPixmap pixmap = renderToPixmap(indexes, &rect); | ||||
| 	//rect.translate(offset()); | ||||
| 	// rect.adjust(horizontalOffset(), verticalOffset(), 0, 0); | ||||
| 	QDrag *drag = new QDrag(this); | ||||
| 	drag->setPixmap(pixmap); | ||||
| 	drag->setMimeData(data); | ||||
| 	Qt::DropAction defaultDropAction = Qt::IgnoreAction; | ||||
| 	if (this->defaultDropAction() != Qt::IgnoreAction && | ||||
| 		(supportedActions & this->defaultDropAction())) | ||||
| 	{ | ||||
| 		defaultDropAction = this->defaultDropAction(); | ||||
| 	} | ||||
| 	if (drag->exec(supportedActions, defaultDropAction) == Qt::MoveAction) | ||||
| 	{ | ||||
| 		const QItemSelection selection = selectionModel()->selection(); | ||||
|  | ||||
| 		for (auto it = selection.constBegin(); it != selection.constEnd(); ++it) | ||||
| 		{ | ||||
| 			QModelIndex parent = (*it).parent(); | ||||
| 			if ((*it).left() != 0) | ||||
| 			{ | ||||
| 				QModelIndex parent = (*it).parent(); | ||||
| 				if ((*it).left() != 0) | ||||
| 				{ | ||||
| 					continue; | ||||
| 				} | ||||
| 				if ((*it).right() != (model()->columnCount(parent) - 1)) | ||||
| 				{ | ||||
| 					continue; | ||||
| 				} | ||||
| 				int count = (*it).bottom() - (*it).top() + 1; | ||||
| 				model()->removeRows((*it).top(), count, parent); | ||||
| 				continue; | ||||
| 			} | ||||
| 			if ((*it).right() != (model()->columnCount(parent) - 1)) | ||||
| 			{ | ||||
| 				continue; | ||||
| 			} | ||||
| 			int count = (*it).bottom() - (*it).top() + 1; | ||||
| 			model()->removeRows((*it).top(), count, parent); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -665,61 +592,20 @@ QRect GroupView::geometryRect(const QModelIndex &index) const | ||||
| 	{ | ||||
| 		return *geometryCache[row]; | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		const Group *cat = category(index); | ||||
| 		QPair<int, int> pos = categoryInternalPosition(index); | ||||
| 		int x = pos.first; | ||||
| 		// int y = pos.second; | ||||
|  | ||||
| 		QRect out; | ||||
| 		out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + categoryInternalRowTop(index)); | ||||
| 		out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); | ||||
| 		out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); | ||||
| 		const_cast<QCache<int, QRect>&>(geometryCache).insert(row, new QRect(out)); | ||||
| 		return out; | ||||
| 	} | ||||
| } | ||||
| 	const VisualGroup *cat = category(index); | ||||
| 	QPair<int, int> pos = cat->positionOf(index); | ||||
| 	int x = pos.first; | ||||
| 	// int y = pos.second; | ||||
|  | ||||
| /* | ||||
| void CategorizedView::startCategoryEditor(Category *category) | ||||
| { | ||||
| 	if (m_categoryEditor != 0) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	m_editedCategory = category; | ||||
| 	m_categoryEditor = new QLineEdit(m_editedCategory->text, this); | ||||
| 	QRect rect = m_editedCategory->textRect; | ||||
| 	rect.setWidth(qMax(contentWidth() / 2, rect.width())); | ||||
| 	m_categoryEditor->setGeometry(rect); | ||||
| 	m_categoryEditor->show(); | ||||
| 	m_categoryEditor->setFocus(); | ||||
| 	connect(m_categoryEditor, &QLineEdit::returnPressed, this, | ||||
| &CategorizedView::endCategoryEditor); | ||||
| 	QRect out; | ||||
| 	out.setTop(cat->verticalPosition() + cat->headerHeight() + 5 + cat->rowTopOf(index)); | ||||
| 	out.setLeft(m_spacing + x * (itemWidth() + m_spacing)); | ||||
| 	out.setSize(itemDelegate()->sizeHint(viewOptions(), index)); | ||||
| 	geometryCache.insert(row, new QRect(out)); | ||||
| 	return out; | ||||
| } | ||||
|  | ||||
| void CategorizedView::endCategoryEditor() | ||||
| { | ||||
| 	if (m_categoryEditor == 0) | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	m_editedCategory->text = m_categoryEditor->text(); | ||||
| 	m_updatesDisabled = true; | ||||
| 	foreach (const QModelIndex &index, itemsForCategory(m_editedCategory)) | ||||
| 	{ | ||||
| 		const_cast<QAbstractItemModel *>(index.model())->setData(index, | ||||
| m_categoryEditor->text(), CategoryRole); | ||||
| 	} | ||||
| 	m_updatesDisabled = false; | ||||
| 	delete m_categoryEditor; | ||||
| 	m_categoryEditor = 0; | ||||
| 	m_editedCategory = 0; | ||||
| 	updateGeometries(); | ||||
| } | ||||
| */ | ||||
|  | ||||
| QModelIndex GroupView::indexAt(const QPoint &point) const | ||||
| { | ||||
| 	for (int i = 0; i < model()->rowCount(); ++i) | ||||
| @@ -733,21 +619,19 @@ QModelIndex GroupView::indexAt(const QPoint &point) const | ||||
| 	return QModelIndex(); | ||||
| } | ||||
|  | ||||
| // FIXME: is rect supposed to be geometry or visual coords? | ||||
| void GroupView::setSelection(const QRect &rect, | ||||
| 							 const QItemSelectionModel::SelectionFlags commands) | ||||
| { | ||||
| 	for (int i = 0; i < model()->rowCount(); ++i) | ||||
| 	{ | ||||
| 		QModelIndex index = model()->index(i, 0); | ||||
| 		QRect itemRect = geometryRect(index); | ||||
| 		QRect itemRect = visualRect(index); | ||||
| 		if (itemRect.intersects(rect)) | ||||
| 		{ | ||||
| 			selectionModel()->select(index, commands); | ||||
| 			update(itemRect.translated(-offset())); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| } | ||||
|  | ||||
| QPixmap GroupView::renderToPixmap(const QModelIndexList &indices, QRect *r) const | ||||
| @@ -790,33 +674,23 @@ QList<QPair<QRect, QModelIndex>> GroupView::draggablePaintPairs(const QModelInde | ||||
|  | ||||
| bool GroupView::isDragEventAccepted(QDropEvent *event) | ||||
| { | ||||
| 	if (event->source() != this) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (!listsIntersect(event->mimeData()->formats(), model()->mimeTypes())) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (!model()->canDropMimeData(event->mimeData(), event->dropAction(), | ||||
| 								  rowDropPos(event->pos()).second, 0, QModelIndex())) | ||||
| 	{ | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| QPair<VisualGroup *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| { | ||||
| 	return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 	// FIXME: PIXIE DUST. | ||||
| 	/* | ||||
| 	// check that we aren't on a category header and calculate which category we're in | ||||
| 	Group *category = 0; | ||||
| 	VisualGroup *category = 0; | ||||
| 	{ | ||||
| 		int y = 0; | ||||
| 		for (auto cat : m_groups) | ||||
| 		{ | ||||
| 			if (pos.y() > y && pos.y() < (y + cat->headerHeight())) | ||||
| 			{ | ||||
| 				return qMakePair<Group*, int>(nullptr, -1); | ||||
| 				return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 			} | ||||
| 			y += cat->totalHeight() + m_categoryMargin; | ||||
| 			if (pos.y() < y) | ||||
| @@ -827,7 +701,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| 		} | ||||
| 		if (category == 0) | ||||
| 		{ | ||||
| 			return qMakePair<Group*, int>(nullptr, -1); | ||||
| 			return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -843,7 +717,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 /*spacing()*/, ++c) | ||||
| 			for (int i = 0, c = 0; i < contentWidth(); i += itemWidth + 10 , ++c) | ||||
| 			{ | ||||
| 				if (pos.x() > (i - itemWidth / 2) && pos.x() <= (i + itemWidth / 2)) | ||||
| 				{ | ||||
| @@ -854,7 +728,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| 		} | ||||
| 		if (internalColumn == -1) | ||||
| 		{ | ||||
| 			return qMakePair<Group*, int>(nullptr, -1); | ||||
| 			return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -874,13 +748,13 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| 		} | ||||
| 		if (internalRow == -1) | ||||
| 		{ | ||||
| 			return qMakePair<Group*, int>(nullptr, -1); | ||||
| 			return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 		} | ||||
| 		// this happens if we're in the margin between a one category and another | ||||
| 		// categories header | ||||
| 		if (internalRow > (indices.size() / itemsPerRow())) | ||||
| 		{ | ||||
| 			return qMakePair<Group*, int>(nullptr, -1); | ||||
| 			return qMakePair<VisualGroup*, int>(nullptr, -1); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -894,6 +768,7 @@ QPair<Group *, int> GroupView::rowDropPos(const QPoint &pos) | ||||
| 	} | ||||
|  | ||||
| 	return qMakePair(category, indices.at(categoryRow).row()); | ||||
| 	*/ | ||||
| } | ||||
|  | ||||
| QPoint GroupView::offset() const | ||||
| @@ -921,34 +796,162 @@ QRegion GroupView::visualRegionForSelection(const QItemSelection &selection) con | ||||
| 	} | ||||
| 	return region; | ||||
| } | ||||
|  | ||||
| QModelIndex GroupView::moveCursor(QAbstractItemView::CursorAction cursorAction, | ||||
| 								  Qt::KeyboardModifiers modifiers) | ||||
| { | ||||
| 	auto current = currentIndex(); | ||||
| 	if(!current.isValid()) | ||||
| 	{ | ||||
| 		QLOG_DEBUG() << "model row: invalid"; | ||||
| 		return current; | ||||
| 	} | ||||
| 	QLOG_DEBUG() << "model row: " << current.row(); | ||||
| 	auto cat = category(current); | ||||
| 	int i = m_groups.indexOf(cat); | ||||
| 	if(i >= 0) | ||||
| 	int group_index = m_groups.indexOf(cat); | ||||
| 	if(group_index < 0) | ||||
| 		return current; | ||||
|  | ||||
| 	auto real_group = m_groups[group_index]; | ||||
| 	int beginning_row = 0; | ||||
| 	for(auto group: m_groups) | ||||
| 	{ | ||||
| 		// this is a pile of something foul | ||||
| 		auto real_group = m_groups[i]; | ||||
| 		int beginning_row = 0; | ||||
| 		for(auto group: m_groups) | ||||
| 		if(group == real_group) | ||||
| 			break; | ||||
| 		beginning_row += group->numRows(); | ||||
| 	} | ||||
|  | ||||
| 	QPair<int, int> pos = cat->positionOf(current); | ||||
| 	int column = pos.first; | ||||
| 	int row = pos.second; | ||||
| 	if(m_currentCursorColumn < 0) | ||||
| 	{ | ||||
| 		m_currentCursorColumn = column; | ||||
| 	} | ||||
| 	switch(cursorAction) | ||||
| 	{ | ||||
| 		case MoveUp: | ||||
| 		{ | ||||
| 			if(group == real_group) | ||||
| 				break; | ||||
| 			beginning_row += group->numRows(); | ||||
| 			if(row == 0) | ||||
| 			{ | ||||
| 				if(group_index == 0) | ||||
| 					return current; | ||||
| 				auto prevgroup = m_groups[group_index-1]; | ||||
| 				int newRow = prevgroup->numRows() - 1; | ||||
| 				int newRowSize = prevgroup->rows[newRow].size(); | ||||
| 				int newColumn = m_currentCursorColumn; | ||||
| 				if (m_currentCursorColumn >= newRowSize) | ||||
| 				{ | ||||
| 					newColumn = newRowSize - 1; | ||||
| 				} | ||||
| 				return prevgroup->rows[newRow][newColumn]; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				int newRow = row - 1; | ||||
| 				int newRowSize = cat->rows[newRow].size(); | ||||
| 				int newColumn = m_currentCursorColumn; | ||||
| 				if (m_currentCursorColumn >= newRowSize) | ||||
| 				{ | ||||
| 					newColumn = newRowSize - 1; | ||||
| 				} | ||||
| 				return cat->rows[newRow][newColumn]; | ||||
| 			} | ||||
| 		} | ||||
| 		QLOG_DEBUG() << "category: " << real_group->text; | ||||
| 		QPair<int, int> pos = categoryInternalPosition(current); | ||||
| 		int row = beginning_row + pos.second; | ||||
| 		QLOG_DEBUG() << "row: " << row; | ||||
| 		QLOG_DEBUG() << "column: " << pos.first; | ||||
| 		case MoveDown: | ||||
| 		{ | ||||
| 			if(row == cat->rows.size() - 1) | ||||
| 			{ | ||||
| 				if(group_index == m_groups.size() - 1) | ||||
| 					return current; | ||||
| 				auto nextgroup = m_groups[group_index+1]; | ||||
| 				int newRowSize = nextgroup->rows[0].size(); | ||||
| 				int newColumn = m_currentCursorColumn; | ||||
| 				if (m_currentCursorColumn >= newRowSize) | ||||
| 				{ | ||||
| 					newColumn = newRowSize - 1; | ||||
| 				} | ||||
| 				return nextgroup->rows[0][newColumn]; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				int newRow = row + 1; | ||||
| 				int newRowSize = cat->rows[newRow].size(); | ||||
| 				int newColumn = m_currentCursorColumn; | ||||
| 				if (m_currentCursorColumn >= newRowSize) | ||||
| 				{ | ||||
| 					newColumn = newRowSize - 1; | ||||
| 				} | ||||
| 				return cat->rows[newRow][newColumn]; | ||||
| 			} | ||||
| 		} | ||||
| 		case MoveLeft: | ||||
| 		{ | ||||
| 			if(column > 0) | ||||
| 			{ | ||||
| 				m_currentCursorColumn = column - 1; | ||||
| 				return cat->rows[row][column - 1]; | ||||
| 			} | ||||
| 			return current; | ||||
| 		} | ||||
| 		case MoveRight: | ||||
| 		{ | ||||
| 			if(column < cat->rows[row].size() - 1) | ||||
| 			{ | ||||
| 				m_currentCursorColumn = column + 1; | ||||
| 				return cat->rows[row][column + 1]; | ||||
| 			} | ||||
| 			return current; | ||||
| 		} | ||||
| 		default: | ||||
| 			break; | ||||
| 	} | ||||
| 	return current; | ||||
| } | ||||
|  | ||||
| int GroupView::horizontalOffset() const | ||||
| { | ||||
| 	return horizontalScrollBar()->value(); | ||||
| } | ||||
|  | ||||
| int GroupView::verticalOffset() const | ||||
| { | ||||
| 	return verticalScrollBar()->value(); | ||||
| } | ||||
|  | ||||
| void GroupView::scrollContentsBy(int dx, int dy) | ||||
| { | ||||
| 	scrollDirtyRegion(dx, dy); | ||||
| 	viewport()->scroll(dx, dy); | ||||
| } | ||||
|  | ||||
| void GroupView::scrollTo(const QModelIndex &index, ScrollHint hint) | ||||
| { | ||||
| 	if (!index.isValid()) | ||||
| 		return; | ||||
|  | ||||
| 	const QRect rect = visualRect(index); | ||||
| 	if (hint == EnsureVisible && viewport()->rect().contains(rect)) | ||||
| 	{ | ||||
| 		viewport()->update(rect); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	verticalScrollBar()->setValue(verticalScrollToValue(index, rect, hint)); | ||||
| } | ||||
|  | ||||
| int GroupView::verticalScrollToValue(const QModelIndex &index, const QRect &rect, | ||||
|                                             QListView::ScrollHint hint) const | ||||
| { | ||||
| 	const QRect area = viewport()->rect(); | ||||
| 	const bool above = (hint == QListView::EnsureVisible && rect.top() < area.top()); | ||||
| 	const bool below = (hint == QListView::EnsureVisible && rect.bottom() > area.bottom()); | ||||
|  | ||||
| 	int verticalValue = verticalScrollBar()->value(); | ||||
| 	QRect adjusted = rect.adjusted(-spacing(), -spacing(), spacing(), spacing()); | ||||
| 	if (hint == QListView::PositionAtTop || above) | ||||
| 		verticalValue += adjusted.top(); | ||||
| 	else if (hint == QListView::PositionAtBottom || below) | ||||
| 		verticalValue += qMin(adjusted.top(), adjusted.bottom() - area.height() + 1); | ||||
| 	else if (hint == QListView::PositionAtCenter) | ||||
| 		verticalValue += adjusted.top() - ((area.height() - adjusted.height()) / 2); | ||||
| 	return verticalValue; | ||||
| } | ||||
|   | ||||
| @@ -15,7 +15,7 @@ struct GroupViewRoles | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| struct Group; | ||||
| struct VisualGroup; | ||||
|  | ||||
| class GroupView : public QAbstractItemView | ||||
| { | ||||
| @@ -36,35 +36,20 @@ public: | ||||
| 	void setSelection(const QRect &rect, | ||||
| 					  const QItemSelectionModel::SelectionFlags commands) override; | ||||
|  | ||||
| 	virtual int horizontalOffset() const override | ||||
| 	{ | ||||
| 		return horizontalScrollBar()->value(); | ||||
| 	} | ||||
|  | ||||
| 	virtual int verticalOffset() const override | ||||
| 	{ | ||||
| 		return verticalScrollBar()->value(); | ||||
| 	} | ||||
|  | ||||
| 	virtual void scrollContentsBy(int dx, int dy) override | ||||
| 	{ | ||||
| 		scrollDirtyRegion(dx, dy); | ||||
| 		viewport()->scroll(dx, dy); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * TODO! | ||||
| 	 */ | ||||
| 	virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override | ||||
| 	{ | ||||
| 		return; | ||||
| 	} | ||||
| 	virtual int horizontalOffset() const override; | ||||
| 	virtual int verticalOffset() const override; | ||||
| 	virtual void scrollContentsBy(int dx, int dy) override; | ||||
| 	virtual void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible) override; | ||||
|  | ||||
| 	virtual QModelIndex moveCursor(CursorAction cursorAction, | ||||
| 								   Qt::KeyboardModifiers modifiers) override; | ||||
|  | ||||
| 	virtual QRegion visualRegionForSelection(const QItemSelection &selection) const override; | ||||
|  | ||||
| 	int spacing() const | ||||
| 	{ | ||||
| 		return m_spacing; | ||||
| 	}; | ||||
| protected | ||||
| slots: | ||||
| 	virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, | ||||
| @@ -91,58 +76,50 @@ protected: | ||||
| 	void startDrag(Qt::DropActions supportedActions) override; | ||||
|  | ||||
| private: | ||||
| 	friend struct Group; | ||||
| 	friend struct VisualGroup; | ||||
| 	QList<VisualGroup *> m_groups; | ||||
|  | ||||
| 	QList<Group *> m_groups; | ||||
| 	// geometry | ||||
| 	int m_leftMargin = 5; | ||||
| 	int m_rightMargin = 5; | ||||
| 	int m_bottomMargin = 5; | ||||
| 	int m_categoryMargin = 5; | ||||
| 	int m_spacing = 5; | ||||
| 	int m_itemWidth = 100; | ||||
| 	int m_currentItemsPerRow = -1; | ||||
| 	int m_currentCursorColumn= -1; | ||||
| 	mutable QCache<int, QRect> geometryCache; | ||||
|  | ||||
| 	int m_leftMargin; | ||||
| 	int m_rightMargin; | ||||
| 	int m_bottomMargin; | ||||
| 	int m_categoryMargin; | ||||
|  | ||||
| 	// bool m_updatesDisabled; | ||||
|  | ||||
| 	Group *category(const QModelIndex &index) const; | ||||
| 	Group *category(const QString &cat) const; | ||||
| 	Group *categoryAt(const QPoint &pos) const; | ||||
|  | ||||
| 	int itemsPerRow() const; | ||||
| 	int contentWidth() const; | ||||
|  | ||||
| private: | ||||
| 	int itemWidth() const; | ||||
| 	int categoryRowHeight(const QModelIndex &index) const; | ||||
|  | ||||
| 	/*QLineEdit *m_categoryEditor; | ||||
| 	Category *m_editedCategory; | ||||
| 	void startCategoryEditor(Category *category); | ||||
|  | ||||
| private slots: | ||||
| 	void endCategoryEditor();*/ | ||||
|  | ||||
| private: /* variables */ | ||||
| 	/// point where the currently active mouse action started in geometry coordinates | ||||
| 	// point where the currently active mouse action started in geometry coordinates | ||||
| 	QPoint m_pressedPosition; | ||||
| 	QPersistentModelIndex m_pressedIndex; | ||||
| 	bool m_pressedAlreadySelected; | ||||
| 	Group *m_pressedCategory; | ||||
| 	VisualGroup *m_pressedCategory; | ||||
| 	QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; | ||||
| 	QPoint m_lastDragPosition; | ||||
| 	int m_spacing = 5; | ||||
| 	QCache<int, QRect> geometryCache; | ||||
|  | ||||
| 	VisualGroup *category(const QModelIndex &index) const; | ||||
| 	VisualGroup *category(const QString &cat) const; | ||||
| 	VisualGroup *categoryAt(const QPoint &pos) const; | ||||
|  | ||||
| 	int itemsPerRow() const | ||||
| 	{ | ||||
| 		return m_currentItemsPerRow; | ||||
| 	}; | ||||
| 	int contentWidth() const; | ||||
|  | ||||
| private: /* methods */ | ||||
| 	QPair<int, int> categoryInternalPosition(const QModelIndex &index) const; | ||||
| 	int categoryInternalRowTop(const QModelIndex &index) const; | ||||
| 	int itemHeightForCategoryRow(const Group *category, const int internalRow) const; | ||||
|  | ||||
| 	int itemWidth() const; | ||||
| 	int calculateItemsPerRow() const; | ||||
| 	int verticalScrollToValue(const QModelIndex &index, const QRect &rect, | ||||
| 							  QListView::ScrollHint hint) const; | ||||
| 	QPixmap renderToPixmap(const QModelIndexList &indices, QRect *r) const; | ||||
| 	QList<QPair<QRect, QModelIndex>> draggablePaintPairs(const QModelIndexList &indices, | ||||
| 														 QRect *r) const; | ||||
|  | ||||
| 	bool isDragEventAccepted(QDropEvent *event); | ||||
|  | ||||
| 	QPair<Group *, int> rowDropPos(const QPoint &pos); | ||||
| 	QPair<VisualGroup *, int> rowDropPos(const QPoint &pos); | ||||
|  | ||||
| 	QPoint offset() const; | ||||
| }; | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| #include "Group.h" | ||||
| #include "VisualGroup.h" | ||||
| 
 | ||||
| #include <QModelIndex> | ||||
| #include <QPainter> | ||||
| @@ -7,30 +7,83 @@ | ||||
| 
 | ||||
| #include "GroupView.h" | ||||
| 
 | ||||
| Group::Group(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) | ||||
| VisualGroup::VisualGroup(const QString &text, GroupView *view) : view(view), text(text), collapsed(false) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| Group::Group(const Group *other) | ||||
| VisualGroup::VisualGroup(const VisualGroup *other) | ||||
| 	: view(other->view), text(other->text), collapsed(other->collapsed) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void Group::update() | ||||
| void VisualGroup::update() | ||||
| { | ||||
| 	firstItemIndex = firstItem().row(); | ||||
| 	auto temp_items = items(); | ||||
| 	auto itemsPerRow = view->itemsPerRow(); | ||||
| 
 | ||||
| 	rowHeights = QVector<int>(numRows()); | ||||
| 	for (int i = 0; i < numRows(); ++i) | ||||
| 	int numRows = qMax(1, qCeil((qreal)temp_items.size() / (qreal)itemsPerRow)); | ||||
| 	rows = QVector<VisualRow>(numRows); | ||||
| 
 | ||||
| 	int maxRowHeight = 0; | ||||
| 	int positionInRow = 0; | ||||
| 	int currentRow = 0; | ||||
| 	int offsetFromTop = 0; | ||||
| 	for (auto item: temp_items) | ||||
| 	{ | ||||
| 		rowHeights[i] = view->categoryRowHeight( | ||||
| 			view->model()->index(i * view->itemsPerRow() + firstItemIndex, 0)); | ||||
| 		if(positionInRow == itemsPerRow) | ||||
| 		{ | ||||
| 			rows[currentRow].height = maxRowHeight; | ||||
| 			rows[currentRow].top = offsetFromTop; | ||||
| 			currentRow ++; | ||||
| 			offsetFromTop += maxRowHeight + 5; | ||||
| 			positionInRow = 0; | ||||
| 			maxRowHeight = 0; | ||||
| 		} | ||||
| 		auto itemHeight = view->itemDelegate()->sizeHint(view->viewOptions(), item).height(); | ||||
| 		if(itemHeight > maxRowHeight) | ||||
| 		{ | ||||
| 			maxRowHeight = itemHeight; | ||||
| 		} | ||||
| 		rows[currentRow].items.append(item); | ||||
| 		positionInRow++; | ||||
| 	} | ||||
| 	rows[currentRow].height = maxRowHeight; | ||||
| 	rows[currentRow].top = offsetFromTop; | ||||
| } | ||||
| 
 | ||||
| Group::HitResults Group::hitScan(const QPoint &pos) const | ||||
| QPair<int, int> VisualGroup::positionOf(const QModelIndex &index) const | ||||
| { | ||||
| 	Group::HitResults results = Group::NoHit; | ||||
| 	int x = 0; | ||||
| 	int y = 0; | ||||
| 	for (auto & row: rows) | ||||
| 	{ | ||||
| 		for(auto x = 0; x < row.items.size(); x++) | ||||
| 		{ | ||||
| 			if(row.items[x] == index) | ||||
| 			{ | ||||
| 				return qMakePair(x,y); | ||||
| 			} | ||||
| 		} | ||||
| 		y++; | ||||
| 	} | ||||
| 	return qMakePair(x, y); | ||||
| } | ||||
| 
 | ||||
| int VisualGroup::rowTopOf(const QModelIndex &index) const | ||||
| { | ||||
| 	auto position = positionOf(index); | ||||
| 	return rows[position.second].top; | ||||
| } | ||||
| 
 | ||||
| int VisualGroup::rowHeightOf(const QModelIndex &index) const | ||||
| { | ||||
| 	auto position = positionOf(index); | ||||
| 	return rows[position.second].height; | ||||
| } | ||||
| 
 | ||||
| VisualGroup::HitResults VisualGroup::hitScan(const QPoint &pos) const | ||||
| { | ||||
| 	VisualGroup::HitResults results = VisualGroup::NoHit; | ||||
| 	int y_start = verticalPosition(); | ||||
| 	int body_start = y_start + headerHeight(); | ||||
| 	int body_end = body_start + contentHeight() + 5; // FIXME: wtf is this 5?
 | ||||
| @@ -38,28 +91,28 @@ Group::HitResults Group::hitScan(const QPoint &pos) const | ||||
| 	// int x = pos.x();
 | ||||
| 	if (y < y_start) | ||||
| 	{ | ||||
| 		results = Group::NoHit; | ||||
| 		results = VisualGroup::NoHit; | ||||
| 	} | ||||
| 	else if (y < body_start) | ||||
| 	{ | ||||
| 		results = Group::HeaderHit; | ||||
| 		results = VisualGroup::HeaderHit; | ||||
| 		int collapseSize = headerHeight() - 4; | ||||
| 
 | ||||
| 		// the icon
 | ||||
| 		QRect iconRect = QRect(view->m_leftMargin + 2, 2 + y_start, collapseSize, collapseSize); | ||||
| 		if (iconRect.contains(pos)) | ||||
| 		{ | ||||
| 			results |= Group::CheckboxHit; | ||||
| 			results |= VisualGroup::CheckboxHit; | ||||
| 		} | ||||
| 	} | ||||
| 	else if (y < body_end) | ||||
| 	{ | ||||
| 		results |= Group::BodyHit; | ||||
| 		results |= VisualGroup::BodyHit; | ||||
| 	} | ||||
| 	return results; | ||||
| } | ||||
| 
 | ||||
| void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) | ||||
| void VisualGroup::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) | ||||
| { | ||||
| 	painter->setRenderHint(QPainter::Antialiasing); | ||||
| 
 | ||||
| @@ -190,12 +243,12 @@ void Group::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) | ||||
| 	//END: text
 | ||||
| } | ||||
| 
 | ||||
| int Group::totalHeight() const | ||||
| int VisualGroup::totalHeight() const | ||||
| { | ||||
| 	return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'?
 | ||||
| } | ||||
| 
 | ||||
| int Group::headerHeight() const | ||||
| int VisualGroup::headerHeight() const | ||||
| { | ||||
| 	QFont font(QApplication::font()); | ||||
|     font.setBold(true); | ||||
| @@ -213,31 +266,27 @@ int Group::headerHeight() const | ||||
| 	*/ | ||||
| } | ||||
| 
 | ||||
| int Group::contentHeight() const | ||||
| int VisualGroup::contentHeight() const | ||||
| { | ||||
| 	if (collapsed) | ||||
| 	{ | ||||
| 		return 0; | ||||
| 	} | ||||
| 	int result = 0; | ||||
| 	for (int i = 0; i < rowHeights.size(); ++i) | ||||
| 	{ | ||||
| 		result += rowHeights[i]; | ||||
| 	} | ||||
| 	return result; | ||||
| 	auto last = rows[numRows() - 1]; | ||||
| 	return last.top + last.height; | ||||
| } | ||||
| 
 | ||||
| int Group::numRows() const | ||||
| int VisualGroup::numRows() const | ||||
| { | ||||
| 	return qMax(1, qCeil((qreal)numItems() / (qreal)view->itemsPerRow())); | ||||
| 	return rows.size(); | ||||
| } | ||||
| 
 | ||||
| int Group::verticalPosition() const | ||||
| int VisualGroup::verticalPosition() const | ||||
| { | ||||
| 	return m_verticalPosition; | ||||
| } | ||||
| 
 | ||||
| QList<QModelIndex> Group::items() const | ||||
| QList<QModelIndex> VisualGroup::items() const | ||||
| { | ||||
| 	QList<QModelIndex> indices; | ||||
| 	for (int i = 0; i < view->model()->rowCount(); ++i) | ||||
| @@ -250,20 +299,3 @@ QList<QModelIndex> Group::items() const | ||||
| 	} | ||||
| 	return indices; | ||||
| } | ||||
| 
 | ||||
| int Group::numItems() const | ||||
| { | ||||
| 	return items().size(); | ||||
| } | ||||
| 
 | ||||
| QModelIndex Group::firstItem() const | ||||
| { | ||||
| 	QList<QModelIndex> indices = items(); | ||||
| 	return indices.isEmpty() ? QModelIndex() : indices.first(); | ||||
| } | ||||
| 
 | ||||
| QModelIndex Group::lastItem() const | ||||
| { | ||||
| 	QList<QModelIndex> indices = items(); | ||||
| 	return indices.isEmpty() ? QModelIndex() : indices.last(); | ||||
| } | ||||
| @@ -9,22 +9,37 @@ class GroupView; | ||||
| class QPainter; | ||||
| class QModelIndex; | ||||
| 
 | ||||
| struct Group | ||||
| struct VisualRow | ||||
| { | ||||
| 	QList<QModelIndex> items; | ||||
| 	int height = 0; | ||||
| 	int top = 0; | ||||
| 	inline int size() const | ||||
| 	{ | ||||
| 		return items.size(); | ||||
| 	} | ||||
| 	inline QModelIndex &operator[](int i) | ||||
| 	{ | ||||
| 		return items[i]; | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| struct VisualGroup | ||||
| { | ||||
| /* constructors */ | ||||
| 	Group(const QString &text, GroupView *view); | ||||
| 	Group(const Group *other); | ||||
| 	VisualGroup(const QString &text, GroupView *view); | ||||
| 	VisualGroup(const VisualGroup *other); | ||||
| 
 | ||||
| /* data */ | ||||
| 	GroupView *view = nullptr; | ||||
| 	QString text; | ||||
| 	bool collapsed = false; | ||||
| 	QVector<int> rowHeights; | ||||
| 	QVector<VisualRow> rows; | ||||
| 	int firstItemIndex = 0; | ||||
| 	int m_verticalPosition = 0; | ||||
| 
 | ||||
| /* logic */ | ||||
| 	/// do stuff. and things. TODO: redo.
 | ||||
| 	/// update the internal list of items and flow them into the rows.
 | ||||
| 	void update(); | ||||
| 
 | ||||
| 	/// draw the header at y-position.
 | ||||
| @@ -42,9 +57,21 @@ struct Group | ||||
| 	/// the number of visual rows this group has
 | ||||
| 	int numRows() const; | ||||
| 
 | ||||
| 	/// actually calculate the above value
 | ||||
| 	int calculateNumRows() const; | ||||
| 
 | ||||
| 	/// the height at which this group starts, in pixels
 | ||||
| 	int verticalPosition() const; | ||||
| 
 | ||||
| 	/// relative geometry - top of the row of the given item
 | ||||
| 	int rowTopOf(const QModelIndex &index) const; | ||||
| 
 | ||||
| 	/// height of the row of the given item
 | ||||
| 	int rowHeightOf(const QModelIndex &index) const; | ||||
| 
 | ||||
| 	/// x/y position of the given item inside the group (in items!)
 | ||||
| 	QPair<int, int> positionOf(const QModelIndex &index) const; | ||||
| 
 | ||||
| 	enum HitResult | ||||
| 	{ | ||||
| 		NoHit = 0x0, | ||||
| @@ -58,12 +85,7 @@ struct Group | ||||
| 	/// shoot! BANG! what did we hit?
 | ||||
| 	HitResults hitScan (const QPoint &pos) const; | ||||
| 
 | ||||
| 	/// super derpy thing.
 | ||||
| 	QList<QModelIndex> items() const; | ||||
| 	/// I don't even
 | ||||
| 	int numItems() const; | ||||
| 	QModelIndex firstItem() const; | ||||
| 	QModelIndex lastItem() const; | ||||
| }; | ||||
| 
 | ||||
| Q_DECLARE_OPERATORS_FOR_FLAGS(Group::HitResults) | ||||
| Q_DECLARE_OPERATORS_FOR_FLAGS(VisualGroup::HitResults) | ||||
		Reference in New Issue
	
	Block a user