mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2026-06-29 01:54:20 +03:00
Merge 1af838db2e into 6f3574f328
This commit is contained in:
commit
a5773f8e4d
40 changed files with 1193 additions and 1218 deletions
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
uses: ./.github/actions/setup-dependencies
|
||||
with:
|
||||
build-type: Debug
|
||||
qt-version: 6.4.3
|
||||
qt-version: 6.9.3
|
||||
|
||||
- name: Configure and Build
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ set(CMAKE_C_STANDARD 11)
|
|||
include(GenerateExportHeader)
|
||||
|
||||
add_compile_definitions($<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG>)
|
||||
add_compile_definitions(QT_WARN_DEPRECATED_UP_TO=0x060400)
|
||||
add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060400)
|
||||
add_compile_definitions(QT_WARN_DEPRECATED_UP_TO=0x060D00)
|
||||
add_compile_definitions(QT_DISABLE_DEPRECATED_UP_TO=0x060903)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
add_compile_options(
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class PixmapCache final : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PixmapCache(QObject* parent) : QObject(parent) {}
|
||||
explicit PixmapCache(QObject* parent) : QObject(parent) {}
|
||||
~PixmapCache() override = default;
|
||||
|
||||
static PixmapCache& instance() { return *s_instance; }
|
||||
|
|
@ -88,7 +88,15 @@ class PixmapCache final : public QObject {
|
|||
QPixmapCache::remove(key);
|
||||
return true;
|
||||
}
|
||||
bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap) { return QPixmapCache::replace(key, pixmap); }
|
||||
bool _replace(const QPixmapCache::Key& key, const QPixmap& pixmap)
|
||||
{
|
||||
if (!key.isValid()) {
|
||||
return false;
|
||||
}
|
||||
remove(key);
|
||||
const_cast<QPixmapCache::Key&>(key) = insert(pixmap);
|
||||
return key.isValid();
|
||||
}
|
||||
bool _setCacheLimit(int n)
|
||||
{
|
||||
QPixmapCache::setCacheLimit(n);
|
||||
|
|
@ -101,33 +109,33 @@ class PixmapCache final : public QObject {
|
|||
*/
|
||||
bool _markCacheMissByEviciton()
|
||||
{
|
||||
static constexpr uint maxCache = static_cast<uint>(std::numeric_limits<int>::max()) / 4;
|
||||
static constexpr uint step = 10240;
|
||||
static constexpr int oneSecond = 1000;
|
||||
static constexpr uint s_maxCache = static_cast<uint>(std::numeric_limits<int>::max()) / 4;
|
||||
static constexpr uint s_step = 10240;
|
||||
static constexpr int s_oneSecond = 1000;
|
||||
|
||||
auto now = QTime::currentTime();
|
||||
if (!m_last_cache_miss_by_eviciton.isNull()) {
|
||||
auto diff = m_last_cache_miss_by_eviciton.msecsTo(now);
|
||||
if (diff < oneSecond) { // less than a second ago
|
||||
++m_consecutive_fast_evicitons;
|
||||
if (!m_lastCacheMissByEviciton.isNull()) {
|
||||
auto diff = m_lastCacheMissByEviciton.msecsTo(now);
|
||||
if (diff < s_oneSecond) { // less than a second ago
|
||||
++m_consecutiveFastEvicitons;
|
||||
} else {
|
||||
m_consecutive_fast_evicitons = 0;
|
||||
m_consecutiveFastEvicitons = 0;
|
||||
}
|
||||
}
|
||||
m_last_cache_miss_by_eviciton = now;
|
||||
if (m_consecutive_fast_evicitons >= m_consecutive_fast_evicitons_threshold) {
|
||||
m_lastCacheMissByEviciton = now;
|
||||
if (m_consecutiveFastEvicitons >= m_consecutiveFastEvicitonsThreshold) {
|
||||
// increase the cache size
|
||||
uint newSize = _cacheLimit() + step;
|
||||
if (newSize >= maxCache) { // increase it until you overflow :D
|
||||
newSize = maxCache;
|
||||
qDebug() << m_consecutive_fast_evicitons
|
||||
uint newSize = _cacheLimit() + s_step;
|
||||
if (newSize >= s_maxCache) { // increase it until you overflow :D
|
||||
newSize = s_maxCache;
|
||||
qDebug() << m_consecutiveFastEvicitons
|
||||
<< tr("pixmap cache misses by eviction happened too fast, doing nothing as the cache size reached it's limit");
|
||||
} else {
|
||||
qDebug() << m_consecutive_fast_evicitons
|
||||
<< tr("pixmap cache misses by eviction happened too fast, increasing cache size to") << static_cast<int>(newSize);
|
||||
qDebug() << m_consecutiveFastEvicitons << tr("pixmap cache misses by eviction happened too fast, increasing cache size to")
|
||||
<< static_cast<int>(newSize);
|
||||
}
|
||||
_setCacheLimit(static_cast<int>(newSize));
|
||||
m_consecutive_fast_evicitons = 0;
|
||||
m_consecutiveFastEvicitons = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -135,13 +143,13 @@ class PixmapCache final : public QObject {
|
|||
|
||||
bool _setFastEvictionThreshold(int threshold)
|
||||
{
|
||||
m_consecutive_fast_evicitons_threshold = threshold;
|
||||
m_consecutiveFastEvicitonsThreshold = threshold;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static PixmapCache* s_instance;
|
||||
QTime m_last_cache_miss_by_eviciton;
|
||||
int m_consecutive_fast_evicitons = 0;
|
||||
int m_consecutive_fast_evicitons_threshold = 15;
|
||||
QTime m_lastCacheMissByEviciton;
|
||||
int m_consecutiveFastEvicitons = 0;
|
||||
int m_consecutiveFastEvicitonsThreshold = 15;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,21 +44,21 @@
|
|||
class VersionFilterModel : public QSortFilterProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent)
|
||||
explicit VersionFilterModel(VersionProxyModel* parent) : QSortFilterProxyModel(parent), m_parent(parent)
|
||||
{
|
||||
m_parent = parent;
|
||||
setSortRole(BaseVersionList::SortRole);
|
||||
sort(0, Qt::DescendingOrder);
|
||||
}
|
||||
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override
|
||||
{
|
||||
const auto& filters = m_parent->filters();
|
||||
const QString& search = m_parent->search();
|
||||
const QModelIndex idx = sourceModel()->index(source_row, 0, source_parent);
|
||||
const QModelIndex idx = sourceModel()->index(sourceRow, 0, sourceParent);
|
||||
|
||||
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive))
|
||||
if (!search.isEmpty() && !sourceModel()->data(idx, BaseVersionList::VersionRole).toString().contains(search, Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto it = filters.begin(); it != filters.end(); ++it) {
|
||||
auto data = sourceModel()->data(idx, it.key());
|
||||
|
|
@ -70,20 +70,27 @@ class VersionFilterModel : public QSortFilterProxyModel {
|
|||
return true;
|
||||
}
|
||||
|
||||
void filterChanged() { invalidateFilter(); }
|
||||
void filterChanged()
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 10, 0)
|
||||
invalidateFilter();
|
||||
#else
|
||||
beginFilterChange();
|
||||
endFilterChange();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
VersionProxyModel* m_parent;
|
||||
};
|
||||
|
||||
VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent)
|
||||
VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(parent), m_filterModel(new VersionFilterModel(this))
|
||||
{
|
||||
filterModel = new VersionFilterModel(this);
|
||||
connect(filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged);
|
||||
connect(filterModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &VersionProxyModel::sourceRowsAboutToBeInserted);
|
||||
connect(filterModel, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted);
|
||||
connect(filterModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &VersionProxyModel::sourceRowsAboutToBeRemoved);
|
||||
connect(filterModel, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved);
|
||||
connect(m_filterModel, &QAbstractItemModel::dataChanged, this, &VersionProxyModel::sourceDataChanged);
|
||||
connect(m_filterModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &VersionProxyModel::sourceRowsAboutToBeInserted);
|
||||
connect(m_filterModel, &QAbstractItemModel::rowsInserted, this, &VersionProxyModel::sourceRowsInserted);
|
||||
connect(m_filterModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &VersionProxyModel::sourceRowsAboutToBeRemoved);
|
||||
connect(m_filterModel, &QAbstractItemModel::rowsRemoved, this, &VersionProxyModel::sourceRowsRemoved);
|
||||
// FIXME: implement when needed
|
||||
/*
|
||||
connect(replacing, &QAbstractItemModel::rowsAboutToBeMoved, this, &VersionProxyModel::sourceRowsAboutToBeMoved);
|
||||
|
|
@ -91,18 +98,20 @@ VersionProxyModel::VersionProxyModel(QObject* parent) : QAbstractProxyModel(pare
|
|||
connect(replacing, &QAbstractItemModel::layoutAboutToBeChanged, this, &VersionProxyModel::sourceLayoutAboutToBeChanged);
|
||||
connect(replacing, &QAbstractItemModel::layoutChanged, this, &VersionProxyModel::sourceLayoutChanged);
|
||||
*/
|
||||
connect(filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset);
|
||||
connect(filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset);
|
||||
connect(m_filterModel, &QAbstractItemModel::modelAboutToBeReset, this, &VersionProxyModel::sourceAboutToBeReset);
|
||||
connect(m_filterModel, &QAbstractItemModel::modelReset, this, &VersionProxyModel::sourceReset);
|
||||
|
||||
QAbstractProxyModel::setSourceModel(filterModel);
|
||||
QAbstractProxyModel::setSourceModel(m_filterModel);
|
||||
}
|
||||
|
||||
QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
{
|
||||
if (section < 0 || section >= m_columns.size())
|
||||
return QVariant();
|
||||
if (orientation != Qt::Horizontal)
|
||||
return QVariant();
|
||||
if (section < 0 || section >= m_columns.size()) {
|
||||
return {};
|
||||
}
|
||||
if (orientation != Qt::Horizontal) {
|
||||
return {};
|
||||
}
|
||||
auto column = m_columns[section];
|
||||
if (role == Qt::DisplayRole) {
|
||||
switch (column) {
|
||||
|
|
@ -153,7 +162,7 @@ QVariant VersionProxyModel::headerData(int section, Qt::Orientation orientation,
|
|||
QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
return {};
|
||||
}
|
||||
auto column = m_columns[index.column()];
|
||||
auto parentIndex = mapToSource(index);
|
||||
|
|
@ -188,11 +197,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||
}
|
||||
}
|
||||
case Qt::ToolTipRole: {
|
||||
if (column == Name && hasRecommended) {
|
||||
if (column == Name && m_hasRecommended) {
|
||||
auto value = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if (value.toBool()) {
|
||||
return tr("Recommended");
|
||||
} else if (hasLatest) {
|
||||
}
|
||||
if (m_hasLatest) {
|
||||
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if (latest.toBool()) {
|
||||
return tr("Latest");
|
||||
|
|
@ -202,11 +212,12 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||
return sourceModel()->data(parentIndex, BaseVersionList::VersionIdRole);
|
||||
}
|
||||
case Qt::DecorationRole: {
|
||||
if (column == Name && hasRecommended) {
|
||||
if (column == Name && m_hasRecommended) {
|
||||
auto recommenced = sourceModel()->data(parentIndex, BaseVersionList::RecommendedRole);
|
||||
if (recommenced.toBool()) {
|
||||
return QIcon::fromTheme("star");
|
||||
} else if (hasLatest) {
|
||||
}
|
||||
if (m_hasLatest) {
|
||||
auto latest = sourceModel()->data(parentIndex, BaseVersionList::LatestRole);
|
||||
if (latest.toBool()) {
|
||||
return QIcon::fromTheme("bug");
|
||||
|
|
@ -225,7 +236,7 @@ QVariant VersionProxyModel::data(const QModelIndex& index, int role) const
|
|||
return QVariant();
|
||||
}
|
||||
default: {
|
||||
if (roles.contains((BaseVersionList::ModelRoles)role)) {
|
||||
if (m_roles.contains((BaseVersionList::ModelRoles)role)) {
|
||||
return sourceModel()->data(parentIndex, role);
|
||||
}
|
||||
return QVariant();
|
||||
|
|
@ -258,18 +269,20 @@ QModelIndex VersionProxyModel::index(int row, int column, const QModelIndex& par
|
|||
{
|
||||
// no trees here... shoo
|
||||
if (parent.isValid()) {
|
||||
return QModelIndex();
|
||||
return {};
|
||||
}
|
||||
if (row < 0 || row >= sourceModel()->rowCount()) {
|
||||
return {};
|
||||
}
|
||||
if (column < 0 || column >= columnCount()) {
|
||||
return {};
|
||||
}
|
||||
if (row < 0 || row >= sourceModel()->rowCount())
|
||||
return QModelIndex();
|
||||
if (column < 0 || column >= columnCount())
|
||||
return QModelIndex();
|
||||
return QAbstractItemModel::createIndex(row, column);
|
||||
}
|
||||
|
||||
int VersionProxyModel::columnCount(const QModelIndex& parent) const
|
||||
{
|
||||
return parent.isValid() ? 0 : m_columns.size();
|
||||
return parent.isValid() ? 0 : static_cast<int>(m_columns.size());
|
||||
}
|
||||
|
||||
int VersionProxyModel::rowCount(const QModelIndex& parent) const
|
||||
|
|
@ -280,30 +293,31 @@ int VersionProxyModel::rowCount(const QModelIndex& parent) const
|
|||
return 0;
|
||||
}
|
||||
|
||||
void VersionProxyModel::sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right)
|
||||
void VersionProxyModel::sourceDataChanged(const QModelIndex& sourceTopLeft, const QModelIndex& sourceBottomRight)
|
||||
{
|
||||
if (source_top_left.parent() != source_bottom_right.parent())
|
||||
return;
|
||||
|
||||
// whole row is getting changed
|
||||
auto topLeft = createIndex(source_top_left.row(), 0);
|
||||
auto bottomRight = createIndex(source_bottom_right.row(), columnCount() - 1);
|
||||
emit dataChanged(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
||||
{
|
||||
auto replacing = dynamic_cast<BaseVersionList*>(replacingRaw);
|
||||
|
||||
m_columns.clear();
|
||||
if (!replacing) {
|
||||
roles.clear();
|
||||
filterModel->setSourceModel(replacing);
|
||||
if (sourceTopLeft.parent() != sourceBottomRight.parent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
roles = replacing->providesRoles();
|
||||
if (roles.contains(BaseVersionList::VersionRole)) {
|
||||
// whole row is getting changed
|
||||
auto topLeft = createIndex(sourceTopLeft.row(), 0);
|
||||
auto bottomRight = createIndex(sourceBottomRight.row(), columnCount() - 1);
|
||||
emit dataChanged(topLeft, bottomRight);
|
||||
}
|
||||
|
||||
void VersionProxyModel::setSourceModel(QAbstractItemModel* sourceModel)
|
||||
{
|
||||
auto* replacing = dynamic_cast<BaseVersionList*>(sourceModel);
|
||||
|
||||
m_columns.clear();
|
||||
if (!replacing) {
|
||||
m_roles.clear();
|
||||
m_filterModel->setSourceModel(replacing);
|
||||
return;
|
||||
}
|
||||
|
||||
m_roles = replacing->providesRoles();
|
||||
if (m_roles.contains(BaseVersionList::VersionRole)) {
|
||||
m_columns.push_back(Name);
|
||||
}
|
||||
/*
|
||||
|
|
@ -312,39 +326,39 @@ void VersionProxyModel::setSourceModel(QAbstractItemModel* replacingRaw)
|
|||
m_columns.push_back(ParentVersion);
|
||||
}
|
||||
*/
|
||||
if (roles.contains(BaseVersionList::CPUArchitectureRole)) {
|
||||
if (m_roles.contains(BaseVersionList::CPUArchitectureRole)) {
|
||||
m_columns.push_back(CPUArchitecture);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::PathRole)) {
|
||||
if (m_roles.contains(BaseVersionList::PathRole)) {
|
||||
m_columns.push_back(Path);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaNameRole)) {
|
||||
if (m_roles.contains(BaseVersionList::JavaNameRole)) {
|
||||
m_columns.push_back(JavaName);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::JavaMajorRole)) {
|
||||
if (m_roles.contains(BaseVersionList::JavaMajorRole)) {
|
||||
m_columns.push_back(JavaMajor);
|
||||
}
|
||||
if (roles.contains(Meta::VersionList::TimeRole)) {
|
||||
if (m_roles.contains(Meta::VersionList::TimeRole)) {
|
||||
m_columns.push_back(Time);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::BranchRole)) {
|
||||
if (m_roles.contains(BaseVersionList::BranchRole)) {
|
||||
m_columns.push_back(Branch);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::TypeRole)) {
|
||||
if (m_roles.contains(BaseVersionList::TypeRole)) {
|
||||
m_columns.push_back(Type);
|
||||
}
|
||||
if (roles.contains(BaseVersionList::RecommendedRole)) {
|
||||
hasRecommended = true;
|
||||
if (m_roles.contains(BaseVersionList::RecommendedRole)) {
|
||||
m_hasRecommended = true;
|
||||
}
|
||||
if (roles.contains(BaseVersionList::LatestRole)) {
|
||||
hasLatest = true;
|
||||
if (m_roles.contains(BaseVersionList::LatestRole)) {
|
||||
m_hasLatest = true;
|
||||
}
|
||||
filterModel->setSourceModel(replacing);
|
||||
m_filterModel->setSourceModel(replacing);
|
||||
}
|
||||
|
||||
QModelIndex VersionProxyModel::getRecommended() const
|
||||
{
|
||||
if (!roles.contains(BaseVersionList::RecommendedRole)) {
|
||||
if (!m_roles.contains(BaseVersionList::RecommendedRole)) {
|
||||
return index(0, 0);
|
||||
}
|
||||
int recommended = 0;
|
||||
|
|
@ -376,19 +390,19 @@ void VersionProxyModel::clearFilters()
|
|||
{
|
||||
m_filters.clear();
|
||||
m_search.clear();
|
||||
filterModel->filterChanged();
|
||||
m_filterModel->filterChanged();
|
||||
}
|
||||
|
||||
void VersionProxyModel::setFilter(const BaseVersionList::ModelRoles column, Filter f)
|
||||
{
|
||||
m_filters[column] = std::move(f);
|
||||
filterModel->filterChanged();
|
||||
m_filterModel->filterChanged();
|
||||
}
|
||||
|
||||
void VersionProxyModel::setSearch(const QString& search)
|
||||
{
|
||||
m_search = search;
|
||||
filterModel->filterChanged();
|
||||
m_filterModel->filterChanged();
|
||||
}
|
||||
|
||||
const VersionProxyModel::FilterMap& VersionProxyModel::filters() const
|
||||
|
|
|
|||
|
|
@ -9,22 +9,22 @@ class VersionFilterModel;
|
|||
class VersionProxyModel : public QAbstractProxyModel {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Column { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
|
||||
enum Column : std::uint8_t { Name, ParentVersion, Branch, Type, CPUArchitecture, Path, Time, JavaName, JavaMajor };
|
||||
using FilterMap = QHash<BaseVersionList::ModelRoles, Filter>;
|
||||
|
||||
public:
|
||||
VersionProxyModel(QObject* parent = 0);
|
||||
virtual ~VersionProxyModel() {};
|
||||
explicit VersionProxyModel(QObject* parent = nullptr);
|
||||
~VersionProxyModel() override = default;
|
||||
|
||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
|
||||
virtual QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
|
||||
virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
virtual QModelIndex parent(const QModelIndex& child) const override;
|
||||
virtual void setSourceModel(QAbstractItemModel* sourceModel) override;
|
||||
int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||
QModelIndex mapFromSource(const QModelIndex& sourceIndex) const override;
|
||||
QModelIndex mapToSource(const QModelIndex& proxyIndex) const override;
|
||||
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
QModelIndex parent(const QModelIndex& child) const override;
|
||||
void setSourceModel(QAbstractItemModel* sourceModel) override;
|
||||
|
||||
const FilterMap& filters() const;
|
||||
const QString& search() const;
|
||||
|
|
@ -36,7 +36,7 @@ class VersionProxyModel : public QAbstractProxyModel {
|
|||
void setCurrentVersion(const QString& version);
|
||||
private slots:
|
||||
|
||||
void sourceDataChanged(const QModelIndex& source_top_left, const QModelIndex& source_bottom_right);
|
||||
void sourceDataChanged(const QModelIndex& sourceTopLeft, const QModelIndex& sourceBottomRight);
|
||||
|
||||
void sourceAboutToBeReset();
|
||||
void sourceReset();
|
||||
|
|
@ -51,9 +51,9 @@ class VersionProxyModel : public QAbstractProxyModel {
|
|||
QList<Column> m_columns;
|
||||
FilterMap m_filters;
|
||||
QString m_search;
|
||||
BaseVersionList::RoleList roles;
|
||||
VersionFilterModel* filterModel;
|
||||
bool hasRecommended = false;
|
||||
bool hasLatest = false;
|
||||
BaseVersionList::RoleList m_roles;
|
||||
VersionFilterModel* m_filterModel;
|
||||
bool m_hasRecommended = false;
|
||||
bool m_hasLatest = false;
|
||||
QString m_currentVersion;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -27,8 +27,9 @@ QString Meta::Version::descriptor() const
|
|||
}
|
||||
QString Meta::Version::name() const
|
||||
{
|
||||
if (m_data)
|
||||
if (m_data) {
|
||||
return m_data->name;
|
||||
}
|
||||
return m_uid;
|
||||
}
|
||||
QString Meta::Version::typeString() const
|
||||
|
|
@ -38,7 +39,7 @@ QString Meta::Version::typeString() const
|
|||
|
||||
QDateTime Meta::Version::time() const
|
||||
{
|
||||
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, Qt::UTC);
|
||||
return QDateTime::fromMSecsSinceEpoch(m_time * 1000, QTimeZone::UTC);
|
||||
}
|
||||
|
||||
void Meta::Version::parse(const QJsonObject& obj)
|
||||
|
|
@ -110,9 +111,9 @@ void Meta::Version::setRequires(const Meta::RequireSet& reqs, const Meta::Requir
|
|||
emit requiresChanged();
|
||||
}
|
||||
|
||||
void Meta::Version::setVolatile(bool volatile_)
|
||||
void Meta::Version::setVolatile(bool volatileVar)
|
||||
{
|
||||
m_volatile = volatile_;
|
||||
m_volatile = volatileVar;
|
||||
}
|
||||
|
||||
void Meta::Version::setData(const VersionFilePtr& data)
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
|
|||
using Ptr = std::shared_ptr<Version>;
|
||||
|
||||
explicit Version(const QString& uid, const QString& version);
|
||||
virtual ~Version() = default;
|
||||
~Version() override = default;
|
||||
|
||||
QString descriptor() const override;
|
||||
QString name() const override;
|
||||
|
|
@ -66,7 +66,7 @@ class Version : public QObject, public BaseVersion, public BaseEntity {
|
|||
void setType(const QString& type);
|
||||
void setTime(qint64 time);
|
||||
void setRequires(const Meta::RequireSet& reqs, const Meta::RequireSet& conflicts);
|
||||
void setVolatile(bool volatile_);
|
||||
void setVolatile(bool volatileVar);
|
||||
void setRecommended(bool recommended);
|
||||
void setProvidesRecommendations();
|
||||
void setData(const VersionFilePtr& data);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <QDebug>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <utility>
|
||||
|
||||
#include "minecraft/auth/AccountData.h"
|
||||
#include "minecraft/auth/steps/EntitlementsStep.h"
|
||||
|
|
@ -17,7 +18,7 @@
|
|||
|
||||
#include <Application.h>
|
||||
|
||||
AuthFlow::AuthFlow(AccountData* data, Action action) : Task(), m_data(data)
|
||||
AuthFlow::AuthFlow(AccountData* data, Action action) : m_data(data)
|
||||
{
|
||||
if (data->type == AccountType::MSA) {
|
||||
if (action == Action::DeviceCode) {
|
||||
|
|
@ -75,11 +76,12 @@ void AuthFlow::nextStep()
|
|||
|
||||
void AuthFlow::stepFinished(AccountTaskState resultingState, QString message)
|
||||
{
|
||||
if (changeState(resultingState, message))
|
||||
if (changeState(resultingState, std::move(message))) {
|
||||
nextStep();
|
||||
}
|
||||
}
|
||||
|
||||
bool AuthFlow::changeState(AccountTaskState newState, QString reason)
|
||||
bool AuthFlow::changeState(AccountTaskState newState, const QString& reason)
|
||||
{
|
||||
m_taskState = newState;
|
||||
setDetails(reason);
|
||||
|
|
@ -148,8 +150,9 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason)
|
|||
}
|
||||
bool AuthFlow::abort()
|
||||
{
|
||||
if (m_currentStep)
|
||||
if (m_currentStep) {
|
||||
m_currentStep->abort();
|
||||
}
|
||||
emitAborted();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ class AuthFlow : public Task {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class Action { Refresh, Login, DeviceCode };
|
||||
enum class Action : std::uint8_t { Refresh, Login, DeviceCode };
|
||||
|
||||
explicit AuthFlow(AccountData* data, Action action = Action::Refresh);
|
||||
virtual ~AuthFlow() = default;
|
||||
~AuthFlow() override = default;
|
||||
|
||||
void executeTask() override;
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ class AuthFlow : public Task {
|
|||
|
||||
signals:
|
||||
void authorizeWithBrowser(const QUrl& url);
|
||||
void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn);
|
||||
void authorizeWithBrowserWithExtra(const QUrl& verificationUrl, const QString& code, const QUrl& completeVerificationUrl);
|
||||
|
||||
protected:
|
||||
void succeed();
|
||||
|
|
@ -35,7 +35,7 @@ class AuthFlow : public Task {
|
|||
|
||||
private slots:
|
||||
// NOTE: true -> non-terminal state, false -> terminal state
|
||||
bool changeState(AccountTaskState newState, QString reason = QString());
|
||||
bool changeState(AccountTaskState newState, const QString& reason = {});
|
||||
void stepFinished(AccountTaskState resultingState, QString message);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -39,15 +39,50 @@
|
|||
#include <QUrlQuery>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Json.h"
|
||||
#include "net/RawHeaderProxy.h"
|
||||
|
||||
// https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-device-code
|
||||
MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data)
|
||||
{
|
||||
m_clientId = APPLICATION->getMSAClientID();
|
||||
connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort);
|
||||
connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser);
|
||||
m_oauth2.setAuthorizationUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode"));
|
||||
m_oauth2.setTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"));
|
||||
m_oauth2.setRequestedScopeTokens({ "XboxLive.SignIn", "XboxLive.offline_access" });
|
||||
m_oauth2.setClientIdentifier(m_clientId);
|
||||
m_oauth2.setNetworkAccessManager(APPLICATION->network());
|
||||
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::granted, this, [this] {
|
||||
m_data->msaClientID = m_oauth2.clientIdentifier();
|
||||
m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||
m_data->msaToken.notAfter = m_oauth2.expirationAt();
|
||||
m_data->msaToken.extra = m_oauth2.extraTokens();
|
||||
m_data->msaToken.refresh_token = m_oauth2.refreshToken();
|
||||
m_data->msaToken.token = m_oauth2.token();
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
||||
});
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::authorizeWithUserCode, this, &MSADeviceCodeStep::authorizeWithBrowser);
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::requestFailed, this, [this](const QAbstractOAuth2::Error err) {
|
||||
auto state = AccountTaskState::STATE_FAILED_HARD;
|
||||
if (m_oauth2.status() == QAbstractOAuth::Status::Granted) {
|
||||
if (err == QAbstractOAuth2::Error::NetworkError) {
|
||||
state = AccountTaskState::STATE_OFFLINE;
|
||||
} else {
|
||||
state = AccountTaskState::STATE_FAILED_SOFT;
|
||||
}
|
||||
}
|
||||
auto message = tr("Microsoft user authentication failed.");
|
||||
qWarning() << message;
|
||||
emit finished(state, message);
|
||||
});
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::serverReportedErrorOccurred, this,
|
||||
[this](const QString& error, const QString& errorDescription, const QUrl& /*uri*/) {
|
||||
qWarning() << "Failed to login because" << error << errorDescription;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription);
|
||||
});
|
||||
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::extraTokensChanged, this,
|
||||
[this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; });
|
||||
|
||||
connect(&m_oauth2, &QOAuth2DeviceAuthorizationFlow::clientIdentifierChanged, this,
|
||||
[this](const QString& clientIdentifier) { m_data->msaClientID = clientIdentifier; });
|
||||
}
|
||||
|
||||
QString MSADeviceCodeStep::describe()
|
||||
|
|
@ -57,222 +92,16 @@ QString MSADeviceCodeStep::describe()
|
|||
|
||||
void MSADeviceCodeStep::perform()
|
||||
{
|
||||
QUrlQuery data;
|
||||
data.addQueryItem("client_id", m_clientId);
|
||||
data.addQueryItem("scope", "XboxLive.SignIn XboxLive.offline_access");
|
||||
auto payload = data.query(QUrl::FullyEncoded).toUtf8();
|
||||
QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode");
|
||||
auto headers = QList<Net::HeaderPair>{
|
||||
{ "Content-Type", "application/x-www-form-urlencoded" },
|
||||
{ "Accept", "application/json" },
|
||||
};
|
||||
auto [request, response] = Net::Upload::makeByteArray(url, payload);
|
||||
m_request = request;
|
||||
m_request->addHeaderProxy(std::make_unique<Net::RawHeaderProxy>(headers));
|
||||
m_request->enableAutoRetry(true);
|
||||
|
||||
m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, [this, response] { deviceAuthorizationFinished(response); });
|
||||
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
struct DeviceAuthorizationResponse {
|
||||
QString device_code;
|
||||
QString user_code;
|
||||
QString verification_uri;
|
||||
int expires_in;
|
||||
int interval;
|
||||
|
||||
QString error;
|
||||
QString error_description;
|
||||
};
|
||||
|
||||
DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& data)
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse device authorization response due to err:" << err.errorString();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!doc.isObject()) {
|
||||
qWarning() << "Device authorization response is not an object";
|
||||
return {};
|
||||
}
|
||||
auto obj = doc.object();
|
||||
return {
|
||||
obj["device_code"].toString(), obj["user_code"].toString(), obj["verification_uri"].toString(), obj["expires_in"].toInt(),
|
||||
obj["interval"].toInt(), obj["error"].toString(), obj["error_description"].toString(),
|
||||
};
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::deviceAuthorizationFinished(QByteArray* response)
|
||||
{
|
||||
auto rsp = parseDeviceAuthorizationResponse(*response);
|
||||
if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) {
|
||||
qWarning() << "Device authorization failed:" << rsp.error;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD,
|
||||
tr("Device authorization failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description));
|
||||
return;
|
||||
}
|
||||
if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Device authorization failed:" << *response;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (rsp.device_code.isEmpty() || rsp.user_code.isEmpty() || rsp.verification_uri.isEmpty() || rsp.expires_in == 0) {
|
||||
qWarning() << "Device authorization failed: required fields missing";
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Device authorization failed: required fields missing"));
|
||||
return;
|
||||
}
|
||||
if (rsp.interval != 0) {
|
||||
interval = rsp.interval;
|
||||
}
|
||||
m_device_code = rsp.device_code;
|
||||
emit authorizeWithBrowser(rsp.verification_uri, rsp.user_code, rsp.expires_in);
|
||||
m_expiration_timer.setTimerType(Qt::VeryCoarseTimer);
|
||||
m_expiration_timer.setInterval(rsp.expires_in * 1000);
|
||||
m_expiration_timer.setSingleShot(true);
|
||||
m_expiration_timer.start();
|
||||
m_pool_timer.setTimerType(Qt::VeryCoarseTimer);
|
||||
m_pool_timer.setSingleShot(true);
|
||||
startPoolTimer();
|
||||
*m_data = AccountData();
|
||||
m_data->msaClientID = m_clientId;
|
||||
m_oauth2.grant();
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::abort()
|
||||
{
|
||||
m_expiration_timer.stop();
|
||||
m_pool_timer.stop();
|
||||
if (m_request) {
|
||||
m_request->abort();
|
||||
if (m_oauth2.isPolling()) {
|
||||
m_oauth2.stopTokenPolling();
|
||||
}
|
||||
m_is_aborted = true;
|
||||
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Task aborted"));
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::startPoolTimer()
|
||||
{
|
||||
if (m_is_aborted) {
|
||||
return;
|
||||
}
|
||||
if (m_expiration_timer.remainingTime() < interval * 1000) {
|
||||
perform();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pool_timer.setInterval(interval * 1000);
|
||||
m_pool_timer.start();
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::authenticateUser()
|
||||
{
|
||||
QUrlQuery data;
|
||||
data.addQueryItem("client_id", m_clientId);
|
||||
data.addQueryItem("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
|
||||
data.addQueryItem("device_code", m_device_code);
|
||||
auto payload = data.query(QUrl::FullyEncoded).toUtf8();
|
||||
QUrl url("https://login.microsoftonline.com/consumers/oauth2/v2.0/token");
|
||||
auto headers = QList<Net::HeaderPair>{
|
||||
{ "Content-Type", "application/x-www-form-urlencoded" },
|
||||
{ "Accept", "application/json" },
|
||||
};
|
||||
auto [request, response] = Net::Upload::makeByteArray(url, payload);
|
||||
m_request = request;
|
||||
m_request->addHeaderProxy(std::make_unique<Net::RawHeaderProxy>(headers));
|
||||
|
||||
connect(m_request.get(), &Task::finished, this, [this, response] { authenticationFinished(response); });
|
||||
|
||||
m_request->setNetwork(APPLICATION->network());
|
||||
m_request->start();
|
||||
}
|
||||
|
||||
struct AuthenticationResponse {
|
||||
QString access_token;
|
||||
QString token_type;
|
||||
QString refresh_token;
|
||||
int expires_in;
|
||||
|
||||
QString error;
|
||||
QString error_description;
|
||||
|
||||
QVariantMap extra;
|
||||
};
|
||||
|
||||
AuthenticationResponse parseAuthenticationResponse(const QByteArray& data)
|
||||
{
|
||||
QJsonParseError err;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(data, &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Failed to parse device authorization response due to err:" << err.errorString();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!doc.isObject()) {
|
||||
qWarning() << "Device authorization response is not an object";
|
||||
return {};
|
||||
}
|
||||
auto obj = doc.object();
|
||||
return { obj["access_token"].toString(),
|
||||
obj["token_type"].toString(),
|
||||
obj["refresh_token"].toString(),
|
||||
obj["expires_in"].toInt(),
|
||||
obj["error"].toString(),
|
||||
obj["error_description"].toString(),
|
||||
obj.toVariantMap() };
|
||||
}
|
||||
|
||||
void MSADeviceCodeStep::authenticationFinished(QByteArray* response)
|
||||
{
|
||||
if (m_request->error() == QNetworkReply::TimeoutError) {
|
||||
// rfc8628#section-3.5
|
||||
// "On encountering a connection timeout, clients MUST unilaterally
|
||||
// reduce their polling frequency before retrying. The use of an
|
||||
// exponential backoff algorithm to achieve this, such as doubling the
|
||||
// polling interval on each such connection timeout, is RECOMMENDED."
|
||||
interval *= 2;
|
||||
startPoolTimer();
|
||||
return;
|
||||
}
|
||||
auto rsp = parseAuthenticationResponse(*response);
|
||||
if (rsp.error == "slow_down") {
|
||||
// rfc8628#section-3.5
|
||||
// "A variant of 'authorization_pending', the authorization request is
|
||||
// still pending and polling should continue, but the interval MUST
|
||||
// be increased by 5 seconds for this and all subsequent requests."
|
||||
interval += 5;
|
||||
startPoolTimer();
|
||||
return;
|
||||
}
|
||||
if (rsp.error == "authorization_pending") {
|
||||
// keep trying - rfc8628#section-3.5
|
||||
// "The authorization request is still pending as the end user hasn't
|
||||
// yet completed the user-interaction steps (Section 3.3)."
|
||||
startPoolTimer();
|
||||
return;
|
||||
}
|
||||
if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) {
|
||||
qWarning() << "Device Access failed:" << rsp.error;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD,
|
||||
tr("Device Access failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description));
|
||||
return;
|
||||
}
|
||||
if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) {
|
||||
startPoolTimer(); // it failed so just try again without increasing the interval
|
||||
return;
|
||||
}
|
||||
|
||||
m_expiration_timer.stop();
|
||||
m_data->msaClientID = m_clientId;
|
||||
m_data->msaToken.issueInstant = QDateTime::currentDateTimeUtc();
|
||||
m_data->msaToken.notAfter = QDateTime::currentDateTime().addSecs(rsp.expires_in);
|
||||
m_data->msaToken.extra = rsp.extra;
|
||||
m_data->msaToken.refresh_token = rsp.refresh_token;
|
||||
m_data->msaToken.token = rsp.access_token;
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got MSA token"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,17 +35,16 @@
|
|||
|
||||
#pragma once
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
#include <QtNetworkAuth/QOAuth2DeviceAuthorizationFlow>
|
||||
|
||||
class MSADeviceCodeStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MSADeviceCodeStep(AccountData* data);
|
||||
virtual ~MSADeviceCodeStep() noexcept = default;
|
||||
~MSADeviceCodeStep() noexcept override = default;
|
||||
|
||||
void perform() override;
|
||||
|
||||
|
|
@ -55,23 +54,9 @@ class MSADeviceCodeStep : public AuthStep {
|
|||
void abort() override;
|
||||
|
||||
signals:
|
||||
void authorizeWithBrowser(QString url, QString code, int expiresIn);
|
||||
|
||||
private slots:
|
||||
void deviceAuthorizationFinished(QByteArray* response);
|
||||
void startPoolTimer();
|
||||
void authenticateUser();
|
||||
void authenticationFinished(QByteArray* response);
|
||||
void authorizeWithBrowser(const QUrl& verificationUrl, const QString& code, const QUrl& completeVerificationUrl);
|
||||
|
||||
private:
|
||||
QString m_clientId;
|
||||
QString m_device_code;
|
||||
bool m_is_aborted = false;
|
||||
int interval = 5;
|
||||
|
||||
QTimer m_pool_timer;
|
||||
QTimer m_expiration_timer;
|
||||
|
||||
Net::Upload::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
QOAuth2DeviceAuthorizationFlow m_oauth2;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,19 +36,19 @@
|
|||
#include "MSAStep.h"
|
||||
|
||||
#include <QAbstractOAuth2>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QOAuthHttpServerReplyHandler>
|
||||
#include <QOAuthOobReplyHandler>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
|
||||
namespace {
|
||||
bool isSchemeHandlerRegistered()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
|
|
@ -110,6 +110,7 @@ class LoggingOAuthHttpServerReplyHandler final : public QOAuthHttpServerReplyHan
|
|||
QOAuthHttpServerReplyHandler::networkReplyFinished(reply);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent)
|
||||
{
|
||||
|
|
@ -117,7 +118,7 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||
if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || APPLICATION->isPortable() || !isSchemeHandlerRegistered())
|
||||
|
||||
{
|
||||
auto replyHandler = new LoggingOAuthHttpServerReplyHandler(this);
|
||||
auto* replyHandler = new LoggingOAuthHttpServerReplyHandler(this);
|
||||
replyHandler->setCallbackText(QString(R"XXX(
|
||||
<noscript>
|
||||
<meta http-equiv="Refresh" content="0; URL=%1" />
|
||||
|
|
@ -133,8 +134,8 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||
m_oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
||||
}
|
||||
m_oauth2.setAuthorizationUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"));
|
||||
m_oauth2.setAccessTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"));
|
||||
m_oauth2.setScope("XboxLive.SignIn XboxLive.offline_access");
|
||||
m_oauth2.setTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"));
|
||||
m_oauth2.setRequestedScopeTokens({ "XboxLive.SignIn", "XboxLive.offline_access" });
|
||||
m_oauth2.setClientIdentifier(m_clientId);
|
||||
m_oauth2.setNetworkAccessManager(APPLICATION->network());
|
||||
|
||||
|
|
@ -164,8 +165,8 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
|||
qWarning() << message;
|
||||
emit finished(state, message);
|
||||
});
|
||||
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::error, this,
|
||||
[this](const QString& error, const QString& errorDescription, const QUrl& uri) {
|
||||
connect(&m_oauth2, &QOAuth2AuthorizationCodeFlow::serverReportedErrorOccurred, this,
|
||||
[this](const QString& error, const QString& errorDescription, const QUrl& /*uri*/) {
|
||||
qWarning() << "Failed to login because" << error << errorDescription;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription);
|
||||
});
|
||||
|
|
@ -195,10 +196,10 @@ void MSAStep::perform()
|
|||
return;
|
||||
}
|
||||
m_oauth2.setRefreshToken(m_data->msaToken.refresh_token);
|
||||
m_oauth2.refreshAccessToken();
|
||||
m_oauth2.refreshTokens();
|
||||
} else {
|
||||
m_oauth2.setModifyParametersFunction(
|
||||
[](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||
[](QAbstractOAuth::Stage /*stage*/, QMultiMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||
|
||||
*m_data = AccountData();
|
||||
m_data->msaClientID = m_clientId;
|
||||
|
|
|
|||
|
|
@ -38,12 +38,13 @@
|
|||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
|
||||
#include <QtNetworkAuth/qoauth2authorizationcodeflow.h>
|
||||
#include <QtNetworkAuth/QOAuth2AuthorizationCodeFlow>
|
||||
|
||||
class MSAStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit MSAStep(AccountData* data, bool silent = false);
|
||||
virtual ~MSAStep() noexcept = default;
|
||||
~MSAStep() noexcept override = default;
|
||||
|
||||
void perform() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -847,7 +847,7 @@ void ResourceFolderModel::onParseFailed(int ticket, const QString& resourceId)
|
|||
// update index
|
||||
m_resourcesIndex.clear();
|
||||
int idx = 0;
|
||||
for (const auto& mod : qAsConst(m_resources)) {
|
||||
for (const auto& mod : std::as_const(m_resources)) {
|
||||
m_resourcesIndex[mod->internalId()] = idx;
|
||||
idx++;
|
||||
}
|
||||
|
|
@ -954,7 +954,7 @@ void ResourceFolderModel::applyUpdates(QSet<QString>& currentSet, QSet<QString>&
|
|||
{
|
||||
m_resourcesIndex.clear();
|
||||
int idx = 0;
|
||||
for (const auto& mod : qAsConst(m_resources)) {
|
||||
for (const auto& mod : std::as_const(m_resources)) {
|
||||
m_resourcesIndex[mod->internalId()] = idx;
|
||||
idx++;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,16 @@
|
|||
#include "SkinModel.h"
|
||||
#include <QFileInfo>
|
||||
#include <QPainter>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
|
||||
#include "FileSystem.h"
|
||||
|
||||
static void setAlpha(QImage& image, const QRect& region, const int alpha)
|
||||
namespace {
|
||||
void setAlpha(QImage& image, const QRect& region, const int alpha)
|
||||
{
|
||||
for (int y = region.top(); y < region.bottom(); ++y) {
|
||||
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||
auto line = std::span(std::bit_cast<QRgb*>(image.scanLine(y)), image.width());
|
||||
for (int x = region.left(); x < region.right(); ++x) {
|
||||
QRgb pixel = line[x];
|
||||
line[x] = qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), alpha);
|
||||
|
|
@ -34,10 +37,10 @@ static void setAlpha(QImage& image, const QRect& region, const int alpha)
|
|||
}
|
||||
}
|
||||
|
||||
static void doNotchTransparencyHack(QImage& image)
|
||||
void doNotchTransparencyHack(QImage& image)
|
||||
{
|
||||
for (int y = 0; y < 32; y++) {
|
||||
QRgb* line = reinterpret_cast<QRgb*>(image.scanLine(y));
|
||||
auto line = std::span(std::bit_cast<QRgb*>(image.scanLine(y)), image.width());
|
||||
for (int x = 32; x < 64; x++) {
|
||||
if (qAlpha(line[x]) < 128) {
|
||||
return;
|
||||
|
|
@ -48,7 +51,7 @@ static void doNotchTransparencyHack(QImage& image)
|
|||
setAlpha(image, { 32, 0, 32, 32 }, 0);
|
||||
}
|
||||
|
||||
static QImage improveSkin(QImage skin)
|
||||
QImage improveSkin(QImage skin)
|
||||
{
|
||||
int height = skin.height();
|
||||
int width = skin.width();
|
||||
|
|
@ -71,47 +74,57 @@ static QImage improveSkin(QImage skin)
|
|||
|
||||
auto copyRect = [&p, &newSkin](int startX, int startY, int offsetX, int offsetY, int sizeX, int sizeY) {
|
||||
QImage region = newSkin.copy(startX, startY, sizeX, sizeY);
|
||||
region = region.mirrored(true, false);
|
||||
region = region.flipped(Qt::Horizontal);
|
||||
|
||||
p.drawImage(startX + offsetX, startY + offsetY, region);
|
||||
};
|
||||
static const struct {
|
||||
struct FaceRect {
|
||||
int x;
|
||||
int y;
|
||||
int offsetX;
|
||||
int offsetY;
|
||||
int width;
|
||||
int height;
|
||||
} faces[] = {
|
||||
{ 4, 16, 16, 32, 4, 4 }, { 8, 16, 16, 32, 4, 4 }, { 0, 20, 24, 32, 4, 12 }, { 4, 20, 16, 32, 4, 12 },
|
||||
{ 8, 20, 8, 32, 4, 12 }, { 12, 20, 16, 32, 4, 12 }, { 44, 16, -8, 32, 4, 4 }, { 48, 16, -8, 32, 4, 4 },
|
||||
{ 40, 20, 0, 32, 4, 12 }, { 44, 20, -8, 32, 4, 12 }, { 48, 20, -16, 32, 4, 12 }, { 52, 20, -8, 32, 4, 12 },
|
||||
};
|
||||
static constexpr std::array s_faces = {
|
||||
FaceRect{ .x = 4, .y = 16, .offsetX = 16, .offsetY = 32, .width = 4, .height = 4 },
|
||||
FaceRect{ .x = 8, .y = 16, .offsetX = 16, .offsetY = 32, .width = 4, .height = 4 },
|
||||
FaceRect{ .x = 0, .y = 20, .offsetX = 24, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 4, .y = 20, .offsetX = 16, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 8, .y = 20, .offsetX = 8, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 12, .y = 20, .offsetX = 16, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 44, .y = 16, .offsetX = -8, .offsetY = 32, .width = 4, .height = 4 },
|
||||
FaceRect{ .x = 48, .y = 16, .offsetX = -8, .offsetY = 32, .width = 4, .height = 4 },
|
||||
FaceRect{ .x = 40, .y = 20, .offsetX = 0, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 44, .y = 20, .offsetX = -8, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 48, .y = 20, .offsetX = -16, .offsetY = 32, .width = 4, .height = 12 },
|
||||
FaceRect{ .x = 52, .y = 20, .offsetX = -8, .offsetY = 32, .width = 4, .height = 12 },
|
||||
};
|
||||
|
||||
for (const auto& face : faces) {
|
||||
for (const auto& face : s_faces) {
|
||||
copyRect(face.x, face.y, face.offsetX, face.offsetY, face.width, face.height);
|
||||
}
|
||||
doNotchTransparencyHack(newSkin);
|
||||
skin = newSkin;
|
||||
}
|
||||
static const QRect opaqueParts[] = {
|
||||
{ 0, 0, 32, 16 },
|
||||
{ 0, 16, 64, 16 },
|
||||
{ 16, 48, 32, 16 },
|
||||
static constexpr std::array s_opaqueParts = {
|
||||
QRect{ 0, 0, 32, 16 },
|
||||
QRect{ 0, 16, 64, 16 },
|
||||
QRect{ 16, 48, 32, 16 },
|
||||
};
|
||||
|
||||
for (const auto& p : opaqueParts) {
|
||||
for (const auto& p : s_opaqueParts) {
|
||||
setAlpha(skin, p, 255);
|
||||
}
|
||||
return skin;
|
||||
}
|
||||
|
||||
static QImage getSkin(const QString path)
|
||||
QImage getSkin(const QString& path)
|
||||
{
|
||||
return improveSkin(QImage(path));
|
||||
}
|
||||
|
||||
static QImage generatePreviews(QImage texture, bool slim)
|
||||
QImage generatePreviews(const QImage& texture, bool slim)
|
||||
{
|
||||
QImage preview(36, 36, QImage::Format_ARGB32);
|
||||
preview.fill(Qt::transparent);
|
||||
|
|
@ -162,13 +175,14 @@ static QImage generatePreviews(QImage texture, bool slim)
|
|||
|
||||
return preview;
|
||||
}
|
||||
SkinModel::SkinModel(QString path) : m_path(path), m_texture(getSkin(path)), m_model(Model::CLASSIC)
|
||||
} // namespace
|
||||
|
||||
SkinModel::SkinModel(const QString& path) : m_path(path), m_texture(getSkin(path))
|
||||
{
|
||||
m_preview = generatePreviews(m_texture, false);
|
||||
}
|
||||
|
||||
SkinModel::SkinModel(QDir skinDir, QJsonObject obj)
|
||||
: m_capeId(obj["capeId"].toString()), m_model(Model::CLASSIC), m_url(obj["url"].toString())
|
||||
SkinModel::SkinModel(const QDir& skinDir, QJsonObject obj) : m_capeId(obj["capeId"].toString()), m_url(obj["url"].toString())
|
||||
{
|
||||
auto name = obj["name"].toString();
|
||||
|
||||
|
|
@ -185,14 +199,14 @@ QString SkinModel::name() const
|
|||
return QFileInfo(m_path).completeBaseName();
|
||||
}
|
||||
|
||||
bool SkinModel::rename(QString newName)
|
||||
bool SkinModel::rename(const QString& newName)
|
||||
{
|
||||
auto info = QFileInfo(m_path);
|
||||
auto new_path = FS::PathCombine(info.absolutePath(), newName + ".png");
|
||||
if (QFileInfo::exists(new_path)) {
|
||||
auto newPath = FS::PathCombine(info.absolutePath(), newName + ".png");
|
||||
if (QFileInfo::exists(newPath)) {
|
||||
return false;
|
||||
}
|
||||
m_path = new_path;
|
||||
m_path = newPath;
|
||||
return FS::move(info.absoluteFilePath(), m_path);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,14 +21,15 @@
|
|||
#include <QDir>
|
||||
#include <QImage>
|
||||
#include <QJsonObject>
|
||||
#include <utility>
|
||||
|
||||
class SkinModel {
|
||||
public:
|
||||
enum Model { CLASSIC, SLIM };
|
||||
enum Model : std::uint8_t { CLASSIC, SLIM };
|
||||
|
||||
SkinModel() = default;
|
||||
SkinModel(QString path);
|
||||
SkinModel(QDir skinDir, QJsonObject obj);
|
||||
explicit SkinModel(const QString& path);
|
||||
SkinModel(const QDir& skinDir, QJsonObject obj);
|
||||
virtual ~SkinModel() = default;
|
||||
|
||||
QString name() const;
|
||||
|
|
@ -41,10 +42,10 @@ class SkinModel {
|
|||
Model getModel() const { return m_model; }
|
||||
QString getURL() const { return m_url; }
|
||||
|
||||
bool rename(QString newName);
|
||||
void setCapeId(QString capeID) { m_capeId = capeID; }
|
||||
bool rename(const QString& newName);
|
||||
void setCapeId(QString capeID) { m_capeId = std::move(capeID); }
|
||||
void setModel(Model model);
|
||||
void setURL(QString url) { m_url = url; }
|
||||
void setURL(QString url) { m_url = std::move(url); }
|
||||
void refresh();
|
||||
|
||||
QJsonObject toJSON() const;
|
||||
|
|
@ -54,6 +55,6 @@ class SkinModel {
|
|||
QImage m_texture;
|
||||
QImage m_preview;
|
||||
QString m_capeId;
|
||||
Model m_model;
|
||||
Model m_model = Model::CLASSIC;
|
||||
QString m_url;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,10 +34,8 @@
|
|||
*/
|
||||
|
||||
#include "PackFetchTask.h"
|
||||
#include "PrivatePackManager.h"
|
||||
|
||||
#include <QDomDocument>
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
|
||||
#include "net/ApiDownload.h"
|
||||
|
|
@ -46,37 +44,37 @@ namespace LegacyFTB {
|
|||
|
||||
void PackFetchTask::fetch()
|
||||
{
|
||||
publicPacks.clear();
|
||||
thirdPartyPacks.clear();
|
||||
m_publicPacks.clear();
|
||||
m_thirdPartyPacks.clear();
|
||||
|
||||
jobPtr.reset(new NetJob("LegacyFTB::ModpackFetch", m_network));
|
||||
m_jobPtr.reset(new NetJob("LegacyFTB::ModpackFetch", m_network));
|
||||
|
||||
QUrl publicPacksUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/modpacks.xml");
|
||||
qDebug() << "Downloading public version info from" << publicPacksUrl.toString();
|
||||
|
||||
auto [publicAction, publicResponse] = Net::ApiDownload::makeByteArray(publicPacksUrl);
|
||||
jobPtr->addNetAction(publicAction);
|
||||
m_jobPtr->addNetAction(publicAction);
|
||||
|
||||
QUrl thirdPartyUrl = QUrl(BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/thirdparty.xml");
|
||||
qDebug() << "Downloading thirdparty version info from" << thirdPartyUrl.toString();
|
||||
|
||||
auto [thirdPartyAction, thirdPartyResponse] = Net::Download::makeByteArray(thirdPartyUrl);
|
||||
jobPtr->addNetAction(thirdPartyAction);
|
||||
m_jobPtr->addNetAction(thirdPartyAction);
|
||||
|
||||
connect(jobPtr.get(), &NetJob::succeeded, this,
|
||||
connect(m_jobPtr.get(), &NetJob::succeeded, this,
|
||||
[this, publicResponse, thirdPartyResponse] { fileDownloadFinished(publicResponse, thirdPartyResponse); });
|
||||
connect(jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
|
||||
connect(jobPtr.get(), &NetJob::aborted, this, &PackFetchTask::fileDownloadAborted);
|
||||
connect(m_jobPtr.get(), &NetJob::failed, this, &PackFetchTask::fileDownloadFailed);
|
||||
connect(m_jobPtr.get(), &NetJob::aborted, this, &PackFetchTask::fileDownloadAborted);
|
||||
|
||||
jobPtr->start();
|
||||
m_jobPtr->start();
|
||||
}
|
||||
|
||||
void PackFetchTask::fetchPrivate(const QStringList& toFetch)
|
||||
{
|
||||
QString privatePackBaseUrl = BuildConfig.LEGACY_FTB_CDN_BASE_URL + "static/%1.xml";
|
||||
|
||||
for (auto& packCode : toFetch) {
|
||||
NetJob* job = new NetJob("Fetching private pack", m_network);
|
||||
for (const auto& packCode : toFetch) {
|
||||
auto* job = new NetJob("Fetching private pack", m_network);
|
||||
|
||||
auto [action, data] = Net::ApiDownload::makeByteArray(privatePackBaseUrl.arg(packCode));
|
||||
job->addNetAction(action);
|
||||
|
|
@ -93,7 +91,7 @@ void PackFetchTask::fetchPrivate(const QStringList& toFetch)
|
|||
job->deleteLater();
|
||||
});
|
||||
|
||||
connect(job, &NetJob::failed, this, [this, job, packCode](QString reason) {
|
||||
connect(job, &NetJob::failed, this, [this, job, packCode](const QString& reason) {
|
||||
emit privateFileDownloadFailed(reason, packCode);
|
||||
job->deleteLater();
|
||||
});
|
||||
|
|
@ -112,21 +110,21 @@ void PackFetchTask::fileDownloadFinished(QByteArray* publicPtr, QByteArray* thir
|
|||
{
|
||||
QStringList failedLists;
|
||||
|
||||
if (!parseAndAddPacks(*publicPtr, PackType::Public, publicPacks)) {
|
||||
if (!parseAndAddPacks(*publicPtr, PackType::Public, m_publicPacks)) {
|
||||
failedLists.append(tr("Public Packs"));
|
||||
}
|
||||
|
||||
if (!parseAndAddPacks(*thirdPartyPtr, PackType::ThirdParty, thirdPartyPacks)) {
|
||||
if (!parseAndAddPacks(*thirdPartyPtr, PackType::ThirdParty, m_thirdPartyPacks)) {
|
||||
failedLists.append(tr("Third Party Packs"));
|
||||
}
|
||||
|
||||
// NOTE(TheKodeToad): we don't want to reset the jobPtr earlier as it may invalidate the responses!
|
||||
jobPtr.reset();
|
||||
m_jobPtr.reset();
|
||||
|
||||
if (failedLists.size() > 0) {
|
||||
emit failed(tr("Failed to download some pack lists: %1").arg(failedLists.join("\n- ")));
|
||||
} else {
|
||||
emit finished(publicPacks, thirdPartyPacks);
|
||||
emit finished(m_publicPacks, m_thirdPartyPacks);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,12 +132,11 @@ bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, Modpac
|
|||
{
|
||||
QDomDocument doc;
|
||||
|
||||
QString errorMsg = "Unknown error.";
|
||||
int errorLine = -1;
|
||||
int errorCol = -1;
|
||||
auto result = doc.setContent(data);
|
||||
|
||||
if (!doc.setContent(data, false, &errorMsg, &errorLine, &errorCol)) {
|
||||
auto fullErrMsg = QString("Failed to fetch modpack data: %1 %2:%3!").arg(errorMsg).arg(errorLine).arg(errorCol);
|
||||
if (!result) {
|
||||
auto fullErrMsg =
|
||||
QString("Failed to fetch modpack data: %1 %2:%3!").arg(result.errorMessage).arg(result.errorLine).arg(result.errorColumn);
|
||||
qWarning() << fullErrMsg;
|
||||
return false;
|
||||
}
|
||||
|
|
@ -160,7 +157,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, Modpac
|
|||
modpack.bugged = false;
|
||||
|
||||
// remove empty if the xml is bugged
|
||||
for (QString curr : modpack.oldVersions) {
|
||||
for (const QString& curr : modpack.oldVersions) {
|
||||
if (curr.isNull() || curr.isEmpty()) {
|
||||
modpack.oldVersions.removeAll(curr);
|
||||
modpack.bugged = true;
|
||||
|
|
@ -191,7 +188,7 @@ bool PackFetchTask::parseAndAddPacks(QByteArray& data, PackType packType, Modpac
|
|||
return true;
|
||||
}
|
||||
|
||||
void PackFetchTask::fileDownloadFailed(QString reason)
|
||||
void PackFetchTask::fileDownloadFailed(const QString& reason)
|
||||
{
|
||||
qWarning() << "Fetching FTBPacks failed:" << reason;
|
||||
emit failed(reason);
|
||||
|
|
|
|||
|
|
@ -12,32 +12,32 @@ class PackFetchTask : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PackFetchTask(QNetworkAccessManager* network) : QObject(nullptr), m_network(network) {};
|
||||
virtual ~PackFetchTask() = default;
|
||||
explicit PackFetchTask(QNetworkAccessManager* network) : QObject(nullptr), m_network(network) {};
|
||||
~PackFetchTask() override = default;
|
||||
|
||||
void fetch();
|
||||
void fetchPrivate(const QStringList& toFetch);
|
||||
|
||||
private:
|
||||
QNetworkAccessManager* m_network;
|
||||
NetJob::Ptr jobPtr;
|
||||
NetJob::Ptr m_jobPtr;
|
||||
|
||||
bool parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list);
|
||||
ModpackList publicPacks;
|
||||
ModpackList thirdPartyPacks;
|
||||
static bool parseAndAddPacks(QByteArray& data, PackType packType, ModpackList& list);
|
||||
ModpackList m_publicPacks;
|
||||
ModpackList m_thirdPartyPacks;
|
||||
|
||||
protected slots:
|
||||
void fileDownloadFinished(QByteArray* publicResponse, QByteArray* thirdPartyResponse);
|
||||
void fileDownloadFailed(QString reason);
|
||||
void fileDownloadFinished(QByteArray* publicPtr, QByteArray* thirdPartyPtr);
|
||||
void fileDownloadFailed(const QString& reason);
|
||||
void fileDownloadAborted();
|
||||
|
||||
signals:
|
||||
void finished(ModpackList publicPacks, ModpackList thirdPartyPacks);
|
||||
void failed(QString reason);
|
||||
void failed(const QString& reason);
|
||||
void aborted();
|
||||
|
||||
void privateFileDownloadFinished(const Modpack& modpack);
|
||||
void privateFileDownloadFailed(QString reason, QString packCode);
|
||||
void privateFileDownloadFailed(const QString& reason, const QString& packCode);
|
||||
};
|
||||
|
||||
} // namespace LegacyFTB
|
||||
|
|
|
|||
|
|
@ -41,11 +41,7 @@
|
|||
#include <QDebug>
|
||||
#include "Application.h"
|
||||
|
||||
NewsChecker::NewsChecker(QNetworkAccessManager* network, const QString& feedUrl)
|
||||
{
|
||||
m_network = network;
|
||||
m_feedUrl = feedUrl;
|
||||
}
|
||||
NewsChecker::NewsChecker(QNetworkAccessManager* network, const QString& feedUrl) : m_feedUrl(feedUrl), m_network(network) {}
|
||||
|
||||
void NewsChecker::reloadNews()
|
||||
{
|
||||
|
|
@ -76,18 +72,18 @@ void NewsChecker::rssDownloadFinished()
|
|||
m_newsNetJob.reset();
|
||||
QDomDocument doc;
|
||||
{
|
||||
// Stuff to store error info in.
|
||||
QString errorMsg = "Unknown error.";
|
||||
int errorLine = -1;
|
||||
int errorCol = -1;
|
||||
|
||||
QFile feed(m_entry->getFullPath());
|
||||
|
||||
if (feed.open(QFile::ReadOnly | QFile::Text)) {
|
||||
QTextStream in(&feed);
|
||||
// Parse the XML.
|
||||
if (!doc.setContent(in.readAll(), false, &errorMsg, &errorLine, &errorCol)) {
|
||||
fail(QString("Error parsing RSS feed XML. %1 at %2:%3.").arg(errorMsg).arg(errorLine).arg(errorCol));
|
||||
auto result = doc.setContent(in.readAll());
|
||||
if (!result) {
|
||||
QString fullErrorMsg = QString("Error parsing RSS feed XML. %1 at %2:%3.")
|
||||
.arg(result.errorMessage)
|
||||
.arg(result.errorLine)
|
||||
.arg(result.errorColumn);
|
||||
fail(fullErrorMsg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -112,7 +108,7 @@ void NewsChecker::rssDownloadFinished()
|
|||
succeed();
|
||||
}
|
||||
|
||||
void NewsChecker::rssDownloadFailed(QString reason)
|
||||
void NewsChecker::rssDownloadFailed(const QString& reason)
|
||||
{
|
||||
// Set an error message and fail.
|
||||
fail(tr("Failed to load news RSS feed:\n%1").arg(reason));
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class NewsChecker : public QObject {
|
|||
|
||||
protected slots:
|
||||
void rssDownloadFinished();
|
||||
void rssDownloadFailed(QString reason);
|
||||
void rssDownloadFailed(const QString& reason);
|
||||
|
||||
protected: /* data */
|
||||
//! The URL for the RSS feed to fetch.
|
||||
|
|
@ -82,7 +82,7 @@ class NewsChecker : public QObject {
|
|||
NetJob::Ptr m_newsNetJob;
|
||||
|
||||
//! True if news has been loaded.
|
||||
bool m_loadedNews;
|
||||
bool m_loadedNews = false;
|
||||
|
||||
//! The cache entry for the feed.
|
||||
MetaEntryPtr m_entry;
|
||||
|
|
|
|||
|
|
@ -31,48 +31,50 @@
|
|||
#include <QJsonDocument>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <utility>
|
||||
|
||||
const QHash<ExportToModList::Formats, QString> ExportToModListDialog::exampleLines = {
|
||||
const QHash<ExportToModList::Formats, QString> ExportToModListDialog::ExampleLines = {
|
||||
{ ExportToModList::HTML, "<li><a href=\"{url}\">{name}</a> [{version}] by {authors}</li>" },
|
||||
{ ExportToModList::MARKDOWN, "[{name}]({url}) [{version}] by {authors}" },
|
||||
{ ExportToModList::PLAINTXT, "{name} ({url}) [{version}] by {authors}" },
|
||||
{ ExportToModList::JSON, "{\"name\":\"{name}\",\"url\":\"{url}\",\"version\":\"{version}\",\"authors\":\"{authors}\"}," },
|
||||
{ ExportToModList::JSON, R"({"name":"{name}","url":"{url}","version":"{version}","authors":"{authors}"},)" },
|
||||
{ ExportToModList::CSV, "{name},{url},{version},\"{authors}\"" },
|
||||
};
|
||||
|
||||
ExportToModListDialog::ExportToModListDialog(QString name, QList<Mod*> mods, QWidget* parent)
|
||||
: QDialog(parent), m_mods(mods), m_template_changed(false), m_name(name), ui(new Ui::ExportToModListDialog)
|
||||
: QDialog(parent), m_mods(std::move(mods)), m_templateChanged(false), m_name(std::move(name)), m_ui(new Ui::ExportToModListDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ui->setupUi(this);
|
||||
enableCustom(false);
|
||||
|
||||
connect(ui->formatComboBox, &QComboBox::currentIndexChanged, this, &ExportToModListDialog::formatChanged);
|
||||
connect(ui->authorsCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(ui->versionCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(ui->urlCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(ui->filenameCheckBox, &QCheckBox::stateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); });
|
||||
connect(ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); });
|
||||
connect(ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); });
|
||||
connect(ui->filenameButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::FileName); });
|
||||
connect(ui->templateText, &QTextEdit::textChanged, this, [this] {
|
||||
if (ui->templateText->toPlainText() != exampleLines[m_format])
|
||||
ui->formatComboBox->setCurrentIndex(5);
|
||||
connect(m_ui->formatComboBox, &QComboBox::currentIndexChanged, this, &ExportToModListDialog::formatChanged);
|
||||
connect(m_ui->authorsCheckBox, &QCheckBox::checkStateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(m_ui->versionCheckBox, &QCheckBox::checkStateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(m_ui->urlCheckBox, &QCheckBox::checkStateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(m_ui->filenameCheckBox, &QCheckBox::checkStateChanged, this, &ExportToModListDialog::trigger);
|
||||
connect(m_ui->authorsButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Authors); });
|
||||
connect(m_ui->versionButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Version); });
|
||||
connect(m_ui->urlButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::Url); });
|
||||
connect(m_ui->filenameButton, &QPushButton::clicked, this, [this](bool) { addExtra(ExportToModList::FileName); });
|
||||
connect(m_ui->templateText, &QTextEdit::textChanged, this, [this] {
|
||||
if (m_ui->templateText->toPlainText() != ExampleLines[m_format]) {
|
||||
m_ui->formatComboBox->setCurrentIndex(5);
|
||||
}
|
||||
triggerImp();
|
||||
});
|
||||
connect(ui->copyButton, &QPushButton::clicked, this, [this](bool) {
|
||||
this->ui->finalText->selectAll();
|
||||
this->ui->finalText->copy();
|
||||
connect(m_ui->copyButton, &QPushButton::clicked, this, [this](bool) {
|
||||
this->m_ui->finalText->selectAll();
|
||||
this->m_ui->finalText->copy();
|
||||
});
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Save)->setText(tr("Save"));
|
||||
triggerImp();
|
||||
}
|
||||
|
||||
ExportToModListDialog::~ExportToModListDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void ExportToModListDialog::formatChanged(int index)
|
||||
|
|
@ -80,41 +82,43 @@ void ExportToModListDialog::formatChanged(int index)
|
|||
switch (index) {
|
||||
case 0: {
|
||||
enableCustom(false);
|
||||
ui->resultText->show();
|
||||
m_ui->resultText->show();
|
||||
m_format = ExportToModList::HTML;
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
enableCustom(false);
|
||||
ui->resultText->show();
|
||||
m_ui->resultText->show();
|
||||
m_format = ExportToModList::MARKDOWN;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
enableCustom(false);
|
||||
ui->resultText->hide();
|
||||
m_ui->resultText->hide();
|
||||
m_format = ExportToModList::PLAINTXT;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
enableCustom(false);
|
||||
ui->resultText->hide();
|
||||
m_ui->resultText->hide();
|
||||
m_format = ExportToModList::JSON;
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
enableCustom(false);
|
||||
ui->resultText->hide();
|
||||
m_ui->resultText->hide();
|
||||
m_format = ExportToModList::CSV;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
m_template_changed = true;
|
||||
m_templateChanged = true;
|
||||
enableCustom(true);
|
||||
ui->resultText->hide();
|
||||
m_ui->resultText->hide();
|
||||
m_format = ExportToModList::CUSTOM;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
triggerImp();
|
||||
}
|
||||
|
|
@ -122,39 +126,40 @@ void ExportToModListDialog::formatChanged(int index)
|
|||
void ExportToModListDialog::triggerImp()
|
||||
{
|
||||
if (m_format == ExportToModList::CUSTOM) {
|
||||
ui->finalText->setPlainText(ExportToModList::exportToModList(m_mods, ui->templateText->toPlainText()));
|
||||
m_ui->finalText->setPlainText(ExportToModList::exportToModList(m_mods, m_ui->templateText->toPlainText()));
|
||||
return;
|
||||
}
|
||||
auto opt = 0;
|
||||
if (ui->authorsCheckBox->isChecked())
|
||||
auto opt = 0U;
|
||||
if (m_ui->authorsCheckBox->isChecked()) {
|
||||
opt |= ExportToModList::Authors;
|
||||
if (ui->versionCheckBox->isChecked())
|
||||
}
|
||||
if (m_ui->versionCheckBox->isChecked()) {
|
||||
opt |= ExportToModList::Version;
|
||||
if (ui->urlCheckBox->isChecked())
|
||||
}
|
||||
if (m_ui->urlCheckBox->isChecked()) {
|
||||
opt |= ExportToModList::Url;
|
||||
if (ui->filenameCheckBox->isChecked())
|
||||
}
|
||||
if (m_ui->filenameCheckBox->isChecked()) {
|
||||
opt |= ExportToModList::FileName;
|
||||
}
|
||||
auto txt = ExportToModList::exportToModList(m_mods, m_format, static_cast<ExportToModList::OptionalData>(opt));
|
||||
ui->finalText->setPlainText(txt);
|
||||
m_ui->finalText->setPlainText(txt);
|
||||
switch (m_format) {
|
||||
case ExportToModList::CUSTOM:
|
||||
return;
|
||||
case ExportToModList::HTML:
|
||||
ui->resultText->setHtml(StringUtils::htmlListPatch(txt));
|
||||
m_ui->resultText->setHtml(StringUtils::htmlListPatch(txt));
|
||||
break;
|
||||
case ExportToModList::MARKDOWN:
|
||||
ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt)));
|
||||
m_ui->resultText->setHtml(StringUtils::htmlListPatch(markdownToHTML(txt)));
|
||||
break;
|
||||
case ExportToModList::PLAINTXT:
|
||||
break;
|
||||
case ExportToModList::JSON:
|
||||
break;
|
||||
case ExportToModList::CSV:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
auto exampleLine = exampleLines[m_format];
|
||||
if (!m_template_changed && ui->templateText->toPlainText() != exampleLine)
|
||||
ui->templateText->setPlainText(exampleLine);
|
||||
auto exampleLine = ExampleLines[m_format];
|
||||
if (!m_templateChanged && m_ui->templateText->toPlainText() != exampleLine) {
|
||||
m_ui->templateText->setPlainText(exampleLine);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportToModListDialog::done(int result)
|
||||
|
|
@ -165,11 +170,12 @@ void ExportToModListDialog::done(int result)
|
|||
QFileDialog::getSaveFileName(this, tr("Export %1").arg(m_name), FS::PathCombine(QDir::homePath(), filename + extension()),
|
||||
tr("File") + " (*.txt *.html *.md *.json *.csv)", nullptr);
|
||||
|
||||
if (output.isEmpty())
|
||||
if (output.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
FS::write(output, ui->finalText->toPlainText().toUtf8());
|
||||
FS::write(output, m_ui->finalText->toPlainText().toUtf8());
|
||||
} catch (const FS::FileSystemException& e) {
|
||||
qCritical() << "Failed to save mod list file :" << e.cause();
|
||||
}
|
||||
|
|
@ -186,7 +192,6 @@ QString ExportToModListDialog::extension()
|
|||
case ExportToModList::MARKDOWN:
|
||||
return ".md";
|
||||
case ExportToModList::PLAINTXT:
|
||||
return ".txt";
|
||||
case ExportToModList::CUSTOM:
|
||||
return ".txt";
|
||||
case ExportToModList::JSON:
|
||||
|
|
@ -199,20 +204,21 @@ QString ExportToModListDialog::extension()
|
|||
|
||||
void ExportToModListDialog::addExtra(ExportToModList::OptionalData option)
|
||||
{
|
||||
if (m_format != ExportToModList::CUSTOM)
|
||||
if (m_format != ExportToModList::CUSTOM) {
|
||||
return;
|
||||
}
|
||||
switch (option) {
|
||||
case ExportToModList::Authors:
|
||||
ui->templateText->insertPlainText("{authors}");
|
||||
m_ui->templateText->insertPlainText("{authors}");
|
||||
break;
|
||||
case ExportToModList::Url:
|
||||
ui->templateText->insertPlainText("{url}");
|
||||
m_ui->templateText->insertPlainText("{url}");
|
||||
break;
|
||||
case ExportToModList::Version:
|
||||
ui->templateText->insertPlainText("{version}");
|
||||
m_ui->templateText->insertPlainText("{version}");
|
||||
break;
|
||||
case ExportToModList::FileName:
|
||||
ui->templateText->insertPlainText("{filename}");
|
||||
m_ui->templateText->insertPlainText("{filename}");
|
||||
break;
|
||||
case ExportToModList::None:
|
||||
break;
|
||||
|
|
@ -220,15 +226,15 @@ void ExportToModListDialog::addExtra(ExportToModList::OptionalData option)
|
|||
}
|
||||
void ExportToModListDialog::enableCustom(bool enabled)
|
||||
{
|
||||
ui->authorsCheckBox->setHidden(enabled);
|
||||
ui->authorsButton->setHidden(!enabled);
|
||||
m_ui->authorsCheckBox->setHidden(enabled);
|
||||
m_ui->authorsButton->setHidden(!enabled);
|
||||
|
||||
ui->versionCheckBox->setHidden(enabled);
|
||||
ui->versionButton->setHidden(!enabled);
|
||||
m_ui->versionCheckBox->setHidden(enabled);
|
||||
m_ui->versionButton->setHidden(!enabled);
|
||||
|
||||
ui->urlCheckBox->setHidden(enabled);
|
||||
ui->urlButton->setHidden(!enabled);
|
||||
m_ui->urlCheckBox->setHidden(enabled);
|
||||
m_ui->urlButton->setHidden(!enabled);
|
||||
|
||||
ui->filenameCheckBox->setHidden(enabled);
|
||||
ui->filenameButton->setHidden(!enabled);
|
||||
m_ui->filenameCheckBox->setHidden(enabled);
|
||||
m_ui->filenameButton->setHidden(!enabled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ class ExportToModListDialog : public QDialog {
|
|||
|
||||
public:
|
||||
explicit ExportToModListDialog(QString name, QList<Mod*> mods, QWidget* parent = nullptr);
|
||||
~ExportToModListDialog();
|
||||
~ExportToModListDialog() override;
|
||||
|
||||
void done(int result) override;
|
||||
|
||||
protected slots:
|
||||
void formatChanged(int index);
|
||||
void triggerImp();
|
||||
void trigger(int) { triggerImp(); };
|
||||
void trigger(Qt::CheckState /*unused*/) { triggerImp(); };
|
||||
void addExtra(ExportToModList::OptionalData option);
|
||||
|
||||
private:
|
||||
|
|
@ -47,9 +47,9 @@ class ExportToModListDialog : public QDialog {
|
|||
void enableCustom(bool enabled);
|
||||
|
||||
QList<Mod*> m_mods;
|
||||
bool m_template_changed;
|
||||
bool m_templateChanged;
|
||||
QString m_name;
|
||||
ExportToModList::Formats m_format = ExportToModList::Formats::HTML;
|
||||
Ui::ExportToModListDialog* ui;
|
||||
static const QHash<ExportToModList::Formats, QString> exampleLines;
|
||||
Ui::ExportToModListDialog* m_ui;
|
||||
static const QHash<ExportToModList::Formats, QString> ExampleLines;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,27 +46,28 @@
|
|||
#include <QClipboard>
|
||||
#include <QColor>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
#include <QPainterPath>
|
||||
#include <QSize>
|
||||
#include <QUrl>
|
||||
#include <QtWidgets/QPushButton>
|
||||
#include <span>
|
||||
|
||||
#include "qrencode.h"
|
||||
|
||||
MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MSALoginDialog)
|
||||
MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), m_ui(new Ui::MSALoginDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ui->setupUi(this);
|
||||
|
||||
// make font monospace
|
||||
QFont font;
|
||||
font.setPixelSize(ui->code->fontInfo().pixelSize());
|
||||
font.setPixelSize(m_ui->code->fontInfo().pixelSize());
|
||||
font.setFamily(APPLICATION->settings()->get("ConsoleFont").toString());
|
||||
font.setStyleHint(QFont::Monospace);
|
||||
font.setFixedPitch(true);
|
||||
ui->code->setFont(font);
|
||||
m_ui->code->setFont(font);
|
||||
|
||||
connect(ui->copyCode, &QPushButton::clicked, this, [this] { QApplication::clipboard()->setText(ui->code->text()); });
|
||||
connect(ui->loginButton, &QPushButton::clicked, this, [this] {
|
||||
connect(m_ui->copyCode, &QPushButton::clicked, this, [this] { QApplication::clipboard()->setText(m_ui->code->text()); });
|
||||
connect(m_ui->loginButton, &QPushButton::clicked, this, [this] {
|
||||
if (m_url.isValid()) {
|
||||
if (!DesktopServices::openUrl(m_url)) {
|
||||
QApplication::clipboard()->setText(m_url.toString());
|
||||
|
|
@ -74,79 +75,80 @@ MSALoginDialog::MSALoginDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MS
|
|||
}
|
||||
});
|
||||
|
||||
ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
}
|
||||
|
||||
int MSALoginDialog::exec()
|
||||
{
|
||||
// Setup the login task and start it
|
||||
m_account = MinecraftAccount::createBlankMSA();
|
||||
m_authflow_task = m_account->login(false);
|
||||
connect(m_authflow_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
|
||||
connect(m_authflow_task.get(), &Task::succeeded, this, &QDialog::accept);
|
||||
connect(m_authflow_task.get(), &Task::aborted, this, &MSALoginDialog::reject);
|
||||
connect(m_authflow_task.get(), &Task::status, this, &MSALoginDialog::onAuthFlowStatus);
|
||||
connect(m_authflow_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
|
||||
connect(m_authflow_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflow_task.get(), &Task::abort);
|
||||
m_authflowTask = m_account->login(false);
|
||||
connect(m_authflowTask.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
|
||||
connect(m_authflowTask.get(), &Task::succeeded, this, &QDialog::accept);
|
||||
connect(m_authflowTask.get(), &Task::aborted, this, &MSALoginDialog::reject);
|
||||
connect(m_authflowTask.get(), &Task::status, this, &MSALoginDialog::onAuthFlowStatus);
|
||||
connect(m_authflowTask.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
|
||||
connect(m_authflowTask.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
|
||||
connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflowTask.get(), &Task::abort);
|
||||
|
||||
m_devicecode_task.reset(new AuthFlow(m_account->accountData(), AuthFlow::Action::DeviceCode));
|
||||
connect(m_devicecode_task.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
|
||||
connect(m_devicecode_task.get(), &Task::succeeded, this, &QDialog::accept);
|
||||
connect(m_devicecode_task.get(), &Task::aborted, this, &MSALoginDialog::reject);
|
||||
connect(m_devicecode_task.get(), &Task::status, this, &MSALoginDialog::onDeviceFlowStatus);
|
||||
connect(m_devicecode_task.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
|
||||
connect(m_devicecode_task.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecode_task.get(), &Task::abort);
|
||||
QMetaObject::invokeMethod(m_authflow_task.get(), &Task::start, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_devicecode_task.get(), &Task::start, Qt::QueuedConnection);
|
||||
m_devicecodeTask.reset(new AuthFlow(m_account->accountData(), AuthFlow::Action::DeviceCode));
|
||||
connect(m_devicecodeTask.get(), &Task::failed, this, &MSALoginDialog::onTaskFailed);
|
||||
connect(m_devicecodeTask.get(), &Task::succeeded, this, &QDialog::accept);
|
||||
connect(m_devicecodeTask.get(), &Task::aborted, this, &MSALoginDialog::reject);
|
||||
connect(m_devicecodeTask.get(), &Task::status, this, &MSALoginDialog::onDeviceFlowStatus);
|
||||
connect(m_devicecodeTask.get(), &AuthFlow::authorizeWithBrowser, this, &MSALoginDialog::authorizeWithBrowser);
|
||||
connect(m_devicecodeTask.get(), &AuthFlow::authorizeWithBrowserWithExtra, this, &MSALoginDialog::authorizeWithBrowserWithExtra);
|
||||
connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecodeTask.get(), &Task::abort);
|
||||
QMetaObject::invokeMethod(m_authflowTask.get(), &Task::start, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_devicecodeTask.get(), &Task::start, Qt::QueuedConnection);
|
||||
|
||||
return QDialog::exec();
|
||||
}
|
||||
|
||||
MSALoginDialog::~MSALoginDialog()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void MSALoginDialog::onTaskFailed(QString reason)
|
||||
void MSALoginDialog::onTaskFailed(const QString& reason)
|
||||
{
|
||||
// Set message
|
||||
m_authflow_task->disconnect();
|
||||
m_devicecode_task->disconnect();
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
m_authflowTask->disconnect();
|
||||
m_devicecodeTask->disconnect();
|
||||
m_ui->stackedWidget->setCurrentIndex(0);
|
||||
auto lines = reason.split('\n');
|
||||
QString processed;
|
||||
for (auto line : lines) {
|
||||
if (line.size()) {
|
||||
for (const auto& line : lines) {
|
||||
if (line.size() != 0) {
|
||||
processed += "<font color='red'>" + line + "</font><br />";
|
||||
} else {
|
||||
processed += "<br />";
|
||||
}
|
||||
}
|
||||
ui->status->setText(processed);
|
||||
auto task = m_authflow_task;
|
||||
m_ui->status->setText(processed);
|
||||
auto task = m_authflowTask;
|
||||
if (task->failReason().isEmpty()) {
|
||||
task = m_devicecode_task;
|
||||
task = m_devicecodeTask;
|
||||
}
|
||||
if (task) {
|
||||
ui->loadingLabel->setText(task->getStatus());
|
||||
m_ui->loadingLabel->setText(task->getStatus());
|
||||
}
|
||||
disconnect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflow_task.get(), &Task::abort);
|
||||
disconnect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecode_task.get(), &Task::abort);
|
||||
connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MSALoginDialog::reject);
|
||||
disconnect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_authflowTask.get(), &Task::abort);
|
||||
disconnect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, m_devicecodeTask.get(), &Task::abort);
|
||||
connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &MSALoginDialog::reject);
|
||||
}
|
||||
|
||||
void MSALoginDialog::authorizeWithBrowser(const QUrl& url)
|
||||
{
|
||||
ui->stackedWidget2->setCurrentIndex(1);
|
||||
ui->stackedWidget2->adjustSize();
|
||||
ui->stackedWidget2->updateGeometry();
|
||||
m_ui->stackedWidget2->setCurrentIndex(1);
|
||||
m_ui->stackedWidget2->adjustSize();
|
||||
m_ui->stackedWidget2->updateGeometry();
|
||||
this->adjustSize();
|
||||
ui->loginButton->setToolTip(QString("<div style='width: 200px;'>%1</div>").arg(url.toString()));
|
||||
m_ui->loginButton->setToolTip(QString("<div style='width: 200px;'>%1</div>").arg(url.toString()));
|
||||
m_url = url;
|
||||
}
|
||||
|
||||
namespace {
|
||||
void paintQR(QPainter& painter, const QSize canvasSize, const QString& data, QColor fg)
|
||||
{
|
||||
const auto* qr = QRcode_encodeString(data.toUtf8().constData(), 0, QRecLevel::QR_ECLEVEL_M, QRencodeMode::QR_MODE_8, 1);
|
||||
|
|
@ -165,32 +167,39 @@ void paintQR(QPainter& painter, const QSize canvasSize, const QString& data, QCo
|
|||
const auto scale = 0.8 * std::min(canvasWidth / qrSize, canvasHeight / qrSize);
|
||||
|
||||
// Find an offset to center it in the canvas
|
||||
const auto offsetX = (canvasWidth - qrSize * scale) / 2;
|
||||
const auto offsetY = (canvasHeight - qrSize * scale) / 2;
|
||||
const auto offsetX = (canvasWidth - (qrSize * scale)) / 2;
|
||||
const auto offsetY = (canvasHeight - (qrSize * scale)) / 2;
|
||||
|
||||
auto qrData = std::span(qr->data, static_cast<long>(qrSize) * qrSize);
|
||||
for (int y = 0; y < qrSize; y++) {
|
||||
for (int x = 0; x < qrSize; x++) {
|
||||
auto shouldFillIn = qr->data[y * qrSize + x] & 1;
|
||||
if (shouldFillIn) {
|
||||
QRectF r(offsetX + x * scale, offsetY + y * scale, scale, scale);
|
||||
auto shouldFillIn = qrData[(y * qrSize) + x] & 1U;
|
||||
if (shouldFillIn != 0) {
|
||||
QRectF r(offsetX + (x * scale), offsetY + (y * scale), scale, scale);
|
||||
painter.drawRects(&r, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, [[maybe_unused]] int expiresIn)
|
||||
void MSALoginDialog::authorizeWithBrowserWithExtra(const QUrl& verificationUrl, const QString& code, const QUrl& completeVerificationUrl)
|
||||
{
|
||||
ui->stackedWidget->setCurrentIndex(1);
|
||||
ui->stackedWidget->adjustSize();
|
||||
ui->stackedWidget->updateGeometry();
|
||||
m_ui->stackedWidget->setCurrentIndex(1);
|
||||
m_ui->stackedWidget->adjustSize();
|
||||
m_ui->stackedWidget->updateGeometry();
|
||||
this->adjustSize();
|
||||
|
||||
auto url = verificationUrl.toString();
|
||||
if (completeVerificationUrl.isValid()) {
|
||||
url = completeVerificationUrl.toString();
|
||||
}
|
||||
|
||||
const auto linkString = QString("<a href=\"%1\">%2</a>").arg(url, url);
|
||||
if (url == "https://www.microsoft.com/link" && !code.isEmpty()) {
|
||||
if (!completeVerificationUrl.isValid() && url == "https://www.microsoft.com/link" && !code.isEmpty()) {
|
||||
url += QString("?otc=%1").arg(code);
|
||||
}
|
||||
ui->code->setText(code);
|
||||
m_ui->code->setText(code);
|
||||
|
||||
auto size = QSize(150, 150);
|
||||
QPixmap pixmap(size);
|
||||
|
|
@ -200,27 +209,27 @@ void MSALoginDialog::authorizeWithBrowserWithExtra(QString url, QString code, [[
|
|||
paintQR(painter, size, url, Qt::black);
|
||||
|
||||
// Set the generated pixmap to the label
|
||||
ui->qr->setPixmap(pixmap);
|
||||
m_ui->qr->setPixmap(pixmap);
|
||||
|
||||
ui->qrMessage->setText(tr("Open %1 or scan the QR and enter the above code if needed.").arg(linkString));
|
||||
m_ui->qrMessage->setText(tr("Open %1 or scan the QR and enter the above code if needed.").arg(linkString));
|
||||
}
|
||||
|
||||
void MSALoginDialog::onDeviceFlowStatus(QString status)
|
||||
void MSALoginDialog::onDeviceFlowStatus(const QString& status)
|
||||
{
|
||||
ui->stackedWidget->setCurrentIndex(0);
|
||||
ui->stackedWidget->adjustSize();
|
||||
ui->stackedWidget->updateGeometry();
|
||||
m_ui->stackedWidget->setCurrentIndex(0);
|
||||
m_ui->stackedWidget->adjustSize();
|
||||
m_ui->stackedWidget->updateGeometry();
|
||||
this->adjustSize();
|
||||
ui->status->setText(status);
|
||||
m_ui->status->setText(status);
|
||||
}
|
||||
|
||||
void MSALoginDialog::onAuthFlowStatus(QString status)
|
||||
void MSALoginDialog::onAuthFlowStatus(const QString& status)
|
||||
{
|
||||
ui->stackedWidget2->setCurrentIndex(0);
|
||||
ui->stackedWidget2->adjustSize();
|
||||
ui->stackedWidget2->updateGeometry();
|
||||
m_ui->stackedWidget2->setCurrentIndex(0);
|
||||
m_ui->stackedWidget2->adjustSize();
|
||||
m_ui->stackedWidget2->updateGeometry();
|
||||
this->adjustSize();
|
||||
ui->status2->setText(status);
|
||||
m_ui->status2->setText(status);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
|
|
|
|||
|
|
@ -29,26 +29,26 @@ class MSALoginDialog : public QDialog {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~MSALoginDialog();
|
||||
~MSALoginDialog() override;
|
||||
|
||||
static MinecraftAccountPtr newAccount(QWidget* parent);
|
||||
int exec() override;
|
||||
|
||||
private:
|
||||
explicit MSALoginDialog(QWidget* parent = 0);
|
||||
explicit MSALoginDialog(QWidget* parent = nullptr);
|
||||
|
||||
protected slots:
|
||||
void onTaskFailed(QString reason);
|
||||
void onDeviceFlowStatus(QString status);
|
||||
void onAuthFlowStatus(QString status);
|
||||
void onTaskFailed(const QString& reason);
|
||||
void onDeviceFlowStatus(const QString& status);
|
||||
void onAuthFlowStatus(const QString& status);
|
||||
void authorizeWithBrowser(const QUrl& url);
|
||||
void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn);
|
||||
void authorizeWithBrowserWithExtra(const QUrl& verificationUrl, const QString& code, const QUrl& completeVerificationUrl);
|
||||
|
||||
private:
|
||||
Ui::MSALoginDialog* ui;
|
||||
Ui::MSALoginDialog* m_ui;
|
||||
MinecraftAccountPtr m_account;
|
||||
shared_qobject_ptr<AuthFlow> m_devicecode_task;
|
||||
shared_qobject_ptr<AuthFlow> m_authflow_task;
|
||||
shared_qobject_ptr<AuthFlow> m_devicecodeTask;
|
||||
shared_qobject_ptr<AuthFlow> m_authflowTask;
|
||||
|
||||
QUrl m_url;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -34,10 +34,9 @@
|
|||
#include <QUrl>
|
||||
|
||||
#include "Application.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "DesktopServices.h"
|
||||
#include "Json.h"
|
||||
#include "QObjectPtr.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
|
||||
#include "minecraft/auth/Parsers.h"
|
||||
#include "minecraft/skins/CapeChange.h"
|
||||
|
|
@ -54,7 +53,7 @@
|
|||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/instanceview/InstanceDelegate.h"
|
||||
|
||||
SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
||||
SkinManageDialog::SkinManageDialog(QWidget* parent, const MinecraftAccountPtr& acct)
|
||||
: QDialog(parent), m_acct(acct), m_ui(new Ui::SkinManageDialog), m_list(this, APPLICATION->settings()->get("SkinsDir").toString(), acct)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
|
@ -68,7 +67,7 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
|
||||
setWindowModality(Qt::WindowModal);
|
||||
|
||||
auto contentsWidget = m_ui->listView;
|
||||
auto* contentsWidget = m_ui->listView;
|
||||
contentsWidget->setViewMode(QListView::IconMode);
|
||||
contentsWidget->setFlow(QListView::LeftToRight);
|
||||
contentsWidget->setIconSize(QSize(48, 48));
|
||||
|
|
@ -98,7 +97,7 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
|
||||
connect(contentsWidget->selectionModel(), &QItemSelectionModel::selectionChanged, this, &SkinManageDialog::selectionChanged);
|
||||
connect(m_ui->listView, &QListView::customContextMenuRequested, this, &SkinManageDialog::show_context_menu);
|
||||
connect(m_ui->elytraCB, &QCheckBox::stateChanged, this, [this]() {
|
||||
connect(m_ui->elytraCB, &QCheckBox::checkStateChanged, this, [this]() {
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->setElytraVisible(m_ui->elytraCB->isChecked());
|
||||
}
|
||||
|
|
@ -122,9 +121,8 @@ SkinManageDialog::SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct)
|
|||
SkinManageDialog::~SkinManageDialog()
|
||||
{
|
||||
delete m_ui;
|
||||
if (m_skinPreview) {
|
||||
delete m_skinPreview;
|
||||
}
|
||||
|
||||
delete m_skinPreview;
|
||||
}
|
||||
|
||||
void SkinManageDialog::activated(QModelIndex index)
|
||||
|
|
@ -133,18 +131,21 @@ void SkinManageDialog::activated(QModelIndex index)
|
|||
accept();
|
||||
}
|
||||
|
||||
void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]] QItemSelection deselected)
|
||||
void SkinManageDialog::selectionChanged(const QItemSelection& selected, [[maybe_unused]] const QItemSelection& deselected)
|
||||
{
|
||||
if (selected.empty())
|
||||
if (selected.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString key = selected.first().indexes().first().data(Qt::UserRole).toString();
|
||||
if (key.isEmpty())
|
||||
if (key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_selectedSkinKey = key;
|
||||
auto skin = getSelectedSkin();
|
||||
if (!skin)
|
||||
auto* skin = getSelectedSkin();
|
||||
if (!skin) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
|
|
@ -157,10 +158,10 @@ void SkinManageDialog::selectionChanged(QItemSelection selected, [[maybe_unused]
|
|||
m_ui->alexBtn->setChecked(skin->getModel() == SkinModel::SLIM);
|
||||
}
|
||||
|
||||
void SkinManageDialog::delayed_scroll(QModelIndex model_index)
|
||||
void SkinManageDialog::delayed_scroll(QModelIndex modelIndex)
|
||||
{
|
||||
auto contentsWidget = m_ui->listView;
|
||||
contentsWidget->scrollTo(model_index);
|
||||
auto* contentsWidget = m_ui->listView;
|
||||
contentsWidget->scrollTo(modelIndex);
|
||||
}
|
||||
|
||||
void SkinManageDialog::on_openDirBtn_clicked()
|
||||
|
|
@ -171,24 +172,25 @@ void SkinManageDialog::on_openDirBtn_clicked()
|
|||
void SkinManageDialog::on_fileBtn_clicked()
|
||||
{
|
||||
auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString();
|
||||
QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter);
|
||||
if (raw_path.isNull()) {
|
||||
QString rawPath = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter);
|
||||
if (rawPath.isNull()) {
|
||||
return;
|
||||
}
|
||||
auto message = m_list.installSkin(raw_path, {});
|
||||
auto message = m_list.installSkin(rawPath, {});
|
||||
if (!message.isEmpty()) {
|
||||
CustomMessageBox::selectable(this, tr("Selected file is not a valid skin"), message, QMessageBox::Critical)->show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
QPixmap previewCape(QImage capeImage, bool elytra = false)
|
||||
namespace {
|
||||
QPixmap previewCape(const QImage& capeImage, bool elytra = false)
|
||||
{
|
||||
if (elytra) {
|
||||
auto wing = capeImage.copy(34, 2, 12, 20);
|
||||
QImage mirrored = wing.mirrored(true, false);
|
||||
QImage mirrored = wing.flipped(Qt::Horizontal);
|
||||
|
||||
QImage combined(wing.width() * 2 + 1, wing.height() + 14, capeImage.format());
|
||||
QImage combined((wing.width() * 2) + 1, wing.height() + 14, capeImage.format());
|
||||
combined.fill(Qt::transparent);
|
||||
|
||||
QPainter painter(&combined);
|
||||
|
|
@ -199,6 +201,7 @@ QPixmap previewCape(QImage capeImage, bool elytra = false)
|
|||
}
|
||||
return QPixmap::fromImage(capeImage.copy(1, 1, 10, 16).scaled(80, 128, Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SkinManageDialog::setupCapes()
|
||||
{
|
||||
|
|
@ -216,7 +219,7 @@ void SkinManageDialog::setupCapes()
|
|||
bool needsToDownload = false;
|
||||
for (auto& cape : accountData.minecraftProfile.capes) {
|
||||
auto path = FS::PathCombine(capesDir, cape.id + ".png");
|
||||
if (cape.data.size()) {
|
||||
if (cape.data.size() != 0) {
|
||||
QImage capeImage;
|
||||
if (capeImage.loadFromData(cape.data, "PNG") && capeImage.save(path)) {
|
||||
m_capes[cape.id] = capeImage;
|
||||
|
|
@ -254,7 +257,7 @@ void SkinManageDialog::setupCapes()
|
|||
}
|
||||
}
|
||||
|
||||
void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
||||
void SkinManageDialog::on_capeCombo_currentIndexChanged(int /*index*/)
|
||||
{
|
||||
auto id = m_ui->capeCombo->currentData();
|
||||
auto cape = m_capes.value(id.toString(), {});
|
||||
|
|
@ -267,7 +270,7 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
|||
if (m_skinPreview) {
|
||||
m_skinPreview->updateCape(cape);
|
||||
}
|
||||
if (auto skin = getSelectedSkin(); skin) {
|
||||
if (auto* skin = getSelectedSkin(); skin) {
|
||||
skin->setCapeId(id.toString());
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
|
|
@ -280,7 +283,7 @@ void SkinManageDialog::on_capeCombo_currentIndexChanged(int index)
|
|||
|
||||
void SkinManageDialog::on_steveBtn_toggled(bool checked)
|
||||
{
|
||||
if (auto skin = getSelectedSkin(); skin) {
|
||||
if (auto* skin = getSelectedSkin(); skin) {
|
||||
skin->setModel(checked ? SkinModel::CLASSIC : SkinModel::SLIM);
|
||||
if (m_skinPreview) {
|
||||
m_skinPreview->updateScene(skin);
|
||||
|
|
@ -293,7 +296,7 @@ void SkinManageDialog::on_steveBtn_toggled(bool checked)
|
|||
|
||||
void SkinManageDialog::accept()
|
||||
{
|
||||
auto skin = m_list.skin(m_selectedSkinKey);
|
||||
auto* skin = m_list.skin(m_selectedSkinKey);
|
||||
if (!skin) {
|
||||
reject();
|
||||
return;
|
||||
|
|
@ -353,7 +356,7 @@ bool SkinManageDialog::eventFilter(QObject* obj, QEvent* ev)
|
|||
{
|
||||
if (obj == m_ui->listView) {
|
||||
if (ev->type() == QEvent::KeyPress) {
|
||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(ev);
|
||||
auto* keyEvent = static_cast<QKeyEvent*>(ev);
|
||||
switch (keyEvent->key()) {
|
||||
case Qt::Key_Delete:
|
||||
on_action_Delete_Skin_triggered(false);
|
||||
|
|
@ -369,26 +372,28 @@ bool SkinManageDialog::eventFilter(QObject* obj, QEvent* ev)
|
|||
return QDialog::eventFilter(obj, ev);
|
||||
}
|
||||
|
||||
void SkinManageDialog::on_action_Rename_Skin_triggered(bool)
|
||||
void SkinManageDialog::on_action_Rename_Skin_triggered(bool /*unused*/)
|
||||
{
|
||||
if (!m_selectedSkinKey.isEmpty()) {
|
||||
m_ui->listView->edit(m_ui->listView->currentIndex());
|
||||
}
|
||||
}
|
||||
|
||||
void SkinManageDialog::on_action_Delete_Skin_triggered(bool)
|
||||
void SkinManageDialog::on_action_Delete_Skin_triggered(bool /*unused*/)
|
||||
{
|
||||
if (m_selectedSkinKey.isEmpty())
|
||||
if (m_selectedSkinKey.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_list.getSkinIndex(m_selectedSkinKey) == m_list.getSelectedAccountSkin()) {
|
||||
CustomMessageBox::selectable(this, tr("Delete error"), tr("Can not delete skin that is in use."), QMessageBox::Warning)->exec();
|
||||
return;
|
||||
}
|
||||
|
||||
auto skin = m_list.skin(m_selectedSkinKey);
|
||||
if (!skin)
|
||||
auto* skin = m_list.skin(m_selectedSkinKey);
|
||||
if (!skin) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto response = CustomMessageBox::selectable(this, tr("Confirm Deletion"),
|
||||
tr("You are about to delete \"%1\".\n"
|
||||
|
|
@ -435,10 +440,11 @@ void SkinManageDialog::on_urlBtn_clicked()
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
class WaitTask : public Task {
|
||||
public:
|
||||
WaitTask() : m_loop(), m_done(false) {};
|
||||
virtual ~WaitTask() = default;
|
||||
WaitTask() = default;
|
||||
~WaitTask() override = default;
|
||||
|
||||
public slots:
|
||||
void quit()
|
||||
|
|
@ -448,17 +454,19 @@ class WaitTask : public Task {
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual void executeTask()
|
||||
void executeTask() override
|
||||
{
|
||||
if (!m_done)
|
||||
if (!m_done) {
|
||||
m_loop.exec();
|
||||
}
|
||||
emitSucceeded();
|
||||
};
|
||||
|
||||
private:
|
||||
QEventLoop m_loop;
|
||||
bool m_done;
|
||||
bool m_done{};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void SkinManageDialog::on_userBtn_clicked()
|
||||
{
|
||||
|
|
@ -482,29 +490,29 @@ void SkinManageDialog::on_userBtn_clicked()
|
|||
QString failReason;
|
||||
|
||||
connect(getUUID.get(), &Task::aborted, uuidLoop.get(), &WaitTask::quit);
|
||||
connect(getUUID.get(), &Task::failed, this, [&failReason](QString reason) {
|
||||
connect(getUUID.get(), &Task::failed, this, [&failReason](const QString& reason) {
|
||||
qCritical() << "Couldn't get user UUID:" << reason;
|
||||
failReason = tr("failed to get user UUID");
|
||||
});
|
||||
connect(getUUID.get(), &Task::failed, uuidLoop.get(), &WaitTask::quit);
|
||||
connect(getProfile.get(), &Task::aborted, profileLoop.get(), &WaitTask::quit);
|
||||
connect(getProfile.get(), &Task::failed, profileLoop.get(), &WaitTask::quit);
|
||||
connect(getProfile.get(), &Task::failed, this, [&failReason](QString reason) {
|
||||
connect(getProfile.get(), &Task::failed, this, [&failReason](const QString& reason) {
|
||||
qCritical() << "Couldn't get user profile:" << reason;
|
||||
failReason = tr("failed to get user profile");
|
||||
});
|
||||
connect(downloadSkin.get(), &Task::failed, this, [&failReason](QString reason) {
|
||||
connect(downloadSkin.get(), &Task::failed, this, [&failReason](const QString& reason) {
|
||||
qCritical() << "Couldn't download skin:" << reason;
|
||||
failReason = tr("failed to download skin");
|
||||
});
|
||||
|
||||
connect(getUUID.get(), &Task::succeeded, this, [uuidLoop, uuidOut, job, getProfile, &failReason] {
|
||||
try {
|
||||
QJsonParseError parse_error{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*uuidOut, &parse_error);
|
||||
if (parse_error.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Minecraft skin service at" << parse_error.offset
|
||||
<< "reason:" << parse_error.errorString();
|
||||
QJsonParseError parseError{};
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*uuidOut, &parseError);
|
||||
if (parseError.error != QJsonParseError::NoError) {
|
||||
qWarning() << "Error while parsing JSON response from Minecraft skin service at" << parseError.offset
|
||||
<< "reason:" << parseError.errorString();
|
||||
failReason = tr("failed to parse get user UUID response");
|
||||
uuidLoop->quit();
|
||||
return;
|
||||
|
|
@ -564,7 +572,7 @@ void SkinManageDialog::on_userBtn_clicked()
|
|||
|
||||
void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
||||
{
|
||||
QWidget::resizeEvent(event);
|
||||
QDialog::resizeEvent(event);
|
||||
QSize s = size() * (1. / 3);
|
||||
|
||||
auto id = m_ui->capeCombo->currentData();
|
||||
|
|
@ -574,7 +582,7 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
|||
} else {
|
||||
m_ui->capeImage->clear();
|
||||
}
|
||||
if (auto skin = getSelectedSkin(); skin && !m_skinPreview) {
|
||||
if (auto* skin = getSelectedSkin(); skin && !m_skinPreview) {
|
||||
m_skinPreviewLabel->setPixmap(
|
||||
QPixmap::fromImage(skin->getPreview()).scaled(m_skinPreviewLabel->size(), Qt::KeepAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
|
|
@ -582,7 +590,7 @@ void SkinManageDialog::resizeEvent(QResizeEvent* event)
|
|||
|
||||
SkinModel* SkinManageDialog::getSelectedSkin()
|
||||
{
|
||||
if (auto skin = m_list.skin(m_selectedSkinKey); skin && skin->isValid()) {
|
||||
if (auto* skin = m_list.skin(m_selectedSkinKey); skin && skin->isValid()) {
|
||||
return skin;
|
||||
}
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -34,15 +34,15 @@ class SkinManageDialog;
|
|||
class SkinManageDialog : public QDialog, public SkinProvider {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SkinManageDialog(QWidget* parent, MinecraftAccountPtr acct);
|
||||
explicit SkinManageDialog(QWidget* parent, const MinecraftAccountPtr& acct);
|
||||
virtual ~SkinManageDialog();
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
|
||||
virtual SkinModel* getSelectedSkin() override;
|
||||
virtual QHash<QString, QImage> capes() override;
|
||||
SkinModel* getSelectedSkin() override;
|
||||
QHash<QString, QImage> capes() override;
|
||||
|
||||
public slots:
|
||||
void selectionChanged(QItemSelection, QItemSelection);
|
||||
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
|
||||
void activated(QModelIndex);
|
||||
void delayed_scroll(QModelIndex);
|
||||
void on_openDirBtn_clicked();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,10 @@
|
|||
#include <QOpenGLWindow>
|
||||
|
||||
namespace opengl {
|
||||
Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctions(), m_slim(slim), m_capeVisible(!cape.isNull())
|
||||
Scene::Scene(const QImage& skin, bool slim, const QImage& cape)
|
||||
: m_cape(new opengl::BoxGeometry(QVector3D(10, 16, 1), QVector3D(0, -8, 2.5), QPoint(0, 0), QVector3D(10, 16, 1), QSize(64, 32)))
|
||||
, m_slim(slim)
|
||||
, m_capeVisible(!cape.isNull())
|
||||
{
|
||||
initializeOpenGLFunctions();
|
||||
m_staticComponents = {
|
||||
|
|
@ -34,9 +37,9 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
// body
|
||||
new opengl::BoxGeometry(QVector3D(8, 12, 4), QVector3D(0, -6, 0), QPoint(16, 16), QVector3D(8, 12, 4)),
|
||||
// right leg
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9f, -18, -0.1f), QPoint(0, 16), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(-1.9F, -18, -0.1F), QPoint(0, 16), QVector3D(4, 12, 4)),
|
||||
// left leg
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9f, -18, -0.1f), QPoint(16, 48), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4, 12, 4), QVector3D(1.9F, -18, -0.1F), QPoint(16, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_staticComponentsOverlay = {
|
||||
|
|
@ -45,9 +48,9 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
// body
|
||||
new opengl::BoxGeometry(QVector3D(8.5, 12.5, 4.5), QVector3D(0, -6, 0), QPoint(16, 32), QVector3D(8, 12, 4)),
|
||||
// right leg
|
||||
new opengl::BoxGeometry(QVector3D(4.5f, 12.5f, 4.5f), QVector3D(-1.9f, -18, -0.1f), QPoint(0, 32), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4.5F, 12.5F, 4.5F), QVector3D(-1.9F, -18, -0.1F), QPoint(0, 32), QVector3D(4, 12, 4)),
|
||||
// left leg
|
||||
new opengl::BoxGeometry(QVector3D(4.5f, 12.5f, 4.5f), QVector3D(1.9f, -18, -0.1f), QPoint(0, 48), QVector3D(4, 12, 4)),
|
||||
new opengl::BoxGeometry(QVector3D(4.5F, 12.5F, 4.5F), QVector3D(1.9F, -18, -0.1F), QPoint(0, 48), QVector3D(4, 12, 4)),
|
||||
};
|
||||
|
||||
m_normalArms = {
|
||||
|
|
@ -78,16 +81,15 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
new opengl::BoxGeometry(QVector3D(3.5, 12.5, 4.5), QVector3D(5.5, -6, 0), QPoint(48, 48), QVector3D(3, 12, 4)),
|
||||
};
|
||||
|
||||
m_cape = new opengl::BoxGeometry(QVector3D(10, 16, 1), QVector3D(0, -8, 2.5), QPoint(0, 0), QVector3D(10, 16, 1), QSize(64, 32));
|
||||
m_cape->rotate(10.8f, QVector3D(1, 0, 0));
|
||||
m_cape->rotate(10.8F, QVector3D(1, 0, 0));
|
||||
m_cape->rotate(180, QVector3D(0, 1, 0));
|
||||
|
||||
auto leftWing =
|
||||
auto* leftWing =
|
||||
new opengl::BoxGeometry(QVector3D(12, 22, 4), QVector3D(0, -13, -2), QPoint(22, 0), QVector3D(10, 20, 2), QSize(64, 32));
|
||||
leftWing->rotate(15, QVector3D(1, 0, 0));
|
||||
leftWing->rotate(15, QVector3D(0, 0, 1));
|
||||
leftWing->rotate(1, QVector3D(1, 0, 0));
|
||||
auto rightWing =
|
||||
auto* rightWing =
|
||||
new opengl::BoxGeometry(QVector3D(12, 22, 4), QVector3D(0, -13, -2), QPoint(22, 0), QVector3D(10, 20, 2), QSize(64, 32));
|
||||
rightWing->scale(QVector3D(-1, 1, 1));
|
||||
rightWing->rotate(15, QVector3D(1, 0, 0));
|
||||
|
|
@ -96,19 +98,19 @@ Scene::Scene(const QImage& skin, bool slim, const QImage& cape) : QOpenGLFunctio
|
|||
m_elytra << leftWing << rightWing;
|
||||
|
||||
// texture init
|
||||
m_skinTexture = new QOpenGLTexture(skin.mirrored());
|
||||
m_skinTexture = new QOpenGLTexture(skin.flipped());
|
||||
m_skinTexture->setMinificationFilter(QOpenGLTexture::Nearest);
|
||||
m_skinTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
|
||||
|
||||
m_capeTexture = new QOpenGLTexture(cape.mirrored());
|
||||
m_capeTexture = new QOpenGLTexture(cape.flipped());
|
||||
m_capeTexture->setMinificationFilter(QOpenGLTexture::Nearest);
|
||||
m_capeTexture->setMagnificationFilter(QOpenGLTexture::Nearest);
|
||||
}
|
||||
Scene::~Scene()
|
||||
{
|
||||
for (auto array :
|
||||
for (const auto& array :
|
||||
{ m_staticComponents, m_normalArms, m_slimArms, m_elytra, m_staticComponentsOverlay, m_normalArmsOverlay, m_slimArmsOverlay }) {
|
||||
for (auto g : array) {
|
||||
for (auto* g : array) {
|
||||
delete g;
|
||||
}
|
||||
}
|
||||
|
|
@ -125,9 +127,9 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
|||
{
|
||||
m_skinTexture->bind();
|
||||
program->setUniformValue("texture", 0);
|
||||
for (auto toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms, m_staticComponentsOverlay,
|
||||
m_slim ? m_slimArmsOverlay : m_normalArmsOverlay }) {
|
||||
for (auto g : toDraw) {
|
||||
for (const auto& toDraw : { m_staticComponents, m_slim ? m_slimArms : m_normalArms, m_staticComponentsOverlay,
|
||||
m_slim ? m_slimArmsOverlay : m_normalArmsOverlay }) {
|
||||
for (auto* g : toDraw) {
|
||||
g->draw(program);
|
||||
}
|
||||
}
|
||||
|
|
@ -138,7 +140,7 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
|||
if (!m_elytraVisible) {
|
||||
m_cape->draw(program);
|
||||
} else {
|
||||
for (auto e : m_elytra) {
|
||||
for (auto* e : m_elytra) {
|
||||
e->draw(program);
|
||||
}
|
||||
}
|
||||
|
|
@ -146,11 +148,13 @@ void Scene::draw(QOpenGLShaderProgram* program)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void updateTexture(QOpenGLTexture* texture, const QImage& img)
|
||||
{
|
||||
if (texture) {
|
||||
if (texture->isBound())
|
||||
if (texture->isBound()) {
|
||||
texture->release();
|
||||
}
|
||||
texture->destroy();
|
||||
texture->create();
|
||||
texture->setSize(img.width(), img.height());
|
||||
|
|
@ -159,10 +163,11 @@ void updateTexture(QOpenGLTexture* texture, const QImage& img)
|
|||
texture->setMagnificationFilter(QOpenGLTexture::Nearest);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void Scene::setSkin(const QImage& skin)
|
||||
{
|
||||
updateTexture(m_skinTexture, skin.mirrored());
|
||||
updateTexture(m_skinTexture, skin.flipped());
|
||||
}
|
||||
|
||||
void Scene::setMode(bool slim)
|
||||
|
|
@ -171,7 +176,7 @@ void Scene::setMode(bool slim)
|
|||
}
|
||||
void Scene::setCape(const QImage& cape)
|
||||
{
|
||||
updateTexture(m_capeTexture, cape.mirrored());
|
||||
updateTexture(m_capeTexture, cape.flipped());
|
||||
}
|
||||
void Scene::setCapeVisible(bool visible)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <utility>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BaseVersionList.h"
|
||||
|
|
@ -31,104 +32,115 @@
|
|||
#include "Filter.h"
|
||||
#include "java/download/ArchiveDownloadTask.h"
|
||||
#include "java/download/ManifestDownloadTask.h"
|
||||
#include "java/download/SymlinkTask.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/MinecraftInstance.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
#include "ui/dialogs/ProgressDialog.h"
|
||||
#include "ui/java/VersionList.h"
|
||||
#include "ui/widgets/PageContainer.h"
|
||||
#include "ui/widgets/VersionSelectWidget.h"
|
||||
|
||||
#if defined(Q_OS_MACOS)
|
||||
#include "java/download/SymlinkTask.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
class InstallJavaPage : public QWidget, public BasePage {
|
||||
public:
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit InstallJavaPage(const QString& id, const QString& iconName, const QString& name, QWidget* parent = nullptr)
|
||||
: QWidget(parent), uid(id), iconName(iconName), name(name)
|
||||
explicit InstallJavaPage(QString id, QString iconName, QString name, QWidget* parent = nullptr)
|
||||
: QWidget(parent)
|
||||
, m_uid(std::move(id))
|
||||
, m_iconName(std::move(iconName))
|
||||
, m_name(std::move(name))
|
||||
, m_horizontalLayout(new QHBoxLayout(this))
|
||||
, m_majorVersionSelect(new VersionSelectWidget(this))
|
||||
, m_javaVersionSelect(new VersionSelectWidget(this))
|
||||
{
|
||||
setObjectName(QStringLiteral("VersionSelectWidget"));
|
||||
horizontalLayout = new QHBoxLayout(this);
|
||||
horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||
horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
majorVersionSelect = new VersionSelectWidget(this);
|
||||
majorVersionSelect->selectCurrent();
|
||||
majorVersionSelect->setEmptyString(tr("No Java versions are currently available in the meta."));
|
||||
majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||
horizontalLayout->addWidget(majorVersionSelect, 1);
|
||||
|
||||
javaVersionSelect = new VersionSelectWidget(this);
|
||||
javaVersionSelect->setEmptyString(tr("No Java versions are currently available for your OS."));
|
||||
javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||
horizontalLayout->addWidget(javaVersionSelect, 4);
|
||||
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion);
|
||||
connect(majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
||||
connect(javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
||||
m_horizontalLayout->setObjectName(QStringLiteral("horizontalLayout"));
|
||||
m_horizontalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
m_majorVersionSelect->selectCurrent();
|
||||
m_majorVersionSelect->setEmptyString(tr("No Java versions are currently available in the meta."));
|
||||
m_majorVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||
m_horizontalLayout->addWidget(m_majorVersionSelect, 1);
|
||||
|
||||
m_javaVersionSelect->setEmptyString(tr("No Java versions are currently available for your OS."));
|
||||
m_javaVersionSelect->setEmptyErrorString(tr("Couldn't load or download the Java version lists!"));
|
||||
m_horizontalLayout->addWidget(m_javaVersionSelect, 4);
|
||||
connect(m_majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::setSelectedVersion);
|
||||
connect(m_majorVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
||||
connect(m_javaVersionSelect, &VersionSelectWidget::selectedVersionChanged, this, &InstallJavaPage::selectionChanged);
|
||||
|
||||
QMetaObject::connectSlotsByName(this);
|
||||
}
|
||||
~InstallJavaPage()
|
||||
~InstallJavaPage() override
|
||||
{
|
||||
delete horizontalLayout;
|
||||
delete majorVersionSelect;
|
||||
delete javaVersionSelect;
|
||||
delete m_horizontalLayout;
|
||||
delete m_majorVersionSelect;
|
||||
delete m_javaVersionSelect;
|
||||
}
|
||||
|
||||
//! loads the list if needed.
|
||||
void initialize(Meta::VersionList::Ptr vlist)
|
||||
void initialize(const Meta::VersionList::Ptr& vlist)
|
||||
{
|
||||
vlist->setProvidedRoles({ BaseVersionList::JavaMajorRole, BaseVersionList::RecommendedRole, BaseVersionList::VersionPointerRole });
|
||||
majorVersionSelect->initialize(vlist.get());
|
||||
m_majorVersionSelect->initialize(vlist.get());
|
||||
}
|
||||
|
||||
void setSelectedVersion(BaseVersion::Ptr version)
|
||||
void setSelectedVersion(const BaseVersion::Ptr& version)
|
||||
{
|
||||
auto dcast = std::dynamic_pointer_cast<Meta::Version>(version);
|
||||
if (!dcast) {
|
||||
return;
|
||||
}
|
||||
javaVersionSelect->initialize(new Java::VersionList(dcast, this));
|
||||
javaVersionSelect->selectCurrent();
|
||||
m_javaVersionSelect->initialize(new Java::VersionList(dcast, this));
|
||||
m_javaVersionSelect->selectCurrent();
|
||||
}
|
||||
|
||||
QString id() const override { return uid; }
|
||||
QString displayName() const override { return name; }
|
||||
QIcon icon() const override { return QIcon::fromTheme(iconName); }
|
||||
QString id() const override { return m_uid; }
|
||||
QString displayName() const override { return m_name; }
|
||||
QIcon icon() const override { return QIcon::fromTheme(m_iconName); }
|
||||
|
||||
void openedImpl() override
|
||||
{
|
||||
if (loaded)
|
||||
if (m_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto versions = APPLICATION->metadataIndex()->get(uid);
|
||||
if (!versions)
|
||||
const auto versions = APPLICATION->metadataIndex()->get(m_uid);
|
||||
if (!versions) {
|
||||
return;
|
||||
}
|
||||
|
||||
initialize(versions);
|
||||
loaded = true;
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
void setParentContainer(BasePageContainer* container) override
|
||||
{
|
||||
auto dialog = dynamic_cast<QDialog*>(dynamic_cast<PageContainer*>(container)->parent());
|
||||
connect(javaVersionSelect->view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept);
|
||||
auto* dialog = dynamic_cast<QDialog*>(dynamic_cast<PageContainer*>(container)->parent());
|
||||
connect(m_javaVersionSelect->view(), &QAbstractItemView::doubleClicked, dialog, &QDialog::accept);
|
||||
}
|
||||
|
||||
BaseVersion::Ptr selectedVersion() const { return javaVersionSelect->selectedVersion(); }
|
||||
void selectSearch() { javaVersionSelect->selectSearch(); }
|
||||
BaseVersion::Ptr selectedVersion() const { return m_javaVersionSelect->selectedVersion(); }
|
||||
void selectSearch() { m_javaVersionSelect->selectSearch(); }
|
||||
void loadList()
|
||||
{
|
||||
majorVersionSelect->loadList(true);
|
||||
javaVersionSelect->loadList(true);
|
||||
m_majorVersionSelect->loadList(true);
|
||||
m_javaVersionSelect->loadList(true);
|
||||
}
|
||||
|
||||
public slots:
|
||||
void setRecommendedMajors(const QStringList& majors)
|
||||
{
|
||||
m_recommended_majors = majors;
|
||||
m_recommendedMajors = majors;
|
||||
recommendedFilterChanged();
|
||||
}
|
||||
void setRecommend(bool recommend)
|
||||
|
|
@ -139,9 +151,9 @@ class InstallJavaPage : public QWidget, public BasePage {
|
|||
void recommendedFilterChanged()
|
||||
{
|
||||
if (m_recommend) {
|
||||
majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, Filters::equalsAny(m_recommended_majors));
|
||||
m_majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, Filters::equalsAny(m_recommendedMajors));
|
||||
} else {
|
||||
majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, Filters::equalsAny());
|
||||
m_majorVersionSelect->setFilter(BaseVersionList::ModelRoles::JavaMajorRole, Filters::equalsAny());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,30 +161,30 @@ class InstallJavaPage : public QWidget, public BasePage {
|
|||
void selectionChanged();
|
||||
|
||||
private:
|
||||
const QString uid;
|
||||
const QString iconName;
|
||||
const QString name;
|
||||
bool loaded = false;
|
||||
const QString m_uid;
|
||||
const QString m_iconName;
|
||||
const QString m_name;
|
||||
bool m_loaded = false;
|
||||
|
||||
QHBoxLayout* horizontalLayout = nullptr;
|
||||
VersionSelectWidget* majorVersionSelect = nullptr;
|
||||
VersionSelectWidget* javaVersionSelect = nullptr;
|
||||
QHBoxLayout* m_horizontalLayout = nullptr;
|
||||
VersionSelectWidget* m_majorVersionSelect = nullptr;
|
||||
VersionSelectWidget* m_javaVersionSelect = nullptr;
|
||||
|
||||
QStringList m_recommended_majors;
|
||||
bool m_recommend;
|
||||
QStringList m_recommendedMajors;
|
||||
bool m_recommend{};
|
||||
};
|
||||
|
||||
static InstallJavaPage* pageCast(BasePage* page)
|
||||
InstallJavaPage* pageCast(BasePage* page)
|
||||
{
|
||||
auto result = dynamic_cast<InstallJavaPage*>(page);
|
||||
auto* result = dynamic_cast<InstallJavaPage*>(page);
|
||||
Q_ASSERT(result != nullptr);
|
||||
return result;
|
||||
}
|
||||
namespace Java {
|
||||
QStringList getRecommendedJavaVersionsFromVersionList(Meta::VersionList::Ptr list)
|
||||
|
||||
QStringList getRecommendedJavaVersionsFromVersionList(const Meta::VersionList::Ptr& list)
|
||||
{
|
||||
QStringList recommendedJavas;
|
||||
for (auto ver : list->versions()) {
|
||||
for (const auto& ver : list->versions()) {
|
||||
auto major = ver->version();
|
||||
if (major.startsWith("java")) {
|
||||
major = "Java " + major.mid(4);
|
||||
|
|
@ -181,34 +193,37 @@ QStringList getRecommendedJavaVersionsFromVersionList(Meta::VersionList::Ptr lis
|
|||
}
|
||||
return recommendedJavas;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace Java {
|
||||
|
||||
InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget* parent)
|
||||
: QDialog(parent), container(new PageContainer(this, QString(), this)), buttons(new QDialogButtonBox(this))
|
||||
: QDialog(parent), m_container(new PageContainer(this, QString(), this)), m_buttons(new QDialogButtonBox(this))
|
||||
{
|
||||
auto layout = new QVBoxLayout(this);
|
||||
// small margins look ugly on macOS on modal windows
|
||||
#ifndef Q_OS_MACOS
|
||||
auto* layout = new QVBoxLayout(this);
|
||||
// small margins look ugly on macOS on modal windows
|
||||
#ifndef Q_OS_MACOS
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
#endif
|
||||
container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
layout->addWidget(container);
|
||||
#endif
|
||||
m_container->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||
layout->addWidget(m_container);
|
||||
|
||||
auto buttonLayout = new QHBoxLayout(this);
|
||||
// small margins look ugly on macOS on modal windows
|
||||
#ifndef Q_OS_MACOS
|
||||
auto* buttonLayout = new QHBoxLayout(this);
|
||||
// small margins look ugly on macOS on modal windows
|
||||
#ifndef Q_OS_MACOS
|
||||
buttonLayout->setContentsMargins(0, 0, 6, 6);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
auto refreshLayout = new QHBoxLayout(this);
|
||||
auto* refreshLayout = new QHBoxLayout(this);
|
||||
|
||||
auto refreshButton = new QPushButton(tr("&Refresh"), this);
|
||||
connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(container->selectedPage())->loadList(); });
|
||||
auto* refreshButton = new QPushButton(tr("&Refresh"), this);
|
||||
connect(refreshButton, &QPushButton::clicked, this, [this] { pageCast(m_container->selectedPage())->loadList(); });
|
||||
refreshLayout->addWidget(refreshButton);
|
||||
|
||||
auto recommendedCheckBox = new QCheckBox("Recommended", this);
|
||||
auto* recommendedCheckBox = new QCheckBox("Recommended", this);
|
||||
recommendedCheckBox->setCheckState(Qt::CheckState::Checked);
|
||||
connect(recommendedCheckBox, &QCheckBox::stateChanged, this, [this](int state) {
|
||||
for (BasePage* page : container->getPages()) {
|
||||
connect(recommendedCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) {
|
||||
for (BasePage* page : m_container->getPages()) {
|
||||
pageCast(page)->setRecommend(state == Qt::Checked);
|
||||
}
|
||||
});
|
||||
|
|
@ -216,22 +231,22 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget
|
|||
refreshLayout->addWidget(recommendedCheckBox);
|
||||
buttonLayout->addLayout(refreshLayout);
|
||||
|
||||
buttons->setOrientation(Qt::Horizontal);
|
||||
buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
buttons->button(QDialogButtonBox::Ok)->setText(tr("Download"));
|
||||
buttons->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
buttonLayout->addWidget(buttons);
|
||||
m_buttons->setOrientation(Qt::Horizontal);
|
||||
m_buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setText(tr("Download"));
|
||||
m_buttons->button(QDialogButtonBox::Cancel)->setText(tr("Cancel"));
|
||||
connect(m_buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(m_buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
buttonLayout->addWidget(m_buttons);
|
||||
|
||||
container->addButtons(buttonLayout);
|
||||
m_container->addButtons(buttonLayout);
|
||||
|
||||
setWindowTitle(dialogTitle());
|
||||
setWindowModality(Qt::WindowModal);
|
||||
resize(840, 480);
|
||||
|
||||
QStringList recommendedJavas;
|
||||
if (auto mcInst = dynamic_cast<MinecraftInstance*>(instance); mcInst) {
|
||||
if (auto* mcInst = dynamic_cast<MinecraftInstance*>(instance); mcInst) {
|
||||
auto mc = mcInst->getPackProfile()->getComponent("net.minecraft");
|
||||
if (mc) {
|
||||
auto file = mc->getVersionFile(); // no need for load as it should already be loaded
|
||||
|
|
@ -251,32 +266,35 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget
|
|||
if (newTask) {
|
||||
connect(newTask.get(), &Task::succeeded, this, [this, versions] {
|
||||
auto recommendedJavas = getRecommendedJavaVersionsFromVersionList(versions);
|
||||
for (BasePage* page : container->getPages()) {
|
||||
for (BasePage* page : m_container->getPages()) {
|
||||
pageCast(page)->setRecommendedMajors(recommendedJavas);
|
||||
}
|
||||
});
|
||||
if (!newTask->isRunning())
|
||||
if (!newTask->isRunning()) {
|
||||
newTask->start();
|
||||
}
|
||||
} else {
|
||||
recommendedJavas = getRecommendedJavaVersionsFromVersionList(versions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (BasePage* page : container->getPages()) {
|
||||
if (page->id() == uid)
|
||||
container->selectPage(page->id());
|
||||
for (BasePage* page : m_container->getPages()) {
|
||||
if (page->id() == uid) {
|
||||
m_container->selectPage(page->id());
|
||||
}
|
||||
|
||||
auto cast = pageCast(page);
|
||||
auto* cast = pageCast(page);
|
||||
cast->setRecommend(true);
|
||||
connect(cast, &InstallJavaPage::selectionChanged, this, [this, cast] { validate(cast); });
|
||||
if (!recommendedJavas.isEmpty()) {
|
||||
cast->setRecommendedMajors(recommendedJavas);
|
||||
}
|
||||
}
|
||||
connect(container, &PageContainer::selectedPageChanged, this, [this](BasePage* previous, BasePage* selected) { validate(selected); });
|
||||
pageCast(container->selectedPage())->selectSearch();
|
||||
validate(container->selectedPage());
|
||||
connect(m_container, &PageContainer::selectedPageChanged, this,
|
||||
[this](BasePage* /*previous*/, BasePage* selected) { validate(selected); });
|
||||
pageCast(m_container->selectedPage())->selectSearch();
|
||||
validate(m_container->selectedPage());
|
||||
}
|
||||
|
||||
QList<BasePage*> InstallDialog::getPages()
|
||||
|
|
@ -289,9 +307,9 @@ QList<BasePage*> InstallDialog::getPages()
|
|||
// Azul
|
||||
new InstallJavaPage("com.azul.java", "azul", tr("Azul Zulu")),
|
||||
// IBM
|
||||
/* Must watch out in case the AdoptOpenJDK infrastructure is deprecated.
|
||||
In case of happening, IBM does not seem to provide as of today (03/2026) an API like Adoptium does and rather uses GitHub directly in its website: `developer.ibm.com`.
|
||||
GitHub is known for rate limiting requests that do not use an API key from an account. */
|
||||
/* Must watch out in case the AdoptOpenJDK infrastructure is deprecated.
|
||||
In case of happening, IBM does not seem to provide as of today (03/2026) an API like Adoptium does and rather uses GitHub directly
|
||||
in its website: `developer.ibm.com`. GitHub is known for rate limiting requests that do not use an API key from an account. */
|
||||
new InstallJavaPage("com.ibm.java", "openj9_hex_custom", tr("IBM Semeru Open")),
|
||||
};
|
||||
}
|
||||
|
|
@ -303,25 +321,25 @@ QString InstallDialog::dialogTitle()
|
|||
|
||||
void InstallDialog::validate(BasePage* selected)
|
||||
{
|
||||
buttons->button(QDialogButtonBox::Ok)->setEnabled(!!std::dynamic_pointer_cast<Java::Metadata>(pageCast(selected)->selectedVersion()));
|
||||
m_buttons->button(QDialogButtonBox::Ok)->setEnabled(!!std::dynamic_pointer_cast<Java::Metadata>(pageCast(selected)->selectedVersion()));
|
||||
}
|
||||
|
||||
void InstallDialog::done(int result)
|
||||
{
|
||||
if (result == Accepted) {
|
||||
auto* page = pageCast(container->selectedPage());
|
||||
auto* page = pageCast(m_container->selectedPage());
|
||||
if (page->selectedVersion()) {
|
||||
auto meta = std::dynamic_pointer_cast<Java::Metadata>(page->selectedVersion());
|
||||
if (meta) {
|
||||
Task::Ptr task;
|
||||
auto final_path = FS::PathCombine(APPLICATION->javaPath(), meta->m_name);
|
||||
auto deletePath = [final_path] { FS::deletePath(final_path); };
|
||||
auto finalPath = FS::PathCombine(APPLICATION->javaPath(), meta->m_name);
|
||||
auto deletePath = [finalPath] { FS::deletePath(finalPath); };
|
||||
switch (meta->downloadType) {
|
||||
case Java::DownloadType::Manifest:
|
||||
task = makeShared<ManifestDownloadTask>(meta->url, final_path, meta->checksumType, meta->checksumHash);
|
||||
task = makeShared<ManifestDownloadTask>(meta->url, finalPath, meta->checksumType, meta->checksumHash);
|
||||
break;
|
||||
case Java::DownloadType::Archive:
|
||||
task = makeShared<ArchiveDownloadTask>(meta->url, final_path, meta->checksumType, meta->checksumHash);
|
||||
task = makeShared<ArchiveDownloadTask>(meta->url, finalPath, meta->checksumType, meta->checksumHash);
|
||||
break;
|
||||
case Java::DownloadType::Unknown:
|
||||
QString error = QString(tr("Could not determine Java download type!"));
|
||||
|
|
@ -332,10 +350,10 @@ void InstallDialog::done(int result)
|
|||
#if defined(Q_OS_MACOS)
|
||||
auto seq = makeShared<SequentialTask>(tr("Install Java"));
|
||||
seq->addTask(task);
|
||||
seq->addTask(makeShared<Java::SymlinkTask>(final_path));
|
||||
seq->addTask(makeShared<Java::SymlinkTask>(finalPath));
|
||||
task = seq;
|
||||
#endif
|
||||
connect(task.get(), &Task::failed, this, [this, &deletePath](QString reason) {
|
||||
connect(task.get(), &Task::failed, this, [this, &deletePath](const QString& reason) {
|
||||
QString error = QString("Java download failed: %1").arg(reason);
|
||||
CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show();
|
||||
deletePath();
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class InstallDialog final : public QDialog, private BasePageProvider {
|
|||
void done(int result) override;
|
||||
|
||||
private:
|
||||
PageContainer* container;
|
||||
QDialogButtonBox* buttons;
|
||||
PageContainer* m_container;
|
||||
QDialogButtonBox* m_buttons;
|
||||
};
|
||||
} // namespace Java
|
||||
|
|
|
|||
|
|
@ -37,42 +37,42 @@
|
|||
#include "ui_CustomPage.h"
|
||||
|
||||
#include <QTabBar>
|
||||
#include <utility>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Filter.h"
|
||||
#include "Version.h"
|
||||
#include "meta/Index.h"
|
||||
#include "meta/VersionList.h"
|
||||
#include "minecraft/VanillaInstanceCreationTask.h"
|
||||
#include "ui/dialogs/NewInstanceDialog.h"
|
||||
|
||||
CustomPage::CustomPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), dialog(dialog), ui(new Ui::CustomPage)
|
||||
CustomPage::CustomPage(NewInstanceDialog* dialog, QWidget* parent) : QWidget(parent), m_dialog(dialog), m_ui(new Ui::CustomPage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->versionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedVersion);
|
||||
m_ui->setupUi(this);
|
||||
connect(m_ui->versionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedVersion);
|
||||
filterChanged();
|
||||
connect(ui->alphaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->betaFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->snapshotFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->releaseFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->experimentsFilter, &QCheckBox::stateChanged, this, &CustomPage::filterChanged);
|
||||
connect(ui->refreshBtn, &QPushButton::clicked, this, &CustomPage::refresh);
|
||||
connect(m_ui->alphaFilter, &QCheckBox::checkStateChanged, this, &CustomPage::filterChanged);
|
||||
connect(m_ui->betaFilter, &QCheckBox::checkStateChanged, this, &CustomPage::filterChanged);
|
||||
connect(m_ui->snapshotFilter, &QCheckBox::checkStateChanged, this, &CustomPage::filterChanged);
|
||||
connect(m_ui->releaseFilter, &QCheckBox::checkStateChanged, this, &CustomPage::filterChanged);
|
||||
connect(m_ui->experimentsFilter, &QCheckBox::checkStateChanged, this, &CustomPage::filterChanged);
|
||||
connect(m_ui->refreshBtn, &QPushButton::clicked, this, &CustomPage::refresh);
|
||||
|
||||
connect(ui->loaderVersionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedLoaderVersion);
|
||||
connect(ui->noneFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->forgeFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->fabricFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->quiltFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->liteLoaderFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(ui->loaderRefreshBtn, &QPushButton::clicked, this, &CustomPage::loaderRefresh);
|
||||
connect(m_ui->loaderVersionList, &VersionSelectWidget::selectedVersionChanged, this, &CustomPage::setSelectedLoaderVersion);
|
||||
connect(m_ui->noneFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(m_ui->forgeFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(m_ui->fabricFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(m_ui->quiltFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(m_ui->liteLoaderFilter, &QRadioButton::toggled, this, &CustomPage::loaderFilterChanged);
|
||||
connect(m_ui->loaderRefreshBtn, &QPushButton::clicked, this, &CustomPage::loaderRefresh);
|
||||
}
|
||||
|
||||
void CustomPage::openedImpl()
|
||||
{
|
||||
if (!initialized) {
|
||||
if (!m_initialized) {
|
||||
auto vlist = APPLICATION->metadataIndex()->get("net.minecraft");
|
||||
ui->versionList->initialize(vlist.get());
|
||||
initialized = true;
|
||||
m_ui->versionList->initialize(vlist.get());
|
||||
m_initialized = true;
|
||||
} else {
|
||||
suggestCurrent();
|
||||
}
|
||||
|
|
@ -80,31 +80,37 @@ void CustomPage::openedImpl()
|
|||
|
||||
void CustomPage::refresh()
|
||||
{
|
||||
ui->versionList->loadList(true);
|
||||
m_ui->versionList->loadList(true);
|
||||
}
|
||||
|
||||
void CustomPage::loaderRefresh()
|
||||
{
|
||||
if (ui->noneFilter->isChecked())
|
||||
if (m_ui->noneFilter->isChecked()) {
|
||||
return;
|
||||
ui->loaderVersionList->loadList(true);
|
||||
}
|
||||
m_ui->loaderVersionList->loadList(true);
|
||||
}
|
||||
|
||||
void CustomPage::filterChanged()
|
||||
{
|
||||
QStringList out;
|
||||
if (ui->alphaFilter->isChecked())
|
||||
if (m_ui->alphaFilter->isChecked()) {
|
||||
out << "(alpha)";
|
||||
if (ui->betaFilter->isChecked())
|
||||
}
|
||||
if (m_ui->betaFilter->isChecked()) {
|
||||
out << "(beta)";
|
||||
if (ui->snapshotFilter->isChecked())
|
||||
}
|
||||
if (m_ui->snapshotFilter->isChecked()) {
|
||||
out << "(snapshot)";
|
||||
if (ui->releaseFilter->isChecked())
|
||||
}
|
||||
if (m_ui->releaseFilter->isChecked()) {
|
||||
out << "(release)";
|
||||
if (ui->experimentsFilter->isChecked())
|
||||
}
|
||||
if (m_ui->experimentsFilter->isChecked()) {
|
||||
out << "(experiment)";
|
||||
}
|
||||
auto regexp = out.join('|');
|
||||
ui->versionList->setFilter(BaseVersionList::TypeRole, Filters::regexp(QRegularExpression(regexp)));
|
||||
m_ui->versionList->setFilter(BaseVersionList::TypeRole, Filters::regexp(QRegularExpression(regexp)));
|
||||
}
|
||||
|
||||
void CustomPage::loaderFilterChanged()
|
||||
|
|
@ -113,50 +119,53 @@ void CustomPage::loaderFilterChanged()
|
|||
if (m_selectedVersion) {
|
||||
minecraftVersion = m_selectedVersion->descriptor();
|
||||
} else {
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list
|
||||
ui->loaderVersionList->setEmptyString(tr("No Minecraft version is selected."));
|
||||
ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list
|
||||
m_ui->loaderVersionList->setEmptyString(tr("No Minecraft version is selected."));
|
||||
m_ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
||||
return;
|
||||
}
|
||||
if (ui->noneFilter->isChecked()) {
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list
|
||||
ui->loaderVersionList->setEmptyString(tr("No mod loader is selected."));
|
||||
ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
||||
if (m_ui->noneFilter->isChecked()) {
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // empty list
|
||||
m_ui->loaderVersionList->setEmptyString(tr("No mod loader is selected."));
|
||||
m_ui->loaderVersionList->setEmptyMode(VersionListView::String);
|
||||
return;
|
||||
} else if (ui->neoForgeFilter->isChecked()) {
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
}
|
||||
if (m_ui->neoForgeFilter->isChecked()) {
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
m_selectedLoader = "net.neoforged";
|
||||
} else if (ui->forgeFilter->isChecked()) {
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
} else if (m_ui->forgeFilter->isChecked()) {
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
m_selectedLoader = "net.minecraftforge";
|
||||
} else if (ui->fabricFilter->isChecked()) {
|
||||
} else if (m_ui->fabricFilter->isChecked()) {
|
||||
// FIXME: dirty hack because the launcher is unaware of Fabric's dependencies
|
||||
if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
|
||||
else // Fabric/Quilt unsupported
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
|
||||
if (Version(minecraftVersion) >= Version("1.14")) { // Fabric/Quilt supported
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
|
||||
} else { // Fabric/Quilt unsupported
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
|
||||
}
|
||||
m_selectedLoader = "net.fabricmc.fabric-loader";
|
||||
} else if (ui->quiltFilter->isChecked()) {
|
||||
} else if (m_ui->quiltFilter->isChecked()) {
|
||||
// FIXME: dirty hack because the launcher is unaware of Quilt's dependencies (same as Fabric)
|
||||
if (Version(minecraftVersion) >= Version("1.14")) // Fabric/Quilt supported
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
|
||||
else // Fabric/Quilt unsupported
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
|
||||
if (Version(minecraftVersion) >= Version("1.14")) { // Fabric/Quilt supported
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "");
|
||||
} else { // Fabric/Quilt unsupported
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, "AAA"); // clear list
|
||||
}
|
||||
m_selectedLoader = "org.quiltmc.quilt-loader";
|
||||
} else if (ui->liteLoaderFilter->isChecked()) {
|
||||
ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
} else if (m_ui->liteLoaderFilter->isChecked()) {
|
||||
m_ui->loaderVersionList->setExactFilter(BaseVersionList::ParentVersionRole, minecraftVersion);
|
||||
m_selectedLoader = "com.mumfrey.liteloader";
|
||||
}
|
||||
|
||||
auto vlist = APPLICATION->metadataIndex()->get(m_selectedLoader);
|
||||
ui->loaderVersionList->initialize(vlist.get());
|
||||
ui->loaderVersionList->selectRecommended();
|
||||
ui->loaderVersionList->setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion));
|
||||
m_ui->loaderVersionList->initialize(vlist.get());
|
||||
m_ui->loaderVersionList->selectRecommended();
|
||||
m_ui->loaderVersionList->setEmptyString(tr("No versions are currently available for Minecraft %1").arg(minecraftVersion));
|
||||
}
|
||||
|
||||
CustomPage::~CustomPage()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
bool CustomPage::shouldDisplay() const
|
||||
|
|
@ -166,7 +175,7 @@ bool CustomPage::shouldDisplay() const
|
|||
|
||||
void CustomPage::retranslate()
|
||||
{
|
||||
ui->retranslateUi(this);
|
||||
m_ui->retranslateUi(this);
|
||||
}
|
||||
|
||||
BaseVersion::Ptr CustomPage::selectedVersion() const
|
||||
|
|
@ -191,29 +200,29 @@ void CustomPage::suggestCurrent()
|
|||
}
|
||||
|
||||
if (!m_selectedVersion) {
|
||||
dialog->setSuggestedPack();
|
||||
m_dialog->setSuggestedPack();
|
||||
return;
|
||||
}
|
||||
|
||||
// There isn't a selected version if the version list is empty
|
||||
if (ui->loaderVersionList->selectedVersion() == nullptr)
|
||||
dialog->setSuggestedPack(m_selectedVersion->descriptor(), new VanillaCreationTask(m_selectedVersion));
|
||||
else {
|
||||
dialog->setSuggestedPack(m_selectedVersion->descriptor(),
|
||||
new VanillaCreationTask(m_selectedVersion, m_selectedLoader, m_selectedLoaderVersion));
|
||||
if (m_ui->loaderVersionList->selectedVersion() == nullptr) {
|
||||
m_dialog->setSuggestedPack(m_selectedVersion->descriptor(), new VanillaCreationTask(m_selectedVersion));
|
||||
} else {
|
||||
m_dialog->setSuggestedPack(m_selectedVersion->descriptor(),
|
||||
new VanillaCreationTask(m_selectedVersion, m_selectedLoader, m_selectedLoaderVersion));
|
||||
}
|
||||
dialog->setSuggestedIcon("default");
|
||||
m_dialog->setSuggestedIcon("default");
|
||||
}
|
||||
|
||||
void CustomPage::setSelectedVersion(BaseVersion::Ptr version)
|
||||
{
|
||||
m_selectedVersion = version;
|
||||
m_selectedVersion = std::move(version);
|
||||
suggestCurrent();
|
||||
loaderFilterChanged();
|
||||
}
|
||||
|
||||
void CustomPage::setSelectedLoaderVersion(BaseVersion::Ptr version)
|
||||
{
|
||||
m_selectedLoaderVersion = version;
|
||||
m_selectedLoaderVersion = std::move(version);
|
||||
suggestCurrent();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#include <QWidget>
|
||||
|
||||
#include "BaseVersion.h"
|
||||
#include "tasks/Task.h"
|
||||
#include "ui/pages/BasePage.h"
|
||||
|
||||
namespace Ui {
|
||||
|
|
@ -51,13 +50,13 @@ class CustomPage : public QWidget, public BasePage {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomPage(NewInstanceDialog* dialog, QWidget* parent = 0);
|
||||
virtual ~CustomPage();
|
||||
virtual QString displayName() const override { return tr("Custom"); }
|
||||
virtual QIcon icon() const override { return QIcon::fromTheme("minecraft"); }
|
||||
virtual QString id() const override { return "vanilla"; }
|
||||
virtual QString helpPage() const override { return "Vanilla-platform"; }
|
||||
virtual bool shouldDisplay() const override;
|
||||
explicit CustomPage(NewInstanceDialog* dialog, QWidget* parent = nullptr);
|
||||
~CustomPage() override;
|
||||
QString displayName() const override { return tr("Custom"); }
|
||||
QIcon icon() const override { return QIcon::fromTheme("minecraft"); }
|
||||
QString id() const override { return "vanilla"; }
|
||||
QString helpPage() const override { return "Vanilla-platform"; }
|
||||
bool shouldDisplay() const override;
|
||||
void retranslate() override;
|
||||
|
||||
void openedImpl() override;
|
||||
|
|
@ -80,9 +79,9 @@ class CustomPage : public QWidget, public BasePage {
|
|||
void suggestCurrent();
|
||||
|
||||
private:
|
||||
bool initialized = false;
|
||||
NewInstanceDialog* dialog = nullptr;
|
||||
Ui::CustomPage* ui = nullptr;
|
||||
bool m_initialized = false;
|
||||
NewInstanceDialog* m_dialog = nullptr;
|
||||
Ui::CustomPage* m_ui = nullptr;
|
||||
bool m_versionSetByUser = false;
|
||||
BaseVersion::Ptr m_selectedVersion;
|
||||
BaseVersion::Ptr m_selectedLoaderVersion;
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@
|
|||
#include "FileSystem.h"
|
||||
#include "HardwareInfo.h"
|
||||
#include "JavaCommon.h"
|
||||
#include "SysInfo.h"
|
||||
#include "java/JavaInstallList.h"
|
||||
#include "java/JavaUtils.h"
|
||||
#include "settings/Setting.h"
|
||||
|
|
@ -62,10 +61,11 @@ JavaSettingsWidget::JavaSettingsWidget(BaseInstance* instance, QWidget* parent)
|
|||
if (m_instance == nullptr) {
|
||||
m_ui->javaDownloadBtn->hide();
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
|
||||
connect(m_ui->autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this](bool state) {
|
||||
connect(m_ui->autodetectJavaCheckBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) {
|
||||
m_ui->autodownloadJavaCheckBox->setEnabled(state);
|
||||
if (!state)
|
||||
if (state != Qt::Checked) {
|
||||
m_ui->autodownloadJavaCheckBox->setChecked(false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
m_ui->autodownloadJavaCheckBox->hide();
|
||||
|
|
@ -88,10 +88,10 @@ JavaSettingsWidget::JavaSettingsWidget(BaseInstance* instance, QWidget* parent)
|
|||
[this, settings] { m_ui->javaPathTextBox->setText(settings->get("JavaPath").toString()); });
|
||||
|
||||
connect(m_ui->javaDownloadBtn, &QPushButton::clicked, this, [this] {
|
||||
auto javaDialog = new Java::InstallDialog({}, m_instance, this);
|
||||
auto* javaDialog = new Java::InstallDialog({}, m_instance, this);
|
||||
javaDialog->exec();
|
||||
});
|
||||
connect(m_ui->javaPathTextBox, &QLineEdit::textChanged, [this](QString newValue) {
|
||||
connect(m_ui->javaPathTextBox, &QLineEdit::textChanged, [this](const QString& newValue) {
|
||||
if (m_instance->settings()->get("JavaPath").toString() != newValue) {
|
||||
m_instance->settings()->set("AutomaticJava", false);
|
||||
}
|
||||
|
|
@ -116,12 +116,13 @@ JavaSettingsWidget::~JavaSettingsWidget()
|
|||
|
||||
void JavaSettingsWidget::loadSettings()
|
||||
{
|
||||
SettingsObject* settings;
|
||||
SettingsObject* settings = nullptr;
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings = m_instance->settings();
|
||||
else
|
||||
} else {
|
||||
settings = APPLICATION->settings();
|
||||
}
|
||||
|
||||
// Java Settings
|
||||
m_ui->javaInstallationGroupBox->setChecked(settings->get("OverrideJavaLocation").toBool());
|
||||
|
|
@ -135,7 +136,6 @@ void JavaSettingsWidget::loadSettings()
|
|||
if (m_instance == nullptr) {
|
||||
m_ui->skipWizardCheckBox->setChecked(settings->get("IgnoreJavaWizard").toBool());
|
||||
m_ui->autodetectJavaCheckBox->setChecked(settings->get("AutomaticJavaSwitch").toBool());
|
||||
m_ui->autodetectJavaCheckBox->stateChanged(m_ui->autodetectJavaCheckBox->isChecked());
|
||||
m_ui->autodownloadJavaCheckBox->setChecked(settings->get("AutomaticJavaDownload").toBool());
|
||||
}
|
||||
|
||||
|
|
@ -160,20 +160,22 @@ void JavaSettingsWidget::loadSettings()
|
|||
|
||||
void JavaSettingsWidget::saveSettings()
|
||||
{
|
||||
SettingsObject* settings;
|
||||
SettingsObject* settings = nullptr;
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings = m_instance->settings();
|
||||
else
|
||||
} else {
|
||||
settings = APPLICATION->settings();
|
||||
}
|
||||
|
||||
SettingsObject::Lock lock(settings);
|
||||
|
||||
// Java Install Settings
|
||||
bool javaInstall = m_instance == nullptr || m_ui->javaInstallationGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideJavaLocation", javaInstall);
|
||||
}
|
||||
|
||||
if (javaInstall) {
|
||||
settings->set("JavaPath", m_ui->javaPathTextBox->text());
|
||||
|
|
@ -192,8 +194,9 @@ void JavaSettingsWidget::saveSettings()
|
|||
// Memory
|
||||
bool memory = m_instance == nullptr || m_ui->memoryGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideMemory", memory);
|
||||
}
|
||||
|
||||
if (memory) {
|
||||
int min = m_ui->minMemSpinBox->value();
|
||||
|
|
@ -217,8 +220,9 @@ void JavaSettingsWidget::saveSettings()
|
|||
// Java arguments
|
||||
bool javaArgs = m_instance == nullptr || m_ui->javaArgumentsGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideJavaArgs", javaArgs);
|
||||
}
|
||||
|
||||
if (javaArgs) {
|
||||
settings->set("JvmArgs", m_ui->jvmArgsTextBox->toPlainText().replace("\n", " "));
|
||||
|
|
@ -246,15 +250,17 @@ void JavaSettingsWidget::onJavaBrowse()
|
|||
|
||||
void JavaSettingsWidget::onJavaTest()
|
||||
{
|
||||
if (m_checker != nullptr)
|
||||
if (m_checker != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString jvmArgs;
|
||||
|
||||
if (m_instance == nullptr || m_ui->javaArgumentsGroupBox->isChecked())
|
||||
if (m_instance == nullptr || m_ui->javaArgumentsGroupBox->isChecked()) {
|
||||
jvmArgs = m_ui->jvmArgsTextBox->toPlainText().replace("\n", " ");
|
||||
else
|
||||
} else {
|
||||
jvmArgs = APPLICATION->settings()->get("JvmArgs").toString();
|
||||
}
|
||||
|
||||
m_checker.reset(new JavaCommon::TestCheck(this, m_ui->javaPathTextBox->text(), jvmArgs, m_ui->minMemSpinBox->value(),
|
||||
m_ui->maxMemSpinBox->value(), m_ui->permGenSpinBox->value()));
|
||||
|
|
@ -299,7 +305,7 @@ void JavaSettingsWidget::updateThresholds()
|
|||
m_ui->labelMaxMemNotice->setText(
|
||||
QString("<span style='color:red'>%1</span>").arg(tr("Your maximum memory allocation exceeds your system memory capacity.")));
|
||||
m_ui->labelMaxMemNotice->show();
|
||||
} else if (maxMem > (sysMiB * 0.9)) {
|
||||
} else if (maxMem > (static_cast<double>(sysMiB) * 0.9)) {
|
||||
m_ui->labelMaxMemNotice->setText(warningColour.arg(tr("Your maximum memory allocation is close to your system memory capacity.")));
|
||||
m_ui->labelMaxMemNotice->show();
|
||||
} else if (maxMem < minMem) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <algorithm>
|
||||
|
||||
#include "DesktopServices.h"
|
||||
#include "FileSystem.h"
|
||||
|
|
@ -29,14 +30,13 @@
|
|||
#include "BuildConfig.h"
|
||||
#include "HardwareInfo.h"
|
||||
|
||||
JavaWizardWidget::JavaWizardWidget(QWidget* parent) : QWidget(parent)
|
||||
JavaWizardWidget::JavaWizardWidget(QWidget* parent)
|
||||
: QWidget(parent), m_availableMemory(HardwareInfo::totalRamMiB()), m_memoryTimer(new QTimer(this))
|
||||
{
|
||||
m_availableMemory = HardwareInfo::totalRamMiB();
|
||||
m_goodIcon = QIcon::fromTheme("status-good");
|
||||
m_yellowIcon = QIcon::fromTheme("status-yellow");
|
||||
m_badIcon = QIcon::fromTheme("status-bad");
|
||||
|
||||
goodIcon = QIcon::fromTheme("status-good");
|
||||
yellowIcon = QIcon::fromTheme("status-yellow");
|
||||
badIcon = QIcon::fromTheme("status-bad");
|
||||
m_memoryTimer = new QTimer(this);
|
||||
setupUi();
|
||||
|
||||
connect(m_minMemSpinBox, &QSpinBox::valueChanged, this, &JavaWizardWidget::onSpinBoxValueChanged);
|
||||
|
|
@ -44,11 +44,11 @@ JavaWizardWidget::JavaWizardWidget(QWidget* parent) : QWidget(parent)
|
|||
connect(m_permGenSpinBox, &QSpinBox::valueChanged, this, &JavaWizardWidget::onSpinBoxValueChanged);
|
||||
connect(m_memoryTimer, &QTimer::timeout, this, &JavaWizardWidget::memoryValueChanged);
|
||||
connect(m_versionWidget, &VersionSelectWidget::selectedVersionChanged, this, &JavaWizardWidget::javaVersionSelected);
|
||||
connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaWizardWidget::on_javaBrowseBtn_clicked);
|
||||
connect(m_javaBrowseBtn, &QPushButton::clicked, this, &JavaWizardWidget::onJavaBrowseBtnClicked);
|
||||
connect(m_javaPathTextBox, &QLineEdit::textEdited, this, &JavaWizardWidget::javaPathEdited);
|
||||
connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaWizardWidget::on_javaStatusBtn_clicked);
|
||||
connect(m_javaStatusBtn, &QToolButton::clicked, this, &JavaWizardWidget::onJavaStatusBtnClicked);
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
|
||||
connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaWizardWidget::javaDownloadBtn_clicked);
|
||||
connect(m_javaDownloadBtn, &QPushButton::clicked, this, &JavaWizardWidget::javaDownloadBtnClicked);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -73,18 +73,18 @@ void JavaWizardWidget::setupUi()
|
|||
m_horizontalLayout->addWidget(m_javaBrowseBtn);
|
||||
|
||||
m_javaStatusBtn = new QToolButton(this);
|
||||
m_javaStatusBtn->setIcon(yellowIcon);
|
||||
m_javaStatusBtn->setIcon(m_yellowIcon);
|
||||
m_horizontalLayout->addWidget(m_javaStatusBtn);
|
||||
|
||||
m_memoryGroupBox = new QGroupBox(this);
|
||||
m_memoryGroupBox->setObjectName(QStringLiteral("memoryGroupBox"));
|
||||
m_gridLayout_2 = new QGridLayout(m_memoryGroupBox);
|
||||
m_gridLayout_2->setObjectName(QStringLiteral("gridLayout_2"));
|
||||
m_gridLayout_2->setColumnStretch(0, 1);
|
||||
m_gridLayout2 = new QGridLayout(m_memoryGroupBox);
|
||||
m_gridLayout2->setObjectName(QStringLiteral("gridLayout_2"));
|
||||
m_gridLayout2->setColumnStretch(0, 1);
|
||||
|
||||
m_labelMinMem = new QLabel(m_memoryGroupBox);
|
||||
m_labelMinMem->setObjectName(QStringLiteral("labelMinMem"));
|
||||
m_gridLayout_2->addWidget(m_labelMinMem, 0, 0, 1, 1);
|
||||
m_gridLayout2->addWidget(m_labelMinMem, 0, 0, 1, 1);
|
||||
|
||||
m_minMemSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
m_minMemSpinBox->setObjectName(QStringLiteral("minMemSpinBox"));
|
||||
|
|
@ -93,11 +93,11 @@ void JavaWizardWidget::setupUi()
|
|||
m_minMemSpinBox->setMaximum(1048576);
|
||||
m_minMemSpinBox->setSingleStep(128);
|
||||
m_labelMinMem->setBuddy(m_minMemSpinBox);
|
||||
m_gridLayout_2->addWidget(m_minMemSpinBox, 0, 1, 1, 1);
|
||||
m_gridLayout2->addWidget(m_minMemSpinBox, 0, 1, 1, 1);
|
||||
|
||||
m_labelMaxMem = new QLabel(m_memoryGroupBox);
|
||||
m_labelMaxMem->setObjectName(QStringLiteral("labelMaxMem"));
|
||||
m_gridLayout_2->addWidget(m_labelMaxMem, 1, 0, 1, 1);
|
||||
m_gridLayout2->addWidget(m_labelMaxMem, 1, 0, 1, 1);
|
||||
|
||||
m_maxMemSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
m_maxMemSpinBox->setObjectName(QStringLiteral("maxMemSpinBox"));
|
||||
|
|
@ -106,16 +106,16 @@ void JavaWizardWidget::setupUi()
|
|||
m_maxMemSpinBox->setMaximum(1048576);
|
||||
m_maxMemSpinBox->setSingleStep(128);
|
||||
m_labelMaxMem->setBuddy(m_maxMemSpinBox);
|
||||
m_gridLayout_2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1);
|
||||
m_gridLayout2->addWidget(m_maxMemSpinBox, 1, 1, 1, 1);
|
||||
|
||||
m_labelMaxMemIcon = new QLabel(m_memoryGroupBox);
|
||||
m_labelMaxMemIcon->setObjectName(QStringLiteral("labelMaxMemIcon"));
|
||||
m_gridLayout_2->addWidget(m_labelMaxMemIcon, 1, 2, 1, 1);
|
||||
m_gridLayout2->addWidget(m_labelMaxMemIcon, 1, 2, 1, 1);
|
||||
|
||||
m_labelPermGen = new QLabel(m_memoryGroupBox);
|
||||
m_labelPermGen->setObjectName(QStringLiteral("labelPermGen"));
|
||||
m_labelPermGen->setText(QStringLiteral("PermGen:"));
|
||||
m_gridLayout_2->addWidget(m_labelPermGen, 2, 0, 1, 1);
|
||||
m_gridLayout2->addWidget(m_labelPermGen, 2, 0, 1, 1);
|
||||
m_labelPermGen->setVisible(false);
|
||||
|
||||
m_permGenSpinBox = new QSpinBox(m_memoryGroupBox);
|
||||
|
|
@ -124,7 +124,7 @@ void JavaWizardWidget::setupUi()
|
|||
m_permGenSpinBox->setMinimum(4);
|
||||
m_permGenSpinBox->setMaximum(1048576);
|
||||
m_permGenSpinBox->setSingleStep(8);
|
||||
m_gridLayout_2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);
|
||||
m_gridLayout2->addWidget(m_permGenSpinBox, 2, 1, 1, 1);
|
||||
m_permGenSpinBox->setVisible(false);
|
||||
|
||||
m_verticalLayout->addWidget(m_memoryGroupBox);
|
||||
|
|
@ -152,13 +152,14 @@ void JavaWizardWidget::setupUi()
|
|||
m_autodownloadCheckBox->setObjectName("autodownloadCheckBox");
|
||||
m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked());
|
||||
m_veriticalJavaLayout->addWidget(m_autodownloadCheckBox);
|
||||
connect(m_autodetectJavaCheckBox, &QCheckBox::stateChanged, this, [this] {
|
||||
connect(m_autodetectJavaCheckBox, &QCheckBox::checkStateChanged, this, [this] {
|
||||
m_autodownloadCheckBox->setEnabled(m_autodetectJavaCheckBox->isChecked());
|
||||
if (!m_autodetectJavaCheckBox->isChecked())
|
||||
if (!m_autodetectJavaCheckBox->isChecked()) {
|
||||
m_autodownloadCheckBox->setChecked(false);
|
||||
}
|
||||
});
|
||||
|
||||
connect(m_autodownloadCheckBox, &QCheckBox::stateChanged, this, [this] {
|
||||
connect(m_autodownloadCheckBox, &QCheckBox::checkStateChanged, this, [this] {
|
||||
auto isChecked = m_autodownloadCheckBox->isChecked();
|
||||
m_versionWidget->setVisible(!isChecked);
|
||||
m_javaStatusBtn->setVisible(!isChecked);
|
||||
|
|
@ -188,14 +189,14 @@ void JavaWizardWidget::initialize()
|
|||
m_versionWidget->initialize(APPLICATION->javalist());
|
||||
m_versionWidget->selectSearch();
|
||||
m_versionWidget->setResizeOn(2);
|
||||
auto s = APPLICATION->settings();
|
||||
auto* s = APPLICATION->settings();
|
||||
// Memory
|
||||
observedMinMemory = s->get("MinMemAlloc").toInt();
|
||||
observedMaxMemory = s->get("MaxMemAlloc").toInt();
|
||||
observedPermGenMemory = s->get("PermGen").toInt();
|
||||
m_minMemSpinBox->setValue(observedMinMemory);
|
||||
m_maxMemSpinBox->setValue(observedMaxMemory);
|
||||
m_permGenSpinBox->setValue(observedPermGenMemory);
|
||||
m_observedMinMemory = s->get("MinMemAlloc").toInt();
|
||||
m_observedMaxMemory = s->get("MaxMemAlloc").toInt();
|
||||
m_observedPermGenMemory = s->get("PermGen").toInt();
|
||||
m_minMemSpinBox->setValue(m_observedMinMemory);
|
||||
m_maxMemSpinBox->setValue(m_observedMaxMemory);
|
||||
m_permGenSpinBox->setValue(m_observedPermGenMemory);
|
||||
updateThresholds();
|
||||
if (BuildConfig.JAVA_DOWNLOADER_ENABLED) {
|
||||
m_autodownloadCheckBox->setChecked(true);
|
||||
|
|
@ -283,8 +284,7 @@ int JavaWizardWidget::maxHeapSize() const
|
|||
{
|
||||
auto min = m_minMemSpinBox->value();
|
||||
auto max = m_maxMemSpinBox->value();
|
||||
if (max < min)
|
||||
max = min;
|
||||
max = (std::max)(max, min);
|
||||
return max;
|
||||
}
|
||||
|
||||
|
|
@ -292,8 +292,7 @@ int JavaWizardWidget::minHeapSize() const
|
|||
{
|
||||
auto min = m_minMemSpinBox->value();
|
||||
auto max = m_maxMemSpinBox->value();
|
||||
if (min > max)
|
||||
min = max;
|
||||
min = (std::min)(min, max);
|
||||
return min;
|
||||
}
|
||||
|
||||
|
|
@ -310,19 +309,19 @@ int JavaWizardWidget::permGenSize() const
|
|||
void JavaWizardWidget::memoryValueChanged()
|
||||
{
|
||||
bool actuallyChanged = false;
|
||||
unsigned int min = m_minMemSpinBox->value();
|
||||
unsigned int max = m_maxMemSpinBox->value();
|
||||
unsigned int permgen = m_permGenSpinBox->value();
|
||||
if (min != observedMinMemory) {
|
||||
observedMinMemory = min;
|
||||
int min = m_minMemSpinBox->value();
|
||||
int max = m_maxMemSpinBox->value();
|
||||
int permgen = m_permGenSpinBox->value();
|
||||
if (min != m_observedMinMemory) {
|
||||
m_observedMinMemory = min;
|
||||
actuallyChanged = true;
|
||||
}
|
||||
if (max != observedMaxMemory) {
|
||||
observedMaxMemory = max;
|
||||
if (max != m_observedMaxMemory) {
|
||||
m_observedMaxMemory = max;
|
||||
actuallyChanged = true;
|
||||
}
|
||||
if (permgen != observedPermGenMemory) {
|
||||
observedPermGenMemory = permgen;
|
||||
if (permgen != m_observedPermGenMemory) {
|
||||
m_observedPermGenMemory = permgen;
|
||||
actuallyChanged = true;
|
||||
}
|
||||
if (actuallyChanged) {
|
||||
|
|
@ -331,7 +330,7 @@ void JavaWizardWidget::memoryValueChanged()
|
|||
}
|
||||
}
|
||||
|
||||
void JavaWizardWidget::javaVersionSelected(BaseVersion::Ptr version)
|
||||
void JavaWizardWidget::javaVersionSelected(const BaseVersion::Ptr& version)
|
||||
{
|
||||
auto java = std::dynamic_pointer_cast<JavaInstall>(version);
|
||||
if (!java) {
|
||||
|
|
@ -344,25 +343,25 @@ void JavaWizardWidget::javaVersionSelected(BaseVersion::Ptr version)
|
|||
checkJavaPath(java->path);
|
||||
}
|
||||
|
||||
void JavaWizardWidget::on_javaBrowseBtn_clicked()
|
||||
void JavaWizardWidget::onJavaBrowseBtnClicked()
|
||||
{
|
||||
auto filter = QString("Java (%1)").arg(JavaUtils::javaExecutable);
|
||||
auto raw_path = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter);
|
||||
if (raw_path.isEmpty()) {
|
||||
auto rawPath = QFileDialog::getOpenFileName(this, tr("Find Java executable"), QString(), filter);
|
||||
if (rawPath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
auto cooked_path = FS::NormalizePath(raw_path);
|
||||
m_javaPathTextBox->setText(cooked_path);
|
||||
checkJavaPath(cooked_path);
|
||||
auto cookedPath = FS::NormalizePath(rawPath);
|
||||
m_javaPathTextBox->setText(cookedPath);
|
||||
checkJavaPath(cookedPath);
|
||||
}
|
||||
|
||||
void JavaWizardWidget::javaDownloadBtn_clicked()
|
||||
void JavaWizardWidget::javaDownloadBtnClicked()
|
||||
{
|
||||
auto jdialog = new Java::InstallDialog({}, nullptr, this);
|
||||
auto* jdialog = new Java::InstallDialog({}, nullptr, this);
|
||||
jdialog->exec();
|
||||
}
|
||||
|
||||
void JavaWizardWidget::on_javaStatusBtn_clicked()
|
||||
void JavaWizardWidget::onJavaStatusBtnClicked()
|
||||
{
|
||||
QString text;
|
||||
bool failed = false;
|
||||
|
|
@ -414,14 +413,14 @@ void JavaWizardWidget::setJavaStatus(JavaWizardWidget::JavaStatus status)
|
|||
javaStatus = status;
|
||||
switch (javaStatus) {
|
||||
case JavaStatus::Good:
|
||||
m_javaStatusBtn->setIcon(goodIcon);
|
||||
m_javaStatusBtn->setIcon(m_goodIcon);
|
||||
break;
|
||||
case JavaStatus::NotSet:
|
||||
case JavaStatus::Pending:
|
||||
m_javaStatusBtn->setIcon(yellowIcon);
|
||||
m_javaStatusBtn->setIcon(m_yellowIcon);
|
||||
break;
|
||||
default:
|
||||
m_javaStatusBtn->setIcon(badIcon);
|
||||
m_javaStatusBtn->setIcon(m_badIcon);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -447,7 +446,7 @@ void JavaWizardWidget::checkJavaPathOnEdit(const QString& path)
|
|||
void JavaWizardWidget::checkJavaPath(const QString& path)
|
||||
{
|
||||
if (m_checker) {
|
||||
queuedCheck = path;
|
||||
m_queuedCheck = path;
|
||||
return;
|
||||
}
|
||||
auto realPath = FS::ResolveExecutable(path);
|
||||
|
|
@ -481,9 +480,9 @@ void JavaWizardWidget::checkFinished(const JavaChecker::Result& result)
|
|||
}
|
||||
updateThresholds();
|
||||
m_checker.reset();
|
||||
if (!queuedCheck.isNull()) {
|
||||
checkJavaPath(queuedCheck);
|
||||
queuedCheck.clear();
|
||||
if (!m_queuedCheck.isNull()) {
|
||||
checkJavaPath(m_queuedCheck);
|
||||
m_queuedCheck.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -507,19 +506,16 @@ void JavaWizardWidget::updateThresholds()
|
|||
{
|
||||
QString iconName;
|
||||
|
||||
if (observedMaxMemory >= m_availableMemory) {
|
||||
if (static_cast<uint64_t>(m_observedMaxMemory) >= m_availableMemory) {
|
||||
iconName = "status-bad";
|
||||
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation exceeds your system memory capacity."));
|
||||
} else if (observedMaxMemory > (m_availableMemory * 0.9)) {
|
||||
} else if (static_cast<double>(m_observedMaxMemory) > (static_cast<double>(m_availableMemory) * 0.9)) {
|
||||
iconName = "status-yellow";
|
||||
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation approaches your system memory capacity."));
|
||||
} else if (observedMaxMemory < observedMinMemory) {
|
||||
} else if (m_observedMaxMemory < m_observedMinMemory) {
|
||||
iconName = "status-yellow";
|
||||
m_labelMaxMemIcon->setToolTip(tr("Your maximum memory allocation is smaller than the minimum value"));
|
||||
} else if (BuildConfig.JAVA_DOWNLOADER_ENABLED && m_autodownloadCheckBox->isChecked()) {
|
||||
iconName = "status-good";
|
||||
m_labelMaxMemIcon->setToolTip("");
|
||||
} else if (observedMaxMemory > 2048 && !m_result.is_64bit) {
|
||||
} else if (m_observedMaxMemory > 2048 && !m_result.is_64bit) {
|
||||
iconName = "status-bad";
|
||||
m_labelMaxMemIcon->setToolTip(tr("You are exceeding the maximum allocation supported by 32-bit installations of Java."));
|
||||
} else {
|
||||
|
|
@ -537,7 +533,7 @@ void JavaWizardWidget::updateThresholds()
|
|||
|
||||
bool JavaWizardWidget::autoDownloadJava() const
|
||||
{
|
||||
return m_autodownloadCheckBox && m_autodownloadCheckBox->isChecked();
|
||||
return (m_autodownloadCheckBox != nullptr) && m_autodownloadCheckBox->isChecked();
|
||||
}
|
||||
|
||||
bool JavaWizardWidget::autoDetectJava() const
|
||||
|
|
@ -545,7 +541,7 @@ bool JavaWizardWidget::autoDetectJava() const
|
|||
return m_autodetectJavaCheckBox->isChecked();
|
||||
}
|
||||
|
||||
void JavaWizardWidget::onSpinBoxValueChanged(int)
|
||||
void JavaWizardWidget::onSpinBoxValueChanged(int /*unused*/)
|
||||
{
|
||||
m_memoryTimer->start(500);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,18 @@ class JavaWizardWidget : public QWidget {
|
|||
|
||||
public:
|
||||
explicit JavaWizardWidget(QWidget* parent);
|
||||
virtual ~JavaWizardWidget();
|
||||
~JavaWizardWidget() override;
|
||||
|
||||
enum class JavaStatus { NotSet, Pending, Good, DoesNotExist, DoesNotStart, ReturnedInvalidData } javaStatus = JavaStatus::NotSet;
|
||||
enum class JavaStatus : std::uint8_t {
|
||||
NotSet,
|
||||
Pending,
|
||||
Good,
|
||||
DoesNotExist,
|
||||
DoesNotStart,
|
||||
ReturnedInvalidData
|
||||
} javaStatus = JavaStatus::NotSet;
|
||||
|
||||
enum class ValidationStatus { Bad, JavaBad, AllOK };
|
||||
enum class ValidationStatus : std::uint8_t { Bad, JavaBad, AllOK };
|
||||
|
||||
void refresh();
|
||||
void initialize();
|
||||
|
|
@ -49,10 +56,10 @@ class JavaWizardWidget : public QWidget {
|
|||
void onSpinBoxValueChanged(int);
|
||||
void memoryValueChanged();
|
||||
void javaPathEdited(const QString& path);
|
||||
void javaVersionSelected(BaseVersion::Ptr version);
|
||||
void on_javaBrowseBtn_clicked();
|
||||
void on_javaStatusBtn_clicked();
|
||||
void javaDownloadBtn_clicked();
|
||||
void javaVersionSelected(const BaseVersion::Ptr& version);
|
||||
void onJavaBrowseBtnClicked();
|
||||
void onJavaStatusBtnClicked();
|
||||
void javaDownloadBtnClicked();
|
||||
void checkFinished(const JavaChecker::Result& result);
|
||||
|
||||
protected: /* methods */
|
||||
|
|
@ -72,7 +79,7 @@ class JavaWizardWidget : public QWidget {
|
|||
QHBoxLayout* m_horizontalLayout = nullptr;
|
||||
|
||||
QGroupBox* m_memoryGroupBox = nullptr;
|
||||
QGridLayout* m_gridLayout_2 = nullptr;
|
||||
QGridLayout* m_gridLayout2 = nullptr;
|
||||
QSpinBox* m_maxMemSpinBox = nullptr;
|
||||
QLabel* m_labelMinMem = nullptr;
|
||||
QLabel* m_labelMaxMem = nullptr;
|
||||
|
|
@ -83,20 +90,20 @@ class JavaWizardWidget : public QWidget {
|
|||
|
||||
QHBoxLayout* m_horizontalBtnLayout = nullptr;
|
||||
QPushButton* m_javaDownloadBtn = nullptr;
|
||||
QIcon goodIcon;
|
||||
QIcon yellowIcon;
|
||||
QIcon badIcon;
|
||||
QIcon m_goodIcon;
|
||||
QIcon m_yellowIcon;
|
||||
QIcon m_badIcon;
|
||||
|
||||
QGroupBox* m_autoJavaGroupBox = nullptr;
|
||||
QVBoxLayout* m_veriticalJavaLayout = nullptr;
|
||||
QCheckBox* m_autodetectJavaCheckBox = nullptr;
|
||||
QCheckBox* m_autodownloadCheckBox = nullptr;
|
||||
|
||||
unsigned int observedMinMemory = 0;
|
||||
unsigned int observedMaxMemory = 0;
|
||||
unsigned int observedPermGenMemory = 0;
|
||||
QString queuedCheck;
|
||||
uint64_t m_availableMemory = 0ull;
|
||||
int m_observedMinMemory = 0;
|
||||
int m_observedMaxMemory = 0;
|
||||
int m_observedPermGenMemory = 0;
|
||||
QString m_queuedCheck;
|
||||
uint64_t m_availableMemory = 0ULL;
|
||||
shared_qobject_ptr<JavaChecker> m_checker;
|
||||
JavaChecker::Result m_result;
|
||||
QTimer* m_memoryTimer;
|
||||
|
|
|
|||
|
|
@ -6,64 +6,67 @@
|
|||
#include <QTreeView>
|
||||
#include <QVBoxLayout>
|
||||
#include "Application.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "settings/Setting.h"
|
||||
#include "settings/SettingsObject.h"
|
||||
#include "translations/TranslationsModel.h"
|
||||
|
||||
LanguageSelectionWidget::LanguageSelectionWidget(QWidget* parent) : QWidget(parent)
|
||||
LanguageSelectionWidget::LanguageSelectionWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_verticalLayout(new QVBoxLayout(this))
|
||||
, m_languageView(new QTreeView(this))
|
||||
, m_helpUsLabel(new QLabel(this))
|
||||
, m_formatCheckbox(new QCheckBox(this))
|
||||
{
|
||||
verticalLayout = new QVBoxLayout(this);
|
||||
verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
languageView = new QTreeView(this);
|
||||
languageView->setObjectName(QStringLiteral("languageView"));
|
||||
languageView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
languageView->setAlternatingRowColors(true);
|
||||
languageView->setRootIsDecorated(false);
|
||||
languageView->setItemsExpandable(false);
|
||||
languageView->setWordWrap(true);
|
||||
languageView->header()->setCascadingSectionResizes(true);
|
||||
languageView->header()->setStretchLastSection(false);
|
||||
verticalLayout->addWidget(languageView);
|
||||
helpUsLabel = new QLabel(this);
|
||||
helpUsLabel->setObjectName(QStringLiteral("helpUsLabel"));
|
||||
helpUsLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
helpUsLabel->setOpenExternalLinks(true);
|
||||
helpUsLabel->setWordWrap(true);
|
||||
verticalLayout->addWidget(helpUsLabel);
|
||||
m_verticalLayout->setObjectName(QStringLiteral("verticalLayout"));
|
||||
|
||||
formatCheckbox = new QCheckBox(this);
|
||||
formatCheckbox->setObjectName(QStringLiteral("formatCheckbox"));
|
||||
formatCheckbox->setCheckState(APPLICATION->settings()->get("UseSystemLocale").toBool() ? Qt::Checked : Qt::Unchecked);
|
||||
connect(formatCheckbox, &QCheckBox::stateChanged,
|
||||
[this]() { APPLICATION->translations()->setUseSystemLocale(formatCheckbox->isChecked()); });
|
||||
verticalLayout->addWidget(formatCheckbox);
|
||||
m_languageView->setObjectName(QStringLiteral("languageView"));
|
||||
m_languageView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
m_languageView->setAlternatingRowColors(true);
|
||||
m_languageView->setRootIsDecorated(false);
|
||||
m_languageView->setItemsExpandable(false);
|
||||
m_languageView->setWordWrap(true);
|
||||
m_languageView->header()->setCascadingSectionResizes(true);
|
||||
m_languageView->header()->setStretchLastSection(false);
|
||||
m_verticalLayout->addWidget(m_languageView);
|
||||
|
||||
auto translations = APPLICATION->translations();
|
||||
m_helpUsLabel->setObjectName(QStringLiteral("helpUsLabel"));
|
||||
m_helpUsLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse);
|
||||
m_helpUsLabel->setOpenExternalLinks(true);
|
||||
m_helpUsLabel->setWordWrap(true);
|
||||
m_verticalLayout->addWidget(m_helpUsLabel);
|
||||
|
||||
m_formatCheckbox->setObjectName(QStringLiteral("formatCheckbox"));
|
||||
m_formatCheckbox->setCheckState(APPLICATION->settings()->get("UseSystemLocale").toBool() ? Qt::Checked : Qt::Unchecked);
|
||||
connect(m_formatCheckbox, &QCheckBox::checkStateChanged,
|
||||
[this] { APPLICATION->translations()->setUseSystemLocale(m_formatCheckbox->isChecked()); });
|
||||
m_verticalLayout->addWidget(m_formatCheckbox);
|
||||
|
||||
auto* translations = APPLICATION->translations();
|
||||
auto index = translations->selectedIndex();
|
||||
languageView->setModel(translations);
|
||||
languageView->setCurrentIndex(index);
|
||||
languageView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
languageView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
connect(languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageSelectionWidget::languageRowChanged);
|
||||
verticalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
m_languageView->setModel(translations);
|
||||
m_languageView->setCurrentIndex(index);
|
||||
m_languageView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
m_languageView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||||
connect(m_languageView->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &LanguageSelectionWidget::languageRowChanged);
|
||||
m_verticalLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
auto language_setting = APPLICATION->settings()->getSetting("Language");
|
||||
connect(language_setting.get(), &Setting::SettingChanged, this, &LanguageSelectionWidget::languageSettingChanged);
|
||||
auto languageSetting = APPLICATION->settings()->getSetting("Language");
|
||||
connect(languageSetting.get(), &Setting::SettingChanged, this, &LanguageSelectionWidget::languageSettingChanged);
|
||||
}
|
||||
|
||||
QString LanguageSelectionWidget::getSelectedLanguageKey() const
|
||||
{
|
||||
auto translations = APPLICATION->translations();
|
||||
return translations->data(languageView->currentIndex(), Qt::UserRole).toString();
|
||||
auto* translations = APPLICATION->translations();
|
||||
return translations->data(m_languageView->currentIndex(), Qt::UserRole).toString();
|
||||
}
|
||||
|
||||
void LanguageSelectionWidget::retranslate()
|
||||
{
|
||||
QString text = tr("Don't see your language or the quality is poor?<br/><a href=\"%1\">Help us with translations!</a>")
|
||||
.arg(BuildConfig.TRANSLATIONS_URL);
|
||||
helpUsLabel->setText(text);
|
||||
formatCheckbox->setText(tr("Use system regional standards"));
|
||||
m_helpUsLabel->setText(text);
|
||||
m_formatCheckbox->setText(tr("Use system regional standards"));
|
||||
}
|
||||
|
||||
void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, const QModelIndex& previous)
|
||||
|
|
@ -71,15 +74,15 @@ void LanguageSelectionWidget::languageRowChanged(const QModelIndex& current, con
|
|||
if (current == previous) {
|
||||
return;
|
||||
}
|
||||
auto translations = APPLICATION->translations();
|
||||
auto* translations = APPLICATION->translations();
|
||||
QString key = translations->data(current, Qt::UserRole).toString();
|
||||
translations->selectLanguage(key);
|
||||
translations->updateLanguage(key);
|
||||
}
|
||||
|
||||
void LanguageSelectionWidget::languageSettingChanged(const Setting&, const QVariant&)
|
||||
void LanguageSelectionWidget::languageSettingChanged(const Setting& /*unused*/, const QVariant& /*unused*/)
|
||||
{
|
||||
auto translations = APPLICATION->translations();
|
||||
auto* translations = APPLICATION->translations();
|
||||
auto index = translations->selectedIndex();
|
||||
languageView->setCurrentIndex(index);
|
||||
m_languageView->setCurrentIndex(index);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,19 +26,19 @@ class QCheckBox;
|
|||
class LanguageSelectionWidget : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LanguageSelectionWidget(QWidget* parent = 0);
|
||||
virtual ~LanguageSelectionWidget() {};
|
||||
explicit LanguageSelectionWidget(QWidget* parent = nullptr);
|
||||
~LanguageSelectionWidget() override = default;
|
||||
|
||||
QString getSelectedLanguageKey() const;
|
||||
void retranslate();
|
||||
|
||||
protected slots:
|
||||
void languageRowChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||
static void languageRowChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||
void languageSettingChanged(const Setting&, const QVariant&);
|
||||
|
||||
private:
|
||||
QVBoxLayout* verticalLayout = nullptr;
|
||||
QTreeView* languageView = nullptr;
|
||||
QLabel* helpUsLabel = nullptr;
|
||||
QCheckBox* formatCheckbox = nullptr;
|
||||
QVBoxLayout* m_verticalLayout = nullptr;
|
||||
QTreeView* m_languageView = nullptr;
|
||||
QLabel* m_helpUsLabel = nullptr;
|
||||
QCheckBox* m_formatCheckbox = nullptr;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@
|
|||
|
||||
#include <QFileDialog>
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "Json.h"
|
||||
#include "minecraft/PackProfile.h"
|
||||
#include "minecraft/WorldList.h"
|
||||
|
|
@ -49,7 +48,7 @@
|
|||
#include "settings/Setting.h"
|
||||
|
||||
MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstance* instance, QWidget* parent)
|
||||
: QWidget(parent), m_instance(std::move(instance)), m_ui(new Ui::MinecraftSettingsWidget)
|
||||
: QWidget(parent), m_instance(instance), m_ui(new Ui::MinecraftSettingsWidget)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
|
|
@ -83,7 +82,7 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstance* instance, QW
|
|||
|
||||
m_quickPlaySingleplayer = m_instance->traits().contains("feature:is_quick_play_singleplayer");
|
||||
if (m_quickPlaySingleplayer) {
|
||||
auto worlds = m_instance->worldList();
|
||||
auto* worlds = m_instance->worldList();
|
||||
worlds->update();
|
||||
for (const auto& world : worlds->allWorlds()) {
|
||||
m_ui->worldsCb->addItem(world.folderName());
|
||||
|
|
@ -102,23 +101,25 @@ MinecraftSettingsWidget::MinecraftSettingsWidget(MinecraftInstance* instance, QW
|
|||
|
||||
connect(m_ui->globalDataPacksGroupBox, &QGroupBox::toggled, this, [this](bool value) {
|
||||
m_instance->settings()->set("GlobalDataPacksEnabled", value);
|
||||
if (!value)
|
||||
if (!value) {
|
||||
m_instance->settings()->reset("GlobalDataPacksPath");
|
||||
}
|
||||
});
|
||||
connect(m_ui->dataPacksPathEdit, &QLineEdit::editingFinished, this, &MinecraftSettingsWidget::saveDataPacksPath);
|
||||
connect(m_ui->dataPacksPathBrowse, &QPushButton::clicked, this, &MinecraftSettingsWidget::selectDataPacksFolder);
|
||||
|
||||
connect(m_ui->loaderGroup, &QGroupBox::toggled, this, [this](bool value) {
|
||||
m_instance->settings()->set("OverrideModDownloadLoaders", value);
|
||||
if (value)
|
||||
if (value) {
|
||||
saveSelectedLoaders();
|
||||
else
|
||||
} else {
|
||||
m_instance->settings()->reset("ModDownloadLoaders");
|
||||
}
|
||||
});
|
||||
|
||||
for (auto c : { m_ui->neoForge, m_ui->forge, m_ui->fabric, m_ui->quilt, m_ui->liteLoader, m_ui->babric, m_ui->btaBabric,
|
||||
m_ui->legacyFabric, m_ui->ornithe, m_ui->rift }) {
|
||||
connect(c, &QCheckBox::stateChanged, this, &MinecraftSettingsWidget::saveSelectedLoaders);
|
||||
for (auto* c : { m_ui->neoForge, m_ui->forge, m_ui->fabric, m_ui->quilt, m_ui->liteLoader, m_ui->babric, m_ui->btaBabric,
|
||||
m_ui->legacyFabric, m_ui->ornithe, m_ui->rift }) {
|
||||
connect(c, &QCheckBox::checkStateChanged, this, &MinecraftSettingsWidget::saveSelectedLoaders);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -154,12 +155,13 @@ MinecraftSettingsWidget::~MinecraftSettingsWidget()
|
|||
|
||||
void MinecraftSettingsWidget::loadSettings()
|
||||
{
|
||||
SettingsObject* settings;
|
||||
SettingsObject* settings = nullptr;
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings = m_instance->settings();
|
||||
else
|
||||
} else {
|
||||
settings = APPLICATION->settings();
|
||||
}
|
||||
|
||||
// Game Window
|
||||
m_ui->windowSizeGroupBox->setChecked(m_instance == nullptr || settings->get("OverrideWindow").toBool() ||
|
||||
|
|
@ -183,8 +185,9 @@ void MinecraftSettingsWidget::loadSettings()
|
|||
m_ui->autoCloseConsoleCheck->setChecked(settings->get("AutoCloseConsole").toBool());
|
||||
m_ui->showConsoleErrorCheck->setChecked(settings->get("ShowConsoleOnError").toBool());
|
||||
|
||||
if (m_javaSettings != nullptr)
|
||||
if (m_javaSettings != nullptr) {
|
||||
m_javaSettings->loadSettings();
|
||||
}
|
||||
|
||||
// Custom commands
|
||||
m_ui->customCommands->initialize(m_instance != nullptr, m_instance == nullptr || settings->get("OverrideCommands").toBool(),
|
||||
|
|
@ -254,7 +257,7 @@ void MinecraftSettingsWidget::loadSettings()
|
|||
auto blockSignalsCheckBoxes = { m_ui->neoForge, m_ui->forge, m_ui->fabric, m_ui->quilt, m_ui->liteLoader,
|
||||
m_ui->babric, m_ui->btaBabric, m_ui->legacyFabric, m_ui->ornithe, m_ui->rift };
|
||||
m_ui->loaderGroup->blockSignals(true);
|
||||
for (auto c : blockSignalsCheckBoxes) {
|
||||
for (auto* c : blockSignalsCheckBoxes) {
|
||||
c->blockSignals(true);
|
||||
}
|
||||
|
||||
|
|
@ -277,20 +280,20 @@ void MinecraftSettingsWidget::loadSettings()
|
|||
} else {
|
||||
auto instLoaders = m_instance->getPackProfile()->getSupportedModLoaders().value_or(ModPlatform::ModLoaderTypes(0));
|
||||
|
||||
m_ui->neoForge->setChecked(instLoaders & ModPlatform::NeoForge);
|
||||
m_ui->forge->setChecked(instLoaders & ModPlatform::Forge);
|
||||
m_ui->fabric->setChecked(instLoaders & ModPlatform::Fabric);
|
||||
m_ui->quilt->setChecked(instLoaders & ModPlatform::Quilt);
|
||||
m_ui->liteLoader->setChecked(instLoaders & ModPlatform::LiteLoader);
|
||||
m_ui->babric->setChecked(instLoaders & ModPlatform::Babric);
|
||||
m_ui->btaBabric->setChecked(instLoaders & ModPlatform::BTA);
|
||||
m_ui->legacyFabric->setChecked(instLoaders & ModPlatform::LegacyFabric);
|
||||
m_ui->ornithe->setChecked(instLoaders & ModPlatform::Ornithe);
|
||||
m_ui->rift->setChecked(instLoaders & ModPlatform::Rift);
|
||||
m_ui->neoForge->setChecked((instLoaders & ModPlatform::NeoForge) != 0U);
|
||||
m_ui->forge->setChecked((instLoaders & ModPlatform::Forge) != 0U);
|
||||
m_ui->fabric->setChecked((instLoaders & ModPlatform::Fabric) != 0U);
|
||||
m_ui->quilt->setChecked((instLoaders & ModPlatform::Quilt) != 0U);
|
||||
m_ui->liteLoader->setChecked((instLoaders & ModPlatform::LiteLoader) != 0U);
|
||||
m_ui->babric->setChecked((instLoaders & ModPlatform::Babric) != 0U);
|
||||
m_ui->btaBabric->setChecked((instLoaders & ModPlatform::BTA) != 0U);
|
||||
m_ui->legacyFabric->setChecked((instLoaders & ModPlatform::LegacyFabric) != 0U);
|
||||
m_ui->ornithe->setChecked((instLoaders & ModPlatform::Ornithe) != 0U);
|
||||
m_ui->rift->setChecked((instLoaders & ModPlatform::Rift) != 0U);
|
||||
}
|
||||
|
||||
m_ui->loaderGroup->blockSignals(false);
|
||||
for (auto c : blockSignalsCheckBoxes) {
|
||||
for (auto* c : blockSignalsCheckBoxes) {
|
||||
c->blockSignals(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -308,12 +311,13 @@ void MinecraftSettingsWidget::loadSettings()
|
|||
|
||||
void MinecraftSettingsWidget::saveSettings()
|
||||
{
|
||||
SettingsObject* settings;
|
||||
SettingsObject* settings = nullptr;
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings = m_instance->settings();
|
||||
else
|
||||
} else {
|
||||
settings = APPLICATION->settings();
|
||||
}
|
||||
|
||||
{
|
||||
SettingsObject::Lock lock(settings);
|
||||
|
|
@ -321,8 +325,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
// Console
|
||||
bool console = m_instance == nullptr || m_ui->consoleSettingsBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideConsole", console);
|
||||
}
|
||||
|
||||
if (console) {
|
||||
settings->set("ShowConsole", m_ui->showConsoleCheck->isChecked());
|
||||
|
|
@ -359,8 +364,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
// Custom Commands
|
||||
bool custcmd = m_instance == nullptr || m_ui->customCommands->checked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideCommands", custcmd);
|
||||
}
|
||||
|
||||
if (custcmd) {
|
||||
settings->set("PreLaunchCommand", m_ui->customCommands->prelaunchCommand());
|
||||
|
|
@ -375,19 +381,22 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
// Environment Variables
|
||||
auto env = m_instance == nullptr || m_ui->environmentVariables->override();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideEnv", env);
|
||||
}
|
||||
|
||||
if (env)
|
||||
if (env) {
|
||||
settings->set("Env", Json::fromMap(m_ui->environmentVariables->value()));
|
||||
else
|
||||
} else {
|
||||
settings->reset("Env");
|
||||
}
|
||||
|
||||
// Workarounds
|
||||
bool workarounds = m_instance == nullptr || m_ui->nativeWorkaroundsGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideNativeWorkarounds", workarounds);
|
||||
}
|
||||
|
||||
if (workarounds) {
|
||||
settings->set("UseNativeGLFW", m_ui->useNativeGLFWCheck->isChecked());
|
||||
|
|
@ -404,8 +413,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
// Performance
|
||||
bool performance = m_instance == nullptr || m_ui->perfomanceGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverridePerformance", performance);
|
||||
}
|
||||
|
||||
if (performance) {
|
||||
settings->set("EnableFeralGamemode", m_ui->enableFeralGamemodeCheck->isChecked());
|
||||
|
|
@ -422,8 +432,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
// Game time
|
||||
bool gameTime = m_instance == nullptr || m_ui->gameTimeGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideGameTime", gameTime);
|
||||
}
|
||||
|
||||
if (gameTime) {
|
||||
settings->set("ShowGameTime", m_ui->showGameTime->isChecked());
|
||||
|
|
@ -463,8 +474,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
|
||||
if (accountIndex != -1) {
|
||||
const MinecraftAccountPtr account = APPLICATION->accounts()->at(accountIndex);
|
||||
if (account != nullptr)
|
||||
if (account != nullptr) {
|
||||
settings->set("InstanceAccountId", account->profileId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
settings->reset("InstanceAccountId");
|
||||
|
|
@ -473,8 +485,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
|
||||
bool overrideLegacySettings = m_instance == nullptr || m_ui->legacySettingsGroupBox->isChecked();
|
||||
|
||||
if (m_instance != nullptr)
|
||||
if (m_instance != nullptr) {
|
||||
settings->set("OverrideLegacySettings", overrideLegacySettings);
|
||||
}
|
||||
|
||||
if (overrideLegacySettings) {
|
||||
settings->set("OnlineFixes", m_ui->onlineFixes->isChecked());
|
||||
|
|
@ -483,8 +496,9 @@ void MinecraftSettingsWidget::saveSettings()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_javaSettings != nullptr)
|
||||
if (m_javaSettings != nullptr) {
|
||||
m_javaSettings->saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
void MinecraftSettingsWidget::openGlobalSettings()
|
||||
|
|
@ -493,16 +507,17 @@ void MinecraftSettingsWidget::openGlobalSettings()
|
|||
|
||||
qDebug() << id;
|
||||
|
||||
if (id == "javaPage")
|
||||
if (id == "javaPage") {
|
||||
APPLICATION->ShowGlobalSettings(this, "java-settings");
|
||||
else // TODO select tab
|
||||
} else { // TODO select tab
|
||||
APPLICATION->ShowGlobalSettings(this, "minecraft-settings");
|
||||
}
|
||||
}
|
||||
|
||||
void MinecraftSettingsWidget::updateAccountsMenu(SettingsObject& settings)
|
||||
{
|
||||
m_ui->instanceAccountSelector->clear();
|
||||
auto accounts = APPLICATION->accounts();
|
||||
auto* accounts = APPLICATION->accounts();
|
||||
int accountIndex = accounts->findAccountByProfileId(settings.get("InstanceAccountId").toString());
|
||||
|
||||
for (int i = 0; i < accounts->count(); i++) {
|
||||
|
|
@ -510,12 +525,14 @@ void MinecraftSettingsWidget::updateAccountsMenu(SettingsObject& settings)
|
|||
|
||||
QIcon face = account->getFace();
|
||||
|
||||
if (face.isNull())
|
||||
if (face.isNull()) {
|
||||
face = QIcon::fromTheme("noaccount");
|
||||
}
|
||||
|
||||
m_ui->instanceAccountSelector->addItem(face, account->profileName(), i);
|
||||
if (i == accountIndex)
|
||||
if (i == accountIndex) {
|
||||
m_ui->instanceAccountSelector->setCurrentIndex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -528,34 +545,45 @@ void MinecraftSettingsWidget::saveSelectedLoaders()
|
|||
{
|
||||
QStringList loaders;
|
||||
|
||||
if (m_ui->neoForge->isChecked())
|
||||
if (m_ui->neoForge->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::NeoForge);
|
||||
if (m_ui->forge->isChecked())
|
||||
}
|
||||
if (m_ui->forge->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Forge);
|
||||
if (m_ui->fabric->isChecked())
|
||||
}
|
||||
if (m_ui->fabric->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Fabric);
|
||||
if (m_ui->quilt->isChecked())
|
||||
}
|
||||
if (m_ui->quilt->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Quilt);
|
||||
if (m_ui->liteLoader->isChecked())
|
||||
}
|
||||
if (m_ui->liteLoader->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::LiteLoader);
|
||||
if (m_ui->babric->isChecked())
|
||||
}
|
||||
if (m_ui->babric->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Babric);
|
||||
if (m_ui->btaBabric->isChecked())
|
||||
}
|
||||
if (m_ui->btaBabric->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::BTA);
|
||||
if (m_ui->legacyFabric->isChecked())
|
||||
}
|
||||
if (m_ui->legacyFabric->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::LegacyFabric);
|
||||
if (m_ui->ornithe->isChecked())
|
||||
}
|
||||
if (m_ui->ornithe->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Ornithe);
|
||||
if (m_ui->rift->isChecked())
|
||||
}
|
||||
if (m_ui->rift->isChecked()) {
|
||||
loaders << getModLoaderAsString(ModPlatform::Rift);
|
||||
}
|
||||
|
||||
m_instance->settings()->set("ModDownloadLoaders", Json::fromStringList(loaders));
|
||||
}
|
||||
|
||||
void MinecraftSettingsWidget::saveDataPacksPath()
|
||||
{
|
||||
if (QDir::separator() != '/')
|
||||
if (QDir::separator() != '/') {
|
||||
m_ui->dataPacksPathEdit->setText(m_ui->dataPacksPathEdit->text().replace(QDir::separator(), '/'));
|
||||
}
|
||||
|
||||
m_instance->settings()->set("GlobalDataPacksPath", m_ui->dataPacksPathEdit->text());
|
||||
}
|
||||
|
|
@ -564,8 +592,9 @@ void MinecraftSettingsWidget::selectDataPacksFolder()
|
|||
{
|
||||
QString path = QFileDialog::getExistingDirectory(this, tr("Select Global Data Packs Folder"), m_instance->gameRoot());
|
||||
|
||||
if (path.isEmpty())
|
||||
if (path.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if it's inside the instance dir, set path relative to .minecraft
|
||||
// (so that if it's directly in instance dir it will still lead with .. but more than two levels up are kept absolute)
|
||||
|
|
@ -573,8 +602,9 @@ void MinecraftSettingsWidget::selectDataPacksFolder()
|
|||
const QUrl instanceRootUrl = QUrl::fromLocalFile(m_instance->instanceRoot());
|
||||
const QUrl pathUrl = QUrl::fromLocalFile(path);
|
||||
|
||||
if (instanceRootUrl.isParentOf(pathUrl))
|
||||
if (instanceRootUrl.isParentOf(pathUrl)) {
|
||||
path = QDir(m_instance->gameRoot()).relativeFilePath(path);
|
||||
}
|
||||
|
||||
m_ui->dataPacksPathEdit->setText(path);
|
||||
m_instance->settings()->set("GlobalDataPacksPath", path);
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class MinecraftSettingsWidget;
|
|||
|
||||
class MinecraftSettingsWidget : public QWidget {
|
||||
public:
|
||||
MinecraftSettingsWidget(MinecraftInstance* instance, QWidget* parent = nullptr);
|
||||
explicit MinecraftSettingsWidget(MinecraftInstance* instance, QWidget* parent = nullptr);
|
||||
~MinecraftSettingsWidget() override;
|
||||
|
||||
void loadSettings();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@
|
|||
#include <QComboBox>
|
||||
#include <QListWidget>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include "BaseVersionList.h"
|
||||
#include "Json.h"
|
||||
#include "Version.h"
|
||||
|
|
@ -55,18 +54,21 @@ std::unique_ptr<ModFilterWidget> ModFilterWidget::create(MinecraftInstance* inst
|
|||
return std::unique_ptr<ModFilterWidget>(new ModFilterWidget(instance, extended));
|
||||
}
|
||||
|
||||
namespace {
|
||||
class VersionBasicModel : public QIdentityProxyModel {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit VersionBasicModel(QObject* parent = nullptr) : QIdentityProxyModel(parent) {}
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override
|
||||
{
|
||||
if (role == Qt::DisplayRole)
|
||||
if (role == Qt::DisplayRole) {
|
||||
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
||||
if (role == Qt::UserRole)
|
||||
}
|
||||
if (role == Qt::UserRole) {
|
||||
return QIdentityProxyModel::data(index, BaseVersionList::VersionIdRole);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
|
@ -75,7 +77,7 @@ class AllVersionProxyModel : public QSortFilterProxyModel {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
AllVersionProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {}
|
||||
explicit AllVersionProxyModel(QObject* parent = nullptr) : QSortFilterProxyModel(parent) {}
|
||||
|
||||
int rowCount(const QModelIndex& parent = QModelIndex()) const override { return QSortFilterProxyModel::rowCount(parent) + 1; }
|
||||
|
||||
|
|
@ -107,74 +109,74 @@ class AllVersionProxyModel : public QSortFilterProxyModel {
|
|||
return QSortFilterProxyModel::flags(index);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended)
|
||||
: QTabWidget(), ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter())
|
||||
: m_ui(new Ui::ModFilterWidget), m_instance(instance), m_filter(new Filter()), m_versionsProxy(new VersionProxyModel(this))
|
||||
{
|
||||
ui->setupUi(this);
|
||||
m_ui->setupUi(this);
|
||||
|
||||
m_versions_proxy = new VersionProxyModel(this);
|
||||
m_versions_proxy->setFilter(BaseVersionList::TypeRole, Filters::equals("release"));
|
||||
m_versionsProxy->setFilter(BaseVersionList::TypeRole, Filters::equals("release"));
|
||||
|
||||
QAbstractProxyModel* proxy = new VersionBasicModel(this);
|
||||
proxy->setSourceModel(m_versions_proxy);
|
||||
proxy->setSourceModel(m_versionsProxy);
|
||||
|
||||
if (extended) {
|
||||
if (!m_instance) {
|
||||
ui->environmentGroup->hide();
|
||||
m_ui->environmentGroup->hide();
|
||||
}
|
||||
ui->versions->setSourceModel(proxy);
|
||||
ui->versions->setSeparator(", ");
|
||||
ui->versions->setDefaultText(tr("All Versions"));
|
||||
ui->version->hide();
|
||||
m_ui->versions->setSourceModel(proxy);
|
||||
m_ui->versions->setSeparator(", ");
|
||||
m_ui->versions->setDefaultText(tr("All Versions"));
|
||||
m_ui->version->hide();
|
||||
} else {
|
||||
auto allVersions = new AllVersionProxyModel(this);
|
||||
auto* allVersions = new AllVersionProxyModel(this);
|
||||
allVersions->setSourceModel(proxy);
|
||||
proxy = allVersions;
|
||||
ui->version->setModel(proxy);
|
||||
ui->versions->hide();
|
||||
ui->showAllVersions->hide();
|
||||
ui->environmentGroup->hide();
|
||||
ui->openSource->hide();
|
||||
m_ui->version->setModel(proxy);
|
||||
m_ui->versions->hide();
|
||||
m_ui->showAllVersions->hide();
|
||||
m_ui->environmentGroup->hide();
|
||||
m_ui->openSource->hide();
|
||||
}
|
||||
|
||||
ui->versions->setStyleSheet("combobox-popup: 0;");
|
||||
ui->version->setStyleSheet("combobox-popup: 0;");
|
||||
connect(ui->showAllVersions, &QCheckBox::stateChanged, this, &ModFilterWidget::onShowAllVersionsChanged);
|
||||
connect(ui->versions, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged);
|
||||
connect(ui->versions, &CheckComboBox::checkedItemsChanged, this, [this] { onVersionFilterChanged(0); });
|
||||
connect(ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged);
|
||||
m_ui->versions->setStyleSheet("combobox-popup: 0;");
|
||||
m_ui->version->setStyleSheet("combobox-popup: 0;");
|
||||
connect(m_ui->showAllVersions, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onShowAllVersionsChanged);
|
||||
connect(m_ui->versions, &QComboBox::currentIndexChanged, this, &ModFilterWidget::onVersionFilterChanged);
|
||||
connect(m_ui->versions, &CheckComboBox::checkedItemsChanged, this, [this] { onVersionFilterChanged(0); });
|
||||
connect(m_ui->version, &QComboBox::currentTextChanged, this, &ModFilterWidget::onVersionFilterTextChanged);
|
||||
|
||||
connect(ui->neoForge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->forge, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->fabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->quilt, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->liteLoader, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->babric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->btaBabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->legacyFabric, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->ornithe, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(ui->rift, &QCheckBox::stateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->neoForge, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->forge, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->fabric, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->quilt, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->liteLoader, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->babric, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->btaBabric, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->legacyFabric, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->ornithe, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
connect(m_ui->rift, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onLoadersFilterChanged);
|
||||
|
||||
connect(ui->showMoreButton, &QPushButton::clicked, this, &ModFilterWidget::onShowMoreClicked);
|
||||
connect(m_ui->showMoreButton, &QPushButton::clicked, this, &ModFilterWidget::onShowMoreClicked);
|
||||
|
||||
if (!extended) {
|
||||
ui->showMoreButton->setVisible(false);
|
||||
ui->extendedModLoadersWidget->setVisible(false);
|
||||
m_ui->showMoreButton->setVisible(false);
|
||||
m_ui->extendedModLoadersWidget->setVisible(false);
|
||||
}
|
||||
|
||||
if (extended) {
|
||||
connect(ui->clientSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||
connect(ui->serverSide, &QCheckBox::stateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||
connect(m_ui->clientSide, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||
connect(m_ui->serverSide, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onSideFilterChanged);
|
||||
}
|
||||
|
||||
connect(ui->hideInstalled, &QCheckBox::stateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged);
|
||||
connect(ui->openSource, &QCheckBox::stateChanged, this, &ModFilterWidget::onOpenSourceFilterChanged);
|
||||
connect(m_ui->hideInstalled, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onHideInstalledFilterChanged);
|
||||
connect(m_ui->openSource, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onOpenSourceFilterChanged);
|
||||
|
||||
connect(ui->releaseCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(ui->betaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(ui->alphaCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(ui->unknownCb, &QCheckBox::stateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(m_ui->releaseCb, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(m_ui->betaCb, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(m_ui->alphaCb, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
connect(m_ui->unknownCb, &QCheckBox::checkStateChanged, this, &ModFilterWidget::onReleaseFilterChanged);
|
||||
|
||||
setHidden(true);
|
||||
loadVersionList();
|
||||
|
|
@ -183,43 +185,45 @@ ModFilterWidget::ModFilterWidget(MinecraftInstance* instance, bool extended)
|
|||
|
||||
auto ModFilterWidget::getFilter() -> std::shared_ptr<Filter>
|
||||
{
|
||||
m_filter_changed = false;
|
||||
m_filterChanged = false;
|
||||
return m_filter;
|
||||
}
|
||||
|
||||
ModFilterWidget::~ModFilterWidget()
|
||||
{
|
||||
delete ui;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
void ModFilterWidget::loadVersionList()
|
||||
{
|
||||
m_version_list = APPLICATION->metadataIndex()->get("net.minecraft");
|
||||
if (!m_version_list->isLoaded()) {
|
||||
QEventLoop load_version_list_loop;
|
||||
m_versionList = APPLICATION->metadataIndex()->get("net.minecraft");
|
||||
if (!m_versionList->isLoaded()) {
|
||||
QEventLoop loadVersionListLoop;
|
||||
|
||||
QTimer time_limit_for_list_load;
|
||||
time_limit_for_list_load.setTimerType(Qt::TimerType::CoarseTimer);
|
||||
time_limit_for_list_load.setSingleShot(true);
|
||||
time_limit_for_list_load.callOnTimeout(&load_version_list_loop, &QEventLoop::quit);
|
||||
time_limit_for_list_load.start(4000);
|
||||
QTimer timeLimitForListLoad;
|
||||
timeLimitForListLoad.setTimerType(Qt::TimerType::CoarseTimer);
|
||||
timeLimitForListLoad.setSingleShot(true);
|
||||
timeLimitForListLoad.callOnTimeout(&loadVersionListLoop, &QEventLoop::quit);
|
||||
timeLimitForListLoad.start(4000);
|
||||
|
||||
auto task = m_version_list->getLoadTask();
|
||||
auto task = m_versionList->getLoadTask();
|
||||
|
||||
connect(task.get(), &Task::failed, [this] {
|
||||
ui->versions->setEnabled(false);
|
||||
ui->showAllVersions->setEnabled(false);
|
||||
m_ui->versions->setEnabled(false);
|
||||
m_ui->showAllVersions->setEnabled(false);
|
||||
});
|
||||
connect(task.get(), &Task::finished, &load_version_list_loop, &QEventLoop::quit);
|
||||
connect(task.get(), &Task::finished, &loadVersionListLoop, &QEventLoop::quit);
|
||||
|
||||
if (!task->isRunning())
|
||||
if (!task->isRunning()) {
|
||||
task->start();
|
||||
}
|
||||
|
||||
load_version_list_loop.exec();
|
||||
if (time_limit_for_list_load.isActive())
|
||||
time_limit_for_list_load.stop();
|
||||
loadVersionListLoop.exec();
|
||||
if (timeLimitForListLoad.isActive()) {
|
||||
timeLimitForListLoad.stop();
|
||||
}
|
||||
}
|
||||
m_versions_proxy->setSourceModel(m_version_list.get());
|
||||
m_versionsProxy->setSourceModel(m_versionList.get());
|
||||
}
|
||||
|
||||
void ModFilterWidget::prepareBasicFilter()
|
||||
|
|
@ -230,121 +234,137 @@ void ModFilterWidget::prepareBasicFilter()
|
|||
m_filter->side = ModPlatform::Side::NoSide; // or "both"
|
||||
ModPlatform::ModLoaderTypes loaders;
|
||||
if (m_instance->settings()->get("OverrideModDownloadLoaders").toBool()) {
|
||||
for (auto loader : Json::toStringList(m_instance->settings()->get("ModDownloadLoaders").toString())) {
|
||||
for (const auto& loader : Json::toStringList(m_instance->settings()->get("ModDownloadLoaders").toString())) {
|
||||
loaders |= ModPlatform::getModLoaderFromString(loader);
|
||||
}
|
||||
} else {
|
||||
loaders = m_instance->getPackProfile()->getSupportedModLoaders().value();
|
||||
}
|
||||
ui->neoForge->setChecked(loaders & ModPlatform::NeoForge);
|
||||
ui->forge->setChecked(loaders & ModPlatform::Forge);
|
||||
ui->fabric->setChecked(loaders & ModPlatform::Fabric);
|
||||
ui->quilt->setChecked(loaders & ModPlatform::Quilt);
|
||||
ui->liteLoader->setChecked(loaders & ModPlatform::LiteLoader);
|
||||
ui->babric->setChecked(loaders & ModPlatform::Babric);
|
||||
ui->btaBabric->setChecked(loaders & ModPlatform::BTA);
|
||||
ui->legacyFabric->setChecked(loaders & ModPlatform::LegacyFabric);
|
||||
ui->ornithe->setChecked(loaders & ModPlatform::Ornithe);
|
||||
ui->rift->setChecked(loaders & ModPlatform::Rift);
|
||||
m_ui->neoForge->setChecked((loaders & ModPlatform::NeoForge) != 0U);
|
||||
m_ui->forge->setChecked((loaders & ModPlatform::Forge) != 0U);
|
||||
m_ui->fabric->setChecked((loaders & ModPlatform::Fabric) != 0U);
|
||||
m_ui->quilt->setChecked((loaders & ModPlatform::Quilt) != 0U);
|
||||
m_ui->liteLoader->setChecked((loaders & ModPlatform::LiteLoader) != 0U);
|
||||
m_ui->babric->setChecked((loaders & ModPlatform::Babric) != 0U);
|
||||
m_ui->btaBabric->setChecked((loaders & ModPlatform::BTA) != 0U);
|
||||
m_ui->legacyFabric->setChecked((loaders & ModPlatform::LegacyFabric) != 0U);
|
||||
m_ui->ornithe->setChecked((loaders & ModPlatform::Ornithe) != 0U);
|
||||
m_ui->rift->setChecked((loaders & ModPlatform::Rift) != 0U);
|
||||
m_filter->loaders = loaders;
|
||||
auto def = m_instance->getPackProfile()->getComponentVersion("net.minecraft");
|
||||
m_filter->versions.emplace_back(def);
|
||||
ui->versions->setCheckedItems({ def });
|
||||
ui->version->setCurrentIndex(ui->version->findText(def));
|
||||
m_ui->versions->setCheckedItems({ def });
|
||||
m_ui->version->setCurrentIndex(m_ui->version->findText(def));
|
||||
} else {
|
||||
ui->hideInstalled->hide();
|
||||
m_ui->hideInstalled->hide();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onShowAllVersionsChanged()
|
||||
{
|
||||
if (ui->showAllVersions->isChecked())
|
||||
m_versions_proxy->clearFilters();
|
||||
else
|
||||
m_versions_proxy->setFilter(BaseVersionList::TypeRole, Filters::equals("release"));
|
||||
if (m_ui->showAllVersions->isChecked()) {
|
||||
m_versionsProxy->clearFilters();
|
||||
} else {
|
||||
m_versionsProxy->setFilter(BaseVersionList::TypeRole, Filters::equals("release"));
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onVersionFilterChanged(int)
|
||||
void ModFilterWidget::onVersionFilterChanged(int /*unused*/)
|
||||
{
|
||||
auto versions = ui->versions->checkedItems();
|
||||
auto versions = m_ui->versions->checkedItems();
|
||||
versions.sort();
|
||||
std::vector<Version> current_list;
|
||||
std::vector<Version> currentList;
|
||||
|
||||
for (const QString& version : versions)
|
||||
current_list.emplace_back(version);
|
||||
for (const QString& version : versions) {
|
||||
currentList.emplace_back(version);
|
||||
}
|
||||
|
||||
m_filter_changed = m_filter->versions.size() != current_list.size() ||
|
||||
!std::equal(m_filter->versions.begin(), m_filter->versions.end(), current_list.begin(), current_list.end());
|
||||
m_filter->versions = current_list;
|
||||
if (m_filter_changed)
|
||||
m_filterChanged = m_filter->versions.size() != currentList.size() ||
|
||||
!std::equal(m_filter->versions.begin(), m_filter->versions.end(), currentList.begin(), currentList.end());
|
||||
m_filter->versions = currentList;
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onLoadersFilterChanged()
|
||||
{
|
||||
ModPlatform::ModLoaderTypes loaders;
|
||||
if (ui->neoForge->isChecked())
|
||||
if (m_ui->neoForge->isChecked()) {
|
||||
loaders |= ModPlatform::NeoForge;
|
||||
if (ui->forge->isChecked())
|
||||
}
|
||||
if (m_ui->forge->isChecked()) {
|
||||
loaders |= ModPlatform::Forge;
|
||||
if (ui->fabric->isChecked())
|
||||
}
|
||||
if (m_ui->fabric->isChecked()) {
|
||||
loaders |= ModPlatform::Fabric;
|
||||
if (ui->quilt->isChecked())
|
||||
}
|
||||
if (m_ui->quilt->isChecked()) {
|
||||
loaders |= ModPlatform::Quilt;
|
||||
if (ui->liteLoader->isChecked())
|
||||
}
|
||||
if (m_ui->liteLoader->isChecked()) {
|
||||
loaders |= ModPlatform::LiteLoader;
|
||||
if (ui->babric->isChecked())
|
||||
}
|
||||
if (m_ui->babric->isChecked()) {
|
||||
loaders |= ModPlatform::Babric;
|
||||
if (ui->btaBabric->isChecked())
|
||||
}
|
||||
if (m_ui->btaBabric->isChecked()) {
|
||||
loaders |= ModPlatform::BTA;
|
||||
if (ui->legacyFabric->isChecked())
|
||||
}
|
||||
if (m_ui->legacyFabric->isChecked()) {
|
||||
loaders |= ModPlatform::LegacyFabric;
|
||||
if (ui->ornithe->isChecked())
|
||||
}
|
||||
if (m_ui->ornithe->isChecked()) {
|
||||
loaders |= ModPlatform::Ornithe;
|
||||
if (ui->rift->isChecked())
|
||||
}
|
||||
if (m_ui->rift->isChecked()) {
|
||||
loaders |= ModPlatform::Rift;
|
||||
m_filter_changed = loaders != m_filter->loaders;
|
||||
}
|
||||
m_filterChanged = loaders != m_filter->loaders;
|
||||
m_filter->loaders = loaders;
|
||||
if (m_filter_changed)
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onSideFilterChanged()
|
||||
{
|
||||
ModPlatform::Side side;
|
||||
ModPlatform::Side side = ModPlatform::Side::NoSide;
|
||||
|
||||
if (ui->clientSide->isChecked() && !ui->serverSide->isChecked()) {
|
||||
if (m_ui->clientSide->isChecked() && !m_ui->serverSide->isChecked()) {
|
||||
side = ModPlatform::Side::ClientSide;
|
||||
} else if (!ui->clientSide->isChecked() && ui->serverSide->isChecked()) {
|
||||
} else if (!m_ui->clientSide->isChecked() && m_ui->serverSide->isChecked()) {
|
||||
side = ModPlatform::Side::ServerSide;
|
||||
} else if (ui->clientSide->isChecked() && ui->serverSide->isChecked()) {
|
||||
} else if (m_ui->clientSide->isChecked() && m_ui->serverSide->isChecked()) {
|
||||
side = ModPlatform::Side::UniversalSide;
|
||||
} else {
|
||||
side = ModPlatform::Side::NoSide;
|
||||
}
|
||||
|
||||
m_filter_changed = side != m_filter->side;
|
||||
m_filterChanged = side != m_filter->side;
|
||||
m_filter->side = side;
|
||||
if (m_filter_changed)
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onHideInstalledFilterChanged()
|
||||
{
|
||||
auto hide = ui->hideInstalled->isChecked();
|
||||
m_filter_changed = hide != m_filter->hideInstalled;
|
||||
auto hide = m_ui->hideInstalled->isChecked();
|
||||
m_filterChanged = hide != m_filter->hideInstalled;
|
||||
m_filter->hideInstalled = hide;
|
||||
if (m_filter_changed)
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onVersionFilterTextChanged(const QString& version)
|
||||
{
|
||||
m_filter->versions.clear();
|
||||
if (ui->version->currentData(Qt::UserRole) != "all") {
|
||||
if (m_ui->version->currentData(Qt::UserRole) != "all") {
|
||||
m_filter->versions.emplace_back(version);
|
||||
}
|
||||
m_filter_changed = true;
|
||||
m_filterChanged = true;
|
||||
emit filterChanged();
|
||||
}
|
||||
|
||||
|
|
@ -352,14 +372,14 @@ void ModFilterWidget::setCategories(const QList<ModPlatform::Category>& categori
|
|||
{
|
||||
m_categories = categories;
|
||||
|
||||
delete ui->categoryGroup->layout();
|
||||
auto layout = new QVBoxLayout(ui->categoryGroup);
|
||||
delete m_ui->categoryGroup->layout();
|
||||
auto* layout = new QVBoxLayout(m_ui->categoryGroup);
|
||||
|
||||
for (const auto& category : categories) {
|
||||
auto name = category.name;
|
||||
name.replace("-", " ");
|
||||
name.replace("&", "&&");
|
||||
auto checkbox = new QCheckBox(name);
|
||||
auto* checkbox = new QCheckBox(name);
|
||||
auto font = checkbox->font();
|
||||
font.setCapitalization(QFont::Capitalize);
|
||||
checkbox->setFont(font);
|
||||
|
|
@ -368,12 +388,13 @@ void ModFilterWidget::setCategories(const QList<ModPlatform::Category>& categori
|
|||
|
||||
const QString id = category.id;
|
||||
connect(checkbox, &QCheckBox::toggled, this, [this, id](bool checked) {
|
||||
if (checked)
|
||||
if (checked) {
|
||||
m_filter->categoryIds.append(id);
|
||||
else
|
||||
} else {
|
||||
m_filter->categoryIds.removeOne(id);
|
||||
}
|
||||
|
||||
m_filter_changed = true;
|
||||
m_filterChanged = true;
|
||||
emit filterChanged();
|
||||
});
|
||||
}
|
||||
|
|
@ -381,34 +402,40 @@ void ModFilterWidget::setCategories(const QList<ModPlatform::Category>& categori
|
|||
|
||||
void ModFilterWidget::onOpenSourceFilterChanged()
|
||||
{
|
||||
auto open = ui->openSource->isChecked();
|
||||
m_filter_changed = open != m_filter->openSource;
|
||||
auto open = m_ui->openSource->isChecked();
|
||||
m_filterChanged = open != m_filter->openSource;
|
||||
m_filter->openSource = open;
|
||||
if (m_filter_changed)
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onReleaseFilterChanged()
|
||||
{
|
||||
std::vector<ModPlatform::IndexedVersionType> releases;
|
||||
if (ui->releaseCb->isChecked())
|
||||
releases.push_back(ModPlatform::IndexedVersionType::Release);
|
||||
if (ui->betaCb->isChecked())
|
||||
releases.push_back(ModPlatform::IndexedVersionType::Beta);
|
||||
if (ui->alphaCb->isChecked())
|
||||
releases.push_back(ModPlatform::IndexedVersionType::Alpha);
|
||||
if (ui->unknownCb->isChecked())
|
||||
releases.push_back(ModPlatform::IndexedVersionType::Unknown);
|
||||
m_filter_changed = releases != m_filter->releases;
|
||||
if (m_ui->releaseCb->isChecked()) {
|
||||
releases.emplace_back(ModPlatform::IndexedVersionType::Release);
|
||||
}
|
||||
if (m_ui->betaCb->isChecked()) {
|
||||
releases.emplace_back(ModPlatform::IndexedVersionType::Beta);
|
||||
}
|
||||
if (m_ui->alphaCb->isChecked()) {
|
||||
releases.emplace_back(ModPlatform::IndexedVersionType::Alpha);
|
||||
}
|
||||
if (m_ui->unknownCb->isChecked()) {
|
||||
releases.emplace_back(ModPlatform::IndexedVersionType::Unknown);
|
||||
}
|
||||
m_filterChanged = releases != m_filter->releases;
|
||||
m_filter->releases = releases;
|
||||
if (m_filter_changed)
|
||||
if (m_filterChanged) {
|
||||
emit filterChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void ModFilterWidget::onShowMoreClicked()
|
||||
{
|
||||
ui->extendedModLoadersWidget->setVisible(true);
|
||||
ui->showMoreButton->setVisible(false);
|
||||
m_ui->extendedModLoadersWidget->setVisible(true);
|
||||
m_ui->showMoreButton->setVisible(false);
|
||||
}
|
||||
|
||||
#include "ModFilterWidget.moc"
|
||||
|
|
|
|||
|
|
@ -73,29 +73,31 @@ class ModFilterWidget : public QTabWidget {
|
|||
}
|
||||
bool operator!=(const Filter& other) const { return !(*this == other); }
|
||||
|
||||
bool checkMcVersions(QStringList value)
|
||||
bool checkMcVersions(const QStringList& value)
|
||||
{
|
||||
for (auto mcVersion : versions)
|
||||
if (value.contains(mcVersion.toString()))
|
||||
for (const auto& mcVersion : versions) {
|
||||
if (value.contains(mcVersion.toString())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return versions.empty();
|
||||
}
|
||||
|
||||
bool checkModpackFilters(const ModPlatform::IndexedVersion& v)
|
||||
{
|
||||
return ((!loaders || !v.loaders || loaders & v.loaders) && // loaders
|
||||
(releases.empty() || // releases
|
||||
return ((!loaders || !v.loaders || ((loaders & v.loaders) != 0)) && // loaders
|
||||
(releases.empty() || // releases
|
||||
std::find(releases.cbegin(), releases.cend(), v.version_type) != releases.cend()) &&
|
||||
checkMcVersions({ v.mcVersion })); // gameVersion}
|
||||
}
|
||||
};
|
||||
|
||||
static std::unique_ptr<ModFilterWidget> create(MinecraftInstance* instance, bool extended);
|
||||
virtual ~ModFilterWidget();
|
||||
~ModFilterWidget() override;
|
||||
|
||||
auto getFilter() -> std::shared_ptr<Filter>;
|
||||
auto changed() const -> bool { return m_filter_changed; }
|
||||
auto changed() const -> bool { return m_filterChanged; }
|
||||
|
||||
signals:
|
||||
void filterChanged();
|
||||
|
|
@ -121,14 +123,14 @@ class ModFilterWidget : public QTabWidget {
|
|||
void onShowMoreClicked();
|
||||
|
||||
private:
|
||||
Ui::ModFilterWidget* ui;
|
||||
Ui::ModFilterWidget* m_ui;
|
||||
|
||||
MinecraftInstance* m_instance = nullptr;
|
||||
std::shared_ptr<Filter> m_filter;
|
||||
bool m_filter_changed = false;
|
||||
bool m_filterChanged = false;
|
||||
|
||||
Meta::VersionList::Ptr m_version_list;
|
||||
VersionProxyModel* m_versions_proxy = nullptr;
|
||||
Meta::VersionList::Ptr m_versionList;
|
||||
VersionProxyModel* m_versionsProxy = nullptr;
|
||||
|
||||
QList<ModPlatform::Category> m_categories;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue