diff --git a/apps/launcher/datafilespage.cpp b/apps/launcher/datafilespage.cpp index ce9891a818..a4fb7fb0d9 100644 --- a/apps/launcher/datafilespage.cpp +++ b/apps/launcher/datafilespage.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -202,8 +203,14 @@ Launcher::DataFilesPage::DataFilesPage(const Files::ConfigurationManager& cfg, C // the addons and don't want to get signals of the system doing it during startup. connect(mSelector, &ContentSelectorView::ContentSelector::signalAddonDataChanged, this, &DataFilesPage::slotAddonDataChanged); + + mReloadCellsTimer = new QTimer(this); + mReloadCellsTimer->setSingleShot(true); + mReloadCellsTimer->setInterval(200); + connect(mReloadCellsTimer, &QTimer::timeout, this, &DataFilesPage::onReloadCellsTimerTimeout); + // Call manually to indicate all changes to addon data during startup. - slotAddonDataChanged(); + onReloadCellsTimerTimeout(); } Launcher::DataFilesPage::~DataFilesPage() @@ -1001,6 +1008,11 @@ bool Launcher::DataFilesPage::showDeleteMessageBox(const QString& text) } void Launcher::DataFilesPage::slotAddonDataChanged() +{ + mReloadCellsTimer->start(); +} + +void Launcher::DataFilesPage::onReloadCellsTimerTimeout() { const ContentSelectorModel::ContentFileList items = mSelector->selectedFiles(); QStringList selectedFiles; diff --git a/apps/launcher/datafilespage.hpp b/apps/launcher/datafilespage.hpp index ac98743dab..ca40bd1319 100644 --- a/apps/launcher/datafilespage.hpp +++ b/apps/launcher/datafilespage.hpp @@ -17,6 +17,7 @@ class QSortFilterProxyModel; class QAbstractItemModel; class QMenu; +class QTimer; namespace Files { @@ -137,6 +138,7 @@ namespace Launcher std::mutex mReloadCellsMutex; std::condition_variable mStartReloadCells; std::thread mReloadCellsThread; + QTimer* mReloadCellsTimer; void addArchive(const QString& name, Qt::CheckState selected, int row = -1); void addArchivesFromDir(const QString& dir); @@ -151,6 +153,7 @@ namespace Launcher void addProfile(const QString& profile, bool setAsCurrent); void checkForDefaultProfile(); void populateFileViews(const QString& contentModelName); + void onReloadCellsTimerTimeout(); void reloadCells(); void refreshDataFilesView(); void updateNavMeshProgress(int minDataSize); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index b18bca98d5..41af96a630 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -193,15 +193,6 @@ namespace MWMechanics mLevel = level; } - void CreatureStats::modifyMagicEffects(const MagicEffects& effects) - { - bool recalc = effects.getOrDefault(ESM::MagicEffect::FortifyMaximumMagicka).getModifier() - != mMagicEffects.getOrDefault(ESM::MagicEffect::FortifyMaximumMagicka).getModifier(); - mMagicEffects.setModifiers(effects); - if (recalc) - recalculateMagicka(); - } - void CreatureStats::setAiSetting(AiSetting index, Stat value) { mAiSettings[static_cast>(index)] = value; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index f65e1a000f..02312385d7 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -155,9 +155,6 @@ namespace MWMechanics void setDynamic(int index, const DynamicStat& value); - /// Set Modifier for each magic effect according to \a effects. Does not touch Base values. - void modifyMagicEffects(const MagicEffects& effects); - void setAttackingOrSpell(bool attackingOrSpell) { mAttackingOrSpell = attackingOrSpell; } void setAttackType(std::string_view attackType) { mAttackType = attackType; } diff --git a/apps/openmw/mwmechanics/magiceffects.cpp b/apps/openmw/mwmechanics/magiceffects.cpp index c2afef7c0d..e4210ed8fc 100644 --- a/apps/openmw/mwmechanics/magiceffects.cpp +++ b/apps/openmw/mwmechanics/magiceffects.cpp @@ -121,11 +121,6 @@ namespace MWMechanics return *this; } - void MagicEffects::remove(const EffectKey& key) - { - mCollection.erase(key); - } - void MagicEffects::add(const EffectKey& key, const EffectParam& param) { Collection::iterator iter = mCollection.find(key); @@ -145,19 +140,6 @@ namespace MWMechanics mCollection[key].modifyBase(diff); } - void MagicEffects::setModifiers(const MagicEffects& effects) - { - for (Collection::iterator it = mCollection.begin(); it != mCollection.end(); ++it) - { - it->second.setModifier(effects.getOrDefault(it->first).getModifier()); - } - - for (Collection::const_iterator it = effects.begin(); it != effects.end(); ++it) - { - mCollection[it->first].setModifier(it->second.getModifier()); - } - } - EffectParam MagicEffects::getOrDefault(const EffectKey& key) const { return get(key).value_or(EffectParam()); @@ -174,40 +156,6 @@ namespace MWMechanics return std::nullopt; } - MagicEffects MagicEffects::diff(const MagicEffects& prev, const MagicEffects& now) - { - MagicEffects result; - - // adding/changing - for (Collection::const_iterator iter(now.begin()); iter != now.end(); ++iter) - { - Collection::const_iterator other = prev.mCollection.find(iter->first); - - if (other == prev.end()) - { - // adding - result.add(iter->first, iter->second); - } - else - { - // changing - result.add(iter->first, iter->second - other->second); - } - } - - // removing - for (Collection::const_iterator iter(prev.begin()); iter != prev.end(); ++iter) - { - Collection::const_iterator other = now.mCollection.find(iter->first); - if (other == now.end()) - { - result.add(iter->first, EffectParam() - iter->second); - } - } - - return result; - } - void MagicEffects::writeState(ESM::MagicEffects& state) const { for (const auto& [key, params] : mCollection) diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 4fe5d9fd4e..80b858644e 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -103,19 +103,12 @@ namespace MWMechanics void writeState(ESM::MagicEffects& state) const; void add(const EffectKey& key, const EffectParam& param); - void remove(const EffectKey& key); void modifyBase(const EffectKey& key, int diff); - /// Copy Modifier values from \a effects, but keep original mBase values. - void setModifiers(const MagicEffects& effects); - EffectParam getOrDefault(const EffectKey& key) const; std::optional get(const EffectKey& key) const; ///< This function can safely be used for keys that are not present. - - static MagicEffects diff(const MagicEffects& prev, const MagicEffects& now); - ///< Return changes from \a prev to \a now. }; std::string getMagicEffectString( diff --git a/components/contentselector/model/contentmodel.cpp b/components/contentselector/model/contentmodel.cpp index d5202edc44..e454951eb5 100644 --- a/components/contentselector/model/contentmodel.cpp +++ b/components/contentselector/model/contentmodel.cpp @@ -241,9 +241,6 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const return false; EsmFile* file = item(index.row()); - QString fileName = file->fileName(); - bool success = false; - switch (role) { case Qt::EditRole: @@ -257,65 +254,23 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const file->setFileProperty(EsmFile::FileProperty_GameFile, list.at(i)); emit dataChanged(index, index); - - success = true; + return true; } - break; - case Qt::UserRole + 1: { - success = (flags(index) & Qt::ItemIsEnabled); - - if (success) - { - success = setCheckState(file, value.toBool()); - emit dataChanged(index, index); - } + return isEnabled(index) && setCheckState(file, value.toBool()); } - break; - case Qt::CheckStateRole: { int checkValue = value.toInt(); - bool setState = false; - if (file->builtIn() || file->fromAnotherConfigFile()) - { - setState = false; - success = false; - } - else if (checkValue == Qt::Checked && !mCheckedFiles.contains(file)) - { - setState = true; - success = true; - } - else if (checkValue == Qt::Checked && mCheckedFiles.contains(file)) - setState = true; - else if (checkValue == Qt::Unchecked) - setState = true; - - if (setState) - { - setCheckState(file, success); - emit dataChanged(index, index); - } - else - return success; - - for (EsmFile* file2 : mFiles) - { - if (file2->gameFiles().contains(fileName, Qt::CaseInsensitive)) - { - QModelIndex idx = indexFromItem(file2); - emit dataChanged(idx, idx); - } - } - - success = true; + if (checkValue == Qt::Checked) + return mCheckedFiles.contains(file) || setCheckState(file, true); + if (checkValue == Qt::Unchecked) + return !mCheckedFiles.contains(file) || setCheckState(file, false); } - break; } - return success; + return false; } bool ContentSelectorModel::ContentModel::insertRows(int position, int rows, const QModelIndex& parent) @@ -709,6 +664,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL { // setCheckState already gracefully handles builtIn and fromAnotherConfigFile // as necessary, move plug-ins in visible list to match sequence of supplied filelist + // FIXME: setCheckState also does tons of other things which we don't want to happen int filePosition = indexFromItem(file).row(); if (filePosition < previousPosition) { @@ -802,38 +758,37 @@ bool ContentSelectorModel::ContentModel::setCheckState(const EsmFile* file, bool else mCheckedFiles.erase(file); - emit dataChanged(indexFromItem(file), indexFromItem(file)); + QModelIndex fileIndex = indexFromItem(file); + emit dataChanged(fileIndex, fileIndex); + // FIXME: this should not happen per-file. + // Consider not hiding files if their game is disabled so that this is completely unnecessary. if (file->isGameFile()) refreshModel(); - // if we're checking an item, ensure all "upstream" files (dependencies) are checked as well. + // Check "upstream" files (dependencies) if the file is checked, + // uncheck downstream files if the file is unchecked. + // Update the data for downstream files unconditionally (load order warnings). + // FIXME: downstream files of toggled upstream/downstream files should be updated, but that would be slow. if (checkState) { for (const QString& upstreamName : file->gameFiles()) { const EsmFile* upstreamFile = item(upstreamName); - - if (!upstreamFile) + if (upstreamFile == nullptr || !mCheckedFiles.insert(upstreamFile).second) continue; - - mCheckedFiles.insert(upstreamFile); - - emit dataChanged(indexFromItem(upstreamFile), indexFromItem(upstreamFile)); + QModelIndex upstreamIndex = indexFromItem(upstreamFile); + emit dataChanged(upstreamIndex, upstreamIndex); } } - // otherwise, if we're unchecking an item (or the file is a game file) ensure all downstream files are unchecked. - else + for (const EsmFile* otherFile : mFiles) { - for (const EsmFile* downstreamFile : mFiles) - { - if (downstreamFile->gameFiles().contains(file->fileName(), Qt::CaseInsensitive)) - { - mCheckedFiles.erase(downstreamFile); - - emit dataChanged(indexFromItem(downstreamFile), indexFromItem(downstreamFile)); - } - } + if (!otherFile->gameFiles().contains(file->fileName(), Qt::CaseInsensitive)) + continue; + if (!checkState) + mCheckedFiles.erase(otherFile); + QModelIndex otherIndex = indexFromItem(otherFile); + emit dataChanged(otherIndex, otherIndex); } // Need to manually let Bloodmoon entry know if Tribunal is checked/unchecked