From e017a10541a8f8ff3697cb03c18c673b0e78b871 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 28 Aug 2024 17:42:09 +0200 Subject: [PATCH] ProjectExplorer: Improve the toolchain combo box in the kit options page - Sort entries by toolchain type and name - Mark invalid and incomplete toolchains the same way as in the toolchains options page Change-Id: Iaf326b06c6b77c3def076793c2441f290862dae7 Reviewed-by: hjk --- .../projectexplorer/toolchainkitaspect.cpp | 130 ++++++++--- .../projectexplorer/toolchainoptionspage.cpp | 201 +++++++++--------- .../projectexplorer/toolchainoptionspage.h | 15 ++ 3 files changed, 220 insertions(+), 126 deletions(-) diff --git a/src/plugins/projectexplorer/toolchainkitaspect.cpp b/src/plugins/projectexplorer/toolchainkitaspect.cpp index 07a76b2b20c..adc1b311611 100644 --- a/src/plugins/projectexplorer/toolchainkitaspect.cpp +++ b/src/plugins/projectexplorer/toolchainkitaspect.cpp @@ -10,10 +10,12 @@ #include "projectexplorerconstants.h" #include "projectexplorertr.h" #include "toolchainmanager.h" +#include "toolchainoptionspage.h" #include #include #include +#include #include #include @@ -21,8 +23,88 @@ using namespace Utils; namespace ProjectExplorer { - namespace Internal { + +class ToolchainListModel : public TreeModel +{ +public: + ToolchainListModel(const Kit &kit, const LanguageCategory &category, QObject *parent) + : TreeModel(parent) + , m_kit(kit) + , m_category(category) + { + reset(); + } + + QModelIndex indexForBundleId(Id bundleId) const + { + if (!bundleId.isValid()) + return index(rowCount() - 1, 0); // The "no compiler" item always comes last + const TreeItem *const item = findItemAtLevel<1>( + [bundleId](TreeItem *item) { + const auto tcItem = static_cast(item); + return tcItem->bundle && tcItem->bundle->bundleId() == bundleId; + }); + return item ? indexForItem(item) : QModelIndex(); + } + + void reset() + { + clear(); + + const Toolchains ltcList = ToolchainManager::toolchains( + [this](const Toolchain *tc) { return m_category.contains(tc->language()); }); + IDeviceConstPtr device = BuildDeviceKitAspect::device(&m_kit); + + const QList toolchainsForBuildDevice + = Utils::filtered(ltcList, [device](Toolchain *tc) { + return tc->compilerCommand().isSameDevice(device->rootPath()); + }); + const QList bundlesForBuildDevice = ToolchainBundle::collectBundles( + toolchainsForBuildDevice, ToolchainBundle::AutoRegister::On); + for (const ToolchainBundle &b : bundlesForBuildDevice) + rootItem()->appendChild(new ToolchainTreeItem(b)); + rootItem()->appendChild(new ToolchainTreeItem); + } + +private: + const Kit &m_kit; + const LanguageCategory m_category; +}; + +class ToolchainSortModel : public SortModel +{ +public: + ToolchainSortModel(QObject *parent) : SortModel(parent) {} + + QModelIndex indexForBundleId(Id bundleId) const + { + return mapFromSource( + static_cast(sourceModel())->indexForBundleId(bundleId)); + } + + void reset() { static_cast(sourceModel())->reset(); } + +private: + bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override + { + const auto source = static_cast(sourceModel()); + const ToolchainTreeItem *item1 = source->itemForIndex(source_left); + const ToolchainTreeItem *item2 = source->itemForIndex(source_right); + QTC_ASSERT(item1 && item2, return false); + if (!item1->bundle) + return false; + if (!item2->bundle) + return true; + if (item1->bundle->type() != item2->bundle->type()) { + return caseFriendlyCompare( + item1->bundle->typeDisplayName(), item2->bundle->typeDisplayName()) + < 0; + } + return SortModel::lessThan(source_left, source_right); + } +}; + class ToolchainKitAspectImpl final : public KitAspect { public: @@ -51,6 +133,11 @@ public: cb->setToolTip(factory->description()); setWheelScrollingWithoutFocusBlocked(cb); + const auto model = new ToolchainListModel(*kit(), lc, this); + const auto sortModel = new ToolchainSortModel(this); + sortModel->setSourceModel(model); + cb->setModel(sortModel); + m_languageComboboxMap.insert(lc, cb); layout->addWidget(cb, row, 1); ++row; @@ -88,34 +175,18 @@ private: [lc](const Toolchain *tc) { return lc.contains(tc->language()); }); QComboBox *cb = *it; - cb->clear(); - cb->addItem(Tr::tr(""), QByteArray()); - - const QList toolchainsForBuildDevice - = Utils::filtered(ltcList, [device](Toolchain *tc) { - return tc->compilerCommand().isSameDevice(device->rootPath()); - }); - const QList bundlesForBuildDevice = ToolchainBundle::collectBundles( - toolchainsForBuildDevice, ToolchainBundle::AutoRegister::On); - for (const ToolchainBundle &b : bundlesForBuildDevice) - cb->addItem(b.displayName(), b.bundleId().toSetting()); - + static_cast(cb->model())->reset(); + cb->model()->sort(0); cb->setEnabled(cb->count() > 1 && !m_isReadOnly); + Id currentBundleId; for (const Id lang : lc) { - Toolchain * const currentTc = ToolchainKitAspect::toolchain(m_kit, lang); - if (!currentTc) - continue; - for (const ToolchainBundle &b : bundlesForBuildDevice) { - if (b.bundleId() == currentTc->bundleId()) { - currentBundleId = b.bundleId(); - break; - } - } - if (currentBundleId.isValid()) + if (Toolchain * const currentTc = ToolchainKitAspect::toolchain(m_kit, lang)) { + currentBundleId = currentTc->bundleId(); break; + } } - cb->setCurrentIndex(currentBundleId.isValid() ? indexOf(cb, currentBundleId) : -1); + cb->setCurrentIndex(indexOf(cb, currentBundleId)); } } @@ -131,8 +202,11 @@ private: if (m_ignoreChanges.isLocked() || idx < 0) return; + const QAbstractItemModel *const model + = m_languageComboboxMap.value(languageCategory)->model(); const Id bundleId = Id::fromSetting( - m_languageComboboxMap.value(languageCategory)->itemData(idx)); + model->data(model->index(idx, 0), ToolchainTreeItem::BundleIdRole)); + const Toolchains bundleTcs = ToolchainManager::toolchains( [bundleId](const Toolchain *tc) { return tc->bundleId() == bundleId; }); for (const Id lang : languageCategory) { @@ -148,11 +222,7 @@ private: int indexOf(QComboBox *cb, Id bundleId) { - for (int i = 0; i < cb->count(); ++i) { - if (bundleId.toSetting() == cb->itemData(i)) - return i; - } - return -1; + return static_cast(cb->model())->indexForBundleId(bundleId).row(); } QWidget *m_mainWidget = nullptr; diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index b9bea20a181..4c3a29177bf 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -46,63 +46,73 @@ using namespace Utils; namespace ProjectExplorer { namespace Internal { -class ToolChainTreeItem : public TreeItem +QVariant ToolchainTreeItem::data(int column, int role) const +{ + switch (role) { + case Qt::DisplayRole: + if (column == 0) + return bundle ? bundle->displayName() : Tr::tr(""); + return bundle->typeDisplayName(); + case Qt::ToolTipRole: { + if (!bundle) + return {}; + QString toolTip; + const ToolchainBundle::Valid validity = bundle->validity(); + if (validity != ToolchainBundle::Valid::None) { + toolTip = Tr::tr("ABI: %1").arg(bundle->targetAbi().toString()); + if (validity == ToolchainBundle::Valid::Some) + toolTip.append("
").append(Tr::tr("Not all compilers are set up correctly.")); + } else { + toolTip = Tr::tr("This toolchain is invalid."); + } + return QVariant("
" + toolTip + "
"); + } + case Qt::DecorationRole: + if (!bundle) + return {}; + if (column == 0) { + switch (bundle->validity()) { + case ToolchainBundle::Valid::All: + break; + case ToolchainBundle::Valid::Some: + return Utils::Icons::WARNING.icon(); + case ToolchainBundle::Valid::None: + return Utils::Icons::CRITICAL.icon(); + } + } + return QVariant(); + case BundleIdRole: + return bundle ? bundle->bundleId().toSetting() : QVariant(); + } + return {}; +} + +class ExtendedToolchainTreeItem : public ToolchainTreeItem { public: - ToolChainTreeItem(QStackedWidget *parentWidget, const ToolchainBundle &bundle, bool c) : - bundle(bundle), changed(c), m_parentWidget(parentWidget) + ExtendedToolchainTreeItem(QStackedWidget *parentWidget, const ToolchainBundle &bundle, bool c) : + ToolchainTreeItem(bundle), changed(c), m_parentWidget(parentWidget) {} QVariant data(int column, int role) const override { switch (role) { - case Qt::DisplayRole: - if (column == 0) - return bundle.displayName(); - return bundle.typeDisplayName(); - case Qt::FontRole: { - QFont font; - font.setBold(changed); - return font; - } - case Qt::ToolTipRole: { - QString toolTip; - const ToolchainBundle::Valid validity = bundle.validity(); - if (validity != ToolchainBundle::Valid::None) { - toolTip = Tr::tr("
ABI: %1").arg( - changed ? Tr::tr("not up-to-date") - : bundle.targetAbi().toString()); - if (validity == ToolchainBundle::Valid::Some) - toolTip.append("
").append( - Tr::tr("Not all compilers are set up correctly.")); - } else { - toolTip = Tr::tr("This toolchain is invalid."); - } - return QVariant("
" + toolTip + "
"); - } - case Qt::DecorationRole: - if (column == 0) { - switch (bundle.validity()) { - case ToolchainBundle::Valid::All: - break; - case ToolchainBundle::Valid::Some: - return Utils::Icons::WARNING.icon(); - case ToolchainBundle::Valid::None: - return Utils::Icons::CRITICAL.icon(); - } - } - return QVariant(); + case Qt::FontRole: { + QFont font; + font.setBold(changed); + return font; } - return {}; + } + return ToolchainTreeItem::data(column, role); } ToolchainConfigWidget *widget() { if (!m_widget) { - m_widget = bundle.factory()->createConfigurationWidget(bundle).release(); + m_widget = bundle->factory()->createConfigurationWidget(*bundle).release(); if (m_widget) { m_parentWidget->addWidget(m_widget); - if (bundle.isAutoDetected()) + if (bundle->isAutoDetected()) m_widget->makeReadOnly(); QObject::connect(m_widget, &ToolchainConfigWidget::dirty, [this] { @@ -114,7 +124,6 @@ public: return m_widget; } - ToolchainBundle bundle; bool changed; private: @@ -226,15 +235,15 @@ public: m_removeAllButton = new QPushButton(Tr::tr("Remove All"), this); connect(m_removeAllButton, &QAbstractButton::clicked, this, [this] { - QList itemsToRemove; + QList itemsToRemove; m_model.forAllItems([&itemsToRemove](TreeItem *item) { if (item->level() != 3) return; - const auto tcItem = static_cast(item); - if (!tcItem->bundle.isSdkProvided()) + const auto tcItem = static_cast(item); + if (!tcItem->bundle->isSdkProvided()) itemsToRemove << tcItem; }); - for (ToolChainTreeItem * const tcItem : std::as_const(itemsToRemove)) + for (ExtendedToolchainTreeItem * const tcItem : std::as_const(itemsToRemove)) markForRemoval(tcItem); }); @@ -292,7 +301,7 @@ public: this, &ToolChainOptionsWidget::toolChainSelectionChanged); connect(m_delButton, &QAbstractButton::clicked, this, [this] { - if (ToolChainTreeItem *item = currentTreeItem()) + if (ExtendedToolchainTreeItem *item = currentTreeItem()) markForRemoval(item); }); @@ -303,10 +312,10 @@ public: void updateState(); void createToolchains(ToolchainFactory *factory, const QList &languages); void cloneToolchains(); - ToolChainTreeItem *currentTreeItem(); + ExtendedToolchainTreeItem *currentTreeItem(); - void markForRemoval(ToolChainTreeItem *item); - ToolChainTreeItem *insertBundle(const ToolchainBundle &bundle, bool changed = false); // Insert directly into model + void markForRemoval(ExtendedToolchainTreeItem *item); + ExtendedToolchainTreeItem *insertBundle(const ToolchainBundle &bundle, bool changed = false); // Insert directly into model void handleToolchainsRegistered(const Toolchains &toolchains); void handleToolchainsDeregistered(const Toolchains &toolchains); @@ -326,7 +335,7 @@ public: void apply() final; private: - TreeModel m_model; + TreeModel m_model; KitSettingsSortModel m_sortModel; QList m_factories; QTreeView *m_toolChainView; @@ -341,7 +350,7 @@ public: QHash> m_languageMap; - using AddRemoveList = QList; + using AddRemoveList = QList; AddRemoveList m_toAddList; AddRemoveList m_toRemoveList; Guard m_registerGuard; @@ -350,12 +359,12 @@ public: ToolchainDetectionSettings m_detectionSettings; }; -void ToolChainOptionsWidget::markForRemoval(ToolChainTreeItem *item) +void ToolChainOptionsWidget::markForRemoval(ExtendedToolchainTreeItem *item) { m_model.takeItem(item); if (const auto it = std::find(m_toAddList.begin(), m_toAddList.end(), item); it != m_toAddList.end()) { - item->bundle.deleteToolchains(); + item->bundle->deleteToolchains(); m_toAddList.erase(it); delete item; } else { @@ -363,11 +372,11 @@ void ToolChainOptionsWidget::markForRemoval(ToolChainTreeItem *item) } } -ToolChainTreeItem *ToolChainOptionsWidget::insertBundle( +ExtendedToolchainTreeItem *ToolChainOptionsWidget::insertBundle( const ToolchainBundle &bundle, bool changed) { StaticTreeItem *parent = parentForBundle(bundle); - auto item = new ToolChainTreeItem(m_widgetStack, bundle, changed); + auto item = new ExtendedToolchainTreeItem(m_widgetStack, bundle, changed); parent->appendChild(item); return item; @@ -382,11 +391,11 @@ void ToolChainOptionsWidget::handleToolchainsRegistered(const Toolchains &toolch if (const auto it = std::find_if( m_toAddList.begin(), m_toAddList.end(), - [&toolchains](ToolChainTreeItem * const item) { - return item->bundle.bundleId() == toolchains.first()->bundleId(); + [&toolchains](ExtendedToolchainTreeItem * const item) { + return item->bundle->bundleId() == toolchains.first()->bundleId(); }); it != m_toAddList.end()) { - if ((*it)->bundle.toolchains().size() == toolchains.size()) + if ((*it)->bundle->toolchains().size() == toolchains.size()) m_toAddList.erase(it); return; } @@ -407,32 +416,32 @@ void ToolChainOptionsWidget::handleToolchainsDeregistered(const Toolchains &tool if (const auto it = std::find_if( m_toRemoveList.begin(), m_toRemoveList.end(), - [&toolchains](const ToolChainTreeItem *item) { - return item->bundle.toolchains() == toolchains; + [&toolchains](const ExtendedToolchainTreeItem *item) { + return item->bundle->toolchains() == toolchains; }); it != m_toRemoveList.end()) { - ToolChainTreeItem * const item = *it; + ExtendedToolchainTreeItem * const item = *it; m_toRemoveList.erase(it); delete item; return; } - QSet affectedItems; + QSet affectedItems; for (Toolchain * const tc : toolchains) { StaticTreeItem *parent = parentForToolchain(*tc); - auto item = static_cast( + auto item = static_cast( parent->findChildAtLevel(1, [tc](TreeItem *item) { - const auto tcItem = static_cast(item); - return tcItem->bundle.size() > 0 && tcItem->bundle.bundleId() == tc->bundleId(); + const auto tcItem = static_cast(item); + return tcItem->bundle->size() > 0 && tcItem->bundle->bundleId() == tc->bundleId(); })); - const bool removed = item->bundle.removeToolchain(tc); + const bool removed = item->bundle->removeToolchain(tc); QTC_CHECK(removed); affectedItems << item; } - for (ToolChainTreeItem *item : std::as_const(affectedItems)) { - ToolchainManager::deregisterToolchains(item->bundle.toolchains()); - item->bundle.clearToolchains(); + for (ExtendedToolchainTreeItem *item : std::as_const(affectedItems)) { + ToolchainManager::deregisterToolchains(item->bundle->toolchains()); + item->bundle->clearToolchains(); m_model.destroyItem(item); } @@ -459,7 +468,7 @@ StaticTreeItem *ToolChainOptionsWidget::parentForToolchain(const Toolchain &tc) void ToolChainOptionsWidget::redetectToolchains() { // The second element is the set of toolchains for the respective bundle that were re-discovered. - using ItemToCheck = std::pair; + using ItemToCheck = std::pair; QList itemsToRemove; Toolchains knownTcs; @@ -468,11 +477,11 @@ void ToolChainOptionsWidget::redetectToolchains() m_model.forAllItems([&itemsToRemove, &knownTcs](TreeItem *item) { if (item->level() != 3) return; - const auto tcItem = static_cast(item); - if (tcItem->bundle.isAutoDetected() && !tcItem->bundle.isSdkProvided()) + const auto tcItem = static_cast(item); + if (tcItem->bundle->isAutoDetected() && !tcItem->bundle->isSdkProvided()) itemsToRemove << std::make_pair(tcItem, Toolchains()); else - knownTcs << tcItem->bundle.toolchains(); + knownTcs << tcItem->bundle->toolchains(); }); Toolchains toAdd; @@ -486,7 +495,7 @@ void ToolChainOptionsWidget::redetectToolchains() continue; knownTcs << tc; const auto matchItem = [&](const ItemToCheck &item) { - return Utils::contains(item.first->bundle.toolchains(), [&](Toolchain *btc) { + return Utils::contains(item.first->bundle->toolchains(), [&](Toolchain *btc) { return *btc == *tc; }); }; @@ -505,7 +514,7 @@ void ToolChainOptionsWidget::redetectToolchains() // Conversely, if not all toolchains of the bundle were re-discovered, we remove the existing // item and the newly discovered toolchains are marked for re-bundling. for (const auto &[item, newToolchains] : itemsToRemove) { - if (item->bundle.toolchains().size() == newToolchains.size()) { + if (item->bundle->toolchains().size() == newToolchains.size()) { qDeleteAll(newToolchains); } else { toAdd << newToolchains; @@ -522,7 +531,7 @@ void ToolChainOptionsWidget::redetectToolchains() void ToolChainOptionsWidget::toolChainSelectionChanged() { - ToolChainTreeItem *item = currentTreeItem(); + ExtendedToolchainTreeItem *item = currentTreeItem(); QWidget *currentTcWidget = item ? item->widget() : nullptr; if (currentTcWidget) @@ -535,8 +544,8 @@ void ToolChainOptionsWidget::apply() { // Remove unused tool chains: const AddRemoveList toRemove = m_toRemoveList; - for (const ToolChainTreeItem * const item : toRemove) - ToolchainManager::deregisterToolchains(item->bundle.toolchains()); + for (const ExtendedToolchainTreeItem * const item : toRemove) + ToolchainManager::deregisterToolchains(item->bundle->toolchains()); Q_ASSERT(m_toRemoveList.isEmpty()); @@ -544,8 +553,8 @@ void ToolChainOptionsWidget::apply() for (const QPair &autoAndManual : m_languageMap) { for (StaticTreeItem *parent : {autoAndManual.first, autoAndManual.second}) { for (TreeItem *item : *parent) { - auto tcItem = static_cast(item); - if (!tcItem->bundle.isAutoDetected() && tcItem->widget() && tcItem->changed) + auto tcItem = static_cast(item); + if (!tcItem->bundle->isAutoDetected() && tcItem->widget() && tcItem->changed) tcItem->widget()->apply(); tcItem->changed = false; tcItem->update(); @@ -556,12 +565,12 @@ void ToolChainOptionsWidget::apply() // Add new (and already updated) toolchains QStringList removedTcs; const AddRemoveList toAdd = m_toAddList; - for (ToolChainTreeItem * const item : toAdd) { - const Toolchains notRegistered = ToolchainManager::registerToolchains(item->bundle.toolchains()); + for (ExtendedToolchainTreeItem * const item : toAdd) { + const Toolchains notRegistered = ToolchainManager::registerToolchains(item->bundle->toolchains()); removedTcs << Utils::transform(notRegistered, &Toolchain::displayName); } - for (ToolChainTreeItem * const item : std::as_const(m_toAddList)) { - item->bundle.deleteToolchains(); + for (ExtendedToolchainTreeItem * const item : std::as_const(m_toAddList)) { + item->bundle->deleteToolchains(); delete item; } m_toAddList.clear(); @@ -603,22 +612,22 @@ void ToolChainOptionsWidget::createToolchains(ToolchainFactory *factory, const Q } const ToolchainBundle bundle(toolchains, ToolchainBundle::AutoRegister::Off); - ToolChainTreeItem * const item = insertBundle(bundle, true); + ExtendedToolchainTreeItem * const item = insertBundle(bundle, true); m_toAddList << item; m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } void ToolChainOptionsWidget::cloneToolchains() { - ToolChainTreeItem *current = currentTreeItem(); + ExtendedToolchainTreeItem *current = currentTreeItem(); if (!current) return; - ToolchainBundle bundle = current->bundle.clone(); + ToolchainBundle bundle = current->bundle->clone(); bundle.setDetection(Toolchain::ManualDetection); - bundle.setDisplayName(Tr::tr("Clone of %1").arg(current->bundle.displayName())); + bundle.setDisplayName(Tr::tr("Clone of %1").arg(current->bundle->displayName())); - ToolChainTreeItem * const item = insertBundle(bundle, true); + ExtendedToolchainTreeItem * const item = insertBundle(bundle, true); m_toAddList << item; m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } @@ -627,19 +636,19 @@ void ToolChainOptionsWidget::updateState() { bool canCopy = false; bool canDelete = false; - if (ToolChainTreeItem *item = currentTreeItem()) { - canCopy = item->bundle.validity() != ToolchainBundle::Valid::None; - canDelete = !item->bundle.isSdkProvided(); + if (ExtendedToolchainTreeItem *item = currentTreeItem()) { + canCopy = item->bundle->validity() != ToolchainBundle::Valid::None; + canDelete = !item->bundle->isSdkProvided(); } m_cloneButton->setEnabled(canCopy); m_delButton->setEnabled(canDelete); } -ToolChainTreeItem *ToolChainOptionsWidget::currentTreeItem() +ExtendedToolchainTreeItem *ToolChainOptionsWidget::currentTreeItem() { TreeItem *item = m_model.itemForIndex(m_sortModel.mapToSource(m_toolChainView->currentIndex())); - return (item && item->level() == 3) ? static_cast(item) : nullptr; + return (item && item->level() == 3) ? static_cast(item) : nullptr; } // -------------------------------------------------------------------------- diff --git a/src/plugins/projectexplorer/toolchainoptionspage.h b/src/plugins/projectexplorer/toolchainoptionspage.h index 552ef1ed630..979104c0007 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.h +++ b/src/plugins/projectexplorer/toolchainoptionspage.h @@ -3,7 +3,10 @@ #pragma once +#include "toolchain.h" + #include +#include #include @@ -16,5 +19,17 @@ public: ToolChainOptionsPage(); }; +class ToolchainTreeItem : public Utils::TreeItem +{ +public: + ToolchainTreeItem(const ToolchainBundle &bundle) : bundle(bundle) {} + ToolchainTreeItem() = default; + + static const int BundleIdRole = Qt::UserRole; + QVariant data(int column, int role) const override; + + std::optional bundle; +}; + } // namespace Internal } // namespace ProjectExplorer