mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 01:54:20 +03:00
Merge 8d9b12d7bb into 62f537dd8d
This commit is contained in:
commit
1ec56b7f0b
4 changed files with 193 additions and 5 deletions
|
|
@ -145,6 +145,30 @@ QStringList InstanceList::getLinkedInstancesById(const QString& id) const
|
|||
return linkedInstances;
|
||||
}
|
||||
|
||||
QStringList InstanceList::groupOrder() const
|
||||
{
|
||||
return m_groupOrder;
|
||||
}
|
||||
|
||||
void InstanceList::moveGroup(const QString& group, int toIndex)
|
||||
{
|
||||
int fromIndex = m_groupOrder.indexOf(group);
|
||||
if (fromIndex < 0)
|
||||
return;
|
||||
|
||||
int size = m_groupOrder.size();
|
||||
toIndex = qBound(0, toIndex, size);
|
||||
if (fromIndex == toIndex)
|
||||
return;
|
||||
|
||||
QString name = m_groupOrder.takeAt(fromIndex);
|
||||
// After takeAt, insert position shifts if target was after the removed position
|
||||
int insertAt = (fromIndex < toIndex) ? (toIndex - 1) : toIndex;
|
||||
m_groupOrder.insert(insertAt, name);
|
||||
emit groupOrderChanged();
|
||||
saveGroupList();
|
||||
}
|
||||
|
||||
int InstanceList::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
|
@ -293,6 +317,8 @@ void InstanceList::deleteGroup(const GroupId& name)
|
|||
}
|
||||
if (removed)
|
||||
saveGroupList();
|
||||
m_groupOrder.removeAll(name);
|
||||
emit groupOrderChanged();
|
||||
}
|
||||
|
||||
void InstanceList::renameGroup(const QString& src, const QString& dst)
|
||||
|
|
@ -318,6 +344,10 @@ void InstanceList::renameGroup(const QString& src, const QString& dst)
|
|||
}
|
||||
if (modified)
|
||||
saveGroupList();
|
||||
int idx = m_groupOrder.indexOf(src);
|
||||
if (idx >= 0)
|
||||
m_groupOrder[idx] = dst;
|
||||
emit groupOrderChanged();
|
||||
}
|
||||
|
||||
bool InstanceList::isGroupCollapsed(const QString& group)
|
||||
|
|
@ -692,7 +722,11 @@ void InstanceList::increaseGroupCount(const QString& group)
|
|||
if (group.isEmpty())
|
||||
return;
|
||||
|
||||
bool wasNew = !m_groupNameCache.contains(group);
|
||||
++m_groupNameCache[group];
|
||||
if (wasNew && !m_groupOrder.contains(group)) {
|
||||
m_groupOrder.append(group);
|
||||
}
|
||||
}
|
||||
|
||||
void InstanceList::decreaseGroupCount(const QString& group)
|
||||
|
|
@ -703,6 +737,7 @@ void InstanceList::decreaseGroupCount(const QString& group)
|
|||
if (--m_groupNameCache[group] < 1) {
|
||||
m_groupNameCache.remove(group);
|
||||
m_collapsedGroups.remove(group);
|
||||
m_groupOrder.removeAll(group);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -757,6 +792,16 @@ void InstanceList::saveGroupList()
|
|||
ungrouped.insert("hidden", QJsonValue(true));
|
||||
toplevel.insert("ungrouped", ungrouped);
|
||||
}
|
||||
// Save group order (deduplicate to prevent silent corruption)
|
||||
QJsonArray orderArr;
|
||||
QStringList savedOrder;
|
||||
for (const auto& name : m_groupOrder) {
|
||||
if (!savedOrder.contains(name) && (name.isEmpty() || m_groupNameCache.contains(name))) {
|
||||
savedOrder.append(name);
|
||||
orderArr.append(name);
|
||||
}
|
||||
}
|
||||
toplevel.insert("groupOrder", orderArr);
|
||||
QJsonDocument doc(toplevel);
|
||||
try {
|
||||
FS::write(groupFileName, doc.toJson());
|
||||
|
|
@ -772,9 +817,13 @@ void InstanceList::loadGroupList()
|
|||
|
||||
QString groupFileName = m_instDir + "/instgroups.json";
|
||||
|
||||
// if there's no group file, fail
|
||||
if (!QFileInfo(groupFileName).exists())
|
||||
// if there's no group file, set up default state
|
||||
if (!QFileInfo(groupFileName).exists()) {
|
||||
m_groupOrder.clear();
|
||||
m_groupOrder.append("");
|
||||
m_groupsLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray jsonData;
|
||||
try {
|
||||
|
|
@ -815,6 +864,16 @@ void InstanceList::loadGroupList()
|
|||
|
||||
m_instanceGroupIndex.clear();
|
||||
m_groupNameCache.clear();
|
||||
m_groupOrder.clear();
|
||||
|
||||
// Load saved group order FIRST, so increaseGroupCount won't re-add groups out of order
|
||||
if (rootObj.value("groupOrder").isArray()) {
|
||||
QJsonArray orderArr = rootObj.value("groupOrder").toArray();
|
||||
for (const auto& val : orderArr) {
|
||||
QString name = val.toString();
|
||||
m_groupOrder.append(name);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through all the groups.
|
||||
QJsonObject groupMapping = rootObj.value("groups").toObject();
|
||||
|
|
@ -862,6 +921,23 @@ void InstanceList::loadGroupList()
|
|||
// empty string represents ungrouped "group"
|
||||
m_collapsedGroups.insert("");
|
||||
}
|
||||
|
||||
// Deduplicate group order (defense against previously-corrupted data)
|
||||
QStringList uniqueOrder;
|
||||
for (const auto& name : m_groupOrder) {
|
||||
if (!uniqueOrder.contains(name))
|
||||
uniqueOrder.append(name);
|
||||
}
|
||||
m_groupOrder = uniqueOrder;
|
||||
|
||||
// Treat ungrouped group (empty string) as a regular group for ordering
|
||||
if (m_groupOrder.isEmpty()) {
|
||||
m_groupOrder = m_groupNameCache.keys();
|
||||
m_groupOrder.sort();
|
||||
}
|
||||
if (!m_groupOrder.contains(""))
|
||||
m_groupOrder.append("");
|
||||
|
||||
m_groupsLoaded = true;
|
||||
qDebug() << "Group list loaded.";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -158,8 +158,11 @@ class InstanceList : public QAbstractListModel {
|
|||
QMimeData* mimeData(const QModelIndexList& indexes) const override;
|
||||
|
||||
QStringList getLinkedInstancesById(const QString& id) const;
|
||||
QStringList groupOrder() const;
|
||||
void moveGroup(const QString& group, int toIndex);
|
||||
|
||||
signals:
|
||||
void groupOrderChanged();
|
||||
void dataIsInvalid();
|
||||
void instancesChanged();
|
||||
void instanceSelectRequest(QString instanceId);
|
||||
|
|
@ -195,6 +198,7 @@ class InstanceList : public QAbstractListModel {
|
|||
std::vector<std::unique_ptr<BaseInstance>> m_instances;
|
||||
// id -> refs
|
||||
QMap<QString, int> m_groupNameCache;
|
||||
QStringList m_groupOrder;
|
||||
|
||||
SettingsObject* m_globalSettings;
|
||||
QString m_instDir;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <QAccessible>
|
||||
#include <QApplication>
|
||||
#include <QCache>
|
||||
|
||||
#include <QDrag>
|
||||
#include <QFont>
|
||||
#include <QListView>
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
#include <QPainter>
|
||||
#include <QPersistentModelIndex>
|
||||
#include <QScrollBar>
|
||||
#include <QSet>
|
||||
#include <QtMath>
|
||||
|
||||
#include "VisualGroup.h"
|
||||
|
|
@ -202,7 +204,23 @@ void InstanceView::updateGeometries()
|
|||
}
|
||||
|
||||
qDeleteAll(m_groups);
|
||||
m_groups = cats.values();
|
||||
m_groups.clear();
|
||||
|
||||
// Build ordered group list from custom group order
|
||||
auto instanceList = APPLICATION->instances();
|
||||
auto groupOrder = instanceList->groupOrder();
|
||||
QSet<QString> placed;
|
||||
for (const auto& groupName : groupOrder) {
|
||||
if (cats.contains(groupName)) {
|
||||
m_groups.append(cats.take(groupName));
|
||||
placed.insert(groupName);
|
||||
}
|
||||
}
|
||||
// Append remaining groups (not in order) alphabetically
|
||||
auto remaining = cats.values();
|
||||
std::sort(remaining.begin(), remaining.end(), [](VisualGroup* a, VisualGroup* b) { return a->text.localeAwareCompare(b->text) < 0; });
|
||||
m_groups.append(remaining);
|
||||
|
||||
updateScrollbar();
|
||||
viewport()->update();
|
||||
}
|
||||
|
|
@ -283,12 +301,20 @@ void InstanceView::mousePressEvent(QMouseEvent* event)
|
|||
m_pressedIndex = index;
|
||||
m_pressedAlreadySelected = selectionModel()->isSelected(m_pressedIndex);
|
||||
m_pressedPosition = geometryPos;
|
||||
m_draggingGroup = false;
|
||||
m_draggedGroupName.clear();
|
||||
m_groupDragTargetIndex = -1;
|
||||
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
VisualGroup::HitResults hitResult;
|
||||
m_pressedCategory = categoryAt(geometryPos, hitResult);
|
||||
if (m_pressedCategory && hitResult & VisualGroup::CheckboxHit) {
|
||||
if (m_pressedCategory && hitResult & VisualGroup::HeaderHit) {
|
||||
// Always allow drag from header, collapse also possible on click
|
||||
m_draggingGroup = true;
|
||||
m_draggedGroupName = m_pressedCategory->text;
|
||||
if (hitResult & VisualGroup::CheckboxHit) {
|
||||
setState(m_pressedCategory->collapsed ? ExpandingState : CollapsingState);
|
||||
}
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
|
@ -325,6 +351,39 @@ void InstanceView::mouseMoveEvent(QMouseEvent* event)
|
|||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
|
||||
if (m_draggingGroup) {
|
||||
topLeft = m_pressedPosition - offset();
|
||||
qreal dist = (topLeft - event->pos()).manhattanLength();
|
||||
qreal threshold = QApplication::startDragDistance();
|
||||
if (dist > threshold) {
|
||||
// Drag preview: compute visual target index and show indicator
|
||||
setState(NoState);
|
||||
QPoint cursorPos = geometryPos;
|
||||
m_groupDragTargetIndex = -1;
|
||||
for (int i = 0; i < m_groups.size(); i++) {
|
||||
if (m_groups[i]->text == m_draggedGroupName)
|
||||
continue;
|
||||
int gTop = m_groups[i]->verticalPosition();
|
||||
int gBot = gTop + m_groups[i]->totalHeight();
|
||||
int hMid = gTop + VisualGroup::headerHeight() / 2;
|
||||
if (cursorPos.y() < hMid) {
|
||||
m_groupDragTargetIndex = i;
|
||||
break;
|
||||
} else if (cursorPos.y() < gBot) {
|
||||
m_groupDragTargetIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_groupDragTargetIndex < 0)
|
||||
m_groupDragTargetIndex = m_groups.size();
|
||||
if (m_groupDragTargetIndex > m_groups.size())
|
||||
m_groupDragTargetIndex = m_groups.size();
|
||||
viewport()->update();
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (state() == ExpandingState || state() == CollapsingState) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -368,6 +427,35 @@ void InstanceView::mouseReleaseEvent(QMouseEvent* event)
|
|||
{
|
||||
executeDelayedItemsLayout();
|
||||
|
||||
// Commit group drag reorder on release
|
||||
if (m_draggingGroup && m_groupDragTargetIndex >= 0) {
|
||||
auto groupOrder = APPLICATION->instances()->groupOrder();
|
||||
int currentOrderIdx = groupOrder.indexOf(m_draggedGroupName);
|
||||
if (currentOrderIdx >= 0) {
|
||||
// Convert visual target index to groupOrder index
|
||||
int orderTarget = 0;
|
||||
for (int i = 0; i < m_groupDragTargetIndex && i < m_groups.size(); i++) {
|
||||
if (m_groups[i]->text == m_draggedGroupName)
|
||||
continue;
|
||||
int orderPos = groupOrder.indexOf(m_groups[i]->text);
|
||||
if (orderPos >= 0 && orderPos >= orderTarget)
|
||||
orderTarget = orderPos + 1;
|
||||
}
|
||||
// Clamp to valid range
|
||||
if (orderTarget > groupOrder.size())
|
||||
orderTarget = groupOrder.size();
|
||||
if (orderTarget != currentOrderIdx) {
|
||||
APPLICATION->instances()->moveGroup(m_draggedGroupName, orderTarget);
|
||||
updateGeometries();
|
||||
}
|
||||
}
|
||||
viewport()->update();
|
||||
}
|
||||
|
||||
m_draggingGroup = false;
|
||||
m_draggedGroupName.clear();
|
||||
m_groupDragTargetIndex = -1;
|
||||
|
||||
QPoint visualPos = event->pos();
|
||||
QPoint geometryPos = event->pos() + offset();
|
||||
QPersistentModelIndex index = indexAt(visualPos);
|
||||
|
|
@ -524,6 +612,21 @@ void InstanceView::paintEvent([[maybe_unused]] QPaintEvent* event)
|
|||
option.rect = backup;
|
||||
}
|
||||
|
||||
// Draw group drag drop indicator
|
||||
if (m_groupDragTargetIndex >= 0) {
|
||||
int indicatorY = 0;
|
||||
if (m_groupDragTargetIndex < m_groups.size()) {
|
||||
indicatorY = m_groups[m_groupDragTargetIndex]->verticalPosition() - verticalOffset();
|
||||
} else if (!m_groups.isEmpty()) {
|
||||
auto* last = m_groups.last();
|
||||
indicatorY = last->verticalPosition() + last->totalHeight() - verticalOffset();
|
||||
}
|
||||
painter.save();
|
||||
painter.setPen(QPen(Qt::white, 1));
|
||||
painter.drawLine(m_leftMargin, indicatorY, wpWidth - m_rightMargin, indicatorY);
|
||||
painter.restore();
|
||||
}
|
||||
|
||||
for (int i = 0; i < model()->rowCount(); ++i) {
|
||||
const QModelIndex index = model()->index(i, 0);
|
||||
if (isIndexHidden(index)) {
|
||||
|
|
|
|||
|
|
@ -139,6 +139,11 @@ class InstanceView : public QAbstractItemView {
|
|||
QItemSelectionModel::SelectionFlag m_ctrlDragSelectionFlag;
|
||||
QPoint m_lastDragPosition;
|
||||
|
||||
// group drag-reorder state
|
||||
bool m_draggingGroup = false;
|
||||
QString m_draggedGroupName;
|
||||
int m_groupDragTargetIndex = -1;
|
||||
|
||||
VisualGroup* category(const QModelIndex& index) const;
|
||||
VisualGroup* category(const QString& cat) const;
|
||||
VisualGroup* categoryAt(const QPoint& pos, VisualGroup::HitResults& result) const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue