summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMikaƫl Capelle <capelle.mikael@gmail.com>2021-01-14 09:11:37 +0100
committerGitHub <noreply@github.com>2021-01-14 09:11:37 +0100
commit34f7ae24501f75d5a74a09b6af939db6edaf6f1c (patch)
tree616c67f6d23f951c98003c710bab63e2192a4b7b /src
parentc3334e7b4d17f3005bd3c4dab8eee42b1e5510f8 (diff)
parent18f70a2dff138f273a9b6babaec226775a03649f (diff)
Merge pull request #1357 from Holt59/collapsible-separators
Collapsible separators in descending priority.
Diffstat (limited to 'src')
-rw-r--r--src/modlistbypriorityproxy.cpp124
-rw-r--r--src/modlistbypriorityproxy.h8
-rw-r--r--src/modlistsortproxy.cpp21
-rw-r--r--src/modlistsortproxy.h4
-rw-r--r--src/modlistview.cpp11
-rw-r--r--src/modlistview.h3
-rw-r--r--src/modlistviewactions.cpp31
-rw-r--r--src/settings.cpp11
-rw-r--r--src/settings.h4
-rw-r--r--src/settingsdialog.ui53
-rw-r--r--src/settingsdialoguserinterface.cpp20
-rw-r--r--src/settingsdialoguserinterface.h6
12 files changed, 243 insertions, 53 deletions
diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp
index 480a4105..9b5b2f03 100644
--- a/src/modlistbypriorityproxy.cpp
+++ b/src/modlistbypriorityproxy.cpp
@@ -43,6 +43,11 @@ void ModListByPriorityProxy::setProfile(Profile* profile)
m_profile = profile;
}
+void ModListByPriorityProxy::setSortOrder(Qt::SortOrder order)
+{
+ m_sortOrder = order;
+}
+
void ModListByPriorityProxy::buildTree()
{
if (!sourceModel()) return;
@@ -56,7 +61,9 @@ void ModListByPriorityProxy::buildTree()
TreeItem* root = &m_Root;
std::unique_ptr<TreeItem> overwrite;
std::vector<std::unique_ptr<TreeItem>> backups;
- for (auto& [priority, index] : m_profile->getAllIndexesByPriority()) {
+
+ auto fn = [&](const auto& p) {
+ auto& [priority, index] = p;
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
TreeItem* item;
@@ -81,11 +88,21 @@ void ModListByPriorityProxy::buildTree()
}
m_IndexToItem[index] = item;
- }
+ };
- m_Root.children.insert(m_Root.children.begin(),
- std::make_move_iterator(backups.begin()), std::make_move_iterator(backups.end()));
- m_Root.children.push_back(std::move(overwrite));
+ auto& ibp = m_profile->getAllIndexesByPriority();
+ if (m_sortOrder == Qt::AscendingOrder) {
+ std::for_each(ibp.begin(), ibp.end(), fn);
+ m_Root.children.insert(m_Root.children.begin(),
+ std::make_move_iterator(backups.begin()), std::make_move_iterator(backups.end()));
+ m_Root.children.push_back(std::move(overwrite));
+ }
+ else {
+ std::for_each(ibp.rbegin(), ibp.rend(), fn);
+ m_Root.children.insert(m_Root.children.begin(), std::move(overwrite));
+ m_Root.children.insert(m_Root.children.end(),
+ std::make_move_iterator(backups.begin()), std::make_move_iterator(backups.end()));
+ }
endResetModel();
}
@@ -185,7 +202,8 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi
int firstRowPriority = INT_MAX;
for (auto sourceRow : dropInfo.rows()) {
hasSeparator = hasSeparator || ModInfo::getByIndex(sourceRow)->isSeparator();
- if (m_profile->getModPriority(sourceRow) < firstRowPriority) {
+ if (m_sortOrder == Qt::AscendingOrder && m_profile->getModPriority(sourceRow) < firstRowPriority
+ || m_sortOrder == Qt::DescendingOrder && m_profile->getModPriority(sourceRow) > firstRowPriority) {
firstRowIndex = sourceRow;
firstRowPriority = m_profile->getModPriority(sourceRow);
}
@@ -193,7 +211,7 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi
bool firstRowSeparator = firstRowIndex != -1 && ModInfo::getByIndex(firstRowIndex)->isSeparator();
- // row = -1 and invalid parent means we're dropping onto an item, we don't want to drop
+ // row = -1 and valid parent means we're dropping onto an item, we don't want to drop
// separators onto items or items into their own separator
if (row == -1 && parent.isValid()) {
auto* parentItem = static_cast<TreeItem*>(parent.internalPointer());
@@ -225,8 +243,9 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi
bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
{
- // we need to fix the source model row
- ModListDropInfo dropInfo(data, m_core);
+ // we need to fix the source model row if we are dropping at a
+ // given priority (not a local file)
+ const ModListDropInfo dropInfo(data, m_core);
int sourceRow = -1;
if (dropInfo.isLocalFileDrop()) {
@@ -239,33 +258,94 @@ bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction
if (row >= 0) {
if (!parent.isValid()) {
if (row < m_Root.children.size()) {
- sourceRow = m_Root.children[row]->index;
- if (row > 0
- && m_Root.children[row - 1]->mod->isSeparator()
- && !m_Root.children[row - 1]->children.empty()
- && m_dropExpanded
- && m_dropPosition == ModListView::DropPosition::BelowItem) {
- sourceRow = m_Root.children[row - 1]->children[0]->index;
+
+ if (m_sortOrder == Qt::AscendingOrder) {
+ sourceRow = m_Root.children[row]->index;
+
+ // fix bug when dropping a mod just below an expanded separator
+ //
+ // by default, Qt consider it's a drop at the end of that separator
+ // but we want to make it a drop at the beginning
+ if (row > 0
+ && m_sortOrder == Qt::AscendingOrder
+ && m_Root.children[row - 1]->mod->isSeparator()
+ && !m_Root.children[row - 1]->children.empty()
+ && m_dropExpanded
+ && m_dropPosition == ModListView::DropPosition::BelowItem) {
+ sourceRow = m_Root.children[row - 1]->children[0]->index;
+ }
+ }
+ else {
+ sourceRow = m_Root.children[row - 1]->index;
+
+ // fix drop below a collapsed separator or at the end of an expanded
+ // separator, above the next item
+ if (row > 0
+ && m_sortOrder == Qt::DescendingOrder
+ && m_Root.children[row - 1]->mod->isSeparator()
+ && !m_Root.children[row - 1]->children.empty()
+ && (!m_dropExpanded
+ || m_dropPosition == ModListView::DropPosition::AboveItem)) {
+ sourceRow = m_Root.children[row - 1]->children.back()->index;
+ }
}
}
else {
sourceRow = ModInfo::getNumMods();
}
}
+
+ // the parent is valid, we are dropping in a separator
else {
auto* item = static_cast<TreeItem*>(parent.internalPointer());
- if (row < item->children.size()) {
- sourceRow = item->children[row]->index;
+ // we usually need to decrement the row in descending priority, but if
+ // it's the first row, we need to go back to the separator itself
+ if (m_sortOrder == Qt::DescendingOrder
+ && row == 0 && m_dropPosition == ModListView::DropPosition::AboveItem) {
+ sourceRow = item->index;
}
- else if (parent.row() + 1 < m_Root.children.size()) {
- sourceRow = m_Root.children[parent.row() + 1]->index;
+ else {
+
+ // in descending priority, we decrement the row to fix the drop position
+ // because this is not done by the sort proxy for us
+ if (m_sortOrder == Qt::DescendingOrder) {
+ row--;
+ }
+
+ if (row < item->children.size()) {
+ sourceRow = item->children[row]->index;
+ }
+ else if (parent.row() + 1 < m_Root.children.size()) {
+ sourceRow = m_Root.children[parent.row() + 1]->index;
+ }
}
+
+
}
}
+
+ // row < 0 and valid parent means we are dropping into an item,
+ // which can only be a separator since dropping into non-separators
+ // is disabled
+ //
+ // we want to drop at the end of the separator, so we need to find
+ // the right priority
else if (parent.isValid()) {
- // this is a drop in a separator
- sourceRow = m_Root.children[parent.row() + 1]->index;
+
+ // in ascending priority, we take the priority of the next top-level
+ // item, which can be a separator or the overwrite mod, but is guaranteed
+ // to exist
+ if (m_sortOrder == Qt::AscendingOrder) {
+ sourceRow = m_Root.children[parent.row() + 1]->index;
+ }
+
+ // in descending priority, we take the separator itself if it's empty or
+ // its last children
+ else {
+ auto* item = m_Root.children[parent.row()].get();
+ sourceRow = item->children.empty() ? item->index : item->children.back()->index;
+ }
}
}
diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h
index 74ecc942..380e257b 100644
--- a/src/modlistbypriorityproxy.h
+++ b/src/modlistbypriorityproxy.h
@@ -28,6 +28,13 @@ public:
~ModListByPriorityProxy();
void setProfile(Profile* profile);
+
+ // set the sort order but does not refresh the proxy
+ //
+ void setSortOrder(Qt::SortOrder order);
+
+ // refresh the proxy by rebuilding the inner structure
+ //
void refresh();
void setSourceModel(QAbstractItemModel* sourceModel) override;
@@ -83,6 +90,7 @@ private:
OrganizerCore& m_core;
Profile* m_profile;
+ Qt::SortOrder m_sortOrder = Qt::AscendingOrder;
bool m_dropExpanded = false;
ModListView::DropPosition m_dropPosition = ModListView::DropPosition::OnItem;
};
diff --git a/src/modlistsortproxy.cpp b/src/modlistsortproxy.cpp
index 9ae37a43..18e91e6e 100644
--- a/src/modlistsortproxy.cpp
+++ b/src/modlistsortproxy.cpp
@@ -18,6 +18,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
*/
#include "modlistsortproxy.h"
+#include "modlistbypriorityproxy.h"
#include "modlistdropinfo.h"
#include "modinfo.h"
#include "profile.h"
@@ -279,7 +280,6 @@ bool ModListSortProxy::filterMatchesModOr(ModInfo::Ptr info, bool enabled) const
bool ModListSortProxy::optionsMatchMod(ModInfo::Ptr info, bool) const
{
-
return true;
}
@@ -598,6 +598,11 @@ bool ModListSortProxy::filterAcceptsRow(int source_row, const QModelIndex &paren
}
}
+bool ModListSortProxy::sourceIsByPriorityProxy() const
+{
+ return dynamic_cast<ModListByPriorityProxy*>(sourceModel()) != nullptr;
+}
+
bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) const
{
ModListDropInfo dropInfo(data, *m_Organizer);
@@ -617,6 +622,11 @@ bool ModListSortProxy::canDropMimeData(const QMimeData* data, Qt::DropAction act
}
}
+ // see dropMimeData for details
+ if (sortOrder() == Qt::DescendingOrder && row != -1 && !sourceIsByPriorityProxy()) {
+ --row;
+ }
+
return QSortFilterProxyModel::canDropMimeData(data, action, row, column, parent);
}
@@ -636,9 +646,12 @@ bool ModListSortProxy::dropMimeData(const QMimeData *data, Qt::DropAction action
}
// in the regular model, when dropping between rows, the row-value passed to
- // the sourceModel is inconsistent between ascending and descending ordering.
- // This should fix that
- if (sortOrder() == Qt::DescendingOrder) {
+ // the sourceModel is inconsistent between ascending and descending ordering
+ //
+ // we want to fix that, but we cannot do it for the by-priority proxy because
+ // it messes up with non top-level items, so we simply forward the row and the
+ // by-priority proxy will fix the row for us
+ if (sortOrder() == Qt::DescendingOrder && row != -1 && !sourceIsByPriorityProxy()) {
--row;
}
diff --git a/src/modlistsortproxy.h b/src/modlistsortproxy.h
index 421c82cc..23be77ed 100644
--- a/src/modlistsortproxy.h
+++ b/src/modlistsortproxy.h
@@ -140,6 +140,10 @@ private:
bool filterMatchesModAnd(ModInfo::Ptr info, bool enabled) const;
bool filterMatchesModOr(ModInfo::Ptr info, bool enabled) const;
+ // check if the source model is the by-priority proxy
+ //
+ bool sourceIsByPriorityProxy() const;
+
private slots:
void aboutToChangeData();
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index 8827733b..ae905a6e 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -209,6 +209,11 @@ int ModListView::sortColumn() const
return m_sortProxy ? m_sortProxy->sortColumn() : -1;
}
+Qt::SortOrder ModListView::sortOrder() const
+{
+ return m_sortProxy ? m_sortProxy->sortOrder() : Qt::AscendingOrder;
+}
+
ModListView::GroupByMode ModListView::groupByMode() const
{
if (m_sortProxy == nullptr) {
@@ -682,9 +687,9 @@ void ModListView::updateGroupByProxy()
else if (groupIndex == GroupBy::NEXUS_ID) {
nextProxy = m_byNexusIdProxy;
}
- else if (m_core->settings().interface().collapsibleSeparators()
- && m_sortProxy->sortColumn() == ModList::COL_PRIORITY
- && m_sortProxy->sortOrder() == Qt::AscendingOrder) {
+ else if (m_core->settings().interface().collapsibleSeparators(m_sortProxy->sortOrder())
+ && m_sortProxy->sortColumn() == ModList::COL_PRIORITY) {
+ m_byPriorityProxy->setSortOrder(m_sortProxy->sortOrder());
nextProxy = m_byPriorityProxy;
}
diff --git a/src/modlistview.h b/src/modlistview.h
index 53c71458..13f868c6 100644
--- a/src/modlistview.h
+++ b/src/modlistview.h
@@ -61,9 +61,10 @@ public:
//
bool hasCollapsibleSeparators() const;
- // the column by which the mod list is currently sorted
+ // the column/order by which the mod list is currently sorted
//
int sortColumn() const;
+ Qt::SortOrder sortOrder() const;
// the current group mode
//
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp
index cc50f009..09142026 100644
--- a/src/modlistviewactions.cpp
+++ b/src/modlistviewactions.cpp
@@ -103,22 +103,33 @@ void ModListViewActions::createEmptyMod(const QModelIndex& index) const
auto info = ModInfo::getByIndex(mIndex);
newPriority = m_core.currentProfile()->getModPriority(mIndex);
if (info->isSeparator()) {
+
+ auto isSeparator = [](const auto& p) {
+ return ModInfo::getByIndex(p.second)->isSeparator();
+ };
+
auto& ibp = m_core.currentProfile()->getAllIndexesByPriority();
- // start right after the current priority and look for the next
+ // start right after/before the current priority and look for the next
// separator
- auto it = ibp.find(newPriority + 1);
- for (; it != ibp.end(); ++it) {
- auto info = ModInfo::getByIndex(it->second);
- if (info->isSeparator()) {
+ if (m_view->sortOrder() == Qt::AscendingOrder) {
+ auto it = std::find_if(ibp.find(newPriority + 1), ibp.end(), isSeparator);
+ if (it != ibp.end()) {
newPriority = it->first;
- break;
+ }
+ else {
+ newPriority = -1;
}
}
-
- // no separator found, create at the end
- if (it == ibp.end()) {
- newPriority = -1;
+ else {
+ auto it = std::find_if(std::reverse_iterator{ ibp.find(newPriority - 1) }, ibp.rend(), isSeparator);
+ if (it != ibp.rend()) {
+ newPriority = it->first + 1;
+ }
+ else {
+ // create "before" priority 0, i.e. at the end in descending priority.
+ newPriority = 0;
+ }
}
}
}
diff --git a/src/settings.cpp b/src/settings.cpp
index 488a9b01..d1f3ef4f 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -2175,14 +2175,17 @@ void InterfaceSettings::setStyleName(const QString& name)
set(m_Settings, "Settings", "style", name);
}
-bool InterfaceSettings::collapsibleSeparators() const
+bool InterfaceSettings::collapsibleSeparators(Qt::SortOrder order) const
{
- return get<bool>(m_Settings, "Settings", "collapsible_separators", true);
+ return get<bool>(m_Settings, "Settings",
+ order == Qt::AscendingOrder ? "collapsible_separators_asc" : "collapsible_separators_dsc",
+ order == Qt::AscendingOrder);
}
-void InterfaceSettings::setCollapsibleSeparators(bool b)
+void InterfaceSettings::setCollapsibleSeparators(bool ascending, bool descending)
{
- set(m_Settings, "Settings", "collapsible_separators", b);
+ set(m_Settings, "Settings", "collapsible_separators_asc", ascending);
+ set(m_Settings, "Settings", "collapsible_separators_dsc", descending);
}
bool InterfaceSettings::collapsibleSeparatorsConflicts() const
diff --git a/src/settings.h b/src/settings.h
index efcfee57..484ce163 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -623,8 +623,8 @@ public:
// whether to use collapsible separators when possible
//
- bool collapsibleSeparators() const;
- void setCollapsibleSeparators(bool b);
+ bool collapsibleSeparators(Qt::SortOrder order) const;
+ void setCollapsibleSeparators(bool ascending, bool descending);
// whether to display mod conflicts on separators when collapsed
//
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index d5c93c5c..b23e6d90 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -306,7 +306,10 @@ If you disable this feature, MO will only display official DLCs this way. Please
<bool>false</bool>
</property>
<property name="checkable">
- <bool>true</bool>
+ <bool>false</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_20">
<property name="leftMargin">
@@ -316,6 +319,54 @@ If you disable this feature, MO will only display official DLCs this way. Please
<number>7</number>
</property>
<item>
+ <widget class="QWidget" name="widget_8" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_18">
+ <property name="text">
+ <string>When sorting by</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="collapsibleSeparatorsAscBox">
+ <property name="text">
+ <string>ascending priority</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="collapsibleSeparatorsDscBox">
+ <property name="text">
+ <string>descending priority</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<widget class="QCheckBox" name="collapsibleSeparatorsConflictsBox">
<property name="toolTip">
<string>Display mod conflicts on and from separator when collapsed, and show plugins from collapsed separators.</string>
diff --git a/src/settingsdialoguserinterface.cpp b/src/settingsdialoguserinterface.cpp
index 5d856395..cdd30f2e 100644
--- a/src/settingsdialoguserinterface.cpp
+++ b/src/settingsdialoguserinterface.cpp
@@ -13,16 +13,15 @@ UserInterfaceSettingsTab::UserInterfaceSettingsTab(Settings& s, SettingsDialog&
{
// connect before setting to trigger
- QObject::connect(ui->collapsibleSeparatorsBox, &QGroupBox::toggled, [=](auto&& on) {
- ui->collapsibleSeparatorsConflictsBox->setEnabled(on);
- ui->collapsibleSeparatorsPerProfileBox->setEnabled(on);
- });
+ QObject::connect(ui->collapsibleSeparatorsAscBox, &QCheckBox::toggled, [=] { updateCollapsibleSeparatorsGroup(); });
+ QObject::connect(ui->collapsibleSeparatorsDscBox, &QCheckBox::toggled, [=] { updateCollapsibleSeparatorsGroup(); });
// mod list
ui->displayForeignBox->setChecked(settings().interface().displayForeign());
ui->colorSeparatorsBox->setChecked(settings().colors().colorSeparatorScrollbar());
ui->collapsibleSeparatorsConflictsBox->setChecked(settings().interface().collapsibleSeparatorsConflicts());
- ui->collapsibleSeparatorsBox->setChecked(settings().interface().collapsibleSeparators());
+ ui->collapsibleSeparatorsAscBox->setChecked(settings().interface().collapsibleSeparators(Qt::AscendingOrder));
+ ui->collapsibleSeparatorsDscBox->setChecked(settings().interface().collapsibleSeparators(Qt::DescendingOrder));
ui->collapsibleSeparatorsPerProfileBox->setChecked(settings().interface().collapsibleSeparatorsPerProfile());
ui->saveFiltersBox->setChecked(settings().interface().saveFilters());
@@ -42,7 +41,8 @@ void UserInterfaceSettingsTab::update()
// mod list
settings().colors().setColorSeparatorScrollbar(ui->colorSeparatorsBox->isChecked());
settings().interface().setDisplayForeign(ui->displayForeignBox->isChecked());
- settings().interface().setCollapsibleSeparators(ui->collapsibleSeparatorsBox->isChecked());
+ settings().interface().setCollapsibleSeparators(
+ ui->collapsibleSeparatorsAscBox->isChecked(), ui->collapsibleSeparatorsDscBox->isChecked());
settings().interface().setCollapsibleSeparatorsConflicts(ui->collapsibleSeparatorsConflictsBox->isChecked());
settings().interface().setCollapsibleSeparatorsPerProfile(ui->collapsibleSeparatorsPerProfileBox->isChecked());
settings().interface().setSaveFilters(ui->saveFiltersBox->isChecked());
@@ -54,3 +54,11 @@ void UserInterfaceSettingsTab::update()
// colors
ui->colorTable->commitColors();
}
+
+void UserInterfaceSettingsTab::updateCollapsibleSeparatorsGroup()
+{
+ const auto checked = ui->collapsibleSeparatorsAscBox->isChecked() ||
+ ui->collapsibleSeparatorsDscBox->isChecked();
+ ui->collapsibleSeparatorsConflictsBox->setEnabled(checked);
+ ui->collapsibleSeparatorsPerProfileBox->setEnabled(checked);
+}
diff --git a/src/settingsdialoguserinterface.h b/src/settingsdialoguserinterface.h
index 8b4d48fa..4609ae54 100644
--- a/src/settingsdialoguserinterface.h
+++ b/src/settingsdialoguserinterface.h
@@ -10,6 +10,12 @@ public:
UserInterfaceSettingsTab(Settings& settings, SettingsDialog& dialog);
void update() override;
+
+protected slots:
+
+ // enable/disable the collapsible separators group depending on
+ // the checkbox states
+ void updateCollapsibleSeparatorsGroup();
};
#endif // SETTINGSDIALOGGENERAL_H