Commit 44ce228a authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Polish the Qt Designer custom widget wizard

Start editing the class name when wizard page
is initialized. Show a disabled widget page
for the dummy widget item (least surprise).
Add +/- buttons. Refuse invalid class names
(by introducing an item model with setData()
validation. Code polishing, explicitness, etc.
Task-number: QTCREATORBUG-1123
parent 65689984
......@@ -39,30 +39,35 @@ ClassDefinition::ClassDefinition(QWidget *parent) :
QTabWidget(parent),
m_domXmlChanged(false)
{
setupUi(this);
iconPathChooser->setExpectedKind(Utils::PathChooser::File);
iconPathChooser->setPromptDialogTitle(tr("Select Icon"));
iconPathChooser->setPromptDialogFilter(tr("Icon files (*.png *.ico *.jpg *.xpm *.tif *.svg)"));
m_ui.setupUi(this);
m_ui.iconPathChooser->setExpectedKind(Utils::PathChooser::File);
m_ui.iconPathChooser->setPromptDialogTitle(tr("Select Icon"));
m_ui.iconPathChooser->setPromptDialogFilter(tr("Icon files (*.png *.ico *.jpg *.xpm *.tif *.svg)"));
}
void ClassDefinition::enableButtons()
{
on_libraryRadio_toggled();
}
void ClassDefinition::on_libraryRadio_toggled()
{
const bool enLib = libraryRadio->isChecked();
widgetLibraryLabel->setEnabled(enLib);
widgetLibraryEdit->setEnabled(enLib);
const bool enLib = m_ui.libraryRadio->isChecked();
m_ui.widgetLibraryLabel->setEnabled(enLib);
m_ui.widgetLibraryEdit->setEnabled(enLib);
const bool enSrc = skeletonCheck->isChecked();
widgetSourceLabel->setEnabled(enSrc);
widgetSourceEdit->setEnabled(enSrc);
widgetBaseClassLabel->setEnabled(enSrc);
widgetBaseClassEdit->setEnabled(enSrc);
const bool enSrc = m_ui.skeletonCheck->isChecked();
m_ui.widgetSourceLabel->setEnabled(enSrc);
m_ui.widgetSourceEdit->setEnabled(enSrc);
m_ui.widgetBaseClassLabel->setEnabled(enSrc);
m_ui.widgetBaseClassEdit->setEnabled(enSrc);
const bool enPrj = !enLib || enSrc;
widgetProjectLabel->setEnabled(enPrj);
widgetProjectEdit->setEnabled(enPrj);
widgetProjectEdit->setText(
QFileInfo(widgetProjectEdit->text()).completeBaseName() +
(libraryRadio->isChecked() ? QLatin1String(".pro") : QLatin1String(".pri")));
m_ui.widgetProjectLabel->setEnabled(enPrj);
m_ui.widgetProjectEdit->setEnabled(enPrj);
m_ui.widgetProjectEdit->setText(
QFileInfo(m_ui.widgetProjectEdit->text()).completeBaseName() +
(m_ui.libraryRadio->isChecked() ? QLatin1String(".pro") : QLatin1String(".pri")));
}
void ClassDefinition::on_skeletonCheck_toggled()
......@@ -86,35 +91,35 @@ static inline QString xmlFromClassName(const QString &name)
void ClassDefinition::setClassName(const QString &name)
{
widgetLibraryEdit->setText(name.toLower());
widgetHeaderEdit->setText(m_fileNamingParameters.headerFileName(name));
pluginClassEdit->setText(name + QLatin1String("Plugin"));
m_ui.widgetLibraryEdit->setText(name.toLower());
m_ui.widgetHeaderEdit->setText(m_fileNamingParameters.headerFileName(name));
m_ui.pluginClassEdit->setText(name + QLatin1String("Plugin"));
if (!m_domXmlChanged) {
domXmlEdit->setText(xmlFromClassName(name));
m_ui.domXmlEdit->setText(xmlFromClassName(name));
m_domXmlChanged = false;
}
}
void ClassDefinition::on_widgetLibraryEdit_textChanged()
{
widgetProjectEdit->setText(
widgetLibraryEdit->text() +
(libraryRadio->isChecked() ? QLatin1String(".pro") : QLatin1String(".pri")));
m_ui.widgetProjectEdit->setText(
m_ui.widgetLibraryEdit->text() +
(m_ui.libraryRadio->isChecked() ? QLatin1String(".pro") : QLatin1String(".pri")));
}
void ClassDefinition::on_widgetHeaderEdit_textChanged()
{
widgetSourceEdit->setText(m_fileNamingParameters.headerToSourceFileName(widgetHeaderEdit->text()));
m_ui.widgetSourceEdit->setText(m_fileNamingParameters.headerToSourceFileName(m_ui.widgetHeaderEdit->text()));
}
void ClassDefinition::on_pluginClassEdit_textChanged()
{
pluginHeaderEdit->setText(m_fileNamingParameters.headerFileName(pluginClassEdit->text()));
m_ui.pluginHeaderEdit->setText(m_fileNamingParameters.headerFileName(m_ui.pluginClassEdit->text()));
}
void ClassDefinition::on_pluginHeaderEdit_textChanged()
{
pluginSourceEdit->setText(m_fileNamingParameters.headerToSourceFileName(pluginHeaderEdit->text()));
m_ui.pluginSourceEdit->setText(m_fileNamingParameters.headerToSourceFileName(m_ui.pluginHeaderEdit->text()));
}
void ClassDefinition::on_domXmlEdit_textChanged()
......@@ -125,26 +130,26 @@ void ClassDefinition::on_domXmlEdit_textChanged()
PluginOptions::WidgetOptions ClassDefinition::widgetOptions(const QString &className) const
{
PluginOptions::WidgetOptions wo;
wo.createSkeleton = skeletonCheck->isChecked();
wo.createSkeleton = m_ui.skeletonCheck->isChecked();
wo.sourceType =
libraryRadio->isChecked() ?
m_ui.libraryRadio->isChecked() ?
PluginOptions::WidgetOptions::LinkLibrary :
PluginOptions::WidgetOptions::IncludeProject;
wo.widgetLibrary = widgetLibraryEdit->text();
wo.widgetProjectFile = widgetProjectEdit->text();
wo.widgetLibrary = m_ui.widgetLibraryEdit->text();
wo.widgetProjectFile = m_ui.widgetProjectEdit->text();
wo.widgetClassName = className;
wo.widgetHeaderFile = widgetHeaderEdit->text();
wo.widgetSourceFile = widgetSourceEdit->text();
wo.widgetBaseClassName = widgetBaseClassEdit->text();
wo.pluginClassName = pluginClassEdit->text();
wo.pluginHeaderFile = pluginHeaderEdit->text();
wo.pluginSourceFile = pluginSourceEdit->text();
wo.iconFile = iconPathChooser->path();
wo.group = groupEdit->text();
wo.toolTip = tooltipEdit->text();
wo.whatsThis = whatsthisEdit->toPlainText();
wo.isContainer = containerCheck->isChecked();
wo.domXml = domXmlEdit->toPlainText();
wo.widgetHeaderFile = m_ui.widgetHeaderEdit->text();
wo.widgetSourceFile = m_ui.widgetSourceEdit->text();
wo.widgetBaseClassName = m_ui.widgetBaseClassEdit->text();
wo.pluginClassName = m_ui.pluginClassEdit->text();
wo.pluginHeaderFile = m_ui.pluginHeaderEdit->text();
wo.pluginSourceFile = m_ui.pluginSourceEdit->text();
wo.iconFile = m_ui.iconPathChooser->path();
wo.group = m_ui.groupEdit->text();
wo.toolTip = m_ui.tooltipEdit->text();
wo.whatsThis = m_ui.whatsthisEdit->toPlainText();
wo.isContainer = m_ui.containerCheck->isChecked();
wo.domXml = m_ui.domXmlEdit->toPlainText();
return wo;
}
......
......@@ -39,12 +39,12 @@
namespace Qt4ProjectManager {
namespace Internal {
class ClassDefinition : public QTabWidget, private Ui::ClassDefinition
class ClassDefinition : public QTabWidget
{
Q_OBJECT
public:
ClassDefinition(QWidget *parent);
explicit ClassDefinition(QWidget *parent = 0);
void setClassName(const QString &name);
FileNamingParameters fileNamingParameters() const { return m_fileNamingParameters; }
......@@ -52,7 +52,9 @@ public:
PluginOptions::WidgetOptions widgetOptions(const QString &className) const;
public Q_SLOTS:
void enableButtons();
private Q_SLOTS:
void on_libraryRadio_toggled();
void on_skeletonCheck_toggled();
void on_widgetLibraryEdit_textChanged();
......@@ -62,6 +64,7 @@ public Q_SLOTS:
void on_domXmlEdit_textChanged();
private:
Ui::ClassDefinition m_ui;
FileNamingParameters m_fileNamingParameters;
bool m_domXmlChanged;
};
......
......@@ -29,59 +29,146 @@
#include "classlist.h"
#include <utils/qtcassert.h>
#include <QtGui/QKeyEvent>
#include <QtGui/QMessageBox>
#include <QtGui/QStandardItemModel>
#include <QtGui/QStandardItem>
#include <QtGui/QLabel>
#include <QtGui/QToolButton>
#include <QtCore/QDebug>
#include <QtCore/QRegExp>
namespace Qt4ProjectManager {
namespace Internal {
// ClassModel: Validates the class name in setData() and
// refuses placeholders and invalid characters.
class ClassModel : public QStandardItemModel {
public:
explicit ClassModel(QObject *parent = 0);
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
void appendPlaceHolder() { appendClass(m_newClassPlaceHolder); }
QModelIndex placeHolderIndex() const;
QString newClassPlaceHolder() const { return m_newClassPlaceHolder; }
private:
void appendClass(const QString &);
const QRegExp m_validator;
const QString m_newClassPlaceHolder;
};
ClassModel::ClassModel(QObject *parent) :
QStandardItemModel(0, 1, parent),
m_validator(QLatin1String("^[a-zA-Z][a-zA-Z0-9_]*$")),
m_newClassPlaceHolder(ClassList::tr("<New class>"))
{
QTC_ASSERT(m_validator.isValid(), return)
appendPlaceHolder();
}
void ClassModel::appendClass(const QString &c)
{
QStandardItem *item = new QStandardItem(c);
item->setFlags(Qt::ItemIsEnabled|Qt::ItemIsEditable);
appendRow(item);
}
bool ClassModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role == Qt::EditRole && !m_validator.exactMatch(value.toString()))
return false;
return QStandardItemModel::setData(index, value, role);
}
QModelIndex ClassModel::placeHolderIndex() const
{
return index(rowCount() - 1, 0);
}
// --------------- ClassList
ClassList::ClassList(QWidget *parent) :
QListWidget(parent)
QListView(parent),
m_model(new ClassModel)
{
setModel(m_model);
connect(itemDelegate(), SIGNAL(closeEditor(QWidget *, QAbstractItemDelegate::EndEditHint)), SLOT(classEdited()));
insertNewItem();
connect(selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
this, SLOT(slotCurrentRowChanged(QModelIndex,QModelIndex)));
}
void ClassList::startEditingNewClassItem()
{
// Start editing the 'new class' item.
setFocus();
const QModelIndex index = m_model->placeHolderIndex();
setCurrentIndex(index);
edit(index);
}
QString ClassList::className(int row) const
{
return m_model->item(row, 0)->text();
}
void ClassList::classEdited()
{
if (currentRow() == count() - 1) {
if (currentItem()->text() != tr("<New class>")) {
emit classAdded(currentItem()->text());
insertNewItem();
const QModelIndex index = currentIndex();
QTC_ASSERT(index.isValid(), return)
const QString name = className(index.row());
if (index == m_model->placeHolderIndex()) {
// Real name class entered.
if (name != m_model->newClassPlaceHolder()) {
emit classAdded(name);
m_model->appendPlaceHolder();
}
} else {
emit classRenamed(currentRow(), currentItem()->text());
emit classRenamed(index.row(), name);
}
}
void ClassList::insertNewItem()
void ClassList::removeCurrentClass()
{
QListWidgetItem *itm = new QListWidgetItem(tr("<New class>"), this);
itm->setFlags(Qt::ItemIsEnabled|Qt::ItemIsEditable);
const QModelIndex index = currentIndex();
if (!index.isValid() || index == m_model->placeHolderIndex())
return;
if (QMessageBox::question(this,
tr("Confirm Delete"),
tr("Delete class %1 from list?").arg(className(index.row())),
QMessageBox::Ok|QMessageBox::Cancel) != QMessageBox::Ok)
return;
// Delete row and set current on same item.
m_model->removeRows(index.row(), 1);
emit classDeleted(index.row());
setCurrentIndex(m_model->indexFromItem(m_model->item(index.row(), 0)));
}
void ClassList::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Delete) {
const int row = currentRow();
if (row != count() - 1) {
if (QMessageBox::question(this,
tr("Confirm Delete"),
tr("Delete class %1 from list?").arg(currentItem()->text()),
QMessageBox::Ok|QMessageBox::Cancel) == QMessageBox::Ok)
{
delete currentItem();
emit classDeleted(row);
setCurrentRow(row);
}
}
} else if (event->key() == Qt::Key_Insert) {
setCurrentRow(count() - 1);
editItem(currentItem());
} else {
QListWidget::keyPressEvent(event);
switch (event->key()) {
case Qt::Key_Delete:
removeCurrentClass();
break;
case Qt::Key_Insert:
startEditingNewClassItem();
break;
default:
QListView::keyPressEvent(event);
break;
}
}
void ClassList::slotCurrentRowChanged(const QModelIndex &current, const QModelIndex &)
{
emit currentRowChanged(current.row());
}
}
} // namespace Internal
} // namespace Qt4ProjectManager
......@@ -30,34 +30,46 @@
#ifndef CLASSLIST_H
#define CLASSLIST_H
#include <QtGui/QListWidget>
#include <QtGui/QListView>
QT_FORWARD_DECLARE_CLASS(QModelIndex)
namespace Qt4ProjectManager {
namespace Internal {
class ClassModel;
class ClassList : public QListWidget
// Class list for new Custom widget classes. Provides
// editable '<new class>' field and Delete/Insert key handling.
class ClassList : public QListView
{
Q_OBJECT
public:
ClassList(QWidget *parent);
explicit ClassList(QWidget *parent = 0);
public slots:
void classEdited();
QString className(int row) const;
signals:
void classAdded(const QString &name);
void classRenamed(int index, const QString &newName);
void classDeleted(int index);
void currentRowChanged(int);
public slots:
void removeCurrentClass();
void startEditingNewClassItem();
private slots:
void classEdited();
void slotCurrentRowChanged(const QModelIndex &,const QModelIndex &);
protected:
void keyPressEvent(QKeyEvent *event);
private:
void insertNewItem();
ClassModel *m_model;
};
}
}
} // namespace Internal
} // namespace Qt4ProjectManager
#endif
......@@ -48,8 +48,8 @@ namespace Ui {
class CustomWidgetPluginWizardPage : public QWizardPage {
Q_OBJECT
public:
CustomWidgetPluginWizardPage(QWidget *parent = 0);
~CustomWidgetPluginWizardPage();
explicit CustomWidgetPluginWizardPage(QWidget *parent = 0);
virtual ~CustomWidgetPluginWizardPage();
void init(const CustomWidgetWidgetsWizardPage *widgetsPage);
......
......@@ -32,9 +32,13 @@
#include "plugingenerator.h"
#include "classdefinition.h"
#include <coreplugin/coreconstants.h>
#include <QtCore/QFileInfo>
#include <QtCore/QTimer>
#include <QtGui/QStackedLayout>
#include <QtGui/QIcon>
namespace Qt4ProjectManager {
namespace Internal {
......@@ -42,13 +46,25 @@ namespace Internal {
CustomWidgetWidgetsWizardPage::CustomWidgetWidgetsWizardPage(QWidget *parent) :
QWizardPage(parent),
m_ui(new Ui::CustomWidgetWidgetsWizardPage),
m_tabStackLayout(new QStackedLayout),
m_complete(false)
{
m_ui->setupUi(this);
m_tabStack = new QStackedLayout(m_ui->tabStackWidget);
m_dummyTab = new QWidget(m_ui->tabStackWidget);
m_tabStack->addWidget(m_dummyTab);
connect(m_ui->classList, SIGNAL(currentRowChanged(int)), m_tabStack, SLOT(setCurrentIndex(int)));
m_ui->tabStackWidget->setLayout(m_tabStackLayout);
m_ui->addButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_PLUS)));
connect(m_ui->addButton, SIGNAL(clicked()), m_ui->classList, SLOT(startEditingNewClassItem()));
m_ui->deleteButton->setIcon(QIcon(QLatin1String(Core::Constants::ICON_MINUS)));
connect(m_ui->deleteButton, SIGNAL(clicked()), m_ui->classList, SLOT(removeCurrentClass()));
m_ui->deleteButton->setEnabled(false);
// Disabled dummy for <new class> column>.
ClassDefinition *dummy = new ClassDefinition;
dummy->setFileNamingParameters(m_fileNamingParameters);
dummy->setEnabled(false);
m_tabStackLayout->addWidget(dummy);
connect(m_ui->classList, SIGNAL(currentRowChanged(int)),
this, SLOT(slotCurrentRowChanged(int)));
}
CustomWidgetWidgetsWizardPage::~CustomWidgetWidgetsWizardPage()
......@@ -61,15 +77,28 @@ bool CustomWidgetWidgetsWizardPage::isComplete() const
return m_complete;
}
void CustomWidgetWidgetsWizardPage::initializePage()
{
// Takes effect only if visible.
QTimer::singleShot(0, m_ui->classList, SLOT(startEditingNewClassItem()));
}
void CustomWidgetWidgetsWizardPage::slotCurrentRowChanged(int row)
{
const bool onDummyItem = row == m_tabStackLayout->count() - 1;
m_ui->deleteButton->setEnabled(!onDummyItem);
m_tabStackLayout->setCurrentIndex(row);
}
void CustomWidgetWidgetsWizardPage::on_classList_classAdded(const QString &name)
{
ClassDefinition *cdef = new ClassDefinition(m_ui->tabStackWidget);
ClassDefinition *cdef = new ClassDefinition;
cdef->setFileNamingParameters(m_fileNamingParameters);
const int index = m_uiClassDefs.count();
m_tabStack->insertWidget(index, cdef);
m_tabStack->setCurrentIndex(index);
m_tabStackLayout->insertWidget(index, cdef);
m_tabStackLayout->setCurrentIndex(index);
m_uiClassDefs.append(cdef);
cdef->on_libraryRadio_toggled();
cdef->enableButtons();
on_classList_classRenamed(index, name);
// First class or collection class, re-check.
slotCheckCompleteness();
......@@ -77,7 +106,7 @@ void CustomWidgetWidgetsWizardPage::on_classList_classAdded(const QString &name)
void CustomWidgetWidgetsWizardPage::on_classList_classDeleted(int index)
{
delete m_tabStack->widget(index);
delete m_tabStackLayout->widget(index);
m_uiClassDefs.removeAt(index);
if (m_uiClassDefs.empty())
slotCheckCompleteness();
......@@ -90,7 +119,7 @@ void CustomWidgetWidgetsWizardPage::on_classList_classRenamed(int index, const Q
QString CustomWidgetWidgetsWizardPage::classNameAt(int i) const
{
return m_ui->classList->item(i)->text();
return m_ui->classList->className(i);
}
QList<PluginOptions::WidgetOptions> CustomWidgetWidgetsWizardPage::widgetOptions() const
......
......@@ -68,19 +68,21 @@ public:
int classCount() const { return m_uiClassDefs.size(); }
QString classNameAt(int i) const;
virtual void initializePage();
private Q_SLOTS:
void on_classList_classAdded(const QString &name);
void on_classList_classDeleted(int index);
void on_classList_classRenamed(int index, const QString &newName);
void slotCheckCompleteness();
void slotCurrentRowChanged(int);
private:
void updatePluginTab();
Ui::CustomWidgetWidgetsWizardPage *m_ui;
QList<ClassDefinition *> m_uiClassDefs;
QStackedLayout *m_tabStack;
QWidget *m_dummyTab;
QStackedLayout *m_tabStackLayout;
FileNamingParameters m_fileNamingParameters;
bool m_complete;
};
......
......@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>668</width>
<height>454</height>
<height>475</height>
</rect>
</property>
<property name="windowTitle">
......@@ -17,16 +17,6 @@
<string>Custom Widget List</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Widget &amp;Classes:</string>
</property>
<property name="buddy">
<cstring>classList</cstring>
</property>
</widget>
</item>
<item row="2" column="1" rowspan="2">
<widget class="QWidget" name="tabStackWidget" native="true">
<property name="minimumSize">
......@@ -70,6 +60,34 @@
</property>
</spacer>
</item>
<item row="2" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Widget &amp;Classes:</string>
</property>
<property name="buddy">
<cstring>classList</cstring>
</property>
</widget>