Commit 09316279 authored by Daniel Teske's avatar Daniel Teske Committed by Daniel Molkentin
Browse files

Overhaul mini project selector



This makes the mini project selector keyboard friendly.
Also adds the deployconfiguration. Arguable it looks less neat now.

Change-Id: I761502df0bca72afdc2e56ef9b5abe4f9521a2f7
Reviewed-by: default avatarDaniel Molkentin <daniel.molkentin@nokia.com>
parent 3053e572
......@@ -31,8 +31,6 @@
**************************************************************************/
#include "miniprojecttargetselector.h"
#include "buildconfigurationmodel.h"
#include "runconfigurationmodel.h"
#include "target.h"
#include <utils/qtcassert.h>
......@@ -46,22 +44,20 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/session.h>
#include <projectexplorer/project.h>
#include <projectexplorer/runconfiguration.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/deployconfiguration.h>
#include <projectexplorer/runconfiguration.h>
#include <QtCore/QTimer>
#include <QtGui/QLayout>
#include <QtGui/QFormLayout>
#include <QtGui/QLabel>
#include <QtGui/QComboBox>
#include <QtGui/QListWidget>
#include <QtGui/QStatusBar>
#include <QtGui/QStackedWidget>
#include <QtGui/QKeyEvent>
#include <QtGui/QPainter>
#include <QtGui/QAction>
#include <QtGui/QItemDelegate>
#include <QtGui/QMainWindow>
#include <QtGui/QApplication>
static QIcon createCenteredIcon(const QIcon &icon, const QIcon &overlay)
......@@ -85,20 +81,31 @@ static QIcon createCenteredIcon(const QIcon &icon, const QIcon &overlay)
using namespace ProjectExplorer;
using namespace ProjectExplorer::Internal;
////////
// TargetSelectorDelegate
////////
class TargetSelectorDelegate : public QItemDelegate
{
public:
TargetSelectorDelegate(QObject *parent) : QItemDelegate(parent) { }
private:
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const;
mutable QImage selectionGradient;
};
QSize TargetSelectorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_UNUSED(option)
Q_UNUSED(index)
return QSize(190, 30);
}
void TargetSelectorDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &) const
const QModelIndex &index) const
{
painter->save();
painter->setClipping(false);
......@@ -119,11 +126,25 @@ void TargetSelectorDelegate::paint(QPainter *painter,
painter->setPen(QColor(0, 0, 0, 80));
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
}
QFontMetrics fm(option.font);
QString text = index.data(Qt::DisplayRole).toString();
painter->setPen(QColor(255, 255, 255, 160));
QString elidedText = fm.elidedText(text, Qt::ElideMiddle, option.rect.width() - 12);
if (elidedText != text)
const_cast<QAbstractItemModel *>(index.model())->setData(index, text, Qt::ToolTipRole);
else
const_cast<QAbstractItemModel *>(index.model())->setData(index, "", Qt::ToolTipRole);
painter->drawText(option.rect.left() + 6, option.rect.top() + (option.rect.height() - fm.height()) / 2 + fm.ascent(), elidedText);
painter->restore();
}
ProjectListWidget::ProjectListWidget(ProjectExplorer::Project *project, QWidget *parent)
: QListWidget(parent), m_project(project)
////////
// ListWidget
////////
ListWidget::ListWidget(QWidget *parent)
: QListWidget(parent), m_maxCount(0)
{
setFocusPolicy(Qt::NoFocus);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
......@@ -131,23 +152,15 @@ ProjectListWidget::ProjectListWidget(ProjectExplorer::Project *project, QWidget
setFocusPolicy(Qt::WheelFocus);
setItemDelegate(new TargetSelectorDelegate(this));
setAttribute(Qt::WA_MacShowFocusRect, false);
connect(this, SIGNAL(currentRowChanged(int)), SLOT(setTarget(int)));
setStyleSheet(QString::fromLatin1("QListWidget { background: #464646; border-style: none; }"));
setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
}
ProjectExplorer::Project *ProjectListWidget::project() const
{
return m_project;
}
QSize ProjectListWidget::sizeHint() const
QSize ListWidget::sizeHint() const
{
int height = 0;
int width = 0;
for (int itemPos = 0; itemPos < count(); ++itemPos) {
height += item(itemPos)->sizeHint().height();
width = qMax(width, item(itemPos)->sizeHint().width());
}
int height = m_maxCount * 30;
int width = 190;
// We try to keep the height of the popup equal to the actionbar
QSize size(width, height);
......@@ -156,195 +169,309 @@ QSize ProjectListWidget::sizeHint() const
Q_ASSERT(actionBar);
QMargins popupMargins = window()->contentsMargins();
if (actionBar)
size.setHeight(qMax(actionBar->height() - statusBar->height() -
(popupMargins.top() + popupMargins.bottom()), height));
int alignedWithActionHeight
= actionBar->height() - statusBar->height() - (popupMargins.top() + popupMargins.bottom());
size.setHeight(qBound(alignedWithActionHeight, height, 2 * alignedWithActionHeight));
return size;
}
void ProjectListWidget::setTarget(int index)
void ListWidget::keyPressEvent(QKeyEvent *event)
{
MiniTargetWidget *mtw = qobject_cast<MiniTargetWidget *>(itemWidget(item(index)));
if (!mtw)
return;
m_project->setActiveTarget(mtw->target());
if (event->key() == Qt::Key_Left)
focusPreviousChild();
else if (event->key() == Qt::Key_Right)
focusNextChild();
else
QListWidget::keyPressEvent(event);
}
MiniTargetWidget::MiniTargetWidget(Target *target, QWidget *parent) :
QWidget(parent), m_target(target)
void ListWidget::keyReleaseEvent(QKeyEvent *event)
{
Q_ASSERT(m_target);
if (event->key() != Qt::LeftArrow && event->key() != Qt::RightArrow)
QListWidget::keyReleaseEvent(event);
}
if (hasBuildConfiguration()) {
m_buildComboBox = new QComboBox;
m_buildComboBox->setProperty("alignarrow", true);
m_buildComboBox->setProperty("hideborder", true);
m_buildComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_buildComboBox->setToolTip(tr("Select active build configuration"));
m_buildComboBox->setModel(new BuildConfigurationModel(m_target, this));
} else {
m_buildComboBox = 0;
}
m_runComboBox = new QComboBox;
m_runComboBox ->setProperty("alignarrow", true);
m_runComboBox ->setProperty("hideborder", true);
m_runComboBox->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
m_runComboBox->setToolTip(tr("Select active run configuration"));
RunConfigurationModel *model = new RunConfigurationModel(m_target, this);
m_runComboBox->setModel(model);
int fontSize = font().pointSize();
setStyleSheet(QString::fromLatin1("QLabel { font-size: %2pt; color: white; } "
"#target { font: bold %1pt;} "
"#buildLabel{ font: bold; color: rgba(255, 255, 255, 160)} "
"#runLabel { font: bold ; color: rgba(255, 255, 255, 160)} "
).arg(fontSize).arg(fontSize - 2));
QGridLayout *gridLayout = new QGridLayout(this);
m_targetName = new QLabel(m_target->displayName());
m_targetName->setObjectName(QString::fromUtf8("target"));
m_targetIcon = new QLabel();
updateIcon();
if (hasBuildConfiguration()) {
Q_FOREACH(BuildConfiguration* bc, m_target->buildConfigurations())
addBuildConfiguration(bc);
BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());
connect(m_target, SIGNAL(addedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
SLOT(addBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
connect(m_target, SIGNAL(removedBuildConfiguration(ProjectExplorer::BuildConfiguration*)),
SLOT(removeBuildConfiguration(ProjectExplorer::BuildConfiguration*)));
void ListWidget::setMaxCount(int maxCount)
{
// Note: the current assumption is that, this is not called while the listwidget is visible
// Otherwise we would need to add code to MiniProjectTargetSelector reacting to the
// updateGeometry (which then would jump ugly)
m_maxCount = maxCount;
updateGeometry();
}
connect(m_target, SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)),
SLOT(setActiveBuildConfiguration()));
connect(m_buildComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setActiveBuildConfiguration(int)));
////////
// ProjectListWidget
////////
ProjectListWidget::ProjectListWidget(SessionManager *sessionManager, QWidget *parent)
: ListWidget(parent), m_sessionManager(sessionManager), m_ignoreIndexChange(false)
{
connect(m_sessionManager, SIGNAL(projectAdded(ProjectExplorer::Project*)),
this, SLOT(addProject(ProjectExplorer::Project*)));
connect(m_sessionManager, SIGNAL(aboutToRemoveProject(ProjectExplorer::Project*)),
this, SLOT(removeProject(ProjectExplorer::Project*)));
connect(m_sessionManager, SIGNAL(startupProjectChanged(ProjectExplorer::Project*)),
this, SLOT(changeStartupProject(ProjectExplorer::Project*)));
connect(this, SIGNAL(currentRowChanged(int)),
this, SLOT(setProject(int)));
}
QListWidgetItem *ProjectListWidget::itemForProject(Project *project)
{
for (int i = 0; i < count(); ++i) {
QListWidgetItem *currentItem = item(i);
if (currentItem->data(Qt::UserRole).value<Project*>() == project)
return currentItem;
}
return 0;
}
QString ProjectListWidget::fullName(ProjectExplorer::Project *project)
{
return tr("%1 (%2)").arg(project->displayName(), project->file()->fileName());
}
m_runComboBox->setEnabled(m_target->runConfigurations().count() > 1);
m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
void ProjectListWidget::addProject(Project *project)
{
m_ignoreIndexChange = true;
connect(m_target, SIGNAL(addedRunConfiguration(ProjectExplorer::RunConfiguration*)),
SLOT(addRunConfiguration(ProjectExplorer::RunConfiguration*)));
connect(m_target, SIGNAL(removedRunConfiguration(ProjectExplorer::RunConfiguration*)),
SLOT(removeRunConfiguration(ProjectExplorer::RunConfiguration*)));
QString sortName = fullName(project);
int pos = 0;
for (int i=0; i < count(); ++i) {
Project *p = item(i)->data(Qt::UserRole).value<Project*>();
QString itemSortName = fullName(p);
if (itemSortName > sortName)
pos = i;
}
connect(m_runComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setActiveRunConfiguration(int)));
bool useFullName = false;
for (int i = 0; i < count(); ++i) {
Project *p = item(i)->data(Qt::UserRole).value<Project*>();
if (p->displayName() == project->displayName()) {
useFullName = true;
item(i)->setText(fullName(p));
}
}
connect(m_target, SIGNAL(activeRunConfigurationChanged(ProjectExplorer::RunConfiguration*)),
SLOT(setActiveRunConfiguration()));
connect(m_target, SIGNAL(iconChanged()), this, SLOT(updateIcon()));
QString displayName = useFullName ? fullName(project) : project->displayName();
QListWidgetItem *item = new QListWidgetItem();
item->setData(Qt::UserRole, QVariant::fromValue(project));
item->setText(displayName);
insertItem(pos, item);
QHBoxLayout *buildHelperLayout = 0;
if (hasBuildConfiguration()) {
buildHelperLayout= new QHBoxLayout;
buildHelperLayout->setMargin(0);
buildHelperLayout->setSpacing(0);
buildHelperLayout->addWidget(m_buildComboBox);
if (project == ProjectExplorerPlugin::instance()->startupProject()) {
setCurrentItem(item);
}
QHBoxLayout *runHelperLayout = new QHBoxLayout;
runHelperLayout->setMargin(0);
runHelperLayout->setSpacing(0);
runHelperLayout->addWidget(m_runComboBox);
QFormLayout *formLayout = new QFormLayout;
formLayout->setLabelAlignment(Qt::AlignRight);
QLabel *lbl;
int indent = 10;
if (hasBuildConfiguration()) {
lbl = new QLabel(tr("Build:"));
lbl->setObjectName(QString::fromUtf8("buildLabel"));
lbl->setMinimumWidth(lbl->fontMetrics().width(lbl->text()) + indent + 4);
lbl->setIndent(indent);
formLayout->addRow(lbl, buildHelperLayout);
m_ignoreIndexChange = false;
}
void ProjectListWidget::removeProject(Project *project)
{
m_ignoreIndexChange = true;
QListWidgetItem *listItem = itemForProject(project);
delete listItem;
// Update display names
QString name = project->displayName();
int countDisplayName = 0;
int otherIndex = -1;
for (int i = 0; i < count(); ++i) {
Project *p = item(i)->data(Qt::UserRole).value<Project *>();
if (p->displayName() == name) {
++countDisplayName;
otherIndex = i;
}
}
if (countDisplayName == 1) {
Project *p = item(otherIndex)->data(Qt::UserRole).value<Project *>();
item(otherIndex)->setText(p->displayName());
}
lbl = new QLabel(tr("Run:"));
lbl->setObjectName(QString::fromUtf8("runLabel"));
lbl->setMinimumWidth(lbl->fontMetrics().width(lbl->text()) + indent + 4);
lbl->setIndent(indent);
formLayout->addRow(lbl, runHelperLayout);
gridLayout->addWidget(m_targetName, 0, 0);
gridLayout->addWidget(m_targetIcon, 0, 1, 2, 1, Qt::AlignTop|Qt::AlignHCenter);
gridLayout->addLayout(formLayout, 1, 0);
m_ignoreIndexChange = false;
}
void MiniTargetWidget::updateIcon()
void ProjectListWidget::setProject(int index)
{
m_targetIcon->setPixmap(createCenteredIcon(m_target->icon(), QIcon()).pixmap(Core::Constants::TARGET_ICON_SIZE));
if (m_ignoreIndexChange)
return;
if (index < 0)
return;
Project *p = item(index)->data(Qt::UserRole).value<Project *>();
m_sessionManager->setStartupProject(p);
}
ProjectExplorer::Target *MiniTargetWidget::target() const
void ProjectListWidget::changeStartupProject(Project *project)
{
return m_target;
setCurrentItem(itemForProject(project));
}
void MiniTargetWidget::setActiveBuildConfiguration(int index)
/////////
// GenericListWidget
/////////
GenericListWidget::GenericListWidget(QWidget *parent)
: ListWidget(parent), m_ignoreIndexChange(false)
{
BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
m_target->setActiveBuildConfiguration(model->buildConfigurationAt(index));
emit changed();
connect(this, SIGNAL(currentRowChanged(int)),
this, SLOT(rowChanged(int)));
}
void MiniTargetWidget::setActiveRunConfiguration(int index)
void GenericListWidget::setProjectConfigurations(const QList<ProjectConfiguration *> &list, ProjectConfiguration *active)
{
RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
m_target->setActiveRunConfiguration(model->runConfigurationAt(index));
updateIcon();
emit changed();
m_ignoreIndexChange = true;
clear();
for (int i = 0; i < count(); ++i) {
ProjectConfiguration *p = item(i)->data(Qt::UserRole).value<ProjectConfiguration *>();
disconnect(p, SIGNAL(displayNameChanged()),
this, SLOT(displayNameChanged()));
}
foreach (ProjectConfiguration *pc, list)
addProjectConfiguration(pc);
setActiveProjectConfiguration(active);
m_ignoreIndexChange = false;
}
void MiniTargetWidget::setActiveBuildConfiguration()
void GenericListWidget::setActiveProjectConfiguration(ProjectConfiguration *active)
{
QTC_ASSERT(m_buildComboBox, return);
BuildConfigurationModel *model = static_cast<BuildConfigurationModel *>(m_buildComboBox->model());
m_buildComboBox->setCurrentIndex(model->indexFor(m_target->activeBuildConfiguration()).row());
QListWidgetItem *item = itemForProjectConfiguration(active);
setCurrentItem(item);
}
void MiniTargetWidget::setActiveRunConfiguration()
void GenericListWidget::addProjectConfiguration(ProjectExplorer::ProjectConfiguration *pc)
{
RunConfigurationModel *model = static_cast<RunConfigurationModel *>(m_runComboBox->model());
m_runComboBox->setCurrentIndex(model->indexFor(m_target->activeRunConfiguration()).row());
m_ignoreIndexChange = true;
QListWidgetItem *lwi = new QListWidgetItem();
lwi->setText(pc->displayName());
lwi->setData(Qt::UserRole, QVariant::fromValue(pc));
// Figure out pos
int pos = count();
for (int i = 0; i < count(); ++i) {
ProjectConfiguration *p = item(i)->data(Qt::UserRole).value<ProjectConfiguration *>();
if (pc->displayName() < p->displayName()) {
pos = i;
break;
}
}
insertItem(pos, lwi);
connect(pc, SIGNAL(displayNameChanged()),
this, SLOT(displayNameChanged()));
m_ignoreIndexChange = false;
}
void MiniTargetWidget::addRunConfiguration(ProjectExplorer::RunConfiguration* rc)
void GenericListWidget::removeProjectConfiguration(ProjectExplorer::ProjectConfiguration *pc)
{
Q_UNUSED(rc);
m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
m_ignoreIndexChange = true;
disconnect(pc, SIGNAL(displayNameChanged()),
this, SLOT(displayNameChanged()));
delete itemForProjectConfiguration(pc);
m_ignoreIndexChange = false;
}
void MiniTargetWidget::removeRunConfiguration(ProjectExplorer::RunConfiguration* rc)
void GenericListWidget::rowChanged(int index)
{
Q_UNUSED(rc);
m_runComboBox->setEnabled(m_target->runConfigurations().count()>1);
if (m_ignoreIndexChange)
return;
if (index < 0)
return;
emit changeActiveProjectConfiguration(item(index)->data(Qt::UserRole).value<ProjectConfiguration *>());
}
void MiniTargetWidget::addBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
void GenericListWidget::displayNameChanged()
{
Q_UNUSED(bc);
connect(bc, SIGNAL(displayNameChanged()), SIGNAL(changed()), Qt::UniqueConnection);
m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
m_ignoreIndexChange = true;
ProjectConfiguration *pc = qobject_cast<ProjectConfiguration *>(sender());
int index = -1;
int i = 0;
for (; i < count(); ++i) {
QListWidgetItem *lwi = item(i);
if (lwi->data(Qt::UserRole).value<ProjectConfiguration *>() == pc) {
index = i;
break;
}
}
if (index == -1)
return;
QListWidgetItem *lwi = takeItem(i);
lwi->setText(pc->displayName());
int pos = count();
for (int i = 0; i < count(); ++i) {
ProjectConfiguration *p = item(i)->data(Qt::UserRole).value<ProjectConfiguration *>();
if (pc->displayName() < p->displayName()) {
pos = i;
break;
}
}
insertItem(pos, lwi);
m_ignoreIndexChange = false;
}
void MiniTargetWidget::removeBuildConfiguration(ProjectExplorer::BuildConfiguration* bc)
QListWidgetItem *GenericListWidget::itemForProjectConfiguration(ProjectConfiguration *pc)
{
Q_UNUSED(bc);
QTC_ASSERT(m_buildComboBox, return);
m_buildComboBox->setEnabled(m_target->buildConfigurations().count() > 1);
for (int i = 0; i < count(); ++i) {
QListWidgetItem *lwi = item(i);
if (lwi->data(Qt::UserRole).value<ProjectConfiguration *>() == pc) {
return lwi;
}
}
return 0;
}
bool MiniTargetWidget::hasBuildConfiguration() const
QWidget *createTitleLabel(const QString &text)
{
return (m_target->buildConfigurationFactory() != 0);
Utils::StyledBar *bar = new Utils::StyledBar;
bar->setSingleRow(true);
QVBoxLayout *toolLayout = new QVBoxLayout(bar);
toolLayout->setMargin(0);
toolLayout->setSpacing(0);
QLabel *l = new QLabel(text);
l->setIndent(6);
QFont f = l->font();
f.setBold(true);
l->setFont(f);
toolLayout->addWidget(l);
int panelHeight = l->fontMetrics().height() + 12;
bar->ensurePolished(); // Required since manhattanstyle overrides height
bar->setFixedHeight(panelHeight);
return bar;
}
MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorAction, QWidget *parent) :
QWidget(parent), m_projectAction(targetSelectorAction), m_ignoreIndexChange(false)
class OnePixelGreyLine : public QWidget
{
public:
OnePixelGreyLine(QWidget *parent)
: QWidget(parent)
{
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
setMinimumWidth(1);
setMaximumWidth(1);
}
void paintEvent(QPaintEvent *e)
{
Q_UNUSED(e);
QPainter p(this);
p.fillRect(contentsRect(), QColor(160, 160, 160, 255));
}
};
MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorAction, SessionManager *sessionManager, QWidget *parent) :
QWidget(parent), m_projectAction(targetSelectorAction), m_sessionManager(sessionManager),
m_project(0),
m_target(0),
m_buildConfiguration(0),
m_deployConfiguration(0),
m_runConfiguration(0),
m_hideOnRelease(false)
{
QPalette p = palette();
p.setColor(QPalette::Text, QColor(255, 255, 255, 160));
setPalette(p);
setProperty("panelwidget", true);
setContentsMargins(QMargins(0, 1, 1, 8));
setWindowFlags(Qt::Popup);
......@@ -352,294 +479,579 @@ MiniProjectTargetSelector::MiniProjectTargetSelector(QAction *targetSelectorActi
targetSelectorAction->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon));
targetSelectorAction->setProperty("titledAction", true);
QVBoxLayout *layout = new QVBoxLayout(this);