From ae4939e0d2e8774b823865cce0a8e822d04673a5 Mon Sep 17 00:00:00 2001 From: Petr Mrázek Date: Mon, 25 Oct 2021 23:51:42 +0200 Subject: GH-4164 Assign instances to groups using drag & drop --- launcher/CMakeLists.txt | 24 +- launcher/InstanceList.cpp | 47 +- launcher/InstanceList.h | 12 +- launcher/InstanceProxyModel.cpp | 38 - launcher/InstanceProxyModel.h | 19 - launcher/Launcher.cpp | 2 +- launcher/MainWindow.cpp | 16 +- launcher/MainWindow.h | 4 +- launcher/dialogs/IconPickerDialog.cpp | 8 +- launcher/groupview/AccessibleGroupView.cpp | 778 ----------------- launcher/groupview/AccessibleGroupView.h | 6 - launcher/groupview/AccessibleGroupView_p.h | 118 --- launcher/groupview/GroupView.cpp | 1020 ---------------------- launcher/groupview/GroupView.h | 157 ---- launcher/groupview/GroupedProxyModel.cpp | 48 - launcher/groupview/GroupedProxyModel.h | 30 - launcher/groupview/InstanceDelegate.cpp | 428 --------- launcher/groupview/InstanceDelegate.h | 39 - launcher/groupview/VisualGroup.cpp | 317 ------- launcher/groupview/VisualGroup.h | 106 --- launcher/instanceview/AccessibleInstanceView.cpp | 778 +++++++++++++++++ launcher/instanceview/AccessibleInstanceView.h | 6 + launcher/instanceview/AccessibleInstanceView_p.h | 118 +++ launcher/instanceview/InstanceDelegate.cpp | 428 +++++++++ launcher/instanceview/InstanceDelegate.h | 39 + launcher/instanceview/InstanceProxyModel.cpp | 71 ++ launcher/instanceview/InstanceProxyModel.h | 35 + launcher/instanceview/InstanceView.cpp | 1011 +++++++++++++++++++++ launcher/instanceview/InstanceView.h | 153 ++++ launcher/instanceview/VisualGroup.cpp | 317 +++++++ launcher/instanceview/VisualGroup.h | 106 +++ 31 files changed, 3145 insertions(+), 3134 deletions(-) delete mode 100644 launcher/InstanceProxyModel.cpp delete mode 100644 launcher/InstanceProxyModel.h delete mode 100644 launcher/groupview/AccessibleGroupView.cpp delete mode 100644 launcher/groupview/AccessibleGroupView.h delete mode 100644 launcher/groupview/AccessibleGroupView_p.h delete mode 100644 launcher/groupview/GroupView.cpp delete mode 100644 launcher/groupview/GroupView.h delete mode 100644 launcher/groupview/GroupedProxyModel.cpp delete mode 100644 launcher/groupview/GroupedProxyModel.h delete mode 100644 launcher/groupview/InstanceDelegate.cpp delete mode 100644 launcher/groupview/InstanceDelegate.h delete mode 100644 launcher/groupview/VisualGroup.cpp delete mode 100644 launcher/groupview/VisualGroup.h create mode 100644 launcher/instanceview/AccessibleInstanceView.cpp create mode 100644 launcher/instanceview/AccessibleInstanceView.h create mode 100644 launcher/instanceview/AccessibleInstanceView_p.h create mode 100644 launcher/instanceview/InstanceDelegate.cpp create mode 100644 launcher/instanceview/InstanceDelegate.h create mode 100644 launcher/instanceview/InstanceProxyModel.cpp create mode 100644 launcher/instanceview/InstanceProxyModel.h create mode 100644 launcher/instanceview/InstanceView.cpp create mode 100644 launcher/instanceview/InstanceView.h create mode 100644 launcher/instanceview/VisualGroup.cpp create mode 100644 launcher/instanceview/VisualGroup.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index d67dcdbf..691ff004 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -573,8 +573,6 @@ SET(LAUNCHER_SOURCES GuiUtil.cpp ColumnResizer.h ColumnResizer.cpp - InstanceProxyModel.h - InstanceProxyModel.cpp VersionProxyModel.h VersionProxyModel.cpp ColorCache.h @@ -802,17 +800,17 @@ SET(LAUNCHER_SOURCES widgets/WideBar.cpp # GUI - instance group view - groupview/GroupedProxyModel.cpp - groupview/GroupedProxyModel.h - groupview/AccessibleGroupView.cpp - groupview/AccessibleGroupView.h - groupview/AccessibleGroupView_p.h - groupview/GroupView.cpp - groupview/GroupView.h - groupview/InstanceDelegate.cpp - groupview/InstanceDelegate.h - groupview/VisualGroup.cpp - groupview/VisualGroup.h + instanceview/InstanceProxyModel.cpp + instanceview/InstanceProxyModel.h + instanceview/AccessibleInstanceView.cpp + instanceview/AccessibleInstanceView.h + instanceview/AccessibleInstanceView_p.h + instanceview/InstanceView.cpp + instanceview/InstanceView.h + instanceview/InstanceDelegate.cpp + instanceview/InstanceDelegate.h + instanceview/VisualGroup.cpp + instanceview/VisualGroup.h ) ######## UIs ######## diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index ae905414..ad18740b 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include "InstanceList.h" #include "BaseInstance.h" @@ -63,6 +64,50 @@ InstanceList::~InstanceList() { } +Qt::DropActions InstanceList::supportedDragActions() const +{ + return Qt::MoveAction; +} + +Qt::DropActions InstanceList::supportedDropActions() const +{ + return Qt::MoveAction; +} + +bool InstanceList::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const +{ + if(data && data->hasFormat("application/x-instanceid")) { + return true; + } + return false; +} + +bool InstanceList::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) +{ + if(data && data->hasFormat("application/x-instanceid")) { + return true; + } + return false; +} + +QStringList InstanceList::mimeTypes() const +{ + auto types = QAbstractListModel::mimeTypes(); + types.push_back("application/x-instanceid"); + return types; +} + +QMimeData * InstanceList::mimeData(const QModelIndexList& indexes) const +{ + auto mimeData = QAbstractListModel::mimeData(indexes); + if(indexes.size() == 1) { + auto instanceId = data(indexes[0], InstanceIDRole).toString(); + mimeData->setData("application/x-instanceid", instanceId.toUtf8()); + } + return mimeData; +} + + int InstanceList::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); @@ -112,7 +157,7 @@ QVariant InstanceList::data(const QModelIndex &index, int role) const { return pdata->iconKey(); } - // HACK: see GroupView.h in gui! + // HACK: see InstanceView.h in gui! case GroupRole: { return getInstanceGroup(pdata->id()); diff --git a/launcher/InstanceList.h b/launcher/InstanceList.h index 4d2dc1f6..bc6c3af0 100644 --- a/launcher/InstanceList.h +++ b/launcher/InstanceList.h @@ -93,7 +93,6 @@ public: InstListError loadList(); void saveNow(); - InstancePtr getInstanceById(QString id) const; QModelIndex getInstanceIndexById(const QString &id) const; QStringList getGroups(); @@ -128,6 +127,17 @@ public: int getTotalPlayTime(); + Qt::DropActions supportedDragActions() const override; + + Qt::DropActions supportedDropActions() const override; + + bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const override; + + bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) override; + + QStringList mimeTypes() const override; + QMimeData *mimeData(const QModelIndexList &indexes) const override; + signals: void dataIsInvalid(); void instancesChanged(); diff --git a/launcher/InstanceProxyModel.cpp b/launcher/InstanceProxyModel.cpp deleted file mode 100644 index 9ee38a65..00000000 --- a/launcher/InstanceProxyModel.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "InstanceProxyModel.h" -#include "Launcher.h" -#include -#include - -InstanceProxyModel::InstanceProxyModel(QObject *parent) : GroupedProxyModel(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(LAUNCHER->icons()->getIcon(data.toString())); - } - return data; -} - -bool InstanceProxyModel::subSortLessThan(const QModelIndex &left, - const QModelIndex &right) const -{ - BaseInstance *pdataLeft = static_cast(left.internalPointer()); - BaseInstance *pdataRight = static_cast(right.internalPointer()); - QString sortMode = LAUNCHER->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/InstanceProxyModel.h b/launcher/InstanceProxyModel.h deleted file mode 100644 index baf2794b..00000000 --- a/launcher/InstanceProxyModel.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "groupview/GroupedProxyModel.h" -#include - -/** - * A proxy model that is responsible for sorting instances into groups - */ -class InstanceProxyModel : public GroupedProxyModel -{ -public: - explicit InstanceProxyModel(QObject *parent = 0); - QVariant data(const QModelIndex & index, int role) const override; - -protected: - virtual bool subSortLessThan(const QModelIndex &left, const QModelIndex &right) const override; -private: - QCollator m_naturalSort; -}; diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index e5deb791..5036b7ff 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -3,7 +3,7 @@ #include "MainWindow.h" #include "InstanceWindow.h" -#include "groupview/AccessibleGroupView.h" +#include "instanceview/AccessibleInstanceView.h" #include #include "pages/BasePageProvider.h" diff --git a/launcher/MainWindow.cpp b/launcher/MainWindow.cpp index 0c414285..21502894 100644 --- a/launcher/MainWindow.cpp +++ b/launcher/MainWindow.cpp @@ -67,11 +67,11 @@ #include #include "InstanceWindow.h" #include "InstancePageProvider.h" -#include "InstanceProxyModel.h" +#include "instanceview/InstanceProxyModel.h" #include "JavaCommon.h" #include "LaunchController.h" -#include "groupview/GroupView.h" -#include "groupview/InstanceDelegate.h" +#include "instanceview/InstanceView.h" +#include "instanceview/InstanceDelegate.h" #include "widgets/LabeledToolButton.h" #include "dialogs/NewInstanceDialog.h" #include "dialogs/ProgressDialog.h" @@ -695,7 +695,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow // Create the instance list widget { - view = new GroupView(ui->centralWidget); + view = new InstanceView(ui->centralWidget); view->setSelectionMode(QAbstractItemView::SingleSelection); // FIXME: leaks ListViewDelegate @@ -707,7 +707,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow view->installEventFilter(this); view->setContextMenuPolicy(Qt::CustomContextMenu); connect(view, &QWidget::customContextMenuRequested, this, &MainWindow::showInstanceContextMenu); - connect(view, &GroupView::droppedURLs, this, &MainWindow::droppedURLs, Qt::QueuedConnection); + connect(view, &InstanceView::droppedURLs, this, &MainWindow::droppedURLs, Qt::QueuedConnection); proxymodel = new InstanceProxyModel(this); proxymodel->setSourceModel(LAUNCHER->instances().get()); @@ -718,7 +718,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow view->setSourceOfGroupCollapseStatus([](const QString & groupName)->bool { return LAUNCHER->instances()->isGroupCollapsed(groupName); }); - connect(view, &GroupView::groupStateChanged, LAUNCHER->instances().get(), &InstanceList::on_GroupStateChanged); + connect(view, &InstanceView::groupStateChanged, LAUNCHER->instances().get(), &InstanceList::on_GroupStateChanged); ui->horizontalLayout->addWidget(view); } // The cat background @@ -730,7 +730,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new MainWindow setCatBackground(cat_enable); } // start instance when double-clicked - connect(view, &GroupView::activated, this, &MainWindow::instanceActivated); + connect(view, &InstanceView::activated, this, &MainWindow::instanceActivated); // track the selection -- update the instance toolbar connect(view->selectionModel(), &QItemSelectionModel::currentChanged, this, &MainWindow::instanceChanged); @@ -1313,7 +1313,7 @@ void MainWindow::setCatBackground(bool enabled) QDateTime xmas(QDate(now.date().year(), 12, 25), QTime(0, 0)); QString cat = (non_stupid_abs(now.daysTo(xmas)) <= 4) ? "catmas" : "kitteh"; view->setStyleSheet(QString(R"( -GroupView +InstanceView { background-image: url(:/backgrounds/%1); background-attachment: fixed; diff --git a/launcher/MainWindow.h b/launcher/MainWindow.h index 38e5b4b5..c2ad46ea 100644 --- a/launcher/MainWindow.h +++ b/launcher/MainWindow.h @@ -35,7 +35,7 @@ class LabeledToolButton; class QLabel; class MinecraftLauncher; class BaseProfilerFactory; -class GroupView; +class InstanceView; class KonamiCode; class InstanceTask; @@ -201,7 +201,7 @@ private: std::unique_ptr ui; // these are managed by Qt's memory management model! - GroupView *view = nullptr; + InstanceView *view = nullptr; InstanceProxyModel *proxymodel = nullptr; QToolButton *newsLabel = nullptr; QLabel *m_statusLeft = nullptr; diff --git a/launcher/dialogs/IconPickerDialog.cpp b/launcher/dialogs/IconPickerDialog.cpp index 3878d8e3..a1c432a8 100644 --- a/launcher/dialogs/IconPickerDialog.cpp +++ b/launcher/dialogs/IconPickerDialog.cpp @@ -22,7 +22,7 @@ #include "IconPickerDialog.h" #include "ui_IconPickerDialog.h" -#include "groupview/InstanceDelegate.h" +#include "instanceview/InstanceDelegate.h" #include "icons/IconList.h" #include "icons/IconUtils.h" @@ -126,8 +126,9 @@ void IconPickerDialog::selectionChanged(QItemSelection selected, QItemSelection return; QString key = selected.first().indexes().first().data(Qt::UserRole).toString(); - if (!key.isEmpty()) + if (!key.isEmpty()) { selectedIconKey = key; + } } int IconPickerDialog::execWithSelection(QString selection) @@ -141,8 +142,7 @@ int IconPickerDialog::execWithSelection(QString selection) contentsWidget->selectionModel()->select( model_index, QItemSelectionModel::Current | QItemSelectionModel::Select); - QMetaObject::invokeMethod(this, "delayed_scroll", Qt::QueuedConnection, - Q_ARG(QModelIndex, model_index)); + QMetaObject::invokeMethod(this, "delayed_scroll", Qt::QueuedConnection, Q_ARG(QModelIndex, model_index)); return QDialog::exec(); } diff --git a/launcher/groupview/AccessibleGroupView.cpp b/launcher/groupview/AccessibleGroupView.cpp deleted file mode 100644 index c6541f18..00000000 --- a/launcher/groupview/AccessibleGroupView.cpp +++ /dev/null @@ -1,778 +0,0 @@ -#include "GroupView.h" -#include "AccessibleGroupView.h" -#include "AccessibleGroupView_p.h" - -#include -#include -#include - -#ifndef QT_NO_ACCESSIBILITY - -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object) -{ - QAccessibleInterface *iface = 0; - if (!object || !object->isWidgetType()) - return iface; - - QWidget *widget = static_cast(object); - - if (classname == QLatin1String("GroupView")) { - iface = new AccessibleGroupView((GroupView *)widget); - } - return iface; -} - - -QAbstractItemView *AccessibleGroupView::view() const -{ - return qobject_cast(object()); -} - -int AccessibleGroupView::logicalIndex(const QModelIndex &index) const -{ - if (!view()->model() || !index.isValid()) - return -1; - return index.row() * (index.model()->columnCount()) + index.column(); -} - -AccessibleGroupView::AccessibleGroupView(QWidget *w) - : QAccessibleObject(w) -{ - Q_ASSERT(view()); -} - -bool AccessibleGroupView::isValid() const -{ - return view(); -} - -AccessibleGroupView::~AccessibleGroupView() -{ - for (QAccessible::Id id : childToId) { - QAccessible::deleteAccessibleInterface(id); - } -} - -QAccessibleInterface *AccessibleGroupView::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() << "AccessibleGroupView::cellAt: invalid index: " << index << " for " << view(); - return 0; - } - - return child(logicalIndex(index)); -} - -QAccessibleInterface *AccessibleGroupView::caption() const -{ - return 0; -} - -QString AccessibleGroupView::columnDescription(int column) const -{ - if (!view()->model()) - return QString(); - - return view()->model()->headerData(column, Qt::Horizontal).toString(); -} - -int AccessibleGroupView::columnCount() const -{ - if (!view()->model()) - return 0; - return 1; -} - -int AccessibleGroupView::rowCount() const -{ - if (!view()->model()) - return 0; - return view()->model()->rowCount(); -} - -int AccessibleGroupView::selectedCellCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedIndexes().count(); -} - -int AccessibleGroupView::selectedColumnCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedColumns().count(); -} - -int AccessibleGroupView::selectedRowCount() const -{ - if (!view()->selectionModel()) - return 0; - return view()->selectionModel()->selectedRows().count(); -} - -QString AccessibleGroupView::rowDescription(int row) const -{ - if (!view()->model()) - return QString(); - return view()->model()->headerData(row, Qt::Vertical).toString(); -} - -QList AccessibleGroupView::selectedCells() const -{ - QList 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 AccessibleGroupView::selectedColumns() const -{ - if (!view()->selectionModel()) { - return QList(); - } - - const QModelIndexList selectedColumns = view()->selectionModel()->selectedColumns(); - - QList columns; - columns.reserve(selectedColumns.size()); - for (const QModelIndex &index : selectedColumns) { - columns.append(index.column()); - } - - return columns; -} - -QList AccessibleGroupView::selectedRows() const -{ - if (!view()->selectionModel()) { - return QList(); - } - - QList rows; - - const QModelIndexList selectedRows = view()->selectionModel()->selectedRows(); - - rows.reserve(selectedRows.size()); - for (const QModelIndex &index : selectedRows) { - rows.append(index.row()); - } - - return rows; -} - -QAccessibleInterface *AccessibleGroupView::summary() const -{ - return 0; -} - -bool AccessibleGroupView::isColumnSelected(int column) const -{ - if (!view()->selectionModel()) { - return false; - } - - return view()->selectionModel()->isColumnSelected(column, QModelIndex()); -} - -bool AccessibleGroupView::isRowSelected(int row) const -{ - if (!view()->selectionModel()) { - return false; - } - - return view()->selectionModel()->isRowSelected(row, QModelIndex()); -} - -bool AccessibleGroupView::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 AccessibleGroupView::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 AccessibleGroupView::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 AccessibleGroupView::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 AccessibleGroupView::role() const -{ - return QAccessible::List; -} - -QAccessible::State AccessibleGroupView::state() const -{ - return QAccessible::State(); -} - -QAccessibleInterface *AccessibleGroupView::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 AccessibleGroupView::childCount() const -{ - if (!view()->model()) { - return 0; - } - return (view()->model()->rowCount()) * (view()->model()->columnCount()); -} - -int AccessibleGroupView::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 AccessibleGroupViewItem* cell = static_cast(iface); - return logicalIndex(cell->m_index); - } else if (iface->role() == QAccessible::Pane) { - return 0; // corner button - } else { - qWarning() << "AccessibleGroupView::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 AccessibleGroupView::text(QAccessible::Text t) const -{ - if (t == QAccessible::Description) - return view()->accessibleDescription(); - return view()->accessibleName(); -} - -QRect AccessibleGroupView::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 *AccessibleGroupView::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 *AccessibleGroupView::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("AccessibleGroupView::child: Invalid index at: %d %d", row, column); - return 0; - } - iface = new AccessibleGroupViewItem(view(), index); - - QAccessible::registerAccessibleInterface(iface); - childToId.insert(logicalIndex, QAccessible::uniqueId(iface)); - return iface; -} - -void *AccessibleGroupView::interface_cast(QAccessible::InterfaceType t) -{ - if (t == QAccessible::TableInterface) - return static_cast(this); - return 0; -} - -void AccessibleGroupView::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()); - AccessibleGroupViewItem *cell = static_cast(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 - -AccessibleGroupViewItem::AccessibleGroupViewItem(QAbstractItemView *view_, const QModelIndex &index_) - : view(view_), m_index(index_) -{ - if (Q_UNLIKELY(!index_.isValid())) - qWarning() << "AccessibleGroupViewItem::AccessibleGroupViewItem with invalid index: " << index_; -} - -void *AccessibleGroupViewItem::interface_cast(QAccessible::InterfaceType t) -{ - if (t == QAccessible::TableCellInterface) - return static_cast(this); - if (t == QAccessible::ActionInterface) - return static_cast(this); - return 0; -} - -int AccessibleGroupViewItem::columnExtent() const { return 1; } -int AccessibleGroupViewItem::rowExtent() const { return 1; } - -QList AccessibleGroupViewItem::rowHeaderCells() const -{ - return {}; -} - -QList AccessibleGroupViewItem::columnHeaderCells() const -{ - return {}; -} - -int AccessibleGroupViewItem::columnIndex() const -{ - if (!isValid()) { - return -1; - } - - return m_index.column(); -} - -int AccessibleGroupViewItem::rowIndex() const -{ - if (!isValid()) { - return -1; - } - - return m_index.row(); -} - -bool AccessibleGroupViewItem::isSelected() const -{ - if (!isValid()) { - return false; - } - - return view->selectionModel()->isSelected(m_index); -} - -QStringList AccessibleGroupViewItem::actionNames() const -{ - QStringList names; - names << toggleAction(); - return names; -} - -void AccessibleGroupViewItem::doAction(const QString& actionName) -{ - if (actionName == toggleAction()) { - if (isSelected()) { - unselectCell(); - } - else { - selectCell(); - } - } -} - -QStringList AccessibleGroupViewItem::keyBindingsForAction(const QString &) const -{ - return QStringList(); -} - - -void AccessibleGroupViewItem::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 AccessibleGroupViewItem::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 *AccessibleGroupViewItem::table() const -{ - return QAccessible::queryAccessibleInterface(view); -} - -QAccessible::Role AccessibleGroupViewItem::role() const -{ - return QAccessible::ListItem; -} - -QAccessible::State AccessibleGroupViewItem::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 AccessibleGroupViewItem::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 AccessibleGroupViewItem::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 AccessibleGroupViewItem::setText(QAccessible::Text /*t*/, const QString &text) -{ - if (!isValid() || !(m_index.flags() & Qt::ItemIsEditable)) - return; - view->model()->setData(m_index, text); -} - -bool AccessibleGroupViewItem::isValid() const -{ - return view && view->model() && m_index.isValid(); -} - -QAccessibleInterface *AccessibleGroupViewItem::parent() const -{ - return QAccessible::queryAccessibleInterface(view); -} - -QAccessibleInterface *AccessibleGroupViewItem::child(int) const -{ - return 0; -} - -#endif /* !QT_NO_ACCESSIBILITY */ diff --git a/launcher/groupview/AccessibleGroupView.h b/launcher/groupview/AccessibleGroupView.h deleted file mode 100644 index 9bfd1745..00000000 --- a/launcher/groupview/AccessibleGroupView.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -class QAccessibleInterface; - -QAccessibleInterface *groupViewAccessibleFactory(const QString &classname, QObject *object); diff --git a/launcher/groupview/AccessibleGroupView_p.h b/launcher/groupview/AccessibleGroupView_p.h deleted file mode 100644 index e74da3be..00000000 --- a/launcher/groupview/AccessibleGroupView_p.h +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include "QtCore/qpointer.h" -#include -#include -#include -#ifndef QT_NO_ACCESSIBILITY -#include "GroupView.h" -// #include - -class QAccessibleTableCell; -class QAccessibleTableHeaderCell; - -class AccessibleGroupView :public QAccessibleTableInterface, public QAccessibleObject -{ -public: - explicit AccessibleGroupView(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 selectedCells() const override; - QList selectedColumns() const override; - QList 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 ChildCache; - mutable ChildCache childToId; - - virtual ~AccessibleGroupView(); - -private: - inline int logicalIndex(const QModelIndex &index) const; -}; - -class AccessibleGroupViewItem: public QAccessibleInterface, public QAccessibleTableCellInterface, public QAccessibleActionInterface -{ -public: - AccessibleGroupViewItem(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 columnHeaderCells() const override; - int columnIndex() const override; - int rowExtent() const override; - QList 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 view; - QPersistentModelIndex m_index; - - void selectCell(); - void unselectCell(); - - friend class AccessibleGroupView; -}; -#endif /* !QT_NO_ACCESSIBILITY */ diff --git a/launcher/groupview/GroupView.cpp b/launcher/groupview/GroupView.cpp deleted file mode 100644 index 6bfc9381..00000000 --- a/launcher/groupview/GroupView.cpp +++ /dev/null @@ -1,1020 +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 "GroupView.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "VisualGroup.h" -#include - -template bool listsIntersect(const QList &l1, const QList t2) -{ - for (auto &item : l1) - { - if (t2.contains(item)) - { - return true; - } - } - return false; -} - -GroupView::GroupView(QWidget *parent) - : QAbstractItemView(parent) -{ - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - setAcceptDrops(true); - setAutoScroll(true); -} - -GroupView::~GroupView() -{ - qDeleteAll(m_groups); - m_groups.clear(); -} - -void GroupView::setModel(QAbstractItemModel *model) -{ - QAbstractItemView::setModel(model); - connect(model, &QAbstractItemModel::modelReset, this, &GroupView::modelReset); - connect(model, &QAbstractItemModel::rowsRemoved, this, &GroupView::rowsRemoved); -} - -void GroupView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, - const QVector &roles) -{ - scheduleDelayedItemsLayout(); -} -void GroupView::rowsInserted(const QModelIndex &parent, int start, int end) -{ - scheduleDelayedItemsLayout(); -} - -void GroupView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) -{ - scheduleDelayedItemsLayout(); -} - -void GroupView::modelReset() -{ - scheduleDelayedItemsLayout(); -} - -void GroupView::rowsRemoved() -{ - scheduleDelayedItemsLayout(); -} - -void GroupView::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 GroupView. -#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 GroupView::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 GroupView::updateGeometries() -{ - geometryCache.clear(); - - QMap cats; - - for (int i = 0; i < model()->rowCount(); ++i) - { - const QString groupName = model()->index(i, 0).data(GroupViewRoles::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 GroupView::isIndexHidden(const QModelIndex &index) const -{ - VisualGroup *cat = category(index); - if (cat) - { - return cat->collapsed; - } - else - { - return false; - } -} - -VisualGroup *GroupView::category(const QModelIndex &index) const -{ - return category(index.data(GroupViewRoles::GroupRole).toString()); -} - -VisualGroup *GroupView::category(const QString &cat) const -{ - for (auto group : m_groups) - { - if (group->text == cat) - { - return group; - } - } - return nullptr; -} - -VisualGroup *GroupView::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 GroupView::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 GroupView::calculateItemsPerRow() const -{ - return qFloor((qreal)(contentWidth()) / (qreal)(itemWidth() + m_spacing)); -} - -int GroupView::contentWidth() const -{ - return width() - m_leftMargin - m_rightMargin; -} - -int GroupView::itemWidth() const -{ - return m_itemWidth; -} - -void GroupView::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 GroupView::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 GroupView::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 GroupView::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 GroupView::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 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 GroupView::resizeEvent(QResizeEvent *event) -{ - int newItemsPerRow = calculateItemsPerRow(); - if(newItemsPerRow != m_currentItemsPerRow) - { - m_currentCursorColumn = -1; - m_currentItemsPerRow = newItemsPerRow; - updateGeometries(); - } - else - { - updateScrollbar(); - } -} - -void GroupView::dragEnterEvent(QDragEnterEvent *event) -{ - executeDelayedItemsLayout(); - - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void GroupView::dragMoveEvent(QDragMoveEvent *event) -{ - executeDelayedItemsLayout(); - - if (!isDragEventAccepted(event)) - { - return; - } - m_lastDragPosition = event->pos() + offset(); - viewport()->update(); - event->accept(); -} - -void GroupView::dragLeaveEvent(QDragLeaveEvent *event) -{ - executeDelayedItemsLayout(); - - m_lastDragPosition = QPoint(); - viewport()->update(); -} - -void GroupView::dropEvent(QDropEvent *event) -{ - executeDelayedItemsLayout(); - - m_lastDragPosition = QPoint(); - - stopAutoScroll(); - setState(NoState); - - if (event->source() == this) - { - if(event->possibleActions() & Qt::MoveAction) - { - QPair dropPos = rowDropPos(event->pos() + offset()); - const VisualGroup *category = dropPos.first; - const int row = dropPos.second; - - if (row == -1) - { - viewport()->update(); - return; - } - - const QString categoryText = category->text; - if (model()->dropMimeData(event->mimeData(), Qt::MoveAction, row, 0, QModelIndex())) - { - model()->setData(model()->index(row, 0), categoryText, GroupViewRoles::GroupRole); - event->setDropAction(Qt::MoveAction); - event->accept(); - } - updateGeometries(); - viewport()->update(); - } - } - auto mimedata = event->mimeData(); - - // 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 GroupView::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); - //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) - { - continue; - } - if ((*it).right() != (model()->columnCount(parent) - 1)) - { - continue; - } - int count = (*it).bottom() - (*it).top() + 1; - model()->removeRows((*it).top(), count, parent); - } - } -} - -QRect GroupView::visualRect(const QModelIndex &index) const -{ - const_cast(this)->executeDelayedItemsLayout(); - - return geometryRect(index).translated(-offset()); -} - -QRect GroupView::geometryRect(const QModelIndex &index) const -{ - const_cast(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 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_s