summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMikaƫl Capelle <capelle.mikael@gmail.com>2021-01-11 12:49:42 +0100
committerGitHub <noreply@github.com>2021-01-11 12:49:42 +0100
commit1dc2b225485a073d82e08a9820493086b2dda8b8 (patch)
tree5a4e5478d577f065c7282cb62992c2b6c735e13f /src
parenta680dfa3e94b34bf6b0ebd65d29d7a9799e3183f (diff)
parent05efc74e402ca3770d9fdaa2557f4030c31ee198 (diff)
Merge pull request #1350 from Holt59/collapsible-separators
Collapsible separators - Fix & Improvements.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/filterlist.cpp5
-rw-r--r--src/genericicondelegate.cpp16
-rw-r--r--src/genericicondelegate.h5
-rw-r--r--src/icondelegate.cpp16
-rw-r--r--src/icondelegate.h22
-rw-r--r--src/listdialog.cpp1
-rw-r--r--src/mainwindow.cpp1
-rw-r--r--src/moapplication.cpp14
-rw-r--r--src/modconflicticondelegate.cpp33
-rw-r--r--src/modconflicticondelegate.h15
-rw-r--r--src/modcontenticondelegate.cpp57
-rw-r--r--src/modcontenticondelegate.h30
-rw-r--r--src/modflagicondelegate.cpp20
-rw-r--r--src/modflagicondelegate.h25
-rw-r--r--src/modinfo.cpp1
-rw-r--r--src/modinfo.h63
-rw-r--r--src/modinfowithconflictinfo.h21
-rw-r--r--src/modlist.cpp39
-rw-r--r--src/modlist.h17
-rw-r--r--src/modlistbypriorityproxy.cpp6
-rw-r--r--src/modlistbypriorityproxy.h5
-rw-r--r--src/modlistcontextmenu.cpp9
-rw-r--r--src/modlistversiondelegate.cpp62
-rw-r--r--src/modlistversiondelegate.h21
-rw-r--r--src/modlistview.cpp380
-rw-r--r--src/modlistview.h62
-rw-r--r--src/modlistviewactions.cpp46
-rw-r--r--src/organizercore.cpp1
-rw-r--r--src/organizercore.h7
-rw-r--r--src/settings.cpp28
-rw-r--r--src/settings.h10
-rw-r--r--src/settingsdialog.cpp2
-rw-r--r--src/settingsdialog.ui798
-rw-r--r--src/settingsdialoggeneral.cpp27
-rw-r--r--src/settingsdialoggeneral.h1
-rw-r--r--src/settingsdialoguserinterface.cpp56
-rw-r--r--src/settingsdialoguserinterface.h15
-rw-r--r--src/settingsdialogworkarounds.cpp2
39 files changed, 1221 insertions, 723 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 4b74137e..d1092637 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -103,7 +103,6 @@ add_filter(NAME src/mainwindow GROUPS
filetree
filetreeitem
filetreemodel
- filterlist
mainwindow
savestab
statusbar
@@ -145,11 +144,14 @@ add_filter(NAME src/modlist GROUPS
)
add_filter(NAME src/modlist/view GROUPS
+ filterlist
modlistview
modlistviewactions
modlistcontextmenu
modflagicondelegate
+ modcontenticondelegate
modconflicticondelegate
+ modlistversiondelegate
)
add_filter(NAME src/plugins GROUPS
@@ -202,6 +204,7 @@ add_filter(NAME src/settingsdialog GROUPS
settingsdialogplugins
settingsdialogsteam
settingsdialogworkarounds
+ settingsdialoguserinterface
disableproxyplugindialog
)
diff --git a/src/filterlist.cpp b/src/filterlist.cpp
index 4c1c6fff..b594bcc6 100644
--- a/src/filterlist.cpp
+++ b/src/filterlist.cpp
@@ -234,7 +234,10 @@ void FilterList::restoreState(const Settings& s)
s.widgets().restoreIndex(ui->filtersSeparators);
s.widgets().restoreChecked(ui->filtersAnd);
s.widgets().restoreChecked(ui->filtersOr);
- s.widgets().restoreTreeCheckState(ui->filters, CriteriaItem::StateRole);
+
+ if (m_core.settings().interface().saveFilters()) {
+ s.widgets().restoreTreeCheckState(ui->filters, CriteriaItem::StateRole);
+ }
checkCriteria();
}
diff --git a/src/genericicondelegate.cpp b/src/genericicondelegate.cpp
index 1e00f0e9..7afaca3f 100644
--- a/src/genericicondelegate.cpp
+++ b/src/genericicondelegate.cpp
@@ -4,28 +4,18 @@
#include <QPixmapCache>
-GenericIconDelegate::GenericIconDelegate(QObject *parent, int role, int logicalIndex, int compactSize)
- : IconDelegate(parent)
+GenericIconDelegate::GenericIconDelegate(QTreeView* parent, int role, int logicalIndex, int compactSize)
+ : IconDelegate(parent, logicalIndex, compactSize)
, m_Role(role)
- , m_LogicalIndex(logicalIndex)
- , m_CompactSize(compactSize)
- , m_Compact(false)
{
}
-void GenericIconDelegate::columnResized(int logicalIndex, int, int newSize)
-{
- if (logicalIndex == m_LogicalIndex) {
- m_Compact = newSize < m_CompactSize;
- }
-}
-
QList<QString> GenericIconDelegate::getIcons(const QModelIndex &index) const
{
QList<QString> result;
if (index.isValid()) {
for (const QVariant &var : index.data(m_Role).toList()) {
- if (!m_Compact || !var.toString().isEmpty()) {
+ if (!compact() || !var.toString().isEmpty()) {
result.append(var.toString());
}
}
diff --git a/src/genericicondelegate.h b/src/genericicondelegate.h
index 98605250..52db2bb6 100644
--- a/src/genericicondelegate.h
+++ b/src/genericicondelegate.h
@@ -20,9 +20,8 @@ public:
* of the view, the delegate will turn off this behaviour if the column is smaller than "compactSize"
* @param compactSize see explanation of logicalIndex
*/
- GenericIconDelegate(QObject *parent = nullptr, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150);
-public slots:
- void columnResized(int logicalIndex, int oldSize, int newSize);
+ GenericIconDelegate(QTreeView* parent, int role = Qt::UserRole + 1, int logicalIndex = -1, int compactSize = 150);
+
private:
virtual QList<QString> getIcons(const QModelIndex &index) const;
virtual size_t getNumIcons(const QModelIndex &index) const;
diff --git a/src/icondelegate.cpp b/src/icondelegate.cpp
index 901b5731..7205ba62 100644
--- a/src/icondelegate.cpp
+++ b/src/icondelegate.cpp
@@ -27,9 +27,16 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
using namespace MOBase;
-IconDelegate::IconDelegate(QObject* parent)
- : QStyledItemDelegate(parent)
+IconDelegate::IconDelegate(QTreeView* view, int column, int compactSize) :
+ QStyledItemDelegate(view), m_column(column), m_compactSize(compactSize), m_compact(false)
{
+ if (view) {
+ connect(view->header(), &QHeaderView::sectionResized, [=](int column, int, int size) {
+ if (column == m_column) {
+ m_compact = size < m_compactSize;
+ }
+ });
+ }
}
void IconDelegate::paintIcons(
@@ -42,6 +49,8 @@ void IconDelegate::paintIcons(
int iconWidth = icons.size() > 0 ? ((option.rect.width() / icons.size()) - 4) : 16;
iconWidth = std::min(16, iconWidth);
+ const int margin = (option.rect.height() - iconWidth) / 2;
+
painter->translate(option.rect.topLeft());
for (const QString &iconId : icons) {
if (iconId.isEmpty()) {
@@ -57,7 +66,7 @@ void IconDelegate::paintIcons(
}
QPixmapCache::insert(fullIconId, icon);
}
- painter->drawPixmap(x, 2, iconWidth, iconWidth, icon);
+ painter->drawPixmap(x, margin, iconWidth, iconWidth, icon);
x += iconWidth + 4;
}
@@ -72,6 +81,7 @@ void IconDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
else {
QStyledItemDelegate::paint(painter, option, index);
}
+
paintIcons(painter, option, index, getIcons(index));
}
diff --git a/src/icondelegate.h b/src/icondelegate.h
index bac71a62..a123470e 100644
--- a/src/icondelegate.h
+++ b/src/icondelegate.h
@@ -20,9 +20,9 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#ifndef ICONDELEGATE_H
#define ICONDELEGATE_H
-#include "modinfo.h"
-#include <QStyledItemDelegate>
#include <QAbstractProxyModel>
+#include <QAbstractItemView>
+#include <QStyledItemDelegate>
class IconDelegate : public QStyledItemDelegate
@@ -30,16 +30,28 @@ class IconDelegate : public QStyledItemDelegate
Q_OBJECT;
public:
- explicit IconDelegate(QObject *parent = 0);
- virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+ explicit IconDelegate(QTreeView* view, int column = -1, int compactSize = 100);
+
+ void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+
+protected:
+
+ // check if icons should be compacted or not
+ //
+ bool compact() const { return m_compact; }
static void paintIcons(
QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index, const QList<QString>& icons);
-protected:
virtual QList<QString> getIcons(const QModelIndex &index) const = 0;
virtual size_t getNumIcons(const QModelIndex &index) const = 0;
+
+private:
+
+ int m_column;
+ int m_compactSize;
+ bool m_compact;
};
#endif // ICONDELEGATE_H
diff --git a/src/listdialog.cpp b/src/listdialog.cpp
index 2ad88408..9334186a 100644
--- a/src/listdialog.cpp
+++ b/src/listdialog.cpp
@@ -26,6 +26,7 @@ ListDialog::ListDialog(QWidget *parent)
{
ui->setupUi(this);
ui->filterEdit->setFocus();
+ connect(ui->choiceList, &QListWidget::itemDoubleClicked, this, &QDialog::accept);
}
ListDialog::~ListDialog()
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index f761a5f0..ce98772a 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -1495,7 +1495,6 @@ void MainWindow::startExeAction()
void MainWindow::activateSelectedProfile()
{
m_OrganizerCore.setCurrentProfile(ui->profileBox->currentText());
- ui->modList->setProfile(m_OrganizerCore.currentProfile());
m_SavesTab->refreshSaveList();
m_OrganizerCore.refresh();
diff --git a/src/moapplication.cpp b/src/moapplication.cpp
index 709bcbda..9a77c17e 100644
--- a/src/moapplication.cpp
+++ b/src/moapplication.cpp
@@ -65,9 +65,19 @@ public:
{
}
- void drawPrimitive(PrimitiveElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const {
+ void drawPrimitive(
+ PrimitiveElement element, const QStyleOption* option,
+ QPainter* painter, const QWidget* widget) const override
+ {
if (element == QStyle::PE_IndicatorItemViewItemDrop) {
+ // 0. Fix a bug that made the drop indicator sometimes appear on top
+ // of the mod list when selecting a mod.
+ if (option->rect.height() == 0
+ && option->rect.bottomRight() == QPoint(-1, -1)) {
+ return;
+ }
+
// 1. full-width drop indicator
QRect rect(option->rect);
if (auto* view = qobject_cast<const QTreeView*>(widget)) {
@@ -92,7 +102,7 @@ public:
rect.topLeft() + QPoint(-5, -5)
};
painter->drawPolygon(tri, 3);
- painter->drawLine(QPoint(rect.topLeft().x(), rect.topLeft().y()), rect.topRight());
+ painter->drawLine(rect.topLeft(), rect.topRight());
}
else {
painter->drawRoundedRect(rect, 5, 5);
diff --git a/src/modconflicticondelegate.cpp b/src/modconflicticondelegate.cpp
index 73c47a03..fc0ee48a 100644
--- a/src/modconflicticondelegate.cpp
+++ b/src/modconflicticondelegate.cpp
@@ -7,21 +7,10 @@
using namespace MOBase;
ModConflictIconDelegate::ModConflictIconDelegate(ModListView* view, int logicalIndex, int compactSize)
- : IconDelegate(view)
- , m_View(view)
- , m_LogicalIndex(logicalIndex)
- , m_CompactSize(compactSize)
- , m_Compact(false)
+ : IconDelegate(view, logicalIndex, compactSize), m_view(view)
{
}
-void ModConflictIconDelegate::columnResized(int logicalIndex, int, int newSize)
-{
- if (logicalIndex == m_LogicalIndex) {
- m_Compact = newSize < m_CompactSize;
- }
-}
-
QList<QString> ModConflictIconDelegate::getIconsForFlags(
std::vector<ModInfo::EConflictFlag> flags, bool compact)
{
@@ -94,8 +83,8 @@ QList<QString> ModConflictIconDelegate::getIcons(const QModelIndex &index) const
}
bool compact;
- auto flags = m_View->conflictFlags(index, &compact);
- return getIconsForFlags(flags, compact || m_Compact);
+ auto flags = m_view->conflictFlags(index, &compact);
+ return getIconsForFlags(flags, compact || this->compact());
}
QString ModConflictIconDelegate::getFlagIcon(ModInfo::EConflictFlag flag)
@@ -119,19 +108,13 @@ QString ModConflictIconDelegate::getFlagIcon(ModInfo::EConflictFlag flag)
size_t ModConflictIconDelegate::getNumIcons(const QModelIndex &index) const
{
- unsigned int modIdx = index.data(ModList::IndexRole).toInt();
- if (modIdx < ModInfo::getNumMods()) {
- ModInfo::Ptr info = ModInfo::getByIndex(modIdx);
- std::vector<ModInfo::EConflictFlag> flags = info->getConflictFlags();
- size_t count = flags.size();
- if (std::find_first_of(flags.begin(), flags.end(),
- s_ConflictFlags.begin(), s_ConflictFlags.end()) == flags.end()) {
- ++count;
- }
- return count;
- } else {
+ QVariant modIndex = index.data(ModList::IndexRole);
+
+ if (!modIndex.isValid()) {
return 0;
}
+
+ return m_view->conflictFlags(index, nullptr).size();
}
QSize ModConflictIconDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &modelIndex) const
diff --git a/src/modconflicticondelegate.h b/src/modconflicticondelegate.h
index a44ce89b..fd524444 100644
--- a/src/modconflicticondelegate.h
+++ b/src/modconflicticondelegate.h
@@ -4,6 +4,7 @@
#include <array>
#include "icondelegate.h"
+#include "modinfo.h"
class ModListView;
@@ -12,12 +13,9 @@ class ModConflictIconDelegate : public IconDelegate
Q_OBJECT;
public:
- explicit ModConflictIconDelegate(ModListView* parent = nullptr, int logicalIndex = -1, int compactSize = 80);
+ explicit ModConflictIconDelegate(ModListView* view, int logicalIndex = -1, int compactSize = 80);
QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex &index) const override;
-public slots:
- void columnResized(int logicalIndex, int oldSize, int newSize);
-
protected:
static QList<QString> getIconsForFlags(std::vector<ModInfo::EConflictFlag> flags, bool compact);
@@ -26,6 +24,10 @@ protected:
QList<QString> getIcons(const QModelIndex &index) const override;
size_t getNumIcons(const QModelIndex &index) const override;
+ // constructor for color table
+ //
+ ModConflictIconDelegate() : ModConflictIconDelegate(nullptr) { }
+
private:
static constexpr std::array s_ConflictFlags{
ModInfo::FLAG_CONFLICT_MIXED,
@@ -43,10 +45,7 @@ private:
ModInfo::FLAG_ARCHIVE_CONFLICT_OVERWRITTEN
};
- ModListView* m_View;
- int m_LogicalIndex;
- int m_CompactSize;
- bool m_Compact;
+ ModListView* m_view;
};
#endif // MODCONFLICTICONDELEGATE_H
diff --git a/src/modcontenticondelegate.cpp b/src/modcontenticondelegate.cpp
new file mode 100644
index 00000000..57a4dc6d
--- /dev/null
+++ b/src/modcontenticondelegate.cpp
@@ -0,0 +1,57 @@
+#include "modcontenticondelegate.h"
+
+#include "modlistview.h"
+
+ModContentIconDelegate::ModContentIconDelegate(ModListView* view, int column, int compactSize)
+ : IconDelegate(view, column, compactSize), m_view(view)
+{
+}
+
+QList<QString> ModContentIconDelegate::getIcons(const QModelIndex& index) const
+{
+ QVariant modIndex = index.data(ModList::IndexRole);
+
+ if (!modIndex.isValid()) {
+ return {};
+ }
+
+ bool compact;
+ auto icons = m_view->contentsIcons(index, &compact);
+
+ if (compact || this->compact()) {
+ icons.removeAll(QString());
+ }
+
+ return icons;
+}
+
+size_t ModContentIconDelegate::getNumIcons(const QModelIndex& index) const
+{
+ return getIcons(index).size();
+}
+
+bool ModContentIconDelegate::helpEvent(
+ QHelpEvent* event, QAbstractItemView* view, const QStyleOptionViewItem& option, const QModelIndex& index)
+{
+ if (!event || !view) {
+ return false;
+ }
+ if (event->type() == QEvent::ToolTip) {
+ // this code is from QAbstractItemDelegate::helpEvent, only the the way
+ // text is retrieved has been changed
+ QHelpEvent* he = static_cast<QHelpEvent*>(event);
+ const int precision = inherits("QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
+ const QString tooltip = index.isValid() ? m_view->contentsTooltip(index) : QString();
+ QRect rect;
+ if (index.isValid()) {
+ const QRect r = view->visualRect(index);
+ rect = QRect(view->mapToGlobal(r.topLeft()), r.size());
+ }
+ QToolTip::showText(he->globalPos(), tooltip, view, rect);
+ event->setAccepted(!tooltip.isEmpty());
+ return true;
+ }
+ else {
+ return IconDelegate::helpEvent(event, view, option, index);
+ }
+}
diff --git a/src/modcontenticondelegate.h b/src/modcontenticondelegate.h
new file mode 100644
index 00000000..c195df18
--- /dev/null
+++ b/src/modcontenticondelegate.h
@@ -0,0 +1,30 @@
+#ifndef MODCONTENTICONDELEGATE_H
+#define MODCONTENTICONDELEGATE_H
+
+#include "icondelegate.h"
+
+class ModListView;
+
+class ModContentIconDelegate : public IconDelegate
+{
+ Q_OBJECT
+public:
+
+ explicit ModContentIconDelegate(ModListView* view, int column = -1, int compactSize = 150);
+
+ bool helpEvent(QHelpEvent* event, QAbstractItemView* view,
+ const QStyleOptionViewItem& option, const QModelIndex& index) override;
+
+protected:
+ QList<QString> getIcons(const QModelIndex& index) const override;
+ size_t getNumIcons(const QModelIndex& index) const override;
+
+ // constructor for color table
+ //
+ ModContentIconDelegate() : ModContentIconDelegate(nullptr) { }
+
+private:
+ ModListView* m_view;
+};
+
+#endif // GENERICICONDELEGATE_H
diff --git a/src/modflagicondelegate.cpp b/src/modflagicondelegate.cpp
index 3ac1085e..30a3c376 100644
--- a/src/modflagicondelegate.cpp
+++ b/src/modflagicondelegate.cpp
@@ -1,25 +1,16 @@
#include "modflagicondelegate.h"
#include "modlist.h"
+#include "modlistview.h"
#include <log.h>
#include <QList>
using namespace MOBase;
-ModFlagIconDelegate::ModFlagIconDelegate(QObject *parent, int logicalIndex, int compactSize)
- : IconDelegate(parent)
- , m_LogicalIndex(logicalIndex)
- , m_CompactSize(compactSize)
- , m_Compact(false)
+ModFlagIconDelegate::ModFlagIconDelegate(ModListView* view, int column, int compactSize)
+ : IconDelegate(view, column, compactSize), m_view(view)
{
}
-void ModFlagIconDelegate::columnResized(int logicalIndex, int, int newSize)
-{
- if (logicalIndex == m_LogicalIndex) {
- m_Compact = newSize < m_CompactSize;
- }
-}
-
QList<QString> ModFlagIconDelegate::getIconsForFlags(
std::vector<ModInfo::EFlag> flags, bool compact)
{
@@ -43,8 +34,9 @@ QList<QString> ModFlagIconDelegate::getIcons(const QModelIndex &index) const
QVariant modid = index.data(ModList::IndexRole);
if (modid.isValid()) {
- ModInfo::Ptr info = ModInfo::getByIndex(modid.toInt());
- return getIconsForFlags(info->getFlags(), m_Compact);
+ bool compact;
+ auto flags = m_view->modFlags(index, &compact);
+ return getIconsForFlags(flags, compact || this->compact());
}
return {};
diff --git a/src/modflagicondelegate.h b/src/modflagicondelegate.h
index c6c7c3e8..24dd4a0e 100644
--- a/src/modflagicondelegate.h
+++ b/src/modflagicondelegate.h
@@ -1,32 +1,35 @@
#ifndef MODFLAGICONDELEGATE_H
#define MODFLAGICONDELEGATE_H
+#include <QTreeView>
+
#include "icondelegate.h"
+#include "modinfo.h"
+
+class ModListView;
class ModFlagIconDelegate : public IconDelegate
{
Q_OBJECT;
public:
- explicit ModFlagIconDelegate(QObject *parent = 0, int logicalIndex = -1, int compactSize = 120);
+ explicit ModFlagIconDelegate(ModListView* view, int column = -1, int compactSize = 120);
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
- static QList<QString> getIconsForFlags(
- std::vector<ModInfo::EFlag> flags, bool compact);
-
+protected:
+ static QList<QString> getIconsForFlags(std::vector<ModInfo::EFlag> flags, bool compact);
static QString getFlagIcon(ModInfo::EFlag flag);
-public slots:
- void columnResized(int logicalIndex, int oldSize, int newSize);
-
-protected:
QList<QString> getIcons(const QModelIndex &index) const override;
size_t getNumIcons(const QModelIndex &index) const override;
+ // constructor for color table
+ //
+ ModFlagIconDelegate() : ModFlagIconDelegate(nullptr) { }
+
private:
- int m_LogicalIndex;
- int m_CompactSize;
- bool m_Compact;
+ ModListView* m_view;
+
};
#endif // MODFLAGICONDELEGATE_H
diff --git a/src/modinfo.cpp b/src/modinfo.cpp
index c76cb4ad..b46a68ab 100644
--- a/src/modinfo.cpp
+++ b/src/modinfo.cpp
@@ -49,6 +49,7 @@ using namespace MOBase;
using namespace MOShared;
+const std::set<unsigned int> ModInfo::s_EmptySet;
std::vector<ModInfo::Ptr> ModInfo::s_Collection;
ModInfo::Ptr ModInfo::s_Overwrite;
std::map<QString, unsigned int> ModInfo::s_ModsByName;
diff --git a/src/modinfo.h b/src/modinfo.h
index 08ed94f8..634ea900 100644
--- a/src/modinfo.h
+++ b/src/modinfo.h
@@ -868,46 +868,35 @@ public: // Nexus stuff
public: // Conflicts
- /**
- * @return retrieve list of mods (as mod index) that are overwritten by this one.
- * Updates may be delayed.
- */
- virtual std::set<unsigned int> getModOverwrite() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) that are overwritten by this one.
+ // Updates may be delayed.
+ //
+ virtual const std::set<unsigned int>& getModOverwrite() const { return s_EmptySet; }
- /**
- * @return list of mods (as mod index) that overwrite this one. Updates may be delayed.
- */
- virtual std::set<unsigned int> getModOverwritten() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) that overwrite this one.
+ // Updates may be delayed.
+ //
+ virtual const std::set<unsigned int>& getModOverwritten() const { return s_EmptySet; }
- /**
- * @return retrieve list of mods (as mod index) with archives that are overwritten by
- * this one. Updates may be delayed
- */
- virtual std::set<unsigned int> getModArchiveOverwrite() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) with archives that are overwritten by
+ // this one. Updates may be delayed
+ //
+ virtual const std::set<unsigned int>& getModArchiveOverwrite() const { return s_EmptySet; }
- /**
- * @return list of mods (as mod index) with archives that overwrite this one. Updates
- * may be delayed.
- */
- virtual std::set<unsigned int> getModArchiveOverwritten() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) with archives that overwrite this one. Updates
+ // may be delayed.
+ //
+ virtual const std::set<unsigned int>& getModArchiveOverwritten() const { return s_EmptySet; }
- /**
- * @return the list of mods (as mod index) with archives that are overwritten by loose
- * files of this mod. Updates may be delayed.
- */
- virtual std::set<unsigned int> getModArchiveLooseOverwrite() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) with archives that are overwritten by loose
+ // files of this mod. Updates may be delayed.
+ //
+ virtual const std::set<unsigned int>& getModArchiveLooseOverwrite() const { return s_EmptySet; }
- /**
- * @return the list of mods (as mod index) with loose files that overwrite this one's
- * archive files. Updates may be delayed.
- */
- virtual std::set<unsigned int> getModArchiveLooseOverwritten() const {
- return std::set<unsigned int>(); }
+ // retrieve the list of mods (as mod index) with loose files that overwrite this one's
+ // archive files. Updates may be delayed.
+ //
+ virtual const std::set<unsigned int>& getModArchiveLooseOverwritten() const { return s_EmptySet; }
/**
* @brief Update conflict information.
@@ -960,6 +949,10 @@ protected:
MOBase::VersionInfo m_Version;
bool m_PluginSelected = false;
+ // empty set that can be returned in overwrite functions by
+ // default
+ static const std::set<unsigned int> s_EmptySet;
+
protected:
friend class OrganizerCore;
diff --git a/src/modinfowithconflictinfo.h b/src/modinfowithconflictinfo.h
index 889f1246..c9cddb60 100644
--- a/src/modinfowithconflictinfo.h
+++ b/src/modinfowithconflictinfo.h
@@ -52,21 +52,16 @@ public:
/**
* @brief clear all caches held for this mod
*/
- virtual void clearCaches() override;
+ void clearCaches() override;
- virtual std::set<unsigned int> getModOverwrite() const override { return m_OverwriteList; }
+ const std::set<unsigned int>& getModOverwrite() const override { return m_OverwriteList; }
+ const std::set<unsigned int>& getModOverwritten() const override { return m_OverwrittenList; }
+ const std::set<unsigned int>& getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; }
+ const std::set<unsigned int>& getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; }
+ const std::set<unsigned int>& getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; }
+ const std::set<unsigned int>& getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; }
- virtual std::set<unsigned int> getModOverwritten() const override { return m_OverwrittenList; }
-
- virtual std::set<unsigned int> getModArchiveOverwrite() const override { return m_ArchiveOverwriteList; }
-
- virtual std::set<unsigned int> getModArchiveOverwritten() const override { return m_ArchiveOverwrittenList; }
-
- virtual std::set<unsigned int> getModArchiveLooseOverwrite() const override { return m_ArchiveLooseOverwriteList; }
-
- virtual std::set<unsigned int> getModArchiveLooseOverwritten() const override { return m_ArchiveLooseOverwrittenList; }
-
- virtual void doConflictCheck() const override;
+ void doConflictCheck() const override;
public slots:
diff --git a/src/modlist.cpp b/src/modlist.cpp
index ea1d2754..f820e4d1 100644
--- a/src/modlist.cpp
+++ b/src/modlist.cpp
@@ -194,30 +194,6 @@ QString ModList::getConflictFlagText(ModInfo::EConflictFlag flag, ModInfo::Ptr m
}
}
-
-QVariantList ModList::contentsToIcons(const std::set<int> &contents) const
-{
- QVariantList result;
- m_Organizer->modDataContents().forEachContentInOrOut(
- contents,
- [&result](auto const& content) { result.append(content.icon()); },
- [&result](auto const&) { result.append(QString()); });
- return result;
-}
-
-QString ModList::contentsToToolTip(const std::set<int> &contents) const
-{
- QString result("<table cellspacing=7>");
- m_Organizer->modDataContents().forEachContentIn(contents, [&result](auto const& content) {
- result.append(QString("<tr><td><img src=\"%1\" width=32/></td>"
- "<td valign=\"middle\">%2</td></tr>")
- .arg(content.icon()).arg(content.name()));
- });
- result.append("</table>");
- return result;
-}
-
-
QVariant ModList::data(const QModelIndex &modelIndex, int role) const
{
if (m_Profile == nullptr) return QVariant();
@@ -385,9 +361,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
default: return QtGroupingProxy::AGGR_NONE;
}
}
- else if (role == ContentsRole) {
- return contentsToIcons(modInfo->getContents());
- }
else if (role == GameNameRole) {
return modInfo->gameName();
}
@@ -494,9 +467,6 @@ QVariant ModList::data(const QModelIndex &modelIndex, int role) const
return result;
}
- else if (column == COL_CONTENT) {
- return contentsToToolTip(modInfo->getContents());
- }
else if (column == COL_NAME) {
try {
return modInfo->getDescription();
@@ -585,15 +555,16 @@ bool ModList::renameMod(int index, const QString &newName)
m_Profile->cancelModlistWrite();
- if (modInfo->setName(nameFixed))
+ if (modInfo->setName(nameFixed)) {
// Notice there is a good chance that setName() updated the modinfo indexes
// the modRenamed() call will refresh the indexes in the current profile
// and update the modlists in all profiles
emit modRenamed(oldName, nameFixed);
- }
+ }
- // invalidate the currently displayed state of this list
- notifyChange(-1);
+ // invalidate the currently displayed state of this list
+ notifyChange(-1);
+ }
return true;
}
diff --git a/src/modlist.h b/src/modlist.h
index 1d94749c..5c7f28ee 100644
--- a/src/modlist.h
+++ b/src/modlist.h
@@ -69,19 +69,14 @@ public:
// grouping I assume?)
AggrRole = Qt::UserRole + 2,
- // data(ContentsRole) contains mod data contents as a QVariantList
- // containing icon paths
- ContentsRole = Qt::UserRole + 3,
-
- GameNameRole = Qt::UserRole + 4,
-
- PriorityRole = Qt::UserRole + 5,
+ GameNameRole = Qt::UserRole + 3,
+ PriorityRole = Qt::UserRole + 4,
// marking role for the scrollbar
- ScrollMarkRole = Qt::UserRole + 6,
+ ScrollMarkRole = Qt::UserRole + 5,
// this is the first available role
- ModUserRole = Qt::UserRole + 7
+ ModUserRole = Qt::UserRole + 6
};
enum EColumn {
@@ -354,10 +349,6 @@ private:
QString getColumnToolTip(int column) const;
- QVariantList contentsToIcons(const std::set<int> &contentIds) const;
-
- QString contentsToToolTip(const std::set<int> &contentsIds) const;
-
ModList::EColumn getEnabledColumn(int index) const;
QVariant categoryData(int categoryID, int column, int role) const;
diff --git a/src/modlistbypriorityproxy.cpp b/src/modlistbypriorityproxy.cpp
index 6f549d91..e0efe585 100644
--- a/src/modlistbypriorityproxy.cpp
+++ b/src/modlistbypriorityproxy.cpp
@@ -192,7 +192,6 @@ bool ModListByPriorityProxy::canDropMimeData(const QMimeData* data, Qt::DropActi
}
if (dropInfo.isModDrop()) {
-
bool hasSeparator = false;
unsigned int firstRowIndex = -1;
@@ -264,6 +263,7 @@ bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction
}
}
else {
+
if (row >= 0) {
if (!parent.isValid()) {
if (row < m_Root.children.size()) {
@@ -271,6 +271,7 @@ bool ModListByPriorityProxy::dropMimeData(const QMimeData* data, Qt::DropAction
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;
}
@@ -316,7 +317,8 @@ QModelIndex ModListByPriorityProxy::index(int row, int column, const QModelIndex
return createIndex(row, column, parentItem->children[row].get());
}
-void ModListByPriorityProxy::onDropEnter(const QMimeData*, ModListView::DropPosition dropPosition)
+void ModListByPriorityProxy::onDropEnter(const QMimeData*, bool dropExpanded, ModListView::DropPosition dropPosition)
{
+ m_dropExpanded = dropExpanded;
m_dropPosition = dropPosition;
}
diff --git a/src/modlistbypriorityproxy.h b/src/modlistbypriorityproxy.h
index 6b48678a..5cd66c62 100644
--- a/src/modlistbypriorityproxy.h
+++ b/src/modlistbypriorityproxy.h
@@ -16,6 +16,7 @@
#include "modlistview.h"
class ModList;
+class ModListDropInfo;
class Profile;
class ModListByPriorityProxy : public QAbstractProxyModel
@@ -46,7 +47,7 @@ public:
public slots:
- void onDropEnter(const QMimeData* data, ModListView::DropPosition dropPosition);
+ void onDropEnter(const QMimeData* data, bool dropExpanded, ModListView::DropPosition dropPosition);
protected slots:
@@ -83,6 +84,8 @@ private:
private:
OrganizerCore& m_core;
Profile* m_profile;
+
+ bool m_dropExpanded = false;
ModListView::DropPosition m_dropPosition = ModListView::DropPosition::OnItem;
};
diff --git a/src/modlistcontextmenu.cpp b/src/modlistcontextmenu.cpp
index f5307dc7..97eef0eb 100644
--- a/src/modlistcontextmenu.cpp
+++ b/src/modlistcontextmenu.cpp
@@ -23,10 +23,15 @@ ModListGlobalContextMenu::ModListGlobalContextMenu(OrganizerCore& core, ModListV
if (modIndex.isValid()) {
auto info = ModInfo::getByIndex(modIndex.toInt());
if (!info->isBackup()) {
- addAction(tr("Create empty mod"), [=]() { view->actions().createEmptyMod(index); });
- addAction(tr("Create Separator"), [=]() { view->actions().createSeparator(index); });
+ addAction(info->isSeparator() ? tr("Create empty mod inside") : tr("Create empty mod before"),
+ [=]() { view->actions().createEmptyMod(index); });
+ addAction(tr("Create separator before"), [=]() { view->actions().createSeparator(index); });
}
}
+ else {
+ addAction(tr("Create empty mod at the end"), [=]() { view->actions().createEmptyMod(); });
+ addAction(tr("Create separator at the end"), [=]() { view->actions().createSeparator(); });
+ }
if (view->hasCollapsibleSeparators()) {
addSeparator();
diff --git a/src/modlistversiondelegate.cpp b/src/modlistversiondelegate.cpp
new file mode 100644
index 00000000..657718f5
--- /dev/null
+++ b/src/modlistversiondelegate.cpp
@@ -0,0 +1,62 @@
+#include "modlistversiondelegate.h"
+
+#include "modlistview.h"
+#include "log.h"
+
+ModListVersionDelegate::ModListVersionDelegate(ModListView* view) :
+ QItemDelegate(view), m_view(view) { }
+
+
+void ModListVersionDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
+{
+ m_view->itemDelegate()->paint(painter, option, index);
+
+ if (m_view->hasCollapsibleSeparators()
+ && m_view->model()->hasChildren(index)
+ && !m_view->isExpanded(index.sibling(index.row(), 0))) {
+ auto* model = m_view->model();
+
+ bool downgrade = false, upgrade = false;
+
+ for (int i = 0; i < model->rowCount(index); ++i) {
+ const auto mIndex = model->index(i, index.column(), index).data(ModList::IndexRole);
+ if (mIndex.isValid()) {
+ auto info = ModInfo::getByIndex(mIndex.toInt());
+ downgrade = downgrade || info->downgradeAvailable();
+ upgrade = upgrade || info->updateAvailable();
+ }
+ }
+
+ const int margin = m_view->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, m_view) + 1;
+
+ QStyleOptionViewItem opt(option);
+ const int sz = m_view->style()->pixelMetric(QStyle::PM_SmallIconSize, nullptr, m_view);
+ opt.decorationSize = QSize(sz, sz);
+ opt.decorationAlignment = Qt::AlignCenter;
+
+ if (upgrade) {
+ QIcon icon(":/MO/gui/update_available");
+ QPixmap pixmap = decoration(opt, icon);
+
+ QSize pm = icon.actualSize(opt.decorationSize);
+ pm.rwidth() += 2 * margin;
+ opt.rect.setRect(opt.rect.x(), opt.rect.y(), pm.width(), opt.rect.height());
+ drawDecoration(painter, opt, opt.rect, pixmap);
+
+ // add margin for next icon (if any)
+ opt.rect.adjust(opt.decorationSize.width() + margin, 0, 0, 0);
+ }
+
+ if (downgrade) {
+ QIcon icon(":/MO/gui/warning");
+ QPixmap pixmap = decoration(opt, icon);
+
+ QSize pm = icon.actualSize(opt.decorationSize);
+ pm.rwidth() += 2 * margin;
+ opt.rect.setRect(opt.rect.x(), opt.rect.y(), pm.width(), opt.rect.height());
+
+ drawDecoration(painter, opt, opt.rect, pixmap);
+
+ }
+ }
+}
diff --git a/src/modlistversiondelegate.h b/src/modlistversiondelegate.h
new file mode 100644
index 00000000..8a51e9b4
--- /dev/null
+++ b/src/modlistversiondelegate.h
@@ -0,0 +1,21 @@
+#ifndef MODLISTVERSIONDELEGATE_H
+#define MODLISTVERSIONDELEGATE_H
+
+#include <QStyledItemDelegate>
+
+class ModListView;
+
+class ModListVersionDelegate : public QItemDelegate
+{
+public:
+ ModListVersionDelegate(ModListView* view);
+
+ void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override;
+
+
+private:
+
+ ModListView* m_view;
+};
+
+#endif
diff --git a/src/modlistview.cpp b/src/modlistview.cpp
index 4b285733..8827733b 100644
--- a/src/modlistview.cpp
+++ b/src/modlistview.cpp
@@ -18,6 +18,8 @@
#include "log.h"
#include "modflagicondelegate.h"
#include "modconflicticondelegate.h"
+#include "modcontenticondelegate.h"
+#include "modlistversiondelegate.h"
#include "modlistviewactions.h"
#include "modlistdropinfo.h"
#include "modlistcontextmenu.h"
@@ -40,13 +42,13 @@ using namespace MOShared;
// handling (e.g. checkbox, edit, etc.), so we also need to override
// the visualRect() function from the mod list view.
//
-class ModListStyledItemDelegated : public QStyledItemDelegate
+class ModListStyledItemDelegate : public QStyledItemDelegate
{
ModListView* m_view;
public:
- ModListStyledItemDelegated(ModListView* view) :
+ ModListStyledItemDelegate(ModListView* view) :
QStyledItemDelegate(view), m_view(view) { }
void initStyleOption(QStyleOptionViewItem* option, const QModelIndex& index) const override
@@ -81,18 +83,29 @@ public:
if (!color.isValid()) {
color = index.data(Qt::BackgroundRole).value<QColor>();
}
- else {
- // disable alternating row if the color is from the children
- opt.features &= ~QStyleOptionViewItem::Alternate;
- }
opt.backgroundBrush = color;
+ // we need to find the background color to compute the ideal text color
+ // but the mod list view uses alternate color so we need to find the
+ // right color
+ auto bg = opt.palette.base().color();
+ auto vrow = (opt.rect.y() - m_view->verticalOffset()) / opt.rect.height();
+ if (vrow % 2 == 1) {
+ bg = opt.palette.alternateBase().color();
+ }
+
// compute ideal foreground color for some rows
if (color.isValid()) {
if ((index.column() == ModList::COL_NAME
&& ModInfo::getByIndex(index.data(ModList::IndexRole).toInt())->isSeparator())
|| index.column() == ModList::COL_NOTES) {
- opt.palette.setBrush(QPalette::Text, ColorSettings::idealTextColor(color));
+
+ // combine the color with the background and then find the "ideal" text color
+ const auto a = color.alpha() / 255.;
+ int r = (1 - a) * bg.red() + a * color.red(),
+ g = (1 - a) * bg.green() + a * color.green(),
+ b = (1 - a) * bg.blue() + a * color.blue();
+ opt.palette.setBrush(QPalette::Text, ColorSettings::idealTextColor(QColor(r, g, b)));
}
}
@@ -132,11 +145,19 @@ ModListView::ModListView(QWidget* parent)
MOBase::setCustomizableColumns(this);
setAutoExpandDelay(750);
- setItemDelegate(new ModListStyledItemDelegated(this));
+ setItemDelegate(new ModListStyledItemDelegate(this));
connect(this, &ModListView::doubleClicked, this, &ModListView::onDoubleClicked);
connect(this, &ModListView::customContextMenuRequested, this, &ModListView::onCustomContextMenuRequested);
+ // the timeout is pretty small because its main purpose is to avoid
+ // refreshing multiple times when calling expandAll() or collapseAll()
+ // which emit a lots of expanded/collapsed signals in a very small
+ // time window
+ m_refreshMarkersTimer.setInterval(50);
+ m_refreshMarkersTimer.setSingleShot(true);
+ connect(&m_refreshMarkersTimer, &QTimer::timeout, [=] { refreshMarkersAndPlugins(); });
+
installEventFilter(new CopyEventFilter(this, [=](auto& index) {
QVariant mIndex = index.data(ModList::IndexRole);
QString name = index.data(Qt::DisplayRole).toString();
@@ -158,10 +179,24 @@ void ModListView::refresh()
updateGroupByProxy();
}
-void ModListView::setProfile(Profile* profile)
+void ModListView::onProfileChanged(Profile* oldProfile, Profile* newProfile)
{
- m_sortProxy->setProfile(profile);
- m_byPriorityProxy->setProfile(profile);
+ const auto perProfileSeparators =
+ m_core->settings().interface().collapsibleSeparatorsPerProfile();
+
+ // save expanded/collapsed state of separators
+ if (oldProfile && perProfileSeparators) {
+ auto& collapsed = m_collapsed[m_byPriorityProxy];
+ oldProfile->storeSetting("UserInterface", "collapsed_separators", QStringList(collapsed.begin(), collapsed.end()));
+ }
+
+ m_sortProxy->setProfile(newProfile);
+ m_byPriorityProxy->setProfile(newProfile);
+
+ if (newProfile && perProfileSeparators) {
+ auto collapsed = newProfile->setting("UserInterface", "collapsed_separators", QStringList()).toStringList();
+ m_collapsed[m_byPriorityProxy] = { collapsed.begin(), collapsed.end() };
+ }
}
bool ModListView::hasCollapsibleSeparators() const
@@ -216,10 +251,8 @@ std::optional<unsigned int> ModListView::nextMod(unsigned int modIndex) const
ModInfo::Ptr mod = ModInfo::getByIndex(modIndex);
- // skip overwrite and backups and separators
- if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
- mod->hasFlag(ModInfo::FLAG_BACKUP) ||
- mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ // skip overwrite, backups and separators
+ if (mod->isOverwrite() || mod->isBackup() || mod->isSeparator()) {
continue;
}
@@ -245,12 +278,9 @@ std::optional<unsigned int> ModListView::prevMod(unsigned int modIndex) const
modIndex = index.data(ModList::IndexRole).toInt();
- // skip overwrite and backups and separators
+ // skip overwrite, backups and separators
ModInfo::Ptr mod = ModInfo::getByIndex(modIndex);
-
- if (mod->hasFlag(ModInfo::FLAG_OVERWRITE) ||
- mod->hasFlag(ModInfo::FLAG_BACKUP) ||
- mod->hasFlag(ModInfo::FLAG_SEPARATOR)) {
+ if (mod->isOverwrite() || mod->isBackup() || mod->isSeparator()) {
continue;
}
@@ -360,6 +390,15 @@ void ModListView::setSelected(const QModelIndex& current, const QModelIndexList&
}
}
+void ModListView::scrollToAndSelect(const QModelIndex& index)
+{
+ // focus, scroll to and select
+ scrollTo(index);
+ setCurrentIndex(index);
+ selectionModel()->select(index, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
+ QTimer::singleShot(50, [=] { setFocus(); });
+}
+
void ModListView::refreshExpandedItems()
{
auto* model = m_sortProxy->sourceModel();
@@ -421,7 +460,7 @@ void ModListView::onModPrioritiesChanged(const QModelIndexList& indices)
}
// update conflict check on the moved mod
modInfo->doConflictCheck();
- setOverwriteMarkers(modInfo);
+ setOverwriteMarkers(selectionModel()->selectedRows());
}
}
}
@@ -436,15 +475,11 @@ void ModListView::onModInstalled(const QString& modName)
QModelIndex qIndex = indexModelToView(m_core->modList()->index(index, 0));
- if (hasCollapsibleSeparators()) {
- setExpanded(qIndex, true);
+ if (hasCollapsibleSeparators() && qIndex.parent().isValid()) {
+ setExpanded(qIndex.parent(), true);
}
- // focus, scroll to and select
- setFocus(Qt::OtherFocusReason);
- scrollTo(qIndex);
- setCurrentIndex(qIndex);
- selectionModel()->select(qIndex, QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
+ scrollToAndSelect(qIndex);
}
void ModListView::onModFilterActive(bool filterActive)
@@ -559,6 +594,8 @@ void ModListView::refreshFilters()
void ModListView::onExternalFolderDropped(const QUrl& url, int priority)
{
+ setWindowState(Qt::WindowActive);
+
QFileInfo fileInfo(url.toLocalFile());
GuessedValue<QString> name;
@@ -594,9 +631,12 @@ void ModListView::onExternalFolderDropped(const QUrl& url, int priority)
m_core->refresh();
+ const auto index = ModInfo::getIndex(name);
if (priority != -1) {
- m_core->modList()->changeModPriority(ModInfo::getIndex(name), priority);
+ m_core->modList()->changeModPriority(index, priority);
}
+
+ scrollToAndSelect(indexModelToView(m_core->modList()->index(index, 0)));
}
bool ModListView::moveSelection(int key)
@@ -686,10 +726,13 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
m_actions = new ModListViewActions(core, *m_filters, factory, this, mwui->espList, mw);
ui = {
mwui->groupCombo, mwui->activeModsCounter, mwui->modFilterEdit,
- mwui->currentCategoryLabel, mwui->clearFiltersButton, mwui->filtersSeparators
+ mwui->currentCategoryLabel, mwui->clearFiltersButton, mwui->filtersSeparators,
+ mwui->espList
};
+
connect(m_core, &OrganizerCore::modInstalled, [=](auto&& name) { onModInstalled(name); });
+ connect(m_core, &OrganizerCore::profileChanged, this, &ModListView::onProfileChanged);
connect(core.modList(), &ModList::modPrioritiesChanged, [=](auto&& indices) { onModPrioritiesChanged(indices); });
connect(core.modList(), &ModList::clearOverwrite, [=] { m_actions->clearOverwrite(); });
connect(core.modList(), &ModList::modStatesChanged, [=] { updateModCount(); });
@@ -741,17 +784,10 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
connect(header(), &QHeaderView::sectionResized, [=](int logicalIndex, int oldSize, int newSize) {
m_sortProxy->setColumnVisible(logicalIndex, newSize != 0); });
- GenericIconDelegate* contentDelegate = new GenericIconDelegate(this, ModList::ContentsRole, ModList::COL_CONTENT, 150);
- ModFlagIconDelegate* flagDelegate = new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120);
- ModConflictIconDelegate* conflictFlagDelegate = new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80);
-
- connect(header(), &QHeaderView::sectionResized, contentDelegate, &GenericIconDelegate::columnResized);
- connect(header(), &QHeaderView::sectionResized, flagDelegate, &ModFlagIconDelegate::columnResized);
- connect(header(), &QHeaderView::sectionResized, conflictFlagDelegate, &ModConflictIconDelegate::columnResized);
-
- setItemDelegateForColumn(ModList::COL_FLAGS, flagDelegate);
- setItemDelegateForColumn(ModList::COL_CONFLICTFLAGS, conflictFlagDelegate);
- setItemDelegateForColumn(ModList::COL_CONTENT, contentDelegate);
+ setItemDelegateForColumn(ModList::COL_FLAGS, new ModFlagIconDelegate(this, ModList::COL_FLAGS, 120));
+ setItemDelegateForColumn(ModList::COL_CONFLICTFLAGS, new ModConflictIconDelegate(this, ModList::COL_CONFLICTFLAGS, 80));
+ setItemDelegateForColumn(ModList::COL_CONTENT, new ModContentIconDelegate(this, ModList::COL_CONTENT, 150));
+ setItemDelegateForColumn(ModList::COL_VERSION, new ModListVersionDelegate(this));
if (m_core->settings().geometry().restoreState(header())) {
// hack: force the resize-signal to be triggered because restoreState doesn't seem to do that
@@ -777,16 +813,6 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
header()->setSectionResizeMode(ModList::COL_NAME, QHeaderView::Stretch);
}
- // highligth plugins
- connect(selectionModel(), &QItemSelectionModel::selectionChanged, [=](auto&& selected) {
- std::vector<unsigned int> modIndices;
- for (auto& idx : selectionModel()->selectedRows()) {
- modIndices.push_back(idx.data(ModList::IndexRole).toInt());
- }
- m_core->pluginList()->highlightPlugins(modIndices, *m_core->directoryStructure());
- mwui->espList->verticalScrollBar()->repaint();
- });
-
// prevent the name-column from being hidden
header()->setSectionHidden(ModList::COL_NAME, false);
@@ -794,11 +820,14 @@ void ModListView::setup(OrganizerCore& core, CategoryFactory& factory, MainWindo
m_core->installDownload(row, priority);
});
connect(m_core->modList(), &ModList::externalArchiveDropped, [=](const QUrl& url, int priority) {
+ setWindowState(Qt::WindowActive);
m_core->installArchive(url.toLocalFile(), priority, false, nullptr);
});
connect(m_core->modList(), &ModList::externalFolderDropped, this, &ModListView::onExternalFolderDropped);
- connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &ModListView::onSelectionChanged);
+ connect(selectionModel(), &QItemSelectionModel::selectionChanged, [=] { m_refreshMarkersTimer.start(); });
+ connect(this, &QTreeView::collapsed, [=] { m_refreshMarkersTimer.start(); });
+ connect(this, &QTreeView::expanded, [=] { m_refreshMarkersTimer.start(); });
// filters
connect(m_sortProxy, &ModListSortProxy::filterActive, this, &ModListView::onModFilterActive);
@@ -839,7 +868,7 @@ void ModListView::saveState(Settings& s) const
QRect ModListView::visualRect(const QModelIndex& index) const
{
// this shift the visualRect() from QTreeView to match the new actual
- // zone after removing indentation (see the ModListStyledItemDelegated)
+ // zone after removing indentation (see the ModListStyledItemDelegate)
QRect rect = QTreeView::visualRect(index);
if (hasCollapsibleSeparators()
&& index.column() == 0 && index.isValid()
@@ -974,40 +1003,56 @@ void ModListView::clearOverwriteMarkers()
m_markers.archiveLooseOverwritten.clear();
}
-void ModListView::setOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten)
+void ModListView::setOverwriteMarkers(const QModelIndexList& indexes)
{
- m_markers.overwrite = overwrite;
- m_markers.overwritten = overwritten;
-}
-
-void ModListView::setArchiveOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten)
-{
- m_markers.archiveOverwrite = overwrite;
- m_markers.archiveOverwritten = overwritten;
+ const auto insert = [](auto& dest, const auto& from) {
+ dest.insert(from.begin(), from.end());
+ };
+ clearOverwriteMarkers();
+ for (auto& idx : indexes) {
+ auto mIndex = idx.data(ModList::IndexRole);
+ if (mIndex.isValid()) {
+ auto info = ModInfo::getByIndex(mIndex.toInt());
+ insert(m_markers.overwrite, info->getModOverwrite());
+ insert(m_markers.overwritten, info->getModOverwritten());
+ insert(m_markers.archiveOverwrite, info->getModArchiveOverwrite());
+ insert(m_markers.archiveOverwritten, info->getModArchiveOverwritten());
+ insert(m_markers.archiveLooseOverwrite, info->getModArchiveLooseOverwrite());
+ insert(m_markers.archiveLooseOverwritten, info->getModArchiveLooseOverwritten());
+ }
+ }
+ dataChanged(model()->index(0, 0), model()->index(model()->rowCount(), model()->columnCount()));
+ verticalScrollBar()->repaint();
}
-void ModListView::setArchiveLooseOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten)
+void ModListView::refreshMarkersAndPlugins()
{
- m_markers.archiveLooseOverwrite = overwrite;
- m_markers.archiveLooseOverwritten = overwritten;
-}
+ QModelIndexList indexes = selectionModel()->selectedRows();
-void ModListView::setOverwriteMarkers(ModInfo::Ptr mod)
-{
- if (mod) {
- setOverwriteMarkers(mod->getModOverwrite(), mod->getModOverwritten());
- setArchiveOverwriteMarkers(mod->getModArchiveOverwrite(), mod->getModArchiveOverwritten());
- setArchiveLooseOverwriteMarkers(mod->getModArchiveLooseOverwrite(), mod->getModArchiveLooseOverwritten());
+ if (m_core->settings().interface().collapsibleSeparatorsConflicts()) {
+ for (auto& idx : selectionModel()->selectedRows()) {
+ if (hasCollapsibleSeparators()
+ && model()->hasChildren(idx)
+ && !isExpanded(idx)) {
+ for (int i = 0; i < model()->rowCount(idx); ++i) {
+ indexes.append(model()->index(i, idx.column(), idx));
+ }
+ }
+ }
}
- else {
- setOverwriteMarkers({}, {});
- setArchiveOverwriteMarkers({}, {});
- setArchiveLooseOverwriteMarkers({}, {});
+
+ setOverwriteMarkers(indexes);
+
+ // highligth plugins
+ std::vector<unsigned int> modIndices;
+ for (auto& idx : indexes) {
+ modIndices.push_back(idx.data(ModList::IndexRole).toInt());
}
- dataChanged(model()->index(0, 0), model()->index(model()->rowCount(), model()->columnCount()));
- verticalScrollBar()->repaint();
+ m_core->pluginList()->highlightPlugins(modIndices, *m_core->directoryStructure());
+ ui.pluginList->verticalScrollBar()->repaint();
}
+
void ModListView::setHighlightedMods(const std::vector<unsigned int>& pluginIndices)
{
m_markers.highlight.clear();
@@ -1087,6 +1132,37 @@ QColor ModListView::markerColor(const QModelIndex& index) const
return QColor();
}
+std::vector<ModInfo::EFlag> ModListView::modFlags(const QModelIndex& index, bool* forceCompact) const
+{
+ ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt());
+
+ auto flags = info->getFlags();
+ bool compact = false;
+ if (info->isSeparator()
+ && hasCollapsibleSeparators()
+ && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && !isExpanded(index.sibling(index.row(), 0))) {
+
+ // combine the child conflicts
+ std::set eFlags(flags.begin(), flags.end());
+ for (int i = 0; i < model()->rowCount(index); ++i) {
+ auto cIndex = model()->index(i, index.column(), index).data(ModList::IndexRole).toInt();
+ auto cFlags = ModInfo::getByIndex(cIndex)->getFlags();
+ eFlags.insert(cFlags.begin(), cFlags.end());
+ }
+ flags = { eFlags.begin(), eFlags.end() };
+
+ // force compact because there can be a lots of flags here
+ compact = true;
+ }
+
+ if (forceCompact) {
+ *forceCompact = true;
+ }
+
+ return flags;
+}
+
std::vector<ModInfo::EConflictFlag> ModListView::conflictFlags(const QModelIndex& index, bool* forceCompact) const
{
ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt());
@@ -1118,25 +1194,64 @@ std::vector<ModInfo::EConflictFlag> ModListView::conflictFlags(const QModelIndex
return flags;
}
-void ModListView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
+std::set<int> ModListView::contents(const QModelIndex& index, bool* includeChildren) const
{
- if (hasCollapsibleSeparators()) {
- for (auto& idx : selected.indexes()) {
- if (idx.parent().isValid() && !isExpanded(idx.parent())) {
- setExpanded(idx.parent(), true);
- }
- }
+ auto modIndex = index.data(ModList::IndexRole);
+ if (!modIndex.isValid()) {
+ return {};
}
+ ModInfo::Ptr info = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt());
+ auto contents = info->getContents();
+ bool children = false;
+
+ if (info->isSeparator()
+ && hasCollapsibleSeparators()
+ && m_core->settings().interface().collapsibleSeparatorsConflicts()
+ && !isExpanded(index.sibling(index.row(), 0))) {
- if (selected.count()) {
- auto index = selected.indexes().last();
- ModInfo::Ptr selectedMod = ModInfo::getByIndex(index.data(ModList::IndexRole).toInt());
- setOverwriteMarkers(selectedMod);
+ // combine the child contents
+ std::set eContents(contents.begin(), contents.end());
+ for (int i = 0; i < model()->rowCount(index); ++i) {
+ auto cIndex = model()->index(i, index.column(), index).data(ModList::IndexRole).toInt();
+ auto cContents = ModInfo::getByIndex(cIndex)->getContents();
+ eContents.insert(cContents.begin(), cContents.end());
+ }
+ contents = { eContents.begin(), eContents.end() };
+ children = true;
}
- else {
- setOverwriteMarkers(nullptr);
+
+ if (includeChildren) {
+ *includeChildren = children;
}
+ return contents;
+}
+
+QList<QString> ModListView::contentsIcons(const QModelIndex& index, bool* forceCompact) const
+{
+ auto contents = this->contents(index, forceCompact);
+ QList<QString> result;
+ m_core->modDataContents().forEachContentInOrOut(
+ contents,
+ [&result](auto const& content) { result.append(content.icon()); },
+ [&result](auto const&) { result.append(QString()); });
+ return result;
+}
+
+QString ModListView::contentsTooltip(const QModelIndex& index) const
+{
+ auto contents = this->contents(index, nullptr);
+ if (contents.empty()) {
+ return {};
+ }
+ QString result("<table cellspacing=7>");
+ m_core->modDataContents().forEachContentIn(contents, [&result](auto const& content) {
+ result.append(QString("<tr><td><img src=\"%1\" width=32/></td>"
+ "<td valign=\"middle\">%2</td></tr>")
+ .arg(content.icon()).arg(content.name()));
+ });
+ result.append("</table>");
+ return result;
}
void ModListView::onFiltersCriteria(const std::vector<ModListSortProxy::Criteria>& criteria)
@@ -1204,10 +1319,18 @@ void ModListView::dragMoveEvent(QDragMoveEvent* event)
void ModListView::dropEvent(QDropEvent* event)
{
+ // from Qt source
+ QModelIndex index;
+ if (viewport()->rect().contains(event->pos())) {
+ index = indexAt(event->pos());
+ if (!index.isValid() || !visualRect(index).contains(event->pos()))
+ index = QModelIndex();
+ }
+
// this event is used by the byPriorityProxy to know if allow
// dropping mod between a separator and its first mod (there
// is no way to deduce this except using dropIndicatorPosition())
- emit dropEntered(event->mimeData(), static_cast<DropPosition>(dropIndicatorPosition()));
+ emit dropEntered(event->mimeData(), isExpanded(index), static_cast<DropPosition>(dropIndicatorPosition()));
// see selectedIndexes()
m_inDragMoveEvent = true;
@@ -1232,6 +1355,79 @@ void ModListView::timerEvent(QTimerEvent* event)
}
}
+void ModListView::mousePressEvent(QMouseEvent* event)
+{
+ // allow alt+click to select all mods inside a separator
+ // when using collapsible separators
+ //
+ // similar code is also present in mouseReleaseEvent to
+ // avoid missing events
+
+ // disable edit if Alt is pressed
+ auto triggers = editTriggers();
+ if (event->modifiers() & Qt::AltModifier) {
+ setEditTriggers(NoEditTriggers);
+ }
+
+ // we call the parent class first so that we can use the actual
+ // selection state of the item after
+ QTreeView::mousePressEvent(event);
+
+ // restore triggers
+ setEditTriggers(triggers);
+
+ const auto index = indexAt(event->pos());
+
+ if (event->isAccepted()
+ && hasCollapsibleSeparators()
+ && index.isValid() && model()->hasChildren(indexAt(event->pos()))
+ && (event->modifiers() & Qt::AltModifier)) {
+
+ const auto flag = selectionModel()->isSelected(index) ?
+ QItemSelectionModel::Select : QItemSelectionModel::Deselect;
+ const QItemSelection selection(
+ model()->index(0, index.column(), index),
+ model()->index(model()->rowCount(index) - 1, index.column(), index));
+ selectionModel()->select(selection, flag | QItemSelectionModel::Rows);
+ }
+}
+
+void ModListView::mouseReleaseEvent(QMouseEvent* event)
+{
+ // this is a duplicate of mousePressEvent because for some reason
+ // the selection is not always triggered in mousePressEvent and only
+ // doing it here create a small lag between the selection of the
+ // separator and the children
+
+ // disable edit if Alt is pressed
+ auto triggers = editTriggers();
+ if (event->modifiers() & Qt::AltModifier) {
+ setEditTriggers(NoEditTriggers);
+ }
+
+ // we call the parent class first so that we can use the actual
+ // selection state of the item after
+ QTreeView::mouseReleaseEvent(event);
+
+ const auto index = indexAt(event->pos());
+
+ // restore triggers
+ setEditTriggers(triggers);
+
+ if (event->isAccepted()
+ && hasCollapsibleSeparators()
+ && index.isValid() && model()->hasChildren(indexAt(event->pos()))
+ && (event->modifiers() & Qt::AltModifier)) {
+
+ const auto flag = selectionModel()->isSelected(index) ?
+ QItemSelectionModel::Select : QItemSelectionModel::Deselect;
+ const QItemSelection selection(
+ model()->index(0, index.column(), index),
+ model()->index(model()->rowCount(index) - 1, index.column(), index));
+ selectionModel()->select(selection, flag | QItemSelectionModel::Rows);
+ }
+}
+
bool ModListView::event(QEvent* event)
{
if (event->type() == QEvent::KeyPress
diff --git a/src/modlistview.h b/src/modlistview.h
index 2fed4969..53c71458 100644
--- a/src/modlistview.h
+++ b/src/modlistview.h
@@ -23,6 +23,7 @@ class MainWindow;
class Profile;
class ModListByPriorityProxy;
class ModListViewActions;
+class PluginListView;
class ModListView : public QTreeView
{
@@ -56,10 +57,6 @@ public:
void restoreState(const Settings& s);
void saveState(Settings& s) const;
- // set the current profile
- //
- void setProfile(Profile* profile);
-
// check if collapsible separators are currently used
//
bool hasCollapsibleSeparators() const;
@@ -82,19 +79,30 @@ public:
std::optional<unsigned int> nextMod(unsigned int index) const;
std::optional<unsigned int> prevMod(unsigned int index) const;
- // check if the given mod is visible
+ // check if the given mod is visible, i.e. not filtered (returns true
+ // for collapsed mods)
//
bool isModVisible(unsigned int index) const;
bool isModVisible(ModInfo::Ptr mod) const;
+ // focus the view, select the given index and scroll to it
+ //
+ void scrollToAndSelect(const QModelIndex& index);
+
// refresh the view (to call when settings have been changed)
//
void refresh();
signals:
+ // emitted for dragEnter events
+ //
void dragEntered(const QMimeData* mimeData);
- void dropEntered(const QMimeData* mimeData, DropPosition position);
+
+ // emitted for dropEnter events, the boolean indicates if the drop target
+ // is expanded and the position of the indicator
+ //
+ void dropEntered(const QMimeData* mimeData, bool dropExpanded, DropPosition position);
public slots:
@@ -161,44 +169,63 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
+ void mousePressEvent(QMouseEvent* event) override;
+ void mouseReleaseEvent(QMouseEvent* event) override;
bool event(QEvent* event) override;
protected slots:
void onCustomContextMenuRequested(const QPoint& pos);
void onDoubleClicked(const QModelIndex& index);
- void onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
void onFiltersCriteria(const std::vector<ModListSortProxy::Criteria>& filters);
+ void onProfileChanged(Profile* oldProfile, Profile* newProfile);
private:
friend class ModConflictIconDelegate;
- friend class ModListStyledItemDelegated;
+ friend class ModFlagIconDelegate;
+ friend class ModContentIconDelegate;
+ friend class ModListStyledItemDelegate;
friend class ModListViewMarkingScrollBar;
void onModPrioritiesChanged(const QModelIndexList& indices);
void onModInstalled(const QString& modName);
void onModFilterActive(bool filterActive);
- // overwrite markers
+ // refresh the overwrite markers and the highligthed plugins from
+ // the current selection
+ //
+ void refreshMarkersAndPlugins();
+
+ // clear overwrite markers (without repainting)
+ //
void clearOverwriteMarkers();
- void setOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten);
- void setArchiveOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten);
- void setArchiveLooseOverwriteMarkers(const std::set<unsigned int>& overwrite, const std::set<unsigned int>& overwritten);
- // set overwrite markers from the mod and repaint (if mod is nullptr, clear overwrite and repaint)
+ // set overwrite markers from the mod in the given list and repaint (if the list
+ // is empty, clear overwrite and repaint)
//
- void setOverwriteMarkers(ModInfo::Ptr mod);
+ void setOverwriteMarkers(const QModelIndexList& indexes);
// retrieve the marker color for the given index
//
QColor markerColor(const QModelIndex& index) const;
+ // retrieve the mod flags for the given index
+ //
+ std::vector<ModInfo::EFlag> modFlags(
+ const QModelIndex& index, bool* forceCompact = nullptr) const;
+
// retrieve the conflicts flags for the given index
//
std::vector<ModInfo::EConflictFlag> conflictFlags(
const QModelIndex& index, bool* forceCompact = nullptr) const;
+ // retrieve the content icons and tooltip for the given index
+ //
+ std::set<int> contents(const QModelIndex& index, bool* includeChildren) const;
+ QList<QString> contentsIcons(const QModelIndex& index, bool* forceCompact = nullptr) const;
+ QString contentsTooltip(const QModelIndex& index) const;
+
// get/set the selected items on the view, this method return/take indices
// from the mod list model, not the view, so it's safe to restore
//
@@ -237,6 +264,9 @@ private:
QLabel* currentCategory;
QPushButton* clearFilters;
QComboBox* filterSeparators;
+
+ // the plugin list (for highligths)
+ PluginListView* pluginList;
};
OrganizerCore* m_core;
@@ -250,6 +280,10 @@ private:
QtGroupingProxy* m_byCategoryProxy;
QtGroupingProxy* m_byNexusIdProxy;
+ // marker used to avoid calling refreshing markers to many
+ // time in a row
+ QTimer m_refreshMarkersTimer;
+
// maintain collapsed items for each proxy to avoid
// losing them on model reset
std::map<QAbstractItemModel*, std::set<QString>> m_collapsed;
diff --git a/src/modlistviewactions.cpp b/src/modlistviewactions.cpp
index 9957618a..cc50f009 100644
--- a/src/modlistviewactions.cpp
+++ b/src/modlistviewactions.cpp
@@ -97,20 +97,44 @@ void ModListViewActions::createEmptyMod(const QModelIndex& index) const
}
int newPriority = -1;
- if (index.isValid() && m_view->sortColumn() == ModList::COL_PRIORITY) {
- newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt());
+ if (index.isValid() && index.data(ModList::IndexRole).isValid()
+ && m_view->sortColumn() == ModList::COL_PRIORITY) {
+ auto mIndex = index.data(ModList::IndexRole).toInt();
+ auto info = ModInfo::getByIndex(mIndex);
+ newPriority = m_core.currentProfile()->getModPriority(mIndex);
+ if (info->isSeparator()) {
+ auto& ibp = m_core.currentProfile()->getAllIndexesByPriority();
+
+ // start right after 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()) {
+ newPriority = it->first;
+ break;
+ }
+ }
+
+ // no separator found, create at the end
+ if (it == ibp.end()) {
+ newPriority = -1;
+ }
+ }
}
- IModInterface* newMod = m_core.createMod(name);
- if (newMod == nullptr) {
+ if (m_core.createMod(name) == nullptr) {
return;
}
m_core.refresh();
+ const auto mIndex = ModInfo::getIndex(name);
if (newPriority >= 0) {
- m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority);
+ m_core.modList()->changeModPriority(mIndex, newPriority);
}
+
+ m_view->scrollToAndSelect(m_view->indexModelToView(m_core.modList()->index(mIndex, 0)));
}
void ModListViewActions::createSeparator(const QModelIndex& index) const
@@ -139,16 +163,22 @@ void ModListViewActions::createSeparator(const QModelIndex& index) const
newPriority = m_core.currentProfile()->getModPriority(index.data(ModList::IndexRole).toInt());
}
- if (m_core.createMod(name) == nullptr) { return; }
+ if (m_core.createMod(name) == nullptr) {
+ return;
+ }
+
m_core.refresh();
+ const auto mIndex = ModInfo::getIndex(name);
if (newPriority >= 0) {
- m_core.modList()->changeModPriority(ModInfo::getIndex(name), newPriority);
+ m_core.modList()->changeModPriority(mIndex, newPriority);
}
if (auto c = m_core.settings().colors().previousSeparatorColor()) {
- ModInfo::getByIndex(ModInfo::getIndex(name))->setColor(*c);
+ ModInfo::getByIndex(mIndex)->setColor(*c);
}
+
+ m_view->scrollToAndSelect(m_view->indexModelToView(m_core.modList()->index(mIndex, 0)));
}
void ModListViewActions::checkModsForUpdates() const
diff --git a/src/organizercore.cpp b/src/organizercore.cpp
index d952bc4c..90771c0a 100644
--- a/src/organizercore.cpp
+++ b/src/organizercore.cpp
@@ -602,6 +602,7 @@ void OrganizerCore::setCurrentProfile(const QString &profileName)
m_CurrentProfile->debugDump();
+ emit profileChanged(oldProfile.get(), m_CurrentProfile.get());
m_ProfileChanged(oldProfile.get(), m_CurrentProfile.get());
}
diff --git a/src/organizercore.h b/src/organizercore.h
index 24de26ee..905fb111 100644
--- a/src/organizercore.h
+++ b/src/organizercore.h
@@ -406,6 +406,13 @@ signals:
void close();
+ // emitted when the profile is changed, before notifying plugins
+ //
+ // the new profile can be stored but the old one is temporary and
+ // should not be
+ //
+ void profileChanged(Profile* oldProfile, Profile* newProfile);
+
// Notify that the directory structure is ready to be used on the main thread
// Use queued connections
void directoryStructureReady();
diff --git a/src/settings.cpp b/src/settings.cpp
index e04cd0c1..488a9b01 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1326,12 +1326,12 @@ void ColorSettings::setColorSeparatorScrollbar(bool b)
QColor ColorSettings::idealTextColor(const QColor& rBackgroundColor)
{
- if (rBackgroundColor.alpha() == 0)
+ if (rBackgroundColor.alpha() < 50)
return QColor(Qt::black);
- const int THRESHOLD = 106 * 255.0f / rBackgroundColor.alpha();
- int BackgroundDelta = (rBackgroundColor.red() * 0.299) + (rBackgroundColor.green() * 0.587) + (rBackgroundColor.blue() * 0.114);
- return QColor((255 - BackgroundDelta <= THRESHOLD) ? Qt::black : Qt::white);
+ // "inverse' of luminance of the background
+ int iLuminance = (rBackgroundColor.red() * 0.299) + (rBackgroundColor.green() * 0.587) + (rBackgroundColor.blue() * 0.114);
+ return QColor(iLuminance >= 128 ? Qt::black : Qt::white);
}
@@ -2195,6 +2195,26 @@ void InterfaceSettings::setCollapsibleSeparatorsConflicts(bool b)
set(m_Settings, "Settings", "collapsible_separators_conflicts", b);
}
+bool InterfaceSettings::collapsibleSeparatorsPerProfile() const
+{
+ return get<bool>(m_Settings, "Settings", "collapsible_separators_per_profile", false);
+}
+
+void InterfaceSettings::setCollapsibleSeparatorsPerProfile(bool b)
+{
+ set(m_Settings, "Settings", "collapsible_separators_per_profile", b);
+}
+
+bool InterfaceSettings::saveFilters() const
+{
+ return get<bool>(m_Settings, "Settings", "save_filters", false);
+}
+
+void InterfaceSettings::setSaveFilters(bool b)
+{
+ set(m_Settings, "Settings", "save_filters", b);
+}
+
bool InterfaceSettings::compactDownloads() const
{
return get<bool>(m_Settings, "Settings", "compact_downloads", false);
diff --git a/src/settings.h b/src/settings.h
index 043b22a4..efcfee57 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -631,6 +631,16 @@ public:
bool collapsibleSeparatorsConflicts() const;
void setCollapsibleSeparatorsConflicts(bool b);
+ // whether each profile should have its own expansion state
+ //
+ bool collapsibleSeparatorsPerProfile() const;
+ void setCollapsibleSeparatorsPerProfile(bool b);
+
+ // whether to save/restore filter states between runs
+ //
+ bool saveFilters() const;
+ void setSaveFilters(bool b);
+
// whether to show compact downloads
//
bool compactDownloads() const;
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index c4a53fcd..d6b7ceec 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -25,6 +25,7 @@ along with Mod Organizer. If not, see <http://www.gnu.org/licenses/>.
#include "settingsdialogpaths.h"
#include "settingsdialogplugins.h"
#include "settingsdialogsteam.h"
+#include "settingsdialoguserinterface.h"
#include "settingsdialogworkarounds.h"
using namespace MOBase;
@@ -39,6 +40,7 @@ SettingsDialog::SettingsDialog(PluginContainer *pluginContainer, Settings& setti
ui->setupUi(this);
m_tabs.push_back(std::unique_ptr<SettingsTab>(new GeneralSettingsTab(settings, *this)));
+ m_tabs.push_back(std::unique_ptr<SettingsTab>(new UserInterfaceSettingsTab(settings, *this)));
m_tabs.push_back(std::unique_ptr<SettingsTab>(new PathsSettingsTab(settings, *this)));
m_tabs.push_back(std::unique_ptr<SettingsTab>(new DiagnosticsSettingsTab(settings, *this)));
m_tabs.push_back(std::unique_ptr<SettingsTab>(new NexusSettingsTab(settings, *this)));
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index ae4f4f34..d5c93c5c 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>820</width>
- <height>652</height>
+ <height>651</height>
</rect>
</property>
<property name="windowTitle">
@@ -23,434 +23,486 @@
<attribute name="title">
<string>General</string>
</attribute>
- <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,1,0">
- <item row="6" column="0" colspan="2">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
+ <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0,0,0,0,0">
+ <item row="5" column="0">
+ <widget class="QPushButton" name="resetDialogsButton">
+ <property name="maximumSize">
<size>
- <width>0</width>
- <height>0</height>
+ <width>16777215</width>
+ <height>16777215</height>
</size>
</property>
- </spacer>
+ <property name="toolTip">
+ <string>Reset all choices made in dialogs.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Reset all choices made in dialogs.</string>
+ </property>
+ <property name="text">
+ <string>Reset Dialog Choices</string>
+ </property>
+ </widget>
</item>
- <item row="2" column="1">
- <widget class="QGroupBox" name="groupBox_6">
- <property name="title">
- <string>Updates</string>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="changeGameConfirmation">
+ <property name="text">
+ <string>Show confirmation when changing instance</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <widget class="QCheckBox" name="checkForUpdates">
- <property name="toolTip">
- <string>Check for Mod Organizer updates on Github on startup.</string>
- </property>
- <property name="whatsThis">
- <string>Check for Mod Organizer updates on Github on startup.</string>
- </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QWidget" name="widget_6" native="true">
+ <layout class="QGridLayout" name="gridLayout_5" columnstretch="0,1,0">
+ <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 row="0" column="0">
+ <widget class="QLabel" name="label_5">
<property name="text">
- <string>Check for updates</string>
+ <string>Language</string>
</property>
</widget>
</item>
- <item>
- <widget class="QCheckBox" name="usePrereleaseBox">
- <property name="toolTip">
- <string>Update to non-stable releases.</string>
- </property>
- <property name="whatsThis">
- <string>Update to non-stable releases.</string>
- </property>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_10">
<property name="text">
- <string>Install Pre-releases (Betas)</string>
+ <string>Style</string>
</property>
</widget>
</item>
- <item>
- <spacer name="verticalSpacer_9">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="0" rowspan="3">
- <widget class="QGroupBox" name="groupBox">
- <property name="title">
- <string>User Interface</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_14">
- <item>
- <widget class="QWidget" name="widget_6" native="true">
- <layout class="QGridLayout" name="gridLayout_5" columnstretch="0,1,0">
- <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 row="0" column="0">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Language</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>Style</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QComboBox" name="styleBox">
- <property name="toolTip">
- <string>Visual theme of the user interface.</string>
- </property>
- <property name="whatsThis">
- <string>Visual theme of the user interface.</string>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QPushButton" name="exploreStyles">
- <property name="text">
- <string>Explore...</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1" colspan="2">
- <widget class="QComboBox" name="languageBox">
- <property name="toolTip">
- <string>The language of the user interface.</string>
- </property>
- <property name="whatsThis">
- <string>The language of the user interface.</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1" colspan="2">
- <widget class="LinkLabel" name="label_33">
- <property name="toolTip">
- <string>https://www.transifex.com/mod-organizer-2-team/mod-organizer-2/</string>
- </property>
- <property name="text">
- <string>&lt;a href=&quot;https://www.transifex.com/mod-organizer-2-team/mod-organizer-2/&quot;&gt;Help translate Mod Organizer&lt;/a&gt;</string>
- </property>
- <property name="openExternalLinks">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="centerDialogs">
+ <item row="2" column="1">
+ <widget class="QComboBox" name="styleBox">
<property name="toolTip">
- <string>Dialogs will always be centered on the main window, but will remember their size.</string>
+ <string>Visual theme of the user interface.</string>
</property>
<property name="whatsThis">
- <string>Dialogs will always be centered on the main window, but will remember their size.</string>
- </property>
- <property name="text">
- <string>Always center dialogs</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="changeGameConfirmation">
- <property name="text">
- <string>Show confirmation when changing instance</string>
+ <string>Visual theme of the user interface.</string>
</property>
</widget>
</item>
- <item>
- <widget class="QCheckBox" name="doubleClickPreviews">
- <property name="toolTip">
- <string>Whether double-clicking on a file opens the preview window or launches the program associated with it. This applies to the Data tab as well as the Conflicts and Filetree tabs in the mod info window.</string>
- </property>
+ <item row="2" column="2">
+ <widget class="QPushButton" name="exploreStyles">
<property name="text">
- <string>Open previews on double-click</string>
+ <string>Explore...</string>
</property>
</widget>
</item>
- <item>
- <widget class="QPushButton" name="resetDialogsButton">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>16777215</height>
- </size>
- </property>
+ <item row="0" column="1" colspan="2">
+ <widget class="QComboBox" name="languageBox">
<property name="toolTip">
- <string>Reset all choices made in dialogs.</string>
+ <string>The language of the user interface.</string>
</property>
<property name="whatsThis">
- <string>Reset all choices made in dialogs.</string>
- </property>
- <property name="text">
- <string>Reset Dialog Choices</string>
+ <string>The language of the user interface.</string>
</property>
</widget>
</item>
- <item>
- <widget class="QPushButton" name="categoriesBtn">
+ <item row="1" column="1" colspan="2">
+ <widget class="LinkLabel" name="label_33">
<property name="toolTip">
- <string>Modify the categories available to arrange your mods.</string>
- </property>
- <property name="whatsThis">
- <string>Modify the categories available to arrange your mods.</string>
+ <string>https://www.transifex.com/mod-organizer-2-team/mod-organizer-2/</string>
</property>
<property name="text">
- <string>Configure Mod Categories</string>
+ <string>&lt;a href=&quot;https://www.transifex.com/mod-organizer-2-team/mod-organizer-2/&quot;&gt;Help translate Mod Organizer&lt;/a&gt;</string>
</property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_11">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
+ <property name="openExternalLinks">
+ <bool>true</bool>
</property>
- </spacer>
+ </widget>
</item>
</layout>
</widget>
</item>
- <item row="0" column="1">
- <widget class="QGroupBox" name="groupBox_5">
+ <item row="6" column="0">
+ <widget class="QPushButton" name="categoriesBtn">
+ <property name="toolTip">
+ <string>Modify the categories available to arrange your mods.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Modify the categories available to arrange your mods.</string>
+ </property>
+ <property name="text">
+ <string>Configure Mod Categories</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="centerDialogs">
+ <property name="toolTip">
+ <string>Dialogs will always be centered on the main window, but will remember their size.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Dialogs will always be centered on the main window, but will remember their size.</string>
+ </property>
+ <property name="text">
+ <string>Always center dialogs</string>
+ </property>
+ </widget>
+ </item>
+ <item row="8" column="0" colspan="2">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="doubleClickPreviews">
+ <property name="toolTip">
+ <string>Whether double-clicking on a file opens the preview window or launches the program associated with it. This applies to the Data tab as well as the Conflicts and Filetree tabs in the mod info window.</string>
+ </property>
+ <property name="text">
+ <string>Open previews on double-click</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QGroupBox" name="groupBox_6">
<property name="title">
- <string>Download List</string>
+ <string>Updates</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_2">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <widget class="QCheckBox" name="showMetaBox">
+ <widget class="QCheckBox" name="checkForUpdates">
<property name="toolTip">
- <string>Show meta information instead of file names in the download list.</string>
+ <string>Check for Mod Organizer updates on Github on startup.</string>
</property>
<property name="whatsThis">
- <string>Show meta information instead of file names in the download list.</string>
+ <string>Check for Mod Organizer updates on Github on startup.</string>
</property>
<property name="text">
- <string>Show Meta Information</string>
+ <string>Check for updates</string>
</property>
</widget>
</item>
<item>
- <widget class="QCheckBox" name="compactBox">
+ <widget class="QCheckBox" name="usePrereleaseBox">
<property name="toolTip">
- <string>Make the download list more compact.</string>
+ <string>Update to non-stable releases.</string>
</property>
<property name="whatsThis">
- <string>Make the download list more compact.</string>
+ <string>Update to non-stable releases.</string>
</property>
<property name="text">
- <string>Compact List</string>
+ <string>Install Pre-releases (Betas)</string>
</property>
</widget>
</item>
- <item>
- <spacer name="verticalSpacer_5">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
</layout>
</widget>
</item>
- <item row="4" column="0" colspan="2">
- <widget class="QGroupBox" name="ModlistGroupBox">
- <property name="title">
- <string>Colors</string>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="uiTab">
+ <property name="accessibleName">
+ <string/>
+ </property>
+ <attribute name="title">
+ <string>User Interface</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_19">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_13">
- <item>
- <widget class="ColorTable" name="colorTable">
- <property name="editTriggers">
- <set>QAbstractItemView::NoEditTriggers</set>
- </property>
- <property name="selectionMode">
- <enum>QAbstractItemView::SingleSelection</enum>
- </property>
- <property name="selectionBehavior">
- <enum>QAbstractItemView::SelectRows</enum>
- </property>
- <property name="verticalScrollMode">
- <enum>QAbstractItemView::ScrollPerPixel</enum>
- </property>
- <property name="cornerButtonEnabled">
- <bool>false</bool>
- </property>
- <attribute name="horizontalHeaderVisible">
- <bool>false</bool>
- </attribute>
- <attribute name="horizontalHeaderHighlightSections">
- <bool>false</bool>
- </attribute>
- <attribute name="horizontalHeaderStretchLastSection">
- <bool>true</bool>
- </attribute>
- <attribute name="verticalHeaderVisible">
- <bool>false</bool>
- </attribute>
- </widget>
- </item>
- <item>
- <widget class="QWidget" name="widget_5" native="true">
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <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="QCheckBox" name="colorSeparatorsBox">
- <property name="toolTip">
- <string>Colors set on separators will also be shown in the mod list scrollbar at the location of the separator. This can be useful for quickly navigating to a specific separator.</string>
- </property>
- <property name="whatsThis">
- <string>Colors set on separators will also be shown in the mod list scrollbar at the location of the separator. This can be useful for quickly navigating to a specific separator.</string>
- </property>
- <property name="text">
- <string>Show mod list separator colors on the scrollbar</string>
- </property>
- <property name="checked">
- <bool>true</bool>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Mod List</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_17">
+ <item>
+ <widget class="QCheckBox" name="colorSeparatorsBox">
+ <property name="toolTip">
+ <string>Colors set on separators will also be shown in the mod list scrollbar at the location of the separator. This can be useful for quickly navigating to a specific separator.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Colors set on separators will also be shown in the mod list scrollbar at the location of the separator. This can be useful for quickly navigating to a specific separator.</string>
+ </property>
+ <property name="text">
+ <string>Show mod list separator colors on the scrollbar</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="displayForeignBox">
+ <property name="toolTip">
+ <string>Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content.</string>
+ </property>
+ <property name="whatsThis">
+ <string>By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods.
+However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly.
+
+If you disable this feature, MO will only display official DLCs this way. Please note that plugins (esps, esms, and esls) displayed in the right pane are completely unaffected by this feature.</string>
+ </property>
+ <property name="text">
+ <string>Display mods installed outside MO</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="saveFiltersBox">
+ <property name="toolTip">
+ <string>Save the current filters when closing MO2 and restore them on startup.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Save the current filters when closing MO2 and restore them on startup.</string>
+ </property>
+ <property name="text">
+ <string>Remember selected filters after restarting MO</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_9">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="collapsibleSeparatorsBox">
+ <property name="title">
+ <string>Collapsible Separators</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_20">
+ <property name="leftMargin">
+ <number>7</number>
</property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <property name="rightMargin">
+ <number>7</number>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
+ <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>
+ </property>
+ <property name="whatsThis">
+ <string>Display mod conflicts on and from separator when collapsed, and show plugins from collapsed separators.</string>
+ </property>
+ <property name="text">
+ <string>Show conflicts and plugins on separators and from separators</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="collapsibleSeparatorsPerProfileBox">
+ <property name="toolTip">
+ <string>Do not share the collapse/expanded state of separators between profiles.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Do not share the collapse/expanded state of separators between profiles.</string>
+ </property>
+ <property name="text">
+ <string>Profile-specific collapse states for separators</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_12">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Expanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_5">
+ <property name="title">
+ <string>Download List</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QCheckBox" name="showMetaBox">
+ <property name="toolTip">
+ <string>Show meta information instead of file names in the download list.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Show meta information instead of file names in the download list.</string>
+ </property>
+ <property name="text">
+ <string>Show Meta Information</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="compactBox">
+ <property name="toolTip">
+ <string>Make the download list more compact.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Make the download list more compact.</string>
+ </property>
+ <property name="text">
+ <string>Compact List</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayout_18">
+ <item>
+ <widget class="QGroupBox" name="ModlistGroupBox">
+ <property name="title">
+ <string>Colors</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <item>
+ <widget class="ColorTable" name="colorTable">
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="verticalScrollMode">
+ <enum>QAbstractItemView::ScrollPerPixel</enum>
+ </property>
+ <property name="cornerButtonEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderHighlightSections">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget_5" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>0</number>
</property>
- </spacer>
- </item>
- <item>
- <widget class="QPushButton" name="resetColorsBtn">
- <property name="toolTip">
- <string>Reset all colors to their default value.</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <property name="whatsThis">
- <string>Reset all colors to their default value.</string>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- <property name="text">
- <string>Reset Colors</string>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
+ <item>
+ <widget class="QPushButton" name="resetColorsBtn">
+ <property name="toolTip">
+ <string>Reset all colors to their default value.</string>
+ </property>
+ <property name="whatsThis">
+ <string>Reset all colors to their default value.</string>
+ </property>
+ <property name="text">
+ <string>Reset Colors</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="1" column="1">
- <widget class="QGroupBox" name="groupBox_4">
- <property name="minimumSize">
+ <item>
+ <spacer name="verticalSpacer_13">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
<size>
- <width>0</width>
- <height>0</height>
+ <width>20</width>
+ <height>40</height>
</size>
</property>
- <property name="title">
- <string>Mod List</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_17">
- <item>
- <widget class="QCheckBox" name="collapsibleSeparatorsBox">
- <property name="toolTip">
- <string>Allow collapsing separators when sorting by ascending priority.</string>
- </property>
- <property name="text">
- <string>Use collapsible separators</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- <property name="tristate">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QCheckBox" name="collapsibleSeparatorsConflictsBox">
- <property name="toolTip">
- <string>Display mod conflicts on separator when collapsed.</string>
- </property>
- <property name="whatsThis">
- <string>Display mod conflicts on separator when collapsed.</string>
- </property>
- <property name="text">
- <string>Show conflicts on separators</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer_12">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Expanding</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
+ </spacer>
</item>
</layout>
</widget>
@@ -1520,25 +1572,6 @@ I don't yet know what the circumstances are, but user reports imply it is in som
</property>
</widget>
</item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="displayForeignBox">
- <property name="toolTip">
- <string>Disable this to no longer display mods installed outside MO in the mod list (left pane). Assets from those mods will then be treated as having lowest mod priority together with the original game content.</string>
- </property>
- <property name="whatsThis">
- <string>By default Mod Organizer will display esp+bsa bundles installed with foreign tools as mods (left pane). This allows you to control their priority in relation to other mods. This is particularly useful if you also use Steam Workshop to install mods.
-However, if you installed loose file mods outside MO which conflict with BSAs also installed outside MO those conflicts can't be resolved correctly.
-
-If you disable this feature, MO will only display official DLCs this way. Please note that plugins (esps, esms, and esls) displayed in the right pane are completely unaffected by this feature.</string>
- </property>
- <property name="text">
- <string>Display mods installed outside MO</string>
- </property>
- <property name="checked">
- <bool>true</bool>
- </property>
- </widget>
- </item>
<item row="1" column="1">
<widget class="QCheckBox" name="forceEnableBox">
<property name="toolTip">
@@ -1572,7 +1605,7 @@ Uncheck this if you want to use Mod Organizer with total conversions (like Nehri
</property>
</widget>
</item>
- <item row="3" column="0">
+ <item row="2" column="1">
<widget class="QCheckBox" name="enableArchiveParsingBox">
<property name="toolTip">
<string>Enable parsing of Archives. This is an Experimental Feature. Has negative effects on performance and known incorrectness.</string>
@@ -1824,16 +1857,7 @@ programs you are intentionally running.</string>
<tabstop>tabWidget</tabstop>
<tabstop>languageBox</tabstop>
<tabstop>styleBox</tabstop>
- <tabstop>centerDialogs</tabstop>
- <tabstop>resetDialogsButton</tabstop>
- <tabstop>categoriesBtn</tabstop>
- <tabstop>showMetaBox</tabstop>
- <tabstop>compactBox</tabstop>
- <tabstop>checkForUpdates</tabstop>
<tabstop>usePrereleaseBox</tabstop>
- <tabstop>colorTable</tabstop>
- <tabstop>colorSeparatorsBox</tabstop>
- <tabstop>resetColorsBtn</tabstop>
<tabstop>baseDirEdit</tabstop>
<tabstop>browseBaseDirBtn</tabstop>
<tabstop>downloadDirEdit</tabstop>
@@ -1869,8 +1893,6 @@ programs you are intentionally running.</string>
<tabstop>hideUncheckedBox</tabstop>
<tabstop>forceEnableBox</tabstop>
<tabstop>lockGUIBox</tabstop>
- <tabstop>displayForeignBox</tabstop>
- <tabstop>enableArchiveParsingBox</tabstop>
<tabstop>bsaDateBtn</tabstop>
<tabstop>execBlacklistBtn</tabstop>
<tabstop>resetGeometryBtn</tabstop>
diff --git a/src/settingsdialoggeneral.cpp b/src/settingsdialoggeneral.cpp
index 7b854260..11294d2a 100644
--- a/src/settingsdialoggeneral.cpp
+++ b/src/settingsdialoggeneral.cpp
@@ -17,23 +17,11 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d)
addStyles();
selectStyle();
- ui->colorTable->load(s);
-
- // connect before setting to trigger
- QObject::connect(ui->collapsibleSeparatorsBox, &QCheckBox::stateChanged, [=](auto&& state) {
- ui->collapsibleSeparatorsConflictsBox->setEnabled(state == Qt::Checked);
- });
-
ui->centerDialogs->setChecked(settings().geometry().centerDialogs());
ui->changeGameConfirmation->setChecked(settings().interface().showChangeGameConfirmation());
ui->doubleClickPreviews->setChecked(settings().interface().doubleClicksOpenPreviews());
- ui->compactBox->setChecked(settings().interface().compactDownloads());
- ui->showMetaBox->setChecked(settings().interface().metaDownloads());
ui->checkForUpdates->setChecked(settings().checkForUpdates());
ui->usePrereleaseBox->setChecked(settings().usePrereleases());
- ui->colorSeparatorsBox->setChecked(settings().colors().colorSeparatorScrollbar());
- ui->collapsibleSeparatorsConflictsBox->setChecked(settings().interface().collapsibleSeparatorsConflicts());
- ui->collapsibleSeparatorsBox->setChecked(settings().interface().collapsibleSeparators());
QObject::connect(ui->exploreStyles, &QPushButton::clicked, [&]{ onExploreStyles(); });
@@ -41,9 +29,6 @@ GeneralSettingsTab::GeneralSettingsTab(Settings& s, SettingsDialog& d)
ui->categoriesBtn, &QPushButton::clicked, [&]{ onEditCategories(); });
QObject::connect(
- ui->resetColorsBtn, &QPushButton::clicked, [&]{ onResetColors(); });
-
- QObject::connect(
ui->resetDialogsButton, &QPushButton::clicked, [&]{ onResetDialogs(); });
}
@@ -67,18 +52,11 @@ void GeneralSettingsTab::update()
emit settings().styleChanged(newStyle);
}
- ui->colorTable->commitColors();
-
settings().geometry().setCenterDialogs(ui->centerDialogs->isChecked());
settings().interface().setShowChangeGameConfirmation(ui->changeGameConfirmation->isChecked());
settings().interface().setDoubleClicksOpenPreviews(ui->doubleClickPreviews->isChecked());
- settings().interface().setCompactDownloads(ui->compactBox->isChecked());
- settings().interface().setMetaDownloads(ui->showMetaBox->isChecked());
settings().setCheckForUpdates(ui->checkForUpdates->isChecked());
settings().setUsePrereleases(ui->usePrereleaseBox->isChecked());
- settings().colors().setColorSeparatorScrollbar(ui->colorSeparatorsBox->isChecked());
- settings().interface().setCollapsibleSeparators(ui->collapsibleSeparatorsBox->isChecked());
- settings().interface().setCollapsibleSeparatorsConflicts(ui->collapsibleSeparatorsConflictsBox->isChecked());
}
void GeneralSettingsTab::addLanguages()
@@ -204,11 +182,6 @@ void GeneralSettingsTab::onEditCategories()
}
}
-void GeneralSettingsTab::onResetColors()
-{
- ui->colorTable->resetColors();
-}
-
void GeneralSettingsTab::onResetDialogs()
{
const auto r = QMessageBox::question(
diff --git a/src/settingsdialoggeneral.h b/src/settingsdialoggeneral.h
index 455edcaf..51e0dc72 100644
--- a/src/settingsdialoggeneral.h
+++ b/src/settingsdialoggeneral.h
@@ -22,7 +22,6 @@ private:
void onExploreStyles();
void onEditCategories();
- void onResetColors();
void onResetDialogs();
};
diff --git a/src/settingsdialoguserinterface.cpp b/src/settingsdialoguserinterface.cpp
new file mode 100644
index 00000000..5d856395
--- /dev/null
+++ b/src/settingsdialoguserinterface.cpp
@@ -0,0 +1,56 @@
+#include "settingsdialoguserinterface.h"
+#include "ui_settingsdialog.h"
+#include "shared/appconfig.h"
+#include "categoriesdialog.h"
+#include "colortable.h"
+#include <utility.h>
+#include <questionboxmemory.h>
+
+using namespace MOBase;
+
+UserInterfaceSettingsTab::UserInterfaceSettingsTab(Settings& s, SettingsDialog& d)
+ : SettingsTab(s, d)
+{
+
+ // connect before setting to trigger
+ QObject::connect(ui->collapsibleSeparatorsBox, &QGroupBox::toggled, [=](auto&& on) {
+ ui->collapsibleSeparatorsConflictsBox->setEnabled(on);
+ ui->collapsibleSeparatorsPerProfileBox->setEnabled(on);
+ });
+
+ // 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->collapsibleSeparatorsPerProfileBox->setChecked(settings().interface().collapsibleSeparatorsPerProfile());
+ ui->saveFiltersBox->setChecked(settings().interface().saveFilters());
+
+ // download list
+ ui->compactBox->setChecked(settings().interface().compactDownloads());
+ ui->showMetaBox->setChecked(settings().interface().metaDownloads());
+
+ // colors
+ ui->colorTable->load(s);
+
+ QObject::connect(ui->resetColorsBtn, &QPushButton::clicked, [&] { ui->colorTable->resetColors(); });
+
+}
+
+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().setCollapsibleSeparatorsConflicts(ui->collapsibleSeparatorsConflictsBox->isChecked());
+ settings().interface().setCollapsibleSeparatorsPerProfile(ui->collapsibleSeparatorsPerProfileBox->isChecked());
+ settings().interface().setSaveFilters(ui->saveFiltersBox->isChecked());
+
+ // download list
+ settings().interface().setCompactDownloads(ui->compactBox->isChecked());
+ settings().interface().setMetaDownloads(ui->showMetaBox->isChecked());
+
+ // colors
+ ui->colorTable->commitColors();
+}
diff --git a/src/settingsdialoguserinterface.h b/src/settingsdialoguserinterface.h
new file mode 100644
index 00000000..8b4d48fa
--- /dev/null
+++ b/src/settingsdialoguserinterface.h
@@ -0,0 +1,15 @@
+#ifndef SETTINGSDIALOGUSERINTERFACE_H
+#define SETTINGSDIALOGUSERINTERFACE_H
+
+#include "settingsdialog.h"
+#include "settings.h"
+
+class UserInterfaceSettingsTab : public SettingsTab
+{
+public:
+ UserInterfaceSettingsTab(Settings& settings, SettingsDialog& dialog);
+
+ void update() override;
+};
+
+#endif // SETTINGSDIALOGGENERAL_H
diff --git a/src/settingsdialogworkarounds.cpp b/src/settingsdialogworkarounds.cpp
index 1c5fbe26..c050c722 100644
--- a/src/settingsdialogworkarounds.cpp
+++ b/src/settingsdialogworkarounds.cpp
@@ -24,7 +24,6 @@ WorkaroundsSettingsTab::WorkaroundsSettingsTab(Settings& s, SettingsDialog& d)
ui->hideUncheckedBox->setChecked(settings().game().hideUncheckedPlugins());
ui->forceEnableBox->setChecked(settings().game().forceEnableCoreFiles());
- ui->displayForeignBox->setChecked(settings().interface().displayForeign());
ui->lockGUIBox->setChecked(settings().interface().lockGUI());
ui->enableArchiveParsingBox->setChecked(settings().archiveParsing());
@@ -48,7 +47,6 @@ void WorkaroundsSettingsTab::update()
settings().game().setHideUncheckedPlugins(ui->hideUncheckedBox->isChecked());
settings().game().setForceEnableCoreFiles(ui->forceEnableBox->isChecked());
- settings().interface().setDisplayForeign(ui->displayForeignBox->isChecked());
settings().interface().setLockGUI(ui->lockGUIBox->isChecked());
settings().setArchiveParsing(ui->enableArchiveParsingBox->isChecked());
settings().setExecutablesBlacklist(m_ExecutableBlacklist);