diff options
Diffstat (limited to 'launcher/instanceview')
-rw-r--r-- | launcher/instanceview/AccessibleInstanceView.cpp | 778 | ||||
-rw-r--r-- | launcher/instanceview/AccessibleInstanceView.h | 6 | ||||
-rw-r--r-- | launcher/instanceview/AccessibleInstanceView_p.h | 118 | ||||
-rw-r--r-- | launcher/instanceview/InstanceDelegate.cpp | 428 | ||||
-rw-r--r-- | launcher/instanceview/InstanceDelegate.h | 39 | ||||
-rw-r--r-- | launcher/instanceview/InstanceProxyModel.cpp | 71 | ||||
-rw-r--r-- | launcher/instanceview/InstanceProxyModel.h | 35 | ||||
-rw-r--r-- | launcher/instanceview/InstanceView.cpp | 1010 | ||||
-rw-r--r-- | launcher/instanceview/InstanceView.h | 153 | ||||
-rw-r--r-- | launcher/instanceview/VisualGroup.cpp | 317 | ||||
-rw-r--r-- | launcher/instanceview/VisualGroup.h | 106 |
11 files changed, 0 insertions, 3061 deletions
diff --git a/launcher/instanceview/AccessibleInstanceView.cpp b/launcher/instanceview/AccessibleInstanceView.cpp deleted file mode 100644 index 7de3ac72..00000000 --- a/launcher/instanceview/AccessibleInstanceView.cpp +++ /dev/null @@ -1,778 +0,0 @@ -#include "InstanceView.h" -#include "AccessibleInstanceView.h" -#include "AccessibleInstanceView_p.h" - -#include <qvariant.h> -#include <qaccessible.h> -#include <qheaderview.h> - -#ifndef QT_NO_ACCESSIBILITY - -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object) -{ - QAccessibleInterface *iface = 0; - if (!object || !object->isWidgetType()) - return iface; - - QWidget *widget = static_cast<QWidget*>(object); - - if (classname == QLatin1String("InstanceView")) { - iface = new AccessibleInstanceView((InstanceView *)widget); - } - return iface; -} - - -QAbstractItemView *AccessibleInstanceView::view() const -{ - return qobject_cast<QAbstractItemView*>(object()); -} - -int AccessibleInstanceView::logicalIndex(const QModelIndex &index) const -{ - if (!view()->model() || !index.isValid()) - return -1; - return index.row() * (index.model()->columnCount()) + index.column(); -} - -AccessibleInstanceView::AccessibleInstanceView(QWidget *w) - : QAccessibleObject(w) -{ - Q_ASSERT(view()); -} - -bool AccessibleInstanceView::isValid() const -{ - return view(); -} - -AccessibleInstanceView::~AccessibleInstanceView() -{ - for (QAccessible::Id id : childToId) { - QAccessible::deleteAccessibleInterface(id); - } -} - -QAccessibleInterface *AccessibleInstanceView::cellAt(int row, int column) const -{ - if (!view()->model()) { - return 0; - } - - QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); - if (Q_UNLIKELY(!index.isValid())) { - qWarning() << "AccessibleInstanceView::cellAt: invalid index: " << index << " for " << view(); - return 0; - } - - return child(logicalIndex(index)); -} - -QAccessibleInterface *AccessibleInstanceView::caption() const -{ - return 0; -} - -QString AccessibleInstanceView::columnDescription(int column) const -{ - if (!view()->model()) - return QString(); - - return view()->model()->headerData(column, Qt::Horizontal).toString(); -} - -int AccessibleInstanceView::columnCount() const -{ - if (!view()->model()) - return 0; - return 1; -} - -int AccessibleInstanceView::rowCount() const -{ - if (!view()->model()) - return 0; - return view()->model()->rowCount(); -} - -int AccessibleInstanceView::selectedCellCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedIndexes().count(); -} - -int AccessibleInstanceView::selectedColumnCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedColumns().count(); -} - -int AccessibleInstanceView::selectedRowCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedRows().count(); -} - -QString AccessibleInstanceView::rowDescription(int row) const -{ - if (!view()->model()) - return QString(); - return view()->model()->headerData(row, Qt::Vertical).toString(); -} - -QList<QAccessibleInterface *> AccessibleInstanceView::selectedCells() const -{ - QList<QAccessibleInterface*> cells; - if (!view()->selectionModel()) - return cells; - const QModelIndexList selectedIndexes = view()->selectionModel()->selectedIndexes(); - cells.reserve(selectedIndexes.size()); - for (const QModelIndex &index : selectedIndexes) - cells.append(child(logicalIndex(index))); - return cells; -} - -QList<int> AccessibleInstanceView::selectedColumns() const -{ - if (!view()->selectionModel()) { - return QList<int>(); - } - - const QModelIndexList selectedColumns = view()->selectionModel()->selectedColumns(); - - QList<int> columns; - columns.reserve(selectedColumns.size()); - for (const QModelIndex &index : selectedColumns) { - columns.append(index.column()); - } - - return columns; -} - -QList<int> AccessibleInstanceView::selectedRows() const -{ - if (!view()->selectionModel()) { - return QList<int>(); - } - - QList<int> rows; - - const QModelIndexList selectedRows = view()->selectionModel()->selectedRows(); - - rows.reserve(selectedRows.size()); - for (const QModelIndex &index : selectedRows) { - rows.append(index.row()); - } - - return rows; -} - -QAccessibleInterface *AccessibleInstanceView::summary() const -{ - return 0; -} - -bool AccessibleInstanceView::isColumnSelected(int column) const -{ - if (!view()->selectionModel()) { - return false; - } - - return view()->selectionModel()->isColumnSelected(column, QModelIndex()); -} - -bool AccessibleInstanceView::isRowSelected(int row) const -{ - if (!view()->selectionModel()) { - return false; - } - - return view()->selectionModel()->isRowSelected(row, QModelIndex()); -} - -bool AccessibleInstanceView::selectRow(int row) -{ - if (!view()->model() || !view()->selectionModel()) { - return false; - } - QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); - - if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectColumns) { - return false; - } - - switch (view()->selectionMode()) { - case QAbstractItemView::NoSelection: { - return false; - } - case QAbstractItemView::SingleSelection: { - if (view()->selectionBehavior() != QAbstractItemView::SelectRows && columnCount() > 1 ) - return false; - view()->clearSelection(); - break; - } - case QAbstractItemView::ContiguousSelection: { - if ((!row || !view()->selectionModel()->isRowSelected(row - 1, view()->rootIndex())) && !view()->selectionModel()->isRowSelected(row + 1, view()->rootIndex())) { - view()->clearSelection(); - } - break; - } - default: { - break; - } - } - - view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Rows); - return true; -} - -bool AccessibleInstanceView::selectColumn(int column) -{ - if (!view()->model() || !view()->selectionModel()) { - return false; - } - QModelIndex index = view()->model()->index(0, column, view()->rootIndex()); - - if (!index.isValid() || view()->selectionBehavior() == QAbstractItemView::SelectRows) { - return false; - } - - switch (view()->selectionMode()) { - case QAbstractItemView::NoSelection: { - return false; - } - case QAbstractItemView::SingleSelection: { - if (view()->selectionBehavior() != QAbstractItemView::SelectColumns && rowCount() > 1) { - return false; - } - // fallthrough intentional - } - case QAbstractItemView::ContiguousSelection: { - if ((!column || !view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) && !view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { - view()->clearSelection(); - } - break; - } - default: { - break; - } - } - - view()->selectionModel()->select(index, QItemSelectionModel::Select | QItemSelectionModel::Columns); - return true; -} - -bool AccessibleInstanceView::unselectRow(int row) -{ - if (!view()->model() || !view()->selectionModel()) { - return false; - } - - QModelIndex index = view()->model()->index(row, 0, view()->rootIndex()); - if (!index.isValid()) { - return false; - } - - QItemSelection selection(index, index); - auto selectionModel = view()->selectionModel(); - - switch (view()->selectionMode()) { - case QAbstractItemView::SingleSelection: - // no unselect - if (selectedRowCount() == 1) { - return false; - } - break; - case QAbstractItemView::ContiguousSelection: { - // no unselect - if (selectedRowCount() == 1) { - return false; - } - - - if ((!row || selectionModel->isRowSelected(row - 1, view()->rootIndex())) && selectionModel->isRowSelected(row + 1, view()->rootIndex())) { - //If there are rows selected both up the current row and down the current rown, - //the ones which are down the current row will be deselected - selection = QItemSelection(index, view()->model()->index(rowCount() - 1, 0, view()->rootIndex())); - } - } - default: { - break; - } - } - - selectionModel->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Rows); - return true; -} - -bool AccessibleInstanceView::unselectColumn(int column) -{ - auto model = view()->model(); - if (!model || !view()->selectionModel()) { - return false; - } - - QModelIndex index = model->index(0, column, view()->rootIndex()); - if (!index.isValid()) { - return false; - } - - QItemSelection selection(index, index); - - switch (view()->selectionMode()) { - case QAbstractItemView::SingleSelection: { - //In SingleSelection and ContiguousSelection once an item - //is selected, there's no way for the user to unselect all items - if (selectedColumnCount() == 1) { - return false; - } - break; - } - case QAbstractItemView::ContiguousSelection: - if (selectedColumnCount() == 1) { - return false; - } - - if ((!column || view()->selectionModel()->isColumnSelected(column - 1, view()->rootIndex())) - && view()->selectionModel()->isColumnSelected(column + 1, view()->rootIndex())) { - //If there are columns selected both at the left of the current row and at the right - //of the current row, the ones which are at the right will be deselected - selection = QItemSelection(index, model->index(0, columnCount() - 1, view()->rootIndex())); - } - default: - break; - } - - view()->selectionModel()->select(selection, QItemSelectionModel::Deselect | QItemSelectionModel::Columns); - return true; -} - -QAccessible::Role AccessibleInstanceView::role() const -{ - return QAccessible::List; -} - -QAccessible::State AccessibleInstanceView::state() const -{ - return QAccessible::State(); -} - -QAccessibleInterface *AccessibleInstanceView::childAt(int x, int y) const -{ - QPoint viewportOffset = view()->viewport()->mapTo(view(), QPoint(0,0)); - QPoint indexPosition = view()->mapFromGlobal(QPoint(x, y) - viewportOffset); - // FIXME: if indexPosition < 0 in one coordinate, return header - - QModelIndex index = view()->indexAt(indexPosition); - if (index.isValid()) { - return child(logicalIndex(index)); - } - return 0; -} - -int AccessibleInstanceView::childCount() const -{ - if (!view()->model()) { - return 0; - } - return (view()->model()->rowCount()) * (view()->model()->columnCount()); -} - -int AccessibleInstanceView::indexOfChild(const QAccessibleInterface *iface) const -{ - if (!view()->model()) - return -1; - QAccessibleInterface *parent = iface->parent(); - if (parent->object() != view()) - return -1; - - Q_ASSERT(iface->role() != QAccessible::TreeItem); // should be handled by tree class - if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { - const AccessibleInstanceViewItem* cell = static_cast<const AccessibleInstanceViewItem*>(iface); - return logicalIndex(cell->m_index); - } else if (iface->role() == QAccessible::Pane) { - return 0; // corner button - } else { - qWarning() << "AccessibleInstanceView::indexOfChild has a child with unknown role..." << iface->role() << iface->text(QAccessible::Name); - } - // FIXME: we are in denial of our children. this should stop. - return -1; -} - -QString AccessibleInstanceView::text(QAccessible::Text t) const -{ - if (t == QAccessible::Description) - return view()->accessibleDescription(); - return view()->accessibleName(); -} - -QRect AccessibleInstanceView::rect() const -{ - if (!view()->isVisible()) - return QRect(); - QPoint pos = view()->mapToGlobal(QPoint(0, 0)); - return QRect(pos.x(), pos.y(), view()->width(), view()->height()); -} - -QAccessibleInterface *AccessibleInstanceView::parent() const -{ - if (view() && view()->parent()) { - if (qstrcmp("QComboBoxPrivateContainer", view()->parent()->metaObject()->className()) == 0) { - return QAccessible::queryAccessibleInterface(view()->parent()->parent()); - } - return QAccessible::queryAccessibleInterface(view()->parent()); - } - return 0; -} - -QAccessibleInterface *AccessibleInstanceView::child(int logicalIndex) const -{ - if (!view()->model()) - return 0; - - auto id = childToId.constFind(logicalIndex); - if (id != childToId.constEnd()) - return QAccessible::accessibleInterface(id.value()); - - int columns = view()->model()->columnCount(); - - int row = logicalIndex / columns; - int column = logicalIndex % columns; - - QAccessibleInterface *iface = 0; - - QModelIndex index = view()->model()->index(row, column, view()->rootIndex()); - if (Q_UNLIKELY(!index.isValid())) { - qWarning("AccessibleInstanceView::child: Invalid index at: %d %d", row, column); - return 0; - } - iface = new AccessibleInstanceViewItem(view(), index); - - QAccessible::registerAccessibleInterface(iface); - childToId.insert(logicalIndex, QAccessible::uniqueId(iface)); - return iface; -} - -void *AccessibleInstanceView::interface_cast(QAccessible::InterfaceType t) -{ - if (t == QAccessible::TableInterface) - return static_cast<QAccessibleTableInterface*>(this); - return 0; -} - -void AccessibleInstanceView::modelChange(QAccessibleTableModelChangeEvent *event) -{ - // if there is no cache yet, we don't update anything - if (childToId.isEmpty()) - return; - - switch (event->modelChangeType()) { - case QAccessibleTableModelChangeEvent::ModelReset: - for (QAccessible::Id id : childToId) - QAccessible::deleteAccessibleInterface(id); - childToId.clear(); - break; - - // rows are inserted: move every row after that - case QAccessibleTableModelChangeEvent::RowsInserted: - case QAccessibleTableModelChangeEvent::ColumnsInserted: { - - ChildCache newCache; - ChildCache::ConstIterator iter = childToId.constBegin(); - - while (iter != childToId.constEnd()) { - QAccessible::Id id = iter.value(); - QAccessibleInterface *iface = QAccessible::accessibleInterface(id); - Q_ASSERT(iface); - if (indexOfChild(iface) >= 0) { - newCache.insert(indexOfChild(iface), id); - } else { - // ### This should really not happen, - // but it might if the view has a root index set. - // This needs to be fixed. - QAccessible::deleteAccessibleInterface(id); - } - ++iter; - } - childToId = newCache; - break; - } - - case QAccessibleTableModelChangeEvent::ColumnsRemoved: - case QAccessibleTableModelChangeEvent::RowsRemoved: { - ChildCache newCache; - ChildCache::ConstIterator iter = childToId.constBegin(); - while (iter != childToId.constEnd()) { - QAccessible::Id id = iter.value(); - QAccessibleInterface *iface = QAccessible::accessibleInterface(id); - Q_ASSERT(iface); - if (iface->role() == QAccessible::Cell || iface->role() == QAccessible::ListItem) { - Q_ASSERT(iface->tableCellInterface()); - AccessibleInstanceViewItem *cell = static_cast<AccessibleInstanceViewItem*>(iface->tableCellInterface()); - // Since it is a QPersistentModelIndex, we only need to check if it is valid - if (cell->m_index.isValid()) - newCache.insert(indexOfChild(cell), id); - else - QAccessible::deleteAccessibleInterface(id); - } - ++iter; - } - childToId = newCache; - break; - } - - case QAccessibleTableModelChangeEvent::DataChanged: - // nothing to do in this case - break; - } -} - -// TABLE CELL - -AccessibleInstanceViewItem::AccessibleInstanceViewItem(QAbstractItemView *view_, const QModelIndex &index_) - : view(view_), m_index(index_) -{ - if (Q_UNLIKELY(!index_.isValid())) - qWarning() << "AccessibleInstanceViewItem::AccessibleInstanceViewItem with invalid index: " << index_; -} - -void *AccessibleInstanceViewItem::interface_cast(QAccessible::InterfaceType t) -{ - if (t == QAccessible::TableCellInterface) - return static_cast<QAccessibleTableCellInterface*>(this); - if (t == QAccessible::ActionInterface) - return static_cast<QAccessibleActionInterface*>(this); - return 0; -} - -int AccessibleInstanceViewItem::columnExtent() const { return 1; } -int AccessibleInstanceViewItem::rowExtent() const { return 1; } - -QList<QAccessibleInterface*> AccessibleInstanceViewItem::rowHeaderCells() const -{ - return {}; -} - -QList<QAccessibleInterface*> AccessibleInstanceViewItem::columnHeaderCells() const -{ - return {}; -} - -int AccessibleInstanceViewItem::columnIndex() const -{ - if (!isValid()) { - return -1; - } - - return m_index.column(); -} - -int AccessibleInstanceViewItem::rowIndex() const -{ - if (!isValid()) { - return -1; - } - - return m_index.row(); -} - -bool AccessibleInstanceViewItem::isSelected() const -{ - if (!isValid()) { - return false; - } - - return view->selectionModel()->isSelected(m_index); -} - -QStringList AccessibleInstanceViewItem::actionNames() const -{ - QStringList names; - names << toggleAction(); - return names; -} - -void AccessibleInstanceViewItem::doAction(const QString& actionName) -{ - if (actionName == toggleAction()) { - if (isSelected()) { - unselectCell(); - } - else { - selectCell(); - } - } -} - -QStringList AccessibleInstanceViewItem::keyBindingsForAction(const QString &) const -{ - return QStringList(); -} - - -void AccessibleInstanceViewItem::selectCell() -{ - if (!isValid()) { - return; - } - QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); - if (selectionMode == QAbstractItemView::NoSelection) { - return; - } - - Q_ASSERT(table()); - QAccessibleTableInterface *cellTable = table()->tableInterface(); - - switch (view->selectionBehavior()) { - case QAbstractItemView::SelectItems: - break; - case QAbstractItemView::SelectColumns: - if (cellTable) - cellTable->selectColumn(m_index.column()); - return; - case QAbstractItemView::SelectRows: - if (cellTable) - cellTable->selectRow(m_index.row()); - return; - } - - if (selectionMode == QAbstractItemView::SingleSelection) { - view->clearSelection(); - } - - view->selectionModel()->select(m_index, QItemSelectionModel::Select); -} - -void AccessibleInstanceViewItem::unselectCell() -{ - if (!isValid()) - return; - QAbstractItemView::SelectionMode selectionMode = view->selectionMode(); - if (selectionMode == QAbstractItemView::NoSelection) - return; - - QAccessibleTableInterface *cellTable = table()->tableInterface(); - - switch (view->selectionBehavior()) { - case QAbstractItemView::SelectItems: - break; - case QAbstractItemView::SelectColumns: - if (cellTable) - cellTable->unselectColumn(m_index.column()); - return; - case QAbstractItemView::SelectRows: - if (cellTable) - cellTable->unselectRow(m_index.row()); - return; - } - - //If the mode is not MultiSelection or ExtendedSelection and only - //one cell is selected it cannot be unselected by the user - if ((selectionMode != QAbstractItemView::MultiSelection) && (selectionMode != QAbstractItemView::ExtendedSelection) && (view->selectionModel()->selectedIndexes().count() <= 1)) - return; - - view->selectionModel()->select(m_index, QItemSelectionModel::Deselect); -} - -QAccessibleInterface *AccessibleInstanceViewItem::table() const -{ - return QAccessible::queryAccessibleInterface(view); -} - -QAccessible::Role AccessibleInstanceViewItem::role() const -{ - return QAccessible::ListItem; -} - -QAccessible::State AccessibleInstanceViewItem::state() const -{ - QAccessible::State st; - if (!isValid()) - return st; - - QRect globalRect = view->rect(); - globalRect.translate(view->mapToGlobal(QPoint(0,0))); - if (!globalRect.intersects(rect())) - st.invisible = true; - - if (view->selectionModel()->isSelected(m_index)) - st.selected = true; - if (view->selectionModel()->currentIndex() == m_index) - st.focused = true; - if (m_index.model()->data(m_index, Qt::CheckStateRole).toInt() == Qt::Checked) - st.checked = true; - - Qt::ItemFlags flags = m_index.flags(); - if (flags & Qt::ItemIsSelectable) { - st.selectable = true; - st.focusable = true; - if (view->selectionMode() == QAbstractItemView::MultiSelection) - st.multiSelectable = true; - if (view->selectionMode() == QAbstractItemView::ExtendedSelection) - st.extSelectable = true; - } - return st; -} - - -QRect AccessibleInstanceViewItem::rect() const -{ - QRect r; - if (!isValid()) - return r; - r = view->visualRect(m_index); - - if (!r.isNull()) { - r.translate(view->viewport()->mapTo(view, QPoint(0,0))); - r.translate(view->mapToGlobal(QPoint(0, 0))); - } - return r; -} - -QString AccessibleInstanceViewItem::text(QAccessible::Text t) const -{ - QString value; - if (!isValid()) - return value; - QAbstractItemModel *model = view->model(); - switch (t) { - case QAccessible::Name: - value = model->data(m_index, Qt::AccessibleTextRole).toString(); - if (value.isEmpty()) - value = model->data(m_index, Qt::DisplayRole).toString(); - break; - case QAccessible::Description: - value = model->data(m_index, Qt::AccessibleDescriptionRole).toString(); - break; - default: - break; - } - return value; -} - -void AccessibleInstanceViewItem::setText(QAccessible::Text /*t*/, const QString &text) -{ - if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable)) - return; - view->model()->setData(m_index, text); -} - -bool AccessibleInstanceViewItem::isValid() const -{ - return view && view->model() && m_index.isValid(); -} - -QAccessibleInterface *AccessibleInstanceViewItem::parent() const -{ - return QAccessible::queryAccessibleInterface(view); -} - -QAccessibleInterface *AccessibleInstanceViewItem::child(int) const -{ - return 0; -} - -#endif /* !QT_NO_ACCESSIBILITY */ diff --git a/launcher/instanceview/AccessibleInstanceView.h b/launcher/instanceview/AccessibleInstanceView.h deleted file mode 100644 index 9bfd1745..00000000 --- a/launcher/instanceview/AccessibleInstanceView.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include <QString> -class QAccessibleInterface; - -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object); diff --git a/launcher/instanceview/AccessibleInstanceView_p.h b/launcher/instanceview/AccessibleInstanceView_p.h deleted file mode 100644 index 26462f51..00000000 --- a/launcher/instanceview/AccessibleInstanceView_p.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include "QtCore/qpointer.h" -#include <QtGui/qaccessible.h> -#include <QAccessibleWidget> -#include <QAbstractItemView> -#ifndef QT_NO_ACCESSIBILITY -#include "InstanceView.h" -// #include <QHeaderView> - -class QAccessibleTableCell; -class QAccessibleTableHeaderCell; - -class AccessibleInstanceView :public QAccessibleTableInterface, public QAccessibleObject -{ -public: - explicit AccessibleInstanceView(QWidget *w); - bool isValid() const override; - - QAccessible::Role role() const override; - QAccessible::State state() const override; - QString text(QAccessible::Text t) const override; - QRect rect() const override; - - QAccessibleInterface *childAt(int x, int y) const override; - int childCount() const override; - int indexOfChild(const QAccessibleInterface *) const override; - - QAccessibleInterface *parent() const override; - QAccessibleInterface *child(int index) const override; - - void *interface_cast(QAccessible::InterfaceType t) override; - - // table interface - QAccessibleInterface *cellAt(int row, int column) const override; - QAccessibleInterface *caption() const override; - QAccessibleInterface *summary() const override; - QString columnDescription(int column) const override; - QString rowDescription(int row) const override; - int columnCount() const override; - int rowCount() const override; - - // selection - int selectedCellCount() const override; - int selectedColumnCount() const override; - int selectedRowCount() const override; - QList<QAccessibleInterface*> selectedCells() const override; - QList<int> selectedColumns() const override; - QList<int> selectedRows() const override; - bool isColumnSelected(int column) const override; - bool isRowSelected(int row) const override; - bool selectRow(int row) override; - bool selectColumn(int column) override; - bool unselectRow(int row) override; - bool unselectColumn(int column) override; - - QAbstractItemView *view() const; - - void modelChange(QAccessibleTableModelChangeEvent *event) override; - -protected: - // maybe vector - typedef QHash<int, QAccessible::Id> ChildCache; - mutable ChildCache childToId; - - virtual ~AccessibleInstanceView(); - -private: - inline int logicalIndex(const QModelIndex &index) const; -}; - -class AccessibleInstanceViewItem: public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface -{ -public: - AccessibleInstanceViewItem(QAbstractItemView *view, const QModelIndex &m_index); - - void *interface_cast(QAccessible::InterfaceType t) override; - QObject *object() const override { return nullptr; } - QAccessible::Role role() const override; - QAccessible::State state() const override; - QRect rect() const override; - bool isValid() const override; - - QAccessibleInterface *childAt(int, int) const override { return nullptr; } - int childCount() const override { return 0; } - int indexOfChild(const QAccessibleInterface *) const override { return -1; } - - QString text(QAccessible::Text t) const override; - void setText(QAccessible::Text t, const QString &text) override; - - QAccessibleInterface *parent() const override; - QAccessibleInterface *child(int) const override; - - // cell interface - int columnExtent() const override; - QList<QAccessibleInterface*> columnHeaderCells() const override; - int columnIndex() const override; - int rowExtent() const override; - QList<QAccessibleInterface*> rowHeaderCells() const override; - int rowIndex() const override; - bool isSelected() const override; - QAccessibleInterface* table() const override; - - //action interface - QStringList actionNames() const override; - void doAction(const QString &actionName) override; - QStringList keyBindingsForAction(const QString &actionName) const override; - -private: - QPointer<QAbstractItemView > view; - QPersistentModelIndex m_index; - - void selectCell(); - void unselectCell(); - - friend class AccessibleInstanceView; -}; -#endif /* !QT_NO_ACCESSIBILITY */ diff --git a/launcher/instanceview/InstanceDelegate.cpp b/launcher/instanceview/InstanceDelegate.cpp deleted file mode 100644 index 3c4ca63f..00000000 --- a/launcher/instanceview/InstanceDelegate.cpp +++ /dev/null @@ -1,428 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InstanceDelegate.h" -#include <QPainter> -#include <QTextOption> -#include <QTextLayout> -#include <QApplication> -#include <QtMath> -#include <QDebug> - -#include "InstanceView.h" -#include "BaseInstance.h" -#include "InstanceList.h" -#include <xdgicon.h> -#include <QTextEdit> - -// Origin: Qt -static void viewItemTextLayout(QTextLayout &textLayout, int lineWidth, qreal &height, - qreal &widthUsed) -{ - height = 0; - widthUsed = 0; - textLayout.beginLayout(); - QString str = textLayout.text(); - while (true) - { - QTextLine line = textLayout.createLine(); - if (!line.isValid()) - break; - if (line.textLength() == 0) - break; - line.setLineWidth(lineWidth); - line.setPosition(QPointF(0, height)); - height += line.height(); - widthUsed = qMax(widthUsed, line.naturalTextWidth()); - } - textLayout.endLayout(); -} - -ListViewDelegate::ListViewDelegate(QObject *parent) : QStyledItemDelegate(parent) -{ -} - -void drawSelectionRect(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect) -{ - if ((option.state & QStyle::State_Selected)) - painter->fillRect(rect, option.palette.brush(QPalette::Highlight)); - else - { - QColor backgroundColor = option.palette.color(QPalette::Background); - backgroundColor.setAlpha(160); - painter->fillRect(rect, QBrush(backgroundColor)); - } -} - -void drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) -{ - if (!(option.state & QStyle::State_HasFocus)) - return; - QStyleOptionFocusRect opt; - opt.direction = option.direction; - opt.fontMetrics = option.fontMetrics; - opt.palette = option.palette; - opt.rect = rect; - // opt.state = option.state | QStyle::State_KeyboardFocusChange | - // QStyle::State_Item; - auto col = option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base; - opt.backgroundColor = option.palette.color(col); - // Apparently some widget styles expect this hint to not be set - painter->setRenderHint(QPainter::Antialiasing, false); - - QStyle *style = option.widget ? option.widget->style() : QApplication::style(); - - style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget); - - painter->setRenderHint(QPainter::Antialiasing); -} - -// TODO this can be made a lot prettier -void drawProgressOverlay(QPainter *painter, const QStyleOptionViewItem &option, - const int value, const int maximum) -{ - if (maximum == 0 || value == maximum) - { - return; - } - - painter->save(); - - qreal percent = (qreal)value / (qreal)maximum; - QColor color = option.palette.color(QPalette::Dark); - color.setAlphaF(0.70f); - painter->setBrush(color); - painter->setPen(QPen(QBrush(), 0)); - painter->drawPie(option.rect, 90 * 16, -percent * 360 * 16); - - painter->restore(); -} - -void drawBadges(QPainter *painter, const QStyleOptionViewItem &option, BaseInstance *instance, QIcon::Mode mode, QIcon::State state) -{ - QList<QString> pixmaps; - if (instance->isRunning()) - { - pixmaps.append("status-running"); - } - else if (instance->hasCrashed() || instance->hasVersionBroken()) - { - pixmaps.append("status-bad"); - } - if (instance->hasUpdateAvailable()) - { - pixmaps.append("checkupdate"); - } - - static const int itemSide = 24; - static const int spacing = 1; - const int itemsPerRow = qMax(1, qFloor(double(option.rect.width() + spacing) / double(itemSide + spacing))); - const int rows = qCeil((double)pixmaps.size() / (double)itemsPerRow); - QListIterator<QString> it(pixmaps); - painter->translate(option.rect.topLeft()); - for (int y = 0; y < rows; ++y) - { - for (int x = 0; x < itemsPerRow; ++x) - { - if (!it.hasNext()) - { - return; - } - // FIXME: inject this. - auto icon = XdgIcon::fromTheme(it.next()); - // opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); - const QPixmap pixmap; - // itemSide - QRect badgeRect( - option.rect.width() - x * itemSide + qMax(x - 1, 0) * spacing - itemSide, - y * itemSide + qMax(y - 1, 0) * spacing, - itemSide, - itemSide - ); - icon.paint(painter, badgeRect, Qt::AlignCenter, mode, state); - } - } - painter->translate(-option.rect.topLeft()); -} - -static QSize viewItemTextSize(const QStyleOptionViewItem *option) -{ - QStyle *style = option->widget ? option->widget->style() : QApplication::style(); - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(option->font); - textLayout.setText(option->text); - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, option, option->widget) + 1; - QRect bounds(0, 0, 100 - 2 * textMargin, 600); - qreal height = 0, widthUsed = 0; - viewItemTextLayout(textLayout, bounds.width(), height, widthUsed); - const QSize size(qCeil(widthUsed), qCeil(height)); - return QSize(size.width() + 2 * textMargin, size.height()); -} - -void ListViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - painter->save(); - painter->setClipRect(opt.rect); - - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - - // const int iconSize = style->pixelMetric(QStyle::PM_IconViewIconSize); - const int iconSize = 48; - QRect iconbox = opt.rect; - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, opt.widget) + 1; - QRect textRect = opt.rect; - QRect textHighlightRect = textRect; - // clip the decoration on top, remove width padding - textRect.adjust(textMargin, iconSize + textMargin + 5, -textMargin, 0); - - textHighlightRect.adjust(0, iconSize + 5, 0, 0); - - // draw background - { - // FIXME: unused - // QSize textSize = viewItemTextSize ( &opt ); - drawSelectionRect(painter, opt, textHighlightRect); - /* - QPalette::ColorGroup cg; - QStyleOptionViewItem opt2(opt); - - if ((opt.widget && opt.widget->isEnabled()) || (opt.state & QStyle::State_Enabled)) - { - if (!(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - else - cg = QPalette::Normal; - } - else - { - cg = QPalette::Disabled; - } - */ - /* - opt2.palette.setCurrentColorGroup(cg); - - // fill in background, if any - - - if (opt.backgroundBrush.style() != Qt::NoBrush) - { - QPointF oldBO = painter->brushOrigin(); - painter->setBrushOrigin(opt.rect.topLeft()); - painter->fillRect(opt.rect, opt.backgroundBrush); - painter->setBrushOrigin(oldBO); - } - - drawSelectionRect(painter, opt2, textHighlightRect); - */ - - /* - if (opt.showDecorationSelected) - { - drawSelectionRect(painter, opt2, opt.rect); - drawFocusRect(painter, opt2, opt.rect); - // painter->fillRect ( opt.rect, opt.palette.brush ( cg, QPalette::Highlight ) ); - } - else - { - - // if ( opt.state & QStyle::State_Selected ) - { - // QRect textRect = subElementRect ( QStyle::SE_ItemViewItemText, opt, - // opt.widget ); - // painter->fillRect ( textHighlightRect, opt.palette.brush ( cg, - // QPalette::Highlight ) ); - drawSelectionRect(painter, opt2, textHighlightRect); - drawFocusRect(painter, opt2, textHighlightRect); - } - } - */ - } - - // icon mode and state, also used for badges - QIcon::Mode mode = QIcon::Normal; - if (!(opt.state & QStyle::State_Enabled)) - mode = QIcon::Disabled; - else if (opt.state & QStyle::State_Selected) - mode = QIcon::Selected; - QIcon::State state = opt.state & QStyle::State_Open ? QIcon::On : QIcon::Off; - - // draw the icon - { - iconbox.setHeight(iconSize); - opt.icon.paint(painter, iconbox, Qt::AlignCenter, mode, state); - } - // set the text colors - QPalette::ColorGroup cg = - opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) - cg = QPalette::Inactive; - if (opt.state & QStyle::State_Selected) - { - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - } - else - { - painter->setPen(opt.palette.color(cg, QPalette::Text)); - } - - // draw the text - QTextOption textOption; - textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textOption.setTextDirection(opt.direction); - textOption.setAlignment(QStyle::visualAlignment(opt.direction, opt.displayAlignment)); - QTextLayout textLayout; - textLayout.setTextOption(textOption); - textLayout.setFont(opt.font); - textLayout.setText(opt.text); - - qreal width, height; - viewItemTextLayout(textLayout, textRect.width(), height, width); - - const int lineCount = textLayout.lineCount(); - - const QRect layoutRect = QStyle::alignedRect( - opt.direction, opt.displayAlignment, QSize(textRect.width(), int(height)), textRect); - const QPointF position = layoutRect.topLeft(); - for (int i = 0; i < lineCount; ++i) - { - const QTextLine line = textLayout.lineAt(i); - line.draw(painter, position); - } - - // FIXME: this really has no business of being here. Make generic. - auto instance = (BaseInstance*)index.data(InstanceList::InstancePointerRole) - .value<void *>(); - if (instance) - { - drawBadges(painter, opt, instance, mode, state); - } - - drawProgressOverlay(painter, opt, index.data(InstanceViewRoles::ProgressValueRole).toInt(), - index.data(InstanceViewRoles::ProgressMaximumRole).toInt()); - - painter->restore(); -} - -QSize ListViewDelegate::sizeHint(const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - QStyleOptionViewItem opt = option; - initStyleOption(&opt, index); - opt.features |= QStyleOptionViewItem::WrapText; - opt.text = index.data().toString(); - opt.textElideMode = Qt::ElideRight; - opt.displayAlignment = Qt::AlignTop | Qt::AlignHCenter; - - QStyle *style = opt.widget ? opt.widget->style() : QApplication::style(); - const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, opt.widget) + 1; - int height = 48 + textMargin * 2 + 5; // TODO: turn constants into variables - QSize szz = viewItemTextSize(&opt); - height += szz.height(); - // FIXME: maybe the icon items could scale and keep proportions? - QSize sz(100, height); - return sz; -} - -class NoReturnTextEdit: public QTextEdit -{ - Q_OBJECT -public: - explicit NoReturnTextEdit(QWidget *parent) : QTextEdit(parent) - { - setTextInteractionFlags(Qt::TextEditorInteraction); - setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); - } - bool event(QEvent * event) override - { - auto eventType = event->type(); - if(eventType == QEvent::KeyPress || eventType == QEvent::KeyRelease) - { - QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); - auto key = keyEvent->key(); - if (key == Qt::Key_Return || key == Qt::Key_Enter) - { - emit editingDone(); - return true; - } - if(key == Qt::Key_Tab) - { - return true; - } - } - return QTextEdit::event(event); - } -signals: - void editingDone(); -}; - -void ListViewDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - const int iconSize = 48; - QRect textRect = option.rect; - // QStyle *style = option.widget ? option.widget->style() : QApplication::style(); - textRect.adjust(0, iconSize + 5, 0, 0); - editor->setGeometry(textRect); -} - -void ListViewDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const -{ - auto text = index.data(Qt::EditRole).toString(); - QTextEdit * realeditor = qobject_cast<NoReturnTextEdit *>(editor); - realeditor->setAlignment(Qt::AlignHCenter | Qt::AlignTop); - realeditor->append(text); - realeditor->selectAll(); - realeditor->document()->clearUndoRedoStacks(); -} - -void ListViewDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const -{ - QTextEdit * realeditor = qobject_cast<NoReturnTextEdit *>(editor); - QString text = realeditor->toPlainText(); - text.replace(QChar('\n'), QChar(' ')); - text = text.trimmed(); - if(text.size() != 0) - { - model->setData(index, text); - } -} - -QWidget * ListViewDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ - auto editor = new NoReturnTextEdit(parent); - connect(editor, &NoReturnTextEdit::editingDone, this, &ListViewDelegate::editingDone); - return editor; -} - -void ListViewDelegate::editingDone() -{ - NoReturnTextEdit *editor = qobject_cast<NoReturnTextEdit *>(sender()); - emit commitData(editor); - emit closeEditor(editor); -} - -#include "InstanceDelegate.moc" diff --git a/launcher/instanceview/InstanceDelegate.h b/launcher/instanceview/InstanceDelegate.h deleted file mode 100644 index d95279f3..00000000 --- a/launcher/instanceview/InstanceDelegate.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QStyledItemDelegate> -#include <QCache> - -class ListViewDelegate : public QStyledItemDelegate -{ - Q_OBJECT - -public: - explicit ListViewDelegate(QObject *parent = 0); - virtual ~ListViewDelegate() {} - - void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; - void updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const override; - QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override; - - void setEditorData(QWidget * editor, const QModelIndex & index) const override; - void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override; - -private slots: - void editingDone(); -}; diff --git a/launcher/instanceview/InstanceProxyModel.cpp b/launcher/instanceview/InstanceProxyModel.cpp deleted file mode 100644 index d8de93ed..00000000 --- a/launcher/instanceview/InstanceProxyModel.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InstanceProxyModel.h" - -#include "InstanceView.h" -#include "Application.h" -#include <BaseInstance.h> -#include <icons/IconList.h> - -#include <QDebug> - -InstanceProxyModel::InstanceProxyModel(QObject *parent) : QSortFilterProxyModel(parent) { - m_naturalSort.setNumericMode(true); - m_naturalSort.setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive); - // FIXME: use loaded translation as source of locale instead, hook this up to translation changes - m_naturalSort.setLocale(QLocale::system()); -} - -QVariant InstanceProxyModel::data(const QModelIndex & index, int role) const -{ - QVariant data = QSortFilterProxyModel::data(index, role); - if(role == Qt::DecorationRole) - { - return QVariant(APPLICATION->icons()->getIcon(data.toString())); - } - return data; -} - -bool InstanceProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { - const QString leftCategory = left.data(InstanceViewRoles::GroupRole).toString(); - const QString rightCategory = right.data(InstanceViewRoles::GroupRole).toString(); - if (leftCategory == rightCategory) { - return subSortLessThan(left, right); - } - else { - // FIXME: real group sorting happens in InstanceView::updateGeometries(), see LocaleString - auto result = leftCategory.localeAwareCompare(rightCategory); - if(result == 0) { - return subSortLessThan(left, right); - } - return result < 0; - } -} - -bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, const QModelIndex &right) const -{ - BaseInstance *pdataLeft = static_cast<BaseInstance *>(left.internalPointer()); - BaseInstance *pdataRight = static_cast<BaseInstance *>(right.internalPointer()); - QString sortMode = APPLICATION->settings()->get("InstSortMode").toString(); - if (sortMode == "LastLaunch") - { - return pdataLeft->lastLaunch() > pdataRight->lastLaunch(); - } - else - { - return m_naturalSort.compare(pdataLeft->name(), pdataRight->name()) < 0; - } -} diff --git a/launcher/instanceview/InstanceProxyModel.h b/launcher/instanceview/InstanceProxyModel.h deleted file mode 100644 index bba8d2b5..00000000 --- a/launcher/instanceview/InstanceProxyModel.h +++ /dev/null @@ -1,35 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QSortFilterProxyModel> -#include <QCollator> - -class InstanceProxyModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - InstanceProxyModel(QObject *parent = 0); - -protected: - QVariant data(const QModelIndex & index, int role) const override; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; - bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const; - -private: - QCollator m_naturalSort; -}; diff --git a/launcher/instanceview/InstanceView.cpp b/launcher/instanceview/InstanceView.cpp deleted file mode 100644 index 1f044866..00000000 --- a/launcher/instanceview/InstanceView.cpp +++ /dev/null @@ -1,1010 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InstanceView.h" - -#include <QPainter> -#include <QApplication> -#include <QtMath> -#include <QMouseEvent> -#include <QListView> -#include <QPersistentModelIndex> -#include <QDrag> -#include <QMimeData> -#include <QCache> -#include <QScrollBar> -#include <QAccessible> - -#include "VisualGroup.h" -#include <QDebug> - -#include <Application.h> -#include <InstanceList.h> - - -template <typename T> bool listsIntersect(const QList<T> &l1, const QList<T> t2) -{ - for (auto &item : l1) - { - if (t2.contains(item)) - { - return true; - } - } - return false; -} - -InstanceView::InstanceView(QWidget *parent) - : QAbstractItemView(parent) -{ - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - setAcceptDrops(true); - setAutoScroll(true); -} - -InstanceView::~InstanceView() -{ - qDeleteAll(m_groups); - m_groups.clear(); -} - -void InstanceView::setModel(QAbstractItemModel *model) -{ - QAbstractItemView::setModel(model); - connect(model, &QAbstractItemModel::modelReset, this, &InstanceView::modelReset); - connect(model, &QAbstractItemModel::rowsRemoved, this, &InstanceView::rowsRemoved); -} - -void InstanceView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) -{ - scheduleDelayedItemsLayout(); -} -void InstanceView::rowsInserted(const QModelIndex &parent, int start, int end) -{ - scheduleDelayedItemsLayout(); -} - -void InstanceView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) -{ - scheduleDelayedItemsLayout(); -} - -void InstanceView::modelReset() -{ - scheduleDelayedItemsLayout(); -} - -void InstanceView::rowsRemoved() -{ - scheduleDelayedItemsLayout(); -} - -void InstanceView::currentChanged(const QModelIndex& current, const QModelIndex& previous) -{ - QAbstractItemView::currentChanged(current, previous); - // TODO: for accessibility support, implement+register a factory, steal QAccessibleTable from Qt and return an instance of it for InstanceView. -#ifndef QT_NO_ACCESSIBILITY - if (QAccessible::isActive() && current.isValid()) { - QAccessibleEvent event(this, QAccessible::Focus); - event.setChild(current.row()); - QAccessible::updateAccessibility(&event); - } -#endif /* !QT_NO_ACCESSIBILITY */ -} - - -class LocaleString : public QString -{ -public: - LocaleString(const char *s) : QString(s) - { - } - LocaleString(const QString &s) : QString(s) - { - } -}; - -inline bool operator<(const LocaleString &lhs, const LocaleString &rhs) -{ - return (QString::localeAwareCompare(lhs, rhs) < 0); -} - -void InstanceView::updateScrollbar() -{ - int previousScroll = verticalScrollBar()->value(); - if (m_groups.isEmpty()) - { - verticalScrollBar()->setRange(0, 0); - } - else - { - int totalHeight = 0; - // top margin - totalHeight += m_categoryMargin; - int itemScroll = 0; - for (auto category : m_groups) - { - category->m_verticalPosition = totalHeight; - totalHeight += category->totalHeight() + m_categoryMargin; - if(!itemScroll && category->totalHeight() != 0) - { - itemScroll = category->contentHeight() / category->numRows(); - } - } - // do not divide by zero - if(itemScroll == 0) - itemScroll = 64; - - totalHeight += m_bottomMargin; - verticalScrollBar()->setSingleStep ( itemScroll ); - const int rowsPerPage = qMax ( viewport()->height() / itemScroll, 1 ); - verticalScrollBar()->setPageStep ( rowsPerPage * itemScroll ); - - verticalScrollBar()->setRange(0, totalHeight - height()); - } - - verticalScrollBar()->setValue(qMin(previousScroll, verticalScrollBar()->maximum())); -} - -void InstanceView::updateGeometries() -{ - geometryCache.clear(); - - QMap<LocaleString, VisualGroup *> cats; - - for (int i = 0; i < model()->rowCount(); ++i) - { - const QString groupName = model()->index(i, 0).data(InstanceViewRoles::GroupRole).toString(); - if (!cats.contains(groupName)) - { - VisualGroup *old = this->category(groupName); - if (old) - { - auto cat = new VisualGroup(old); - cats.insert(groupName, cat); - cat->update(); - } - else - { - auto cat = new VisualGroup(groupName, this); - if(fVisibility) { - cat->collapsed = fVisibility(groupName); - } - cats.insert(groupName, cat); - cat->update(); - } - } - } - - qDeleteAll(m_groups); - m_groups = cats.values(); - updateScrollbar(); - viewport()->update(); -} - -bool InstanceView::isIndexHidden(const QModelIndex &index) const -{ - VisualGroup *cat = category(index); - if (cat) - { - return cat->collapsed; - } - else - { - return false; - } -} - -VisualGroup *InstanceView::category(const QModelIndex &index) const -{ - return category(index.data(InstanceViewRoles::GroupRole).toString()); -} - -VisualGroup *InstanceView::category(const QString &cat) const -{ - for (auto group : m_groups) - { - if (group->text == cat) - { - return group; - } - } - return nullptr; -} - -VisualGroup *InstanceView::categoryAt(const QPoint &pos, VisualGroup::HitResults & result) const -{ - for (auto group : m_groups) - { - result = group->hitScan(pos); - if(result != VisualGroup::NoHit) - { - return group; - } - } - result = VisualGroup::NoHit; - return nullptr; -} - -QString InstanceView::groupNameAt(const QPoint &point) -{ - executeDelayedItemsLayout(); - - VisualGroup::HitResults hitresult; - auto group = categoryAt(point + offset(), hitresult); - if(group && (hitresult & (VisualGroup::HeaderHit | VisualGroup::BodyHit))) - { - return group->text; - } - return QString(); -} - -int InstanceView::calculateItemsPerRow() const -{ - return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); -} - -int InstanceView::contentWidth() const -{ - return width() - m_leftMargin - m_rightMargin; -} - -int InstanceView::itemWidth() const -{ - return m_itemWidth; -} - -void InstanceView::mousePressEvent(QMouseEvent *event) -{ - executeDelayedItemsLayout(); - - QPoint visualPos = event->pos(); - QPoint geometryPos = event->pos() + offset(); - - QPersistentModelIndex index = indexAt(visualPos); - - m_pressedIndex = index; - m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex); - m_pressedPosition = geometryPos; - - VisualGroup::HitResults hitresult; - m_pressedCategory = categoryAt(geometryPos, hitresult); - if (m_pressedCategory && hitresult & VisualGroup::CheckboxHit) - { - setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState); - event->accept(); - return; - } - - 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(visualPos, visualPos); - setSelection(rect, QItemSelectionModel::ClearAndSelect); - - // signal handlers may change the model - emit pressed(index); - } - else - { - // Forces a finalize() even if mouse is pressed, but not on a item - selectionModel()->select(QModelIndex(), QItemSelectionModel::Select); - } -} - -void InstanceView::mouseMoveEvent(QMouseEvent *event) -{ - executeDelayedItemsLayout(); - - QPoint topLeft; - QPoint visualPos = event->pos(); - QPoint geometryPos = event->pos() + offset(); - - if (state() == ExpandingState || state() == CollapsingState) - { - return; - } - - if (state() == DraggingState) - { - topLeft = m_pressedPosition - offset(); - if ((topLeft - event->pos()).manhattanLength() > QApplication::startDragDistance()) - { - m_pressedIndex = QModelIndex(); - startDrag(model()->supportedDragActions()); - setState(NoState); - stopAutoScroll(); - } - return; - } - - if (selectionMode() != SingleSelection) - { - topLeft = m_pressedPosition - offset(); - } - else - { - topLeft = geometryPos; - } - - if (m_pressedIndex.isValid() && (state() != DragSelectingState) && - (event->buttons() != Qt::NoButton) && !selectedIndexes().isEmpty()) - { - setState(DraggingState); - return; - } - - if ((event->buttons() & Qt::LeftButton) && selectionModel()) - { - setState(DragSelectingState); - - setSelection(QRect(visualPos, visualPos), QItemSelectionModel::ClearAndSelect); - QModelIndex index = indexAt(visualPos); - - // set at the end because it might scroll the view - if (index.isValid() && (index != selectionModel()->currentIndex()) && - (index.flags() & Qt::ItemIsEnabled)) - { - selectionModel()->setCurrentIndex(index, QItemSelectionModel::NoUpdate); - } - } -} - -void InstanceView::mouseReleaseEvent(QMouseEvent *event) -{ - executeDelayedItemsLayout(); - - QPoint visualPos = event->pos(); - QPoint geometryPos = event->pos() + offset(); - QPersistentModelIndex index = indexAt(visualPos); - - VisualGroup::HitResults hitresult; - - bool click = (index == m_pressedIndex && index.isValid()) || - (m_pressedCategory && m_pressedCategory == categoryAt(geometryPos, hitresult)); - - if (click && m_pressedCategory) - { - if (state() == ExpandingState) - { - m_pressedCategory->collapsed = false; - emit groupStateChanged(m_pressedCategory->text, false); - - updateGeometries(); - viewport()->update(); - event->accept(); - m_pressedCategory = nullptr; - setState(NoState); - return; - } - else if (state() == CollapsingState) - { - m_pressedCategory->collapsed = true; - emit groupStateChanged(m_pressedCategory->text, true); - - updateGeometries(); - viewport()->update(); - event->accept(); - m_pressedCategory = nullptr; - setState(NoState); - return; - } - } - - m_ctrlDragSelectionFlag = QItemSelectionModel::NoUpdate; - - setState(NoState); - - if (click) - { - if (event->button() == Qt::LeftButton) - { - emit clicked(index); - } - QStyleOptionViewItem option = viewOptions(); - if (m_pressedAlreadySelected) - { - option.state |= QStyle::State_Selected; - } - if ((model()->flags(index) & Qt::ItemIsEnabled) && - style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) - { - emit activated(index); - } - } -} - -void InstanceView::mouseDoubleClickEvent(QMouseEvent *event) -{ - executeDelayedItemsLayout(); - - QModelIndex index = indexAt(event->pos()); - if (!index.isValid() || !(index.flags() & Qt::ItemIsEnabled) || (m_pressedIndex != index)) - { - QMouseEvent me( - QEvent::MouseButtonPress, - event->localPos(), - event->windowPos(), - event->screenPos(), - event->button(), - event->buttons(), - event->modifiers() - ); - mousePressEvent(&me); - return; - } - // signal handlers may change the model - QPersistentModelIndex persistent = index; - emit doubleClicked(persistent); - - QStyleOptionViewItem option = viewOptions(); - if ((model()->flags(index) & Qt::ItemIsEnabled) && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, &option, this)) - { - emit activated(index); - } -} - -void InstanceView::paintEvent(QPaintEvent *event) -{ - executeDelayedItemsLayout(); - - QPainter painter(this->viewport()); - - QStyleOptionViewItem option(viewOptions()); - option.widget = this; - - int wpWidth = viewport()->width(); - option.rect.setWidth(wpWidth); - for (int i = 0; i < m_groups.size(); ++i) - { - VisualGroup *category = m_groups.at(i); - int y = category->verticalPosition(); - y -= verticalOffset(); - QRect backup = option.rect; - int height = category->totalHeight(); - option.rect.setTop(y); - option.rect.setHeight(height); - option.rect.setLeft(m_leftMargin); - option.rect.setRight(wpWidth - m_rightMargin); - category->drawHeader(&painter, option); - y += category->totalHeight() + m_categoryMargin; - option.rect = backup; - } - - for (int i = 0; i < model()->rowCount(); ++i) - { - const QModelIndex index = model()->index(i, 0); - if (isIndexHidden(index)) - { - continue; - } - Qt::ItemFlags flags = index.flags(); - option.rect = visualRect(index); - option.features |= QStyleOptionViewItem::WrapText; - if (flags & Qt::ItemIsSelectable && selectionModel()->isSelected(index)) - { - option.state |= selectionModel()->isSelected(index) ? QStyle::State_Selected - : QStyle::State_None; - } - else - { - option.state &= ~QStyle::State_Selected; - } - option.state |= (index == currentIndex()) ? QStyle::State_HasFocus : QStyle::State_None; - if (!(flags & Qt::ItemIsEnabled)) - { - option.state &= ~QStyle::State_Enabled; - } - itemDelegate()->paint(&painter, option, index); - } - - /* - * Drop indicators for manual reordering... - */ -#if 0 - if (!m_lastDragPosition.isNull()) - { - QPair<Group *, int> pair = rowDropPos(m_lastDragPosition); - Group *category = pair.first; - int row = pair.second; - if (category) - { - int internalRow = row - category->firstItemIndex; - QLine line; - if (internalRow >= category->numItems()) - { - QRect toTheRightOfRect = visualRect(category->lastItem()); - line = QLine(toTheRightOfRect.topRight(), toTheRightOfRect.bottomRight()); - } - else - { - QRect toTheLeftOfRect = visualRect(model()->index(row, 0)); - line = QLine(toTheLeftOfRect.topLeft(), toTheLeftOfRect.bottomLeft()); - } - painter.save(); - painter.setPen(QPen(Qt::black, 3)); - painter.drawLine(line); - painter.restore(); - } - } -#endif -} - -void InstanceView::resizeEvent(QResizeEvent *event) -{ - int newItemsPerRow = calculateItemsPerRow(); - if(newItemsPerRow != m_currentItemsPerRow) - { - m_currentCursorColumn = -1; - m_currentItemsPerRow = newItemsPerRow; - updateGeometries(); - } - else - { - updateScrollbar(); - } -} - -void InstanceView::dragEnterEvent(QDragEnterEvent *event) -{ - executeDelayedItemsLayout(); - - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void InstanceView::dragMoveEvent(QDragMoveEvent *event) -{ - executeDelayedItemsLayout(); - - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void InstanceView::dragLeaveEvent(QDragLeaveEvent *event) -{ - executeDelayedItemsLayout(); - - m_lastDragPosition = QPoint(); - viewport()->update(); -} - -void InstanceView::dropEvent(QDropEvent *event) -{ - executeDelayedItemsLayout(); - - m_lastDragPosition = QPoint(); - - stopAutoScroll(); - setState(NoState); - - auto mimedata = event->mimeData(); - - if (event->source() == this) - { - if(event->possibleActions() & Qt::MoveAction) - { - QPair<VisualGroup *, VisualGroup::HitResults> dropPos = rowDropPos(event->pos()); - const VisualGroup *group = dropPos.first; - auto hitresult = dropPos.second; - - if (hitresult == VisualGroup::HitResult::NoHit) - { - viewport()->update(); - return; - } - auto instanceId = QString::fromUtf8(mimedata->data("application/x-instanceid")); - auto instanceList = APPLICATION->instances().get(); - instanceList->setInstanceGroup(instanceId, group->text); - event->setDropAction(Qt::MoveAction); - event->accept(); - - updateGeometries(); - viewport()->update(); - } - return; - } - - // check if the action is supported - if (!mimedata) - { - return; - } - - // files dropped from outside? - if (mimedata->hasUrls()) - { - auto urls = mimedata->urls(); - event->accept(); - emit droppedURLs(urls); - } -} - -void InstanceView::startDrag(Qt::DropActions supportedActions) -{ - executeDelayedItemsLayout(); - - QModelIndexList indexes = selectionModel()->selectedIndexes(); - if(indexes.count() == 0) - return; - - QMimeData *data = model()->mimeData(indexes); - if (!data) - { - return; - } - QRect rect; - QPixmap pixmap = renderToPixmap(indexes, &rect); - QDrag *drag = new QDrag(this); - drag->setPixmap(pixmap); - drag->setMimeData(data); - drag->setHotSpot(m_pressedPosition - rect.topLeft()); - Qt::DropAction defaultDropAction = Qt::IgnoreAction; - if (this->defaultDropAction() != Qt::IgnoreAction && (supportedActions & this->defaultDropAction())) - { - defaultDropAction = this->defaultDropAction(); - } - /*auto action = */ - drag->exec(supportedActions, defaultDropAction); -} - -QRect InstanceView::visualRect(const QModelIndex &index) const -{ - const_cast<InstanceView*>(this)->executeDelayedItemsLayout(); - - return geometryRect(index).translated(-offset()); -} - -QRect InstanceView::geometryRect(const QModelIndex &index) const -{ - const_cast<InstanceView*>(this)->executeDelayedItemsLayout(); - - if (!index.isValid() || isIndexHidden(index) || index.column() > 0) - { - return QRect(); - } - - int row = index.row(); - if(geometryCache.contains(row)) - { - return *geometryCache[row]; - } - - const VisualGroup *cat = category(index); - QPair<int, int> pos = cat->positionOf(index); - int x = pos.first; - // int y = pos.second; - - 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; -} - -QModelIndex InstanceView::indexAt(const QPoint &point) const -{ - const_cast<InstanceView*>(this)->executeDelayedItemsLayout(); - - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - if (visualRect(index).contains(point)) - { - return index; - } - } - return QModelIndex(); -} - -void InstanceView::setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) -{ - executeDelayedItemsLayout(); - - for (int i = 0; i < model()->rowCount(); ++i) - { - QModelIndex index = model()->index(i, 0); - QRect itemRect = visualRect(index); - if (itemRect.intersects(rect)) - { - selectionModel()->select(index, commands); - update(itemRect.translated(-offset())); - } - } -} - -QPixmap InstanceView::renderToPixmap(const QModelIndexList &indices, QRect *r) const -{ - Q_ASSERT(r); - auto paintPairs = draggablePaintPairs(indices, r); - if (paintPairs.isEmpty()) - { - return QPixmap(); - } - QPixmap pixmap(r->size()); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - QStyleOptionViewItem option = viewOptions(); - option.state |= QStyle::State_Selected; - for (int j = 0; j < paintPairs.count(); ++j) - { - option.rect = paintPairs.at(j).first.translated(-r->topLeft()); - const QModelIndex ¤t = paintPairs.at(j).second; - itemDelegate()->paint(&painter, option, current); - } - return pixmap; -} - -QList<QPair<QRect, QModelIndex>> InstanceView::draggablePaintPairs(const QModelIndexList &indices, QRect *r) const -{ - Q_ASSERT(r); - QRect &rect = *r; - QList<QPair<QRect, QModelIndex>> ret; - for (int i = 0; i < indices.count(); ++i) - { - const QModelIndex &index = indices.at(i); - const QRect current = geometryRect(index); - ret += qMakePair(current, index); - rect |= current; - } - return ret; -} - -bool InstanceView::isDragEventAccepted(QDropEvent *event) -{ - return true; -} - -QPair<VisualGroup *, VisualGroup::HitResults> InstanceView::rowDropPos(const QPoint &pos) -{ - VisualGroup::HitResults hitresult; - auto group = categoryAt(pos + offset(), hitresult); - return qMakePair<VisualGroup*, int>(group, hitresult); -} - -QPoint InstanceView::offset() const -{ - return QPoint(horizontalOffset(), verticalOffset()); -} - -QRegion InstanceView::visualRegionForSelection(const QItemSelection &selection) const -{ - QRegion region; - for (auto &range : selection) - { - int start_row = range.top(); - int end_row = range.bottom(); - for (int row = start_row; row <= end_row; ++row) - { - int start_column = range.left(); - int end_column = range.right(); - for (int column = start_column; column <= end_column; ++column) - { - QModelIndex index = model()->index(row, column, rootIndex()); - region += visualRect(index); // OK - } - } - } - return region; -} - -QModelIndex InstanceView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) -{ - auto current = currentIndex(); - if(!current.isValid()) - { - return current; - } - auto cat = category(current); - 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) - { - 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(row == 0) - { - int prevgroupindex = group_index-1; - while(prevgroupindex >= 0) - { - auto prevgroup = m_groups[prevgroupindex]; - if(prevgroup->collapsed) - { - prevgroupindex--; - continue; - } - 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]; - } - return current; - } - case MoveDown: - { - if(row == cat->rows.size() - 1) - { - int nextgroupindex = group_index+1; - while (nextgroupindex < m_groups.size()) - { - auto nextgroup = m_groups[nextgroupindex]; - if(nextgroup->collapsed) - { - nextgroupindex++; - continue; - } - 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]; - } - return current; - } - case MoveLeft: - { - if(column > 0) - { - m_currentCursorColumn = column - 1; - return cat->rows[row][column - 1]; - } - // TODO: moving to previous line - return current; - } - case MoveRight: - { - if(column < cat->rows[row].size() - 1) - { - m_currentCursorColumn = column + 1; - return cat->rows[row][column + 1]; - } - // TODO: moving to next line - return current; - } - case MoveHome: - { - m_currentCursorColumn = 0; - return cat->rows[row][0]; - } - case MoveEnd: - { - auto last = cat->rows[row].size() - 1; - m_currentCursorColumn = last; - return cat->rows[row][last]; - } - default: - break; - } - return current; -} - -int InstanceView::horizontalOffset() const -{ - return horizontalScrollBar()->value(); -} - -int InstanceView::verticalOffset() const -{ - return verticalScrollBar()->value(); -} - -void InstanceView::scrollContentsBy(int dx, int dy) -{ - scrollDirtyRegion(dx, dy); - viewport()->scroll(dx, dy); -} - -void InstanceView::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 InstanceView::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; -} diff --git a/launcher/instanceview/InstanceView.h b/launcher/instanceview/InstanceView.h deleted file mode 100644 index 406362e6..00000000 --- a/launcher/instanceview/InstanceView.h +++ /dev/null @@ -1,153 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QListView> -#include <QLineEdit> -#include <QScrollBar> -#include <QCache> -#include "VisualGroup.h" -#include <functional> - -struct InstanceViewRoles -{ - enum - { - GroupRole = Qt::UserRole, - ProgressValueRole, - ProgressMaximumRole - }; -}; - -class InstanceView : public QAbstractItemView -{ - Q_OBJECT - -public: - InstanceView(QWidget *parent = 0); - ~InstanceView(); - - void setModel(QAbstractItemModel *model) override; - - using visibilityFunction = std::function<bool(const QString &)>; - void setSourceOfGroupCollapseStatus(visibilityFunction f) { - fVisibility = f; - } - - /// return geometry rectangle occupied by the specified model item - QRect geometryRect(const QModelIndex &index) const; - /// return visual rectangle occupied by the specified model item - virtual QRect visualRect(const QModelIndex &index) const override; - /// get the model index at the specified visual point - virtual QModelIndex indexAt(const QPoint &point) const override; - QString groupNameAt(const QPoint &point); - void setSelection(const QRect &rect, const QItemSelectionModel::SelectionFlags commands) override; - - 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; - }; - -public slots: - virtual void updateGeometries() override; - -protected slots: - virtual void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) override; - virtual void rowsInserted(const QModelIndex &parent, int start, int end) override; - virtual void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) override; - void modelReset(); - void rowsRemoved(); - void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; - -signals: - void droppedURLs(QList<QUrl> urls); - void groupStateChanged(QString group, bool collapsed); - -protected: - bool isIndexHidden(const QModelIndex &index) const override; - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void mouseDoubleClickEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *event) override; - void resizeEvent(QResizeEvent *event) override; - - void dragEnterEvent(QDragEnterEvent *event) override; - void dragMoveEvent(QDragMoveEvent *event) override; - void dragLeaveEvent(QDragLeaveEvent *event) override; - void dropEvent(QDropEvent *event) override; - - void startDrag(Qt::DropActions supportedActions) override; - - void updateScrollbar(); - -private: - friend struct VisualGroup; - QList<VisualGroup *> m_groups; - - visibilityFunction fVisibility; - - // 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; - - // point where the currently active mouse action started in geometry coordinates - QPoint m_pressedPosition; - QPersistentModelIndex m_pressedIndex; - bool m_pressedAlreadySelected; - VisualGroup *m_pressedCategory; - QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag; - QPoint m_lastDragPosition; - - VisualGroup *category(const QModelIndex &index) const; - VisualGroup *category(const QString &cat) const; - VisualGroup *categoryAt(const QPoint &pos, VisualGroup::HitResults & result) const; - - int itemsPerRow() const - { - return m_currentItemsPerRow; - }; - int contentWidth() const; - -private: /* methods */ - 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<VisualGroup *, VisualGroup::HitResults> rowDropPos(const QPoint &pos); - - QPoint offset() const; -}; diff --git a/launcher/instanceview/VisualGroup.cpp b/launcher/instanceview/VisualGroup.cpp deleted file mode 100644 index 8991fb2d..00000000 --- a/launcher/instanceview/VisualGroup.cpp +++ /dev/null @@ -1,317 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "VisualGroup.h" - -#include <QModelIndex> -#include <QPainter> -#include <QtMath> -#include <QApplication> -#include <QDebug> - -#include "InstanceView.h" - -VisualGroup::VisualGroup(const QString &text, InstanceView *view) : view(view), text(text), collapsed(false) -{ -} - -VisualGroup::VisualGroup(const VisualGroup *other) - : view(other->view), text(other->text), collapsed(other->collapsed) -{ -} - -void VisualGroup::update() -{ - auto temp_items = items(); - auto itemsPerRow = view->itemsPerRow(); - - 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) - { - 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; -} - -QPair<int, int> VisualGroup::positionOf(const QModelIndex &index) const -{ - 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++; - } - qWarning() << "Item" << index.row() << index.data(Qt::DisplayRole).toString() << "not found in visual group" << text; - return qMakePair(0, 0); -} - -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? - int y = pos.y(); - // int x = pos.x(); - if (y < y_start) - { - results = VisualGroup::NoHit; - } - else if (y < body_start) - { - 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 |= VisualGroup::CheckboxHit; - } - } - else if (y < body_end) - { - results |= VisualGroup::BodyHit; - } - return results; -} - -void VisualGroup::drawHeader(QPainter *painter, const QStyleOptionViewItem &option) -{ - painter->setRenderHint(QPainter::Antialiasing); - - const QRect optRect = option.rect; - QFont font(QApplication::font()); - font.setBold(true); - const QFontMetrics fontMetrics = QFontMetrics(font); - - QColor outlineColor = option.palette.text().color(); - outlineColor.setAlphaF(0.35); - - //BEGIN: top left corner - { - painter->save(); - painter->setPen(outlineColor); - const QPointF topLeft(optRect.topLeft()); - QRectF arc(topLeft, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 1440, 1440); - painter->restore(); - } - //END: top left corner - - //BEGIN: left vertical line - { - QPoint start(optRect.topLeft()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topLeft()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: left vertical line - - //BEGIN: horizontal line - { - QPoint start(optRect.topLeft()); - start.rx() += 3; - QPoint horizontalGradTop(optRect.topLeft()); - horizontalGradTop.rx() += optRect.width() - 6; - painter->fillRect(QRect(start, QSize(optRect.width() - 6, 1)), outlineColor); - } - //END: horizontal line - - //BEGIN: top right corner - { - painter->save(); - painter->setPen(outlineColor); - QPointF topRight(optRect.topRight()); - topRight.rx() -= 4; - QRectF arc(topRight, QSizeF(4, 4)); - arc.translate(0.5, 0.5); - painter->drawArc(arc, 0, 1440); - painter->restore(); - } - //END: top right corner - - //BEGIN: right vertical line - { - QPoint start(optRect.topRight()); - start.ry() += 3; - QPoint verticalGradBottom(optRect.topRight()); - verticalGradBottom.ry() += fontMetrics.height() + 5; - QLinearGradient gradient(start, verticalGradBottom); - gradient.setColorAt(0, outlineColor); - gradient.setColorAt(1, Qt::transparent); - painter->fillRect(QRect(start, QSize(1, fontMetrics.height() + 5)), gradient); - } - //END: right vertical line - - //BEGIN: checkboxy thing - { - painter->save(); - painter->setRenderHint(QPainter::Antialiasing, false); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - QRect iconSubRect(option.rect); - iconSubRect.setTop(iconSubRect.top() + 7); - iconSubRect.setLeft(iconSubRect.left() + 7); - - int sizing = fontMetrics.height(); - int even = ( (sizing - 1) % 2 ); - - iconSubRect.setHeight(sizing - even); - iconSubRect.setWidth(sizing - even); - painter->drawRect(iconSubRect); - - - /* - if(collapsed) - painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "+"); - else - painter->drawText(iconSubRect, Qt::AlignHCenter | Qt::AlignVCenter, "-"); - */ - painter->setBrush(option.palette.text()); - painter->fillRect(iconSubRect.x(), iconSubRect.y() + iconSubRect.height() / 2, - iconSubRect.width(), 2, penColor); - if (collapsed) - { - painter->fillRect(iconSubRect.x() + iconSubRect.width() / 2, iconSubRect.y(), 2, - iconSubRect.height(), penColor); - } - - painter->restore(); - } - //END: checkboxy thing - - //BEGIN: text - { - QRect textRect(option.rect); - textRect.setTop(textRect.top() + 7); - textRect.setLeft(textRect.left() + 7 + fontMetrics.height() + 7); - textRect.setHeight(fontMetrics.height()); - textRect.setRight(textRect.right() - 7); - - painter->save(); - painter->setFont(font); - QColor penColor(option.palette.text().color()); - penColor.setAlphaF(0.6); - painter->setPen(penColor); - painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text); - painter->restore(); - } - //END: text -} - -int VisualGroup::totalHeight() const -{ - return headerHeight() + 5 + contentHeight(); // FIXME: wtf is that '5'? -} - -int VisualGroup::headerHeight() const -{ - QFont font(QApplication::font()); - font.setBold(true); - QFontMetrics fontMetrics(font); - - const int height = fontMetrics.height() + 1 /* 1 pixel-width gradient */ - + 11 /* top and bottom separation */; - return height; - /* - int raw = view->viewport()->fontMetrics().height() + 4; - // add english. maybe. depends on font height. - if (raw % 2 == 0) - raw++; - return std::min(raw, 25); - */ -} - -int VisualGroup::contentHeight() const -{ - if (collapsed) - { - return 0; - } - auto last = rows[numRows() - 1]; - return last.top + last.height; -} - -int VisualGroup::numRows() const -{ - return rows.size(); -} - -int VisualGroup::verticalPosition() const -{ - return m_verticalPosition; -} - -QList<QModelIndex> VisualGroup::items() const -{ - QList<QModelIndex> indices; - for (int i = 0; i < view->model()->rowCount(); ++i) - { - const QModelIndex index = view->model()->index(i, 0); - if (index.data(InstanceViewRoles::GroupRole).toString() == text) - { - indices.append(index); - } - } - return indices; -} diff --git a/launcher/instanceview/VisualGroup.h b/launcher/instanceview/VisualGroup.h deleted file mode 100644 index 5a743aa1..00000000 --- a/launcher/instanceview/VisualGroup.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <QString> -#include <QRect> -#include <QVector> -#include <QStyleOption> - -class InstanceView; -class QPainter; -class QModelIndex; - -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 */ - VisualGroup(const QString &text, InstanceView *view); - VisualGroup(const VisualGroup *other); - -/* data */ - InstanceView *view = nullptr; - QString text; - bool collapsed = false; - QVector<VisualRow> rows; - int firstItemIndex = 0; - int m_verticalPosition = 0; - -/* logic */ - /// update the internal list of items and flow them into the rows. - void update(); - - /// draw the header at y-position. - void drawHeader(QPainter *painter, const QStyleOptionViewItem &option); - - /// height of the group, in total. includes a small bit of padding. - int totalHeight() const; - - /// height of the group header, in pixels - int headerHeight() const; - - /// height of the group content, in pixels - int contentHeight() const; - - /// 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, - TextHit = 0x1, - CheckboxHit = 0x2, - HeaderHit = 0x4, - BodyHit = 0x8 - }; - Q_DECLARE_FLAGS(HitResults, HitResult) - - /// shoot! BANG! what did we hit? - HitResults hitScan (const QPoint &pos) const; - - QList<QModelIndex> items() const; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(VisualGroup::HitResults) |