diff options
| author | Mikaƫl Capelle <capelle.mikael@gmail.com> | 2021-01-14 09:11:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-01-14 09:11:37 +0100 |
| commit | 34f7ae24501f75d5a74a09b6af939db6edaf6f1c (patch) | |
| tree | 616c67f6d23f951c98003c710bab63e2192a4b7b /src | |
| parent | c3334e7b4d17f3005bd3c4dab8eee42b1e5510f8 (diff) | |
| parent | 18f70a2dff138f273a9b6babaec226775a03649f (diff) | |
Merge pull request #1357 from Holt59/collapsible-separators
Collapsible separators in descending priority.
Diffstat (limited to 'src')
| -rw-r--r-- | src/modlistbypriorityproxy.cpp | 124 | ||||
| -rw-r--r-- | src/modlistbypriorityproxy.h | 8 | ||||
| -rw-r--r-- | src/modlistsortproxy.cpp | 21 | ||||
| -rw-r--r-- | src/modlistsortproxy.h | 4 | ||||
| -rw-r--r-- | src/modlistview.cpp | 11 | ||||
| -rw-r--r-- | src/modlistview.h | 3 | ||||
| -rw-r--r-- | src/modlistviewactions.cpp | 31 | ||||
| -rw-r--r-- | src/settings.cpp | 11 | ||||
| -rw-r--r-- | src/settings.h | 4 | ||||
| -rw-r--r-- | src/settingsdialog.ui | 53 | ||||
| -rw-r--r-- | src/settingsdialoguserinterface.cpp | 20 | ||||
| -rw-r--r-- | src/settingsdialoguserinterface.h | 6 |
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 |
