MiniProjectTargetSelector: Merge two pairs of classes

Change-Id: I7b6e1c603da2c6457e150fcb5f60c11a844b2f34
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2020-02-19 14:47:55 +01:00
parent e7f784ca73
commit 72d83fddf2

View File

@@ -92,76 +92,43 @@ static QIcon createCenteredIcon(const QIcon &icon, const QIcon &overlay)
return QIcon(targetPixmap);
}
class SelectorView : public TreeView
{
Q_OBJECT
public:
SelectorView(QWidget *parent);
void setMaxCount(int maxCount);
int maxCount();
int optimalWidth() const;
void setOptimalWidth(int width);
int padding();
private:
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
int m_maxCount = 0;
int m_optimalWidth = 0;
};
// The project-specific classes look annoyingly similar to the generic ones.
// Can we merge them?
class SelectorProjectItem : public TreeItem
{
public:
SelectorProjectItem() = default;
SelectorProjectItem(Project *project) : m_project(project) {}
Project *project() const { return m_project; }
QString displayName() const
{
const auto hasSameProjectName = [this](TreeItem *ti) {
return ti != this && static_cast<SelectorProjectItem *>(ti)->project()->displayName()
== project()->displayName();
};
QString displayName = m_project->displayName();
if (parent()->findAnyChild(hasSameProjectName)) {
displayName.append(" (").append(project()->projectFilePath().toUserOutput())
.append(')');
}
return displayName;
}
private:
QVariant data(int column, int role) const override
{
return column == 0 && role == Qt::DisplayRole ? displayName() : QString();
}
Project *m_project = nullptr;
};
class GenericItem : public TreeItem
{
public:
GenericItem() = default;
GenericItem(QObject *object) : m_object(object) {}
QObject *object() const { return m_object; }
QString displayName() const
QString rawDisplayName() const
{
if (const auto p = qobject_cast<Project *>(object()))
return p->displayName();
if (const auto t = qobject_cast<Target *>(object()))
return t->displayName();
return static_cast<ProjectConfiguration *>(object())->displayName();
}
QString displayName() const
{
if (const auto p = qobject_cast<Project *>(object())) {
const auto hasSameProjectName = [this](TreeItem *ti) {
return ti != this
&& static_cast<GenericItem *>(ti)->rawDisplayName() == rawDisplayName();
};
QString displayName = p->displayName();
if (parent()->findAnyChild(hasSameProjectName)) {
displayName.append(" (").append(p->projectFilePath().toUserOutput())
.append(')');
}
return displayName;
}
return rawDisplayName();
}
private:
QString toolTip() const
QVariant toolTip() const
{
if (qobject_cast<Project *>(object()))
return {};
if (const auto t = qobject_cast<Target *>(object()))
return t->toolTip();
return static_cast<ProjectConfiguration *>(object())->toolTip();
@@ -178,7 +145,8 @@ private:
return displayName();
case Qt::ToolTipRole:
return toolTip();
default:break;
default:
break;
}
return {};
}
@@ -186,131 +154,15 @@ private:
QObject *m_object = nullptr;
};
static QString rawDisplayName(const SelectorProjectItem *item)
static bool compareItems(const TreeItem *ti1, const TreeItem *ti2)
{
return item->project()->displayName();
}
static QString rawDisplayName(const GenericItem *item) { return item->displayName(); }
template<typename T> bool compareItems(const TreeItem *ti1, const TreeItem *ti2)
{
const int result = caseFriendlyCompare(rawDisplayName(static_cast<const T*>(ti1)),
rawDisplayName(static_cast<const T*>(ti2)));
const int result = caseFriendlyCompare(static_cast<const GenericItem *>(ti1)->rawDisplayName(),
static_cast<const GenericItem *>(ti2)->rawDisplayName());
if (result != 0)
return result < 0;
return ti1 < ti2;
}
class ProjectsModel : public TreeModel<SelectorProjectItem, SelectorProjectItem>
{
Q_OBJECT
public:
ProjectsModel(QObject *parent) : TreeModel(parent)
{
connect(SessionManager::instance(), &SessionManager::projectAdded,
this, [this](Project *project) {
emit projectAdded(addItemForProject(project));
});
connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
this, [this](const Project *project) {
SelectorProjectItem * const item = itemForProject(project);
if (!item)
return;
destroyItem(item);
emit optimalWidthChanged();
});
buildTree();
}
SelectorProjectItem *itemForProject(const Project *project) const
{
return findItemAtLevel<1>([project](const SelectorProjectItem *item) {
return item->project() == project;
});
}
signals:
void projectAdded(const SelectorProjectItem *projectItem);
void requestRestoreCurrentIndex();
void optimalWidthChanged();
private:
const SelectorProjectItem *addItemForProject(Project *project)
{
const auto projectItem = new SelectorProjectItem(project);
rootItem()->insertOrderedChild(projectItem, &compareItems<SelectorProjectItem>);
connect(project, &Project::displayNameChanged, this, [this] {
rootItem()->sortChildren(&compareItems<SelectorProjectItem>);
emit optimalWidthChanged();
emit requestRestoreCurrentIndex();
});
return projectItem;
}
void buildTree()
{
for (Project * const project : SessionManager::projects())
addItemForProject(project);
}
};
class ProjectListView : public SelectorView
{
Q_OBJECT
public:
explicit ProjectListView(QWidget *parent = nullptr) : SelectorView(parent)
{
ProjectsModel * const model = new ProjectsModel(this);
connect(model, &ProjectsModel::projectAdded, this,
[this](const SelectorProjectItem *projectItem) {
QFontMetrics fn(font());
const int width = fn.horizontalAdvance(projectItem->displayName()) + padding();
if (width > optimalWidth())
setOptimalWidth(width);
restoreCurrentIndex();
});
connect(model, &ProjectsModel::optimalWidthChanged,
this, &ProjectListView::resetOptimalWidth);
connect(model, &ProjectsModel::requestRestoreCurrentIndex,
this, &ProjectListView::restoreCurrentIndex);
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
this, [this, model](const Project *project) {
const SelectorProjectItem * const item = model->itemForProject(project);
if (item)
setCurrentIndex(item->index());
});
setModel(model);
connect(selectionModel(), &QItemSelectionModel::currentChanged,
this, [model](const QModelIndex &index) {
const SelectorProjectItem * const item = model->itemForIndex(index);
if (item && item->project())
SessionManager::setStartupProject(item->project());
});
}
private:
ProjectsModel *theModel() const { return static_cast<ProjectsModel *>(model()); }
void restoreCurrentIndex()
{
const SelectorProjectItem * const itemForStartupProject
= theModel()->itemForProject(SessionManager::startupProject());
if (itemForStartupProject)
setCurrentIndex(theModel()->indexForItem(itemForStartupProject));
}
void resetOptimalWidth()
{
int width = 0;
QFontMetrics fn(font());
theModel()->forItemsAtLevel<1>([this, &width, &fn](const SelectorProjectItem *item) {
width = qMax(fn.horizontalAdvance(item->displayName()) + padding(), width);
});
setOptimalWidth(width);
}
};
class GenericModel : public TreeModel<GenericItem, GenericItem>
{
Q_OBJECT
@@ -327,8 +179,11 @@ public:
const GenericItem *addItemForObject(QObject *object)
{
const auto item = new GenericItem(object);
rootItem()->insertOrderedChild(item, &compareItems<GenericItem>);
if (const auto target = qobject_cast<Target *>(object)) {
rootItem()->insertOrderedChild(item, &compareItems);
if (const auto project = qobject_cast<Project *>(object)) {
connect(project, &Project::displayNameChanged,
this, &GenericModel::displayNameChanged);
} else if (const auto target = qobject_cast<Target *>(object)) {
connect(target, &Target::kitChanged, this, &GenericModel::displayNameChanged);
} else {
const auto pc = qobject_cast<ProjectConfiguration *>(object);
@@ -341,6 +196,13 @@ public:
return item;
}
GenericItem *itemForObject(const QObject *object) const
{
return findItemAtLevel<1>([object](const GenericItem *item) {
return item->object() == object;
});
}
void setColumnCount(int columns) { m_columnCount = columns; }
signals:
@@ -353,6 +215,99 @@ private:
}
};
class SelectorView : public TreeView
{
Q_OBJECT
public:
SelectorView(QWidget *parent);
void setMaxCount(int maxCount);
int maxCount();
int optimalWidth() const;
void setOptimalWidth(int width);
int padding();
GenericModel *theModel() const { return static_cast<GenericModel *>(model()); }
protected:
void resetOptimalWidth()
{
int width = 0;
QFontMetrics fn(font());
theModel()->forItemsAtLevel<1>([this, &width, &fn](const GenericItem *item) {
width = qMax(fn.horizontalAdvance(item->displayName()) + padding(), width);
});
setOptimalWidth(width);
}
private:
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
int m_maxCount = 0;
int m_optimalWidth = 0;
};
class ProjectListView : public SelectorView
{
Q_OBJECT
public:
explicit ProjectListView(QWidget *parent = nullptr) : SelectorView(parent)
{
const auto model = new GenericModel(this);
model->rebuild(transform<QList<QObject *>>(SessionManager::projects(),
[](Project *p) { return p; }));
connect(SessionManager::instance(), &SessionManager::projectAdded,
this, [this, model](Project *project) {
const GenericItem *projectItem = model->addItemForObject(project);
QFontMetrics fn(font());
const int width = fn.horizontalAdvance(projectItem->displayName()) + padding();
if (width > optimalWidth())
setOptimalWidth(width);
restoreCurrentIndex();
});
connect(SessionManager::instance(), &SessionManager::aboutToRemoveProject,
this, [this, model](const Project *project) {
GenericItem * const item = model->itemForObject(project);
if (!item)
return;
model->destroyItem(item);
resetOptimalWidth();
});
connect(SessionManager::instance(), &SessionManager::startupProjectChanged,
this, [this, model](const Project *project) {
const GenericItem * const item = model->itemForObject(project);
if (item)
setCurrentIndex(item->index());
});
connect(model, &GenericModel::displayNameChanged, this, [this, model] {
model->rootItem()->sortChildren(&compareItems);
resetOptimalWidth();
restoreCurrentIndex();
});
setModel(model);
connect(selectionModel(), &QItemSelectionModel::currentChanged,
this, [model](const QModelIndex &index) {
const GenericItem * const item = model->itemForIndex(index);
if (item && item->object())
SessionManager::setStartupProject(qobject_cast<Project *>(item->object()));
});
}
private:
void restoreCurrentIndex()
{
const GenericItem * const itemForStartupProject
= theModel()->itemForObject(SessionManager::startupProject());
if (itemForStartupProject)
setCurrentIndex(theModel()->indexForItem(itemForStartupProject));
}
};
class GenericListWidget : public SelectorView
{
Q_OBJECT
@@ -363,7 +318,7 @@ public:
const auto model = new GenericModel(this);
connect(model, &GenericModel::displayNameChanged, this, [this, model] {
const GenericItem * const activeItem = model->itemForIndex(currentIndex());
model->rootItem()->sortChildren(&compareItems<GenericItem>);
model->rootItem()->sortChildren(&compareItems);
resetOptimalWidth();
if (activeItem)
setCurrentIndex(activeItem->index());
@@ -386,7 +341,7 @@ public:
void setActiveProjectConfiguration(QObject *active)
{
if (const GenericItem * const item = itemForObject(active))
if (const GenericItem * const item = theModel()->itemForObject(active))
setCurrentIndex(item->index());
}
@@ -405,7 +360,7 @@ public:
void removeProjectConfiguration(QObject *pc)
{
const auto activeItem = theModel()->itemForIndex(currentIndex());
if (GenericItem * const item = itemForObject(pc)) {
if (GenericItem * const item = theModel()->itemForObject(pc)) {
theModel()->destroyItem(item);
resetOptimalWidth();
if (activeItem && activeItem != item)
@@ -413,8 +368,6 @@ public:
}
}
GenericModel *theModel() const { return static_cast<GenericModel *>(model()); }
private:
void mousePressEvent(QMouseEvent *event) override
{
@@ -442,23 +395,6 @@ private:
TreeView::mouseReleaseEvent(event);
}
GenericItem *itemForObject(const QObject *object)
{
return theModel()->findItemAtLevel<1>([object](const GenericItem *item) {
return item->object() == object;
});
}
void resetOptimalWidth()
{
int width = 0;
QFontMetrics fn(font());
theModel()->forItemsAtLevel<1>([this, &width, &fn](const GenericItem *item) {
width = qMax(fn.horizontalAdvance(item->displayName()) + padding(), width);
});
setOptimalWidth(width);
}
QObject *objectAt(const QModelIndex &index) const
{
return theModel()->itemForIndex(index)->object();