From e6a937accfb511fd70ab261ec766f30183739e55 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 30 Sep 2024 17:52:19 +0200 Subject: [PATCH] Debugger: Use DebuggerTreeItem in DebuggerKitAspect For sorting and icons. Task-number: QTCREATORBUG-31574 Change-Id: Ieefe3f2dfc0078439ca70db61b512394d9c35fdf Reviewed-by: hjk --- src/plugins/debugger/debuggeritem.cpp | 2 +- src/plugins/debugger/debuggeritemmanager.cpp | 77 +++++------ src/plugins/debugger/debuggeritemmanager.h | 25 +++- src/plugins/debugger/debuggerkitaspect.cpp | 134 ++++++++++++++----- 4 files changed, 163 insertions(+), 75 deletions(-) diff --git a/src/plugins/debugger/debuggeritem.cpp b/src/plugins/debugger/debuggeritem.cpp index 6709bb4c55c..98dac23f01d 100644 --- a/src/plugins/debugger/debuggeritem.cpp +++ b/src/plugins/debugger/debuggeritem.cpp @@ -308,7 +308,7 @@ QDateTime DebuggerItem::lastModified() const DebuggerItem::Problem DebuggerItem::problem() const { - if (isGeneric()) + if (isGeneric() || !m_id.isValid()) // Id can only be invalid for the "none" item. return Problem::None; if (m_engineType == NoEngineType) return Problem::NoEngine; diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index e4aed2d3343..53abcd151bb 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -3,7 +3,6 @@ #include "debuggeritemmanager.h" -#include "debuggeritem.h" #include "debuggertr.h" #include @@ -26,7 +25,6 @@ #include #include #include -#include #include #include @@ -102,50 +100,45 @@ private: // DebuggerTreeItem // -------------------------------------------------------------------------- -class DebuggerTreeItem : public TreeItem +QVariant DebuggerTreeItem::data(int column, int role) const { -public: - DebuggerTreeItem(const DebuggerItem &item, bool changed) - : m_item(item), m_orig(item), m_added(changed), m_changed(changed) - {} - - QVariant data(int column, int role) const override - { - switch (role) { - case Qt::DisplayRole: - switch (column) { - case 0: return m_item.displayName(); - case 1: return m_item.command().toUserOutput(); - case 2: return m_item.engineTypeName(); - } - break; - - case Qt::FontRole: { - QFont font; - if (m_changed) - font.setBold(true); - if (m_removed) - font.setStrikeOut(true); - return font; - } - - case Qt::DecorationRole: - if (column == 0) - return m_item.decoration(); - break; - - case Qt::ToolTipRole: - return m_item.validityMessage(); + switch (role) { + case Qt::DisplayRole: + switch (column) { + case 0: + return m_item.displayName(); + case 1: + return m_item.command().toUserOutput(); + case 2: + return m_item.engineTypeName(); } - return QVariant(); + break; + + case Qt::FontRole: { + QFont font; + if (m_changed) + font.setBold(true); + if (m_removed) + font.setStrikeOut(true); + return font; } - DebuggerItem m_item; // Displayed, possibly unapplied data. - DebuggerItem m_orig; // Stored original data. - bool m_added; - bool m_changed; - bool m_removed = false; -}; + case Qt::DecorationRole: + if (column == 0) + return m_item.decoration(); + break; + + case Qt::ToolTipRole: + return m_item.validityMessage(); + + case IdRole: + return m_item.id(); + + case ProblemRole: + return int(m_item.problem()); + } + return QVariant(); +} // -------------------------------------------------------------------------- // DebuggerItemModel diff --git a/src/plugins/debugger/debuggeritemmanager.h b/src/plugins/debugger/debuggeritemmanager.h index 662306a3f31..f8b695a9ee7 100644 --- a/src/plugins/debugger/debuggeritemmanager.h +++ b/src/plugins/debugger/debuggeritemmanager.h @@ -5,15 +5,15 @@ #include "debugger_global.h" #include "debuggerconstants.h" +#include "debuggeritem.h" #include +#include #include namespace Debugger { -class DebuggerItem; - namespace DebuggerItemManager { DEBUGGER_EXPORT void restoreDebuggers(); @@ -34,4 +34,25 @@ DEBUGGER_EXPORT const DebuggerItem *findById(const QVariant &id); DEBUGGER_EXPORT const DebuggerItem *findByEngineType(DebuggerEngineType engineType); } // DebuggerItemManager + +namespace Internal { +class DebuggerTreeItem : public Utils::TreeItem +{ +public: + DebuggerTreeItem(const DebuggerItem &item, bool changed) + : m_item(item), m_orig(item), m_added(changed), m_changed(changed) + {} + + static const inline int IdRole = Qt::UserRole; + static const inline int ProblemRole = Qt::UserRole + 1; + QVariant data(int column, int role) const override; + + DebuggerItem m_item; // Displayed, possibly unapplied data. + DebuggerItem m_orig; // Stored original data. + bool m_added; + bool m_changed; + bool m_removed = false; +}; + +} // Internal } // Debugger diff --git a/src/plugins/debugger/debuggerkitaspect.cpp b/src/plugins/debugger/debuggerkitaspect.cpp index 0e40ee60d19..e70ab1430cd 100644 --- a/src/plugins/debugger/debuggerkitaspect.cpp +++ b/src/plugins/debugger/debuggerkitaspect.cpp @@ -22,7 +22,7 @@ #include -#include +#include using namespace ProjectExplorer; using namespace Utils; @@ -35,6 +35,98 @@ namespace Debugger { namespace Internal { +class DebuggerItemListModel : public TreeModel +{ +public: + DebuggerItemListModel(const Kit &kit, QObject *parent) + : TreeModel(parent) + , m_kit(kit) + {} + + QModelIndex indexForId(const QVariant &id) const + { + // The "None" item always comes last + const auto noneIndex = [this] { return index(rowCount() - 1, 0); }; + + if (id.isNull()) + return noneIndex(); + const TreeItem *const item = findItemAtLevel<1>( + [id](TreeItem *item) { return item->data(0, DebuggerTreeItem::IdRole) == id; }); + return item ? indexForItem(item) : noneIndex(); + } + + void reset() + { + clear(); + + const IDeviceConstPtr device = BuildDeviceKitAspect::device(&m_kit); + const Utils::FilePath rootPath = device->rootPath(); + const QList debuggersForBuildDevice + = Utils::filtered(DebuggerItemManager::debuggers(), [&](const DebuggerItem &item) { + if (item.isGeneric()) + return device->id() != ProjectExplorer::Constants::DESKTOP_DEVICE_ID; + return item.command().isSameDevice(rootPath); + }); + for (const DebuggerItem &item : debuggersForBuildDevice) + rootItem()->appendChild(new DebuggerTreeItem(item, false)); + DebuggerItem noneItem; + noneItem.setUnexpandedDisplayName(Tr::tr("None")); + rootItem()->appendChild(new DebuggerTreeItem(noneItem, false)); + } + +private: + const Kit &m_kit; +}; + +class DebuggerItemSortModel : public SortModel +{ +public: + DebuggerItemSortModel(QObject *parent) : SortModel(parent) {} + + QModelIndex indexForId(const QVariant &id) const + { + return mapFromSource( + static_cast(sourceModel())->indexForId(id)); + } + + 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 auto item1 = static_cast(source->itemForIndex(source_left)); + const auto item2 = static_cast(source->itemForIndex(source_right)); + QTC_ASSERT(item1 && item2, return false); + + // Criterion 1: "None" comes last + if (!item1->data(0, DebuggerTreeItem::IdRole).isValid()) + return false; + if (!item2->data(0, DebuggerTreeItem::IdRole).isValid()) + return true; + + // Criterion 2: Invalid items come after valid ones with warnings, which come + // after valid ones without warnings. + if (const QVariant &p1 = item1->data(0, DebuggerTreeItem::ProblemRole), + &p2 = item2->data(0, DebuggerTreeItem::ProblemRole); + p1 != p2) { + const auto problem1 = static_cast(p1.toInt()); + const auto problem2 = static_cast(p2.toInt()); + if (problem1 == DebuggerItem::Problem::None + || problem2 == DebuggerItem::Problem::NoEngine) { + return true; + } + if (problem2 == DebuggerItem::Problem::None + || problem1 == DebuggerItem::Problem::NoEngine) { + return false; + } + } + + // Criterion 3: Name. + return SortModel::lessThan(source_left, source_right); + } +}; + class DebuggerKitAspectImpl final : public KitAspect { public: @@ -46,16 +138,16 @@ public: m_comboBox = createSubWidget(); m_comboBox->setSizePolicy(QSizePolicy::Ignored, m_comboBox->sizePolicy().verticalPolicy()); m_comboBox->setEnabled(true); + const auto sortModel = new DebuggerItemSortModel(this); + sortModel->setSourceModel(new DebuggerItemListModel(*workingCopy, this)); + m_comboBox->setModel(sortModel); refresh(); m_comboBox->setToolTip(factory->description()); connect(m_comboBox, &QComboBox::currentIndexChanged, this, [this] { if (m_ignoreChanges.isLocked()) return; - - int currentIndex = m_comboBox->currentIndex(); - QVariant id = m_comboBox->itemData(currentIndex); - m_kit->setValue(DebuggerKitAspect::id(), id); + m_kit->setValue(DebuggerKitAspect::id(), currentId()); }); } @@ -81,34 +173,16 @@ private: void refresh() override { const GuardLocker locker(m_ignoreChanges); - m_comboBox->clear(); - m_comboBox->addItem(Tr::tr("None"), QString()); - - IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); - const Utils::FilePath path = device->rootPath(); - - const QList debuggersForBuildDevice - = Utils::filtered(DebuggerItemManager::debuggers(), [path](const DebuggerItem &item) { - return item.command().isSameDevice(path); - }); - for (const DebuggerItem &item : debuggersForBuildDevice) - m_comboBox->addItem(item.displayName(), item.id()); - - const DebuggerItem *item = DebuggerKitAspect::debugger(m_kit); - updateComboBox(item ? item->id() : QVariant()); + const auto sortModel = static_cast(m_comboBox->model()); + sortModel->reset(); + sortModel->sort(0); + const DebuggerItem * const item = DebuggerKitAspect::debugger(m_kit); + m_comboBox->setCurrentIndex(sortModel->indexForId(item ? item->id() : QVariant()).row()); } - QVariant currentId() const { return m_comboBox->itemData(m_comboBox->currentIndex()); } - - void updateComboBox(const QVariant &id) + QVariant currentId() const { - for (int i = 0; i < m_comboBox->count(); ++i) { - if (id == m_comboBox->itemData(i)) { - m_comboBox->setCurrentIndex(i); - return; - } - } - m_comboBox->setCurrentIndex(0); + return m_comboBox->itemData(m_comboBox->currentIndex(), DebuggerTreeItem::IdRole); } Guard m_ignoreChanges;