QtSupport: Use QtVersionItem in QtKitAspect

For proper sorting and icons.

Task-number: QTCREATORBUG-31574
Change-Id: Ib835091684527ce22b0294c494227979e3b093ec
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-09-26 12:00:43 +02:00
parent a80195603e
commit 7540dbeb8e
3 changed files with 237 additions and 127 deletions

View File

@@ -3,6 +3,7 @@
#include "qtkitaspect.h"
#include "qtoptionspage.h"
#include "qtparser.h"
#include "qtsupportconstants.h"
#include "qtsupporttr.h"
@@ -30,6 +31,80 @@ using namespace Utils;
namespace QtSupport {
namespace Internal {
class QtVersionListModel : public TreeModel<TreeItem, QtVersionItem>
{
public:
QtVersionListModel(const Kit &kit, QObject *parent)
: TreeModel(parent)
, m_kit(kit)
{}
QModelIndex indexForQtId(int id) const
{
if (id == -1)
return index(rowCount() - 1, 0); // The "No Qt" item always comes last
const TreeItem *const item = findItemAtLevel<1>(
[id](TreeItem *item) { return static_cast<QtVersionItem *>(item)->uniqueId() == id; });
return item ? indexForItem(item) : QModelIndex();
}
void reset()
{
clear();
const FilePath deviceRoot = BuildDeviceKitAspect::device(&m_kit)->rootPath();
const QtVersions versionsForBuildDevice = QtVersionManager::versions(
[&deviceRoot](const QtVersion *qt) {
return qt->qmakeFilePath().isSameDevice(deviceRoot);
});
for (QtVersion *v : versionsForBuildDevice)
rootItem()->appendChild(new QtVersionItem(v->uniqueId()));
rootItem()->appendChild(new QtVersionItem(-1)); // The "No Qt" entry.
}
private:
const Kit &m_kit;
};
class QtVersionSortModel : public SortModel
{
public:
QtVersionSortModel(QObject *parent) : SortModel(parent) {}
QModelIndex indexForId(int id) const
{
return mapFromSource(
static_cast<QtVersionListModel *>(sourceModel())->indexForQtId(id));
}
void reset() { static_cast<QtVersionListModel *>(sourceModel())->reset(); }
private:
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override
{
const auto source = static_cast<QtVersionListModel *>(sourceModel());
const auto item1 = static_cast<QtVersionItem *>(source->itemForIndex(source_left));
const auto item2 = static_cast<QtVersionItem *>(source->itemForIndex(source_right));
QTC_ASSERT(item1 && item2, return false);
// Criterion 1: "No Qt" comes last
if (item1->uniqueId() == -1)
return false;
if (item2->uniqueId() == -1)
return true;
// Criterion 2: Invalid Qt versions come after valid ones with warnings, which come
// after valid ones without warnings.
if (const QtVersionItem::Quality qual1 = item1->quality(), qual2 = item2->quality();
qual1 != qual2) {
return qual1 == QtVersionItem::Quality::Good || qual2 == QtVersionItem::Quality::Bad;
}
// Criterion 3: Name.
return SortModel::lessThan(source_left, source_right);
}
};
class QtKitAspectImpl final : public KitAspect
{
public:
@@ -39,8 +114,13 @@ public:
m_combo = createSubWidget<QComboBox>();
m_combo->setSizePolicy(QSizePolicy::Ignored, m_combo->sizePolicy().verticalPolicy());
const auto sortModel = new QtVersionSortModel(this);
sortModel->setSourceModel(new QtVersionListModel(*k, this));
m_combo->setModel(sortModel);
refresh();
// FIXME: We want the tooltip for the current item (also for toolchains etc).
m_combo->setToolTip(ki->description());
connect(m_combo, &QComboBox::currentIndexChanged, this, [this] {
@@ -48,10 +128,10 @@ public:
currentWasChanged(m_combo->currentIndex());
});
connect(QtVersionManager::instance(),
&QtVersionManager::qtVersionsChanged,
this,
&QtKitAspectImpl::refresh);
connect(KitManager::instance(), &KitManager::kitUpdated, this, [this](Kit *k) {
if (k == kit())
refresh();
});
}
~QtKitAspectImpl() final
@@ -71,20 +151,10 @@ private:
void refresh() final
{
const GuardLocker locker(m_ignoreChanges);
m_combo->clear();
m_combo->addItem(Tr::tr("None"), -1);
IDeviceConstPtr device = BuildDeviceKitAspect::device(kit());
const FilePath deviceRoot = device->rootPath();
const QtVersions versionsForBuildDevice
= Utils::filtered(QtVersionManager::versions(), [device](QtVersion *qt) {
return qt->qmakeFilePath().isSameDevice(device->rootPath());
});
for (QtVersion *item : versionsForBuildDevice)
m_combo->addItem(item->displayName(), item->uniqueId());
m_combo->setCurrentIndex(findQtVersion(QtKitAspect::qtVersionId(m_kit)));
const auto sortModel = static_cast<QtVersionSortModel *>(m_combo->model());
sortModel->reset();
sortModel->sort(0);
m_combo->setCurrentIndex(sortModel->indexForId(QtKitAspect::qtVersionId(m_kit)).row());
}
private:
@@ -99,16 +169,9 @@ private:
void currentWasChanged(int idx)
{
QtKitAspect::setQtVersionId(m_kit, m_combo->itemData(idx).toInt());
}
int findQtVersion(const int id) const
{
for (int i = 0; i < m_combo->count(); ++i) {
if (id == m_combo->itemData(i).toInt())
return i;
}
return -1;
const QAbstractItemModel * const model = m_combo->model();
const int versionId = model->data(model->index(idx, 0), QtVersionItem::IdRole).toInt();
QtKitAspect::setQtVersionId(m_kit, versionId);
}
Guard m_ignoreChanges;

View File

@@ -27,7 +27,6 @@
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
#include <utils/variablechooser.h>
@@ -44,7 +43,6 @@
#include <QTextBrowser>
#include <QTreeView>
#include <functional>
#include <utility>
using namespace Core;
@@ -128,117 +126,132 @@ UnsupportedAbisInfo checkForUnsupportedAbis(const QtVersion *version)
return info;
}
class QtVersionItem : public TreeItem
void QtVersionItem::setIsNameUnique(const std::function<bool(QtVersion *)> &isNameUnique)
{
public:
explicit QtVersionItem(QtVersion *version)
: m_version(version)
{}
m_isNameUnique = isNameUnique;
}
~QtVersionItem()
{
delete m_version;
QtVersionItem::Quality QtVersionItem::quality() const
{
const QtVersion *version = this->version();
QTC_ASSERT(version, return Quality::Bad);
if (!version->isValid())
return Quality::Bad;
if (!version->warningReason().isEmpty() || hasNonUniqueDisplayName())
return Quality::Limited;
const UnsupportedAbisInfo abisInfo = checkForUnsupportedAbis(version);
switch (abisInfo.status) {
case UnsupportedAbisInfo::Status::AllMissing:
return Quality::Bad;
case UnsupportedAbisInfo::Status::SomeMissing:
return Quality::Limited;
case UnsupportedAbisInfo::Status::Ok:
break;
}
return Quality::Good;
}
void QtVersionItem::setChanged(bool changed)
{
if (changed == m_changed)
return;
m_changed = changed;
update();
}
QVariant QtVersionItem::data(int column, int role) const
{
const QtVersion *version = this->version();
if (!version) {
if (role == Qt::DisplayRole && column == 0)
return Tr::tr("No Qt");
if (role == IdRole)
return -1;
return TreeItem::data(column, role);
}
void setVersion(QtVersion *version)
{
m_version = version;
update();
if (role == Qt::DisplayRole) {
if (column == 0)
return version->displayName();
if (column == 1)
return version->qmakeFilePath().toUserOutput();
}
int uniqueId() const
{
return m_version ? m_version->uniqueId() : -1;
if (role == Qt::FontRole && m_changed) {
QFont font;
font.setBold(true);
return font;
}
QtVersion *version() const
{
return m_version;
}
QVariant data(int column, int role) const final
{
if (!m_version)
return TreeItem::data(column, role);
if (role == Qt::DisplayRole) {
if (column == 0)
return m_version->displayName();
if (column == 1)
return m_version->qmakeFilePath().toUserOutput();
if (role == Qt::DecorationRole && column == 0) {
switch (quality()) {
case Quality::Good:
return validVersionIcon();
case Quality::Limited:
return warningVersionIcon();
case Quality::Bad:
return invalidVersionIcon();
}
}
if (role == Qt::FontRole && m_changed) {
QFont font;
font.setBold(true);
return font;
}
if (role == Qt::DecorationRole && column == 0) {
if (!m_version->isValid())
return invalidVersionIcon();
if (!m_version->warningReason().isEmpty() || hasNonUniqueDisplayName())
return warningVersionIcon();
const UnsupportedAbisInfo abisInfo = checkForUnsupportedAbis(m_version);
switch (abisInfo.status) {
case UnsupportedAbisInfo::Status::AllMissing:
return invalidVersionIcon();
case UnsupportedAbisInfo::Status::SomeMissing:
return warningVersionIcon();
case UnsupportedAbisInfo::Status::Ok:
break;
}
return validVersionIcon();
if (role == Qt::ToolTipRole) {
const QString row = "<dt style=\"font-weight:bold\">%1:</dt>"
"<dd>%2</dd>";
QString desc = "<dl style=\"white-space:pre\">";
if (version->isValid())
desc += row.arg(Tr::tr("Qt Version"), version->qtVersionString());
desc += row.arg(Tr::tr("Location of qmake"), version->qmakeFilePath().toUserOutput());
if (version->isValid()) {
const UnsupportedAbisInfo abisInfo = checkForUnsupportedAbis(version);
if (abisInfo.status == UnsupportedAbisInfo::Status::AllMissing)
desc += row.arg(Tr::tr("Error"), abisInfo.message);
if (abisInfo.status == UnsupportedAbisInfo::Status::SomeMissing)
desc += row.arg(Tr::tr("Warning"), abisInfo.message);
const QStringList warnings = version->warningReason();
for (const QString &w : warnings)
desc += row.arg(Tr::tr("Warning"), w);
} else {
desc += row.arg(Tr::tr("Error"), version->invalidReason());
}
if (role == Qt::ToolTipRole) {
const QString row = "<dt style=\"font-weight:bold\">%1:</dt>"
"<dd>%2</dd>";
QString desc = "<dl style=\"white-space:pre\">";
if (m_version->isValid())
desc += row.arg(Tr::tr("Qt Version"), m_version->qtVersionString());
desc += row.arg(Tr::tr("Location of qmake"), m_version->qmakeFilePath().toUserOutput());
if (m_version->isValid()) {
const UnsupportedAbisInfo abisInfo = checkForUnsupportedAbis(m_version);
if (abisInfo.status == UnsupportedAbisInfo::Status::AllMissing)
desc += row.arg(Tr::tr("Error"), abisInfo.message);
if (abisInfo.status == UnsupportedAbisInfo::Status::SomeMissing)
desc += row.arg(Tr::tr("Warning"), abisInfo.message);
const QStringList warnings = m_version->warningReason();
for (const QString &w : warnings)
desc += row.arg(Tr::tr("Warning"), w);
} else {
desc += row.arg(Tr::tr("Error"), m_version->invalidReason());
}
if (hasNonUniqueDisplayName())
desc += row.arg(Tr::tr("Warning"), nonUniqueDisplayNameWarning());
desc += "</dl>";
return desc;
}
return QVariant();
if (hasNonUniqueDisplayName())
desc += row.arg(Tr::tr("Warning"), nonUniqueDisplayNameWarning());
desc += "</dl>";
return desc;
}
void setChanged(bool changed)
{
if (changed == m_changed)
return;
m_changed = changed;
update();
}
if (role == IdRole)
return uniqueId();
void setIsNameUnique(const std::function<bool(QtVersion *)> &isNameUnique)
{
m_isNameUnique = isNameUnique;
}
return QVariant();
}
private:
bool hasNonUniqueDisplayName() const { return m_isNameUnique && !m_isNameUnique(m_version); }
int QtVersionItem::uniqueId() const
{
if (const auto v = std::get_if<QtVersion *>(&m_version))
return v ? (*v)->uniqueId() : -1;
return *std::get_if<int>(&m_version);
}
QtVersion *m_version = nullptr;
std::function<bool(QtVersion *)> m_isNameUnique;
bool m_changed = false;
};
QtVersion *QtVersionItem::version() const
{
if (const auto v = std::get_if<QtVersion *>(&m_version))
return *v;
return QtVersionManager::version(*std::get_if<int>(&m_version));
}
void QtVersionItem::setVersion(QtVersion *version)
{
m_version = version;
update();
}
QtVersionItem::~QtVersionItem()
{
if (const auto v = std::get_if<QtVersion *>(&m_version))
delete *v;
}
// QtSettingsPageWidget
@@ -1130,7 +1143,7 @@ void setupQtSettingsPage()
static QtSettingsPage theQtSettingsPage;
}
} // Internal
} // namespace Internal
bool LinkWithQtSupport::canLinkWithQt()
{

View File

@@ -6,8 +6,13 @@
#include "qtsupport_global.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <utils/treemodel.h>
#include <functional>
#include <variant>
namespace QtSupport {
class QtVersion;
namespace LinkWithQtSupport {
QTSUPPORT_EXPORT bool canLinkWithQt();
@@ -16,6 +21,35 @@ QTSUPPORT_EXPORT Utils::FilePath linkedQt();
QTSUPPORT_EXPORT void linkWithQt();
}
namespace Internal { void setupQtSettingsPage(); }
namespace Internal {
class QtVersionItem : public Utils::TreeItem
{
public:
explicit QtVersionItem(QtVersion *version) : m_version(version) {}
explicit QtVersionItem(int versionId) : m_version(versionId) {}
~QtVersionItem();
void setVersion(QtVersion *version);
int uniqueId() const;
QtVersion *version() const;
void setChanged(bool changed);
void setIsNameUnique(const std::function<bool(QtVersion *)> &isNameUnique);
enum class Quality { Good, Limited, Bad };
Quality quality() const;
static const int IdRole = Qt::UserRole;
private:
QVariant data(int column, int role) const final;
bool hasNonUniqueDisplayName() const { return m_isNameUnique && !m_isNameUnique(version()); }
std::variant<QtVersion *, int> m_version;
std::function<bool(QtVersion *)> m_isNameUnique;
bool m_changed = false;
};
void setupQtSettingsPage();
}
} // QtSupport