forked from qt-creator/qt-creator
MiniProjectTargetSelector: Merge two pairs of classes
Change-Id: I7b6e1c603da2c6457e150fcb5f60c11a844b2f34 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -92,76 +92,43 @@ static QIcon createCenteredIcon(const QIcon &icon, const QIcon &overlay)
|
|||||||
return QIcon(targetPixmap);
|
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
|
class GenericItem : public TreeItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GenericItem() = default;
|
GenericItem() = default;
|
||||||
GenericItem(QObject *object) : m_object(object) {}
|
GenericItem(QObject *object) : m_object(object) {}
|
||||||
QObject *object() const { return m_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()))
|
if (const auto t = qobject_cast<Target *>(object()))
|
||||||
return t->displayName();
|
return t->displayName();
|
||||||
return static_cast<ProjectConfiguration *>(object())->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:
|
private:
|
||||||
QString toolTip() const
|
QVariant toolTip() const
|
||||||
{
|
{
|
||||||
|
if (qobject_cast<Project *>(object()))
|
||||||
|
return {};
|
||||||
if (const auto t = qobject_cast<Target *>(object()))
|
if (const auto t = qobject_cast<Target *>(object()))
|
||||||
return t->toolTip();
|
return t->toolTip();
|
||||||
return static_cast<ProjectConfiguration *>(object())->toolTip();
|
return static_cast<ProjectConfiguration *>(object())->toolTip();
|
||||||
@@ -178,7 +145,8 @@ private:
|
|||||||
return displayName();
|
return displayName();
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
return toolTip();
|
return toolTip();
|
||||||
default:break;
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -186,131 +154,15 @@ private:
|
|||||||
QObject *m_object = nullptr;
|
QObject *m_object = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static QString rawDisplayName(const SelectorProjectItem *item)
|
static bool compareItems(const TreeItem *ti1, const TreeItem *ti2)
|
||||||
{
|
{
|
||||||
return item->project()->displayName();
|
const int result = caseFriendlyCompare(static_cast<const GenericItem *>(ti1)->rawDisplayName(),
|
||||||
}
|
static_cast<const GenericItem *>(ti2)->rawDisplayName());
|
||||||
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)));
|
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
return result < 0;
|
return result < 0;
|
||||||
return ti1 < ti2;
|
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>
|
class GenericModel : public TreeModel<GenericItem, GenericItem>
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -327,8 +179,11 @@ public:
|
|||||||
const GenericItem *addItemForObject(QObject *object)
|
const GenericItem *addItemForObject(QObject *object)
|
||||||
{
|
{
|
||||||
const auto item = new GenericItem(object);
|
const auto item = new GenericItem(object);
|
||||||
rootItem()->insertOrderedChild(item, &compareItems<GenericItem>);
|
rootItem()->insertOrderedChild(item, &compareItems);
|
||||||
if (const auto target = qobject_cast<Target *>(object)) {
|
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);
|
connect(target, &Target::kitChanged, this, &GenericModel::displayNameChanged);
|
||||||
} else {
|
} else {
|
||||||
const auto pc = qobject_cast<ProjectConfiguration *>(object);
|
const auto pc = qobject_cast<ProjectConfiguration *>(object);
|
||||||
@@ -341,6 +196,13 @@ public:
|
|||||||
return item;
|
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; }
|
void setColumnCount(int columns) { m_columnCount = columns; }
|
||||||
|
|
||||||
signals:
|
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
|
class GenericListWidget : public SelectorView
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -363,7 +318,7 @@ public:
|
|||||||
const auto model = new GenericModel(this);
|
const auto model = new GenericModel(this);
|
||||||
connect(model, &GenericModel::displayNameChanged, this, [this, model] {
|
connect(model, &GenericModel::displayNameChanged, this, [this, model] {
|
||||||
const GenericItem * const activeItem = model->itemForIndex(currentIndex());
|
const GenericItem * const activeItem = model->itemForIndex(currentIndex());
|
||||||
model->rootItem()->sortChildren(&compareItems<GenericItem>);
|
model->rootItem()->sortChildren(&compareItems);
|
||||||
resetOptimalWidth();
|
resetOptimalWidth();
|
||||||
if (activeItem)
|
if (activeItem)
|
||||||
setCurrentIndex(activeItem->index());
|
setCurrentIndex(activeItem->index());
|
||||||
@@ -386,7 +341,7 @@ public:
|
|||||||
|
|
||||||
void setActiveProjectConfiguration(QObject *active)
|
void setActiveProjectConfiguration(QObject *active)
|
||||||
{
|
{
|
||||||
if (const GenericItem * const item = itemForObject(active))
|
if (const GenericItem * const item = theModel()->itemForObject(active))
|
||||||
setCurrentIndex(item->index());
|
setCurrentIndex(item->index());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,7 +360,7 @@ public:
|
|||||||
void removeProjectConfiguration(QObject *pc)
|
void removeProjectConfiguration(QObject *pc)
|
||||||
{
|
{
|
||||||
const auto activeItem = theModel()->itemForIndex(currentIndex());
|
const auto activeItem = theModel()->itemForIndex(currentIndex());
|
||||||
if (GenericItem * const item = itemForObject(pc)) {
|
if (GenericItem * const item = theModel()->itemForObject(pc)) {
|
||||||
theModel()->destroyItem(item);
|
theModel()->destroyItem(item);
|
||||||
resetOptimalWidth();
|
resetOptimalWidth();
|
||||||
if (activeItem && activeItem != item)
|
if (activeItem && activeItem != item)
|
||||||
@@ -413,8 +368,6 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericModel *theModel() const { return static_cast<GenericModel *>(model()); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void mousePressEvent(QMouseEvent *event) override
|
void mousePressEvent(QMouseEvent *event) override
|
||||||
{
|
{
|
||||||
@@ -442,23 +395,6 @@ private:
|
|||||||
TreeView::mouseReleaseEvent(event);
|
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
|
QObject *objectAt(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
return theModel()->itemForIndex(index)->object();
|
return theModel()->itemForIndex(index)->object();
|
||||||
|
Reference in New Issue
Block a user